pax_global_header00006660000000000000000000000064122741466670014531gustar00rootroot0000000000000052 comment=93ee3022dc3fdb6e199a57c14b6cc008fe29d921 wpa-2.1/000077500000000000000000000000001227414666700121625ustar00rootroot00000000000000wpa-2.1/COPYING000066400000000000000000000017051227414666700132200ustar00rootroot00000000000000wpa_supplicant and hostapd -------------------------- Copyright (c) 2002-2012, Jouni Malinen and contributors All Rights Reserved. See the README file for the current license terms. This software was previously distributed under BSD/GPL v2 dual license terms that allowed either of those license alternatives to be selected. As of February 11, 2012, the project has chosen to use only the BSD license option for future distribution. As such, the GPL v2 license option is no longer used. It should be noted that the BSD license option (the one with advertisement clause removed) is compatible with GPL and as such, does not prevent use of this software in projects that use GPL. Some of the files may still include pointers to GPL version 2 license terms. However, such copyright and license notifications are maintained only for attribution purposes and any distribution of this software after February 11, 2012 is no longer under the GPL v2 option. wpa-2.1/README000066400000000000000000000044571227414666700130540ustar00rootroot00000000000000wpa_supplicant and hostapd -------------------------- Copyright (c) 2002-2014, Jouni Malinen and contributors All Rights Reserved. These programs are 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. This package may include either wpa_supplicant, hostapd, or both. See README file respective subdirectories (wpa_supplicant/README or hostapd/README) for more details. Source code files were moved around in v0.6.x releases and compared to earlier releases, the programs are now built by first going to a subdirectory (wpa_supplicant or hostapd) and creating build configuration (.config) and running 'make' there (for Linux/BSD/cygwin builds). 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. wpa-2.1/hostapd/000077500000000000000000000000001227414666700136245ustar00rootroot00000000000000wpa-2.1/hostapd/Android.mk000066400000000000000000000446431227414666700155500ustar00rootroot00000000000000# 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\" ifeq ($(BOARD_WLAN_DEVICE), bcmdhd) L_CFLAGS += -DANDROID_P2P endif ifeq ($(BOARD_WLAN_DEVICE), qcwcn) L_CFLAGS += -DANDROID_P2P endif ifeq ($(BOARD_WLAN_DEVICE), mrvl) L_CFLAGS += -DANDROID_P2P 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\" # 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 += external/openssl/include INCLUDES += system/security/keystore/include ifdef CONFIG_DRIVER_NL80211 INCLUDES += external/libnl-headers 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_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 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/common/ieee802_11_common.c OBJS += src/common/wpa_common.c OBJS += src/eapol_auth/eapol_auth_sm.c ifndef CONFIG_NO_DUMP_STATE # define HOSTAPD_DUMP_STATE to include support for dumping internal state # through control interface commands (undefine it, if you want to save in # binary size) L_CFLAGS += -DHOSTAPD_DUMP_STATE OBJS += src/eapol_auth/eapol_auth_dump.c endif ifdef CONFIG_NO_RADIUS L_CFLAGS += -DCONFIG_NO_RADIUS CONFIG_NO_ACCOUNTING=y else OBJS += src/radius/radius.c OBJS += src/radius/radius_client.c OBJS += src/radius/radius_das.c endif ifdef CONFIG_NO_ACCOUNTING L_CFLAGS += -DCONFIG_NO_ACCOUNTING else OBJS += src/ap/accounting.c endif ifdef CONFIG_NO_VLAN L_CFLAGS += -DCONFIG_NO_VLAN else OBJS += src/ap/vlan_init.c ifdef CONFIG_VLAN_NETLINK ifdef CONFIG_FULL_DYNAMIC_VLAN OBJS += src/ap/vlan_util.c endif L_CFLAGS += -DCONFIG_VLAN_NETLINK endif endif ifdef CONFIG_NO_CTRL_IFACE L_CFLAGS += -DCONFIG_NO_CTRL_IFACE else OBJS += ctrl_iface.c OBJS += src/ap/ctrl_iface_ap.c endif OBJS += src/crypto/md5.c L_CFLAGS += -DCONFIG_CTRL_IFACE -DCONFIG_CTRL_IFACE_UNIX ifdef CONFIG_IAPP L_CFLAGS += -DCONFIG_IAPP OBJS += src/ap/iapp.c endif ifdef CONFIG_RSN_PREAUTH L_CFLAGS += -DCONFIG_RSN_PREAUTH CONFIG_L2_PACKET=y endif ifdef CONFIG_PEERKEY L_CFLAGS += -DCONFIG_PEERKEY OBJS += src/ap/peerkey_auth.c endif ifdef CONFIG_IEEE80211W L_CFLAGS += -DCONFIG_IEEE80211W NEED_SHA256=y NEED_AES_OMAC1=y endif ifdef CONFIG_IEEE80211R L_CFLAGS += -DCONFIG_IEEE80211R OBJS += src/ap/wpa_auth_ft.c NEED_SHA256=y NEED_AES_OMAC1=y NEED_AES_UNWRAP=y endif ifdef CONFIG_SAE L_CFLAGS += -DCONFIG_SAE OBJS += src/common/sae.c NEED_ECC=y NEED_DH_GROUPS=y endif ifdef CONFIG_WNM L_CFLAGS += -DCONFIG_WNM OBJS += src/ap/wnm_ap.c endif ifdef CONFIG_IEEE80211N L_CFLAGS += -DCONFIG_IEEE80211N endif ifdef CONFIG_IEEE80211AC L_CFLAGS += -DCONFIG_IEEE80211AC 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_SHA256=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 implementating 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_OMAC1=y 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 NEED_SHA256=y NEED_AES_OMAC1=y 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_SHA256=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_WPS ifdef CONFIG_WPS2 L_CFLAGS += -DCONFIG_WPS2 endif 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_SHA256=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_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 MS_FUNCS OBJS += src/crypto/ms_funcs.c NEED_DES=y NEED_MD4=y endif ifdef CHAP OBJS += src/eap_common/chap.c endif ifdef TLS_FUNCS NEED_DES=y # Shared TLS functions (needed for EAP_TLS, EAP_PEAP, and EAP_TTLS) L_CFLAGS += -DEAP_TLS_FUNCS OBJS += src/eap_server/eap_server_tls_common.c NEED_TLS_PRF=y endif ifndef CONFIG_TLS CONFIG_TLS=openssl endif ifdef CONFIG_TLSV11 L_CFLAGS += -DCONFIG_TLSV11 endif ifdef CONFIG_TLSV12 L_CFLAGS += -DCONFIG_TLSV12 NEED_SHA256=y endif ifeq ($(CONFIG_TLS), openssl) ifdef TLS_FUNCS OBJS += src/crypto/tls_openssl.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 LIBS += -lcrypto LIBS_h += -lcrypto endif ifeq ($(CONFIG_TLS), gnutls) ifdef TLS_FUNCS OBJS += src/crypto/tls_gnutls.c LIBS += -lgnutls -lgpg-error endif OBJS += src/crypto/crypto_gnutls.c HOBJS += src/crypto/crypto_gnutls.c ifdef NEED_FIPS186_2_PRF OBJS += src/crypto/fips_prf_gnutls.c endif LIBS += -lgcrypt LIBS_h += -lgcrypt CONFIG_INTERNAL_SHA256=y CONFIG_INTERNAL_RC4=y CONFIG_INTERNAL_DH_GROUP5=y endif ifeq ($(CONFIG_TLS), schannel) ifdef TLS_FUNCS OBJS += src/crypto/tls_schannel.c endif OBJS += src/crypto/crypto_cryptoapi.c OBJS_p += src/crypto/crypto_cryptoapi.c CONFIG_INTERNAL_SHA256=y CONFIG_INTERNAL_RC4=y CONFIG_INTERNAL_DH_GROUP5=y endif ifeq ($(CONFIG_TLS), nss) ifdef TLS_FUNCS OBJS += src/crypto/tls_nss.c LIBS += -lssl3 endif OBJS += src/crypto/crypto_nss.c ifdef NEED_FIPS186_2_PRF OBJS += src/crypto/fips_prf_nss.c endif LIBS += -lnss3 LIBS_h += -lnss3 CONFIG_INTERNAL_MD4=y CONFIG_INTERNAL_DH_GROUP5=y 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/asn1.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_SHA256=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_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 AESOBJS += src/crypto/aes-wrap.c ifdef NEED_AES_EAX AESOBJS += src/crypto/aes-eax.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 ifdef NEED_AES_OMAC1 AESOBJS += src/crypto/aes-omac1.c endif ifdef NEED_AES_UNWRAP NEED_AES_DEC=y AESOBJS += src/crypto/aes-unwrap.c endif ifdef NEED_AES_CBC NEED_AES_DEC=y AESOBJS += src/crypto/aes-cbc.c 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) SHA1OBJS += src/crypto/sha1.c 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 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 NEED_RC4 ifdef CONFIG_INTERNAL_RC4 OBJS += src/crypto/rc4.c endif endif ifdef NEED_SHA256 L_CFLAGS += -DCONFIG_SHA256 ifneq ($(CONFIG_TLS), openssl) OBJS += src/crypto/sha256.c 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 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) HOBJS += src/crypto/md5.c 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 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 endif ifdef NEED_BASE64 OBJS += src/utils/base64.c endif ifdef NEED_AP_MLME OBJS += src/ap/wmm.c OBJS += src/ap/ap_list.c OBJS += src/ap/ieee802_11.c OBJS += src/ap/hw_features.c OBJS += src/ap/dfs.c L_CFLAGS += -DNEED_AP_MLME endif ifdef CONFIG_IEEE80211N OBJS += src/ap/ieee802_11_ht.c endif ifdef CONFIG_IEEE80211AC OBJS += src/ap/ieee802_11_vht.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 OBJS += src/common/gas.c OBJS += src/ap/gas_serv.c 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_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 src/common/wpa_ctrl.c src/utils/os_$(CONFIG_OS).c OBJS_c += src/utils/eloop.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_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 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 LOCAL_STATIC_LIBRARIES += libnl_2 endif LOCAL_CFLAGS := $(L_CFLAGS) LOCAL_SRC_FILES := $(OBJS) LOCAL_C_INCLUDES := $(INCLUDES) include $(BUILD_EXECUTABLE) endif # ifeq ($(WPA_BUILD_HOSTAPD),true) wpa-2.1/hostapd/ChangeLog000066400000000000000000001316051227414666700154040ustar00rootroot00000000000000ChangeLog for hostapd 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 obsolte 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. wpa-2.1/hostapd/Makefile000066400000000000000000000473571227414666700153040ustar00rootroot00000000000000ifndef CC CC=gcc endif ifndef CFLAGS CFLAGS = -MMD -O2 -Wall -g endif CFLAGS += -I$(abspath ../src) CFLAGS += -I$(abspath ../src/utils) # Uncomment following line and set the path to your kernel tree include # directory if your C library does not include all header files. # CFLAGS += -DUSE_KERNEL_HEADERS -I/usr/src/linux/include -include .config 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_c = hostapd_cli.o ../src/common/wpa_ctrl.o ../src/utils/os_$(CONFIG_OS).o NEED_RC4=y NEED_AES=y NEED_MD5=y NEED_SHA1=y OBJS += ../src/drivers/drivers.o CFLAGS += -DHOSTAPD 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 += -DWPA_TRACE_BFD LIBS += -lbfd LIBS_c += -lbfd LIBS_h += -lbfd 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 endif OBJS += ../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/common/ieee802_11_common.o OBJS += ../src/common/wpa_common.o OBJS += ../src/eapol_auth/eapol_auth_sm.o ifdef CONFIG_CODE_COVERAGE CFLAGS += -O0 -fprofile-arcs -ftest-coverage LIBS += -lgcov LIBS_c += -lgcov LIBS_h += -lgcov LIBS_n += -lgcov endif ifndef CONFIG_NO_DUMP_STATE # define HOSTAPD_DUMP_STATE to include support for dumping internal state # through control interface commands (undefine it, if you want to save in # binary size) CFLAGS += -DHOSTAPD_DUMP_STATE OBJS += ../src/eapol_auth/eapol_auth_dump.o endif ifdef CONFIG_NO_RADIUS CFLAGS += -DCONFIG_NO_RADIUS CONFIG_NO_ACCOUNTING=y else OBJS += ../src/radius/radius.o OBJS += ../src/radius/radius_client.o OBJS += ../src/radius/radius_das.o endif ifdef CONFIG_NO_ACCOUNTING CFLAGS += -DCONFIG_NO_ACCOUNTING else OBJS += ../src/ap/accounting.o endif ifdef CONFIG_NO_VLAN CFLAGS += -DCONFIG_NO_VLAN else OBJS += ../src/ap/vlan_init.o ifdef CONFIG_VLAN_NETLINK ifdef CONFIG_FULL_DYNAMIC_VLAN OBJS += ../src/ap/vlan_util.o endif CFLAGS += -DCONFIG_VLAN_NETLINK endif endif ifdef CONFIG_NO_CTRL_IFACE CFLAGS += -DCONFIG_NO_CTRL_IFACE else OBJS += ctrl_iface.o OBJS += ../src/ap/ctrl_iface_ap.o endif OBJS += ../src/crypto/md5.o CFLAGS += -DCONFIG_CTRL_IFACE -DCONFIG_CTRL_IFACE_UNIX ifdef CONFIG_IAPP CFLAGS += -DCONFIG_IAPP OBJS += ../src/ap/iapp.o endif ifdef CONFIG_RSN_PREAUTH CFLAGS += -DCONFIG_RSN_PREAUTH CONFIG_L2_PACKET=y endif ifdef CONFIG_PEERKEY CFLAGS += -DCONFIG_PEERKEY OBJS += ../src/ap/peerkey_auth.o endif ifdef CONFIG_IEEE80211W CFLAGS += -DCONFIG_IEEE80211W NEED_SHA256=y NEED_AES_OMAC1=y endif ifdef CONFIG_IEEE80211R CFLAGS += -DCONFIG_IEEE80211R OBJS += ../src/ap/wpa_auth_ft.o NEED_SHA256=y NEED_AES_OMAC1=y NEED_AES_UNWRAP=y endif ifdef CONFIG_SAE CFLAGS += -DCONFIG_SAE OBJS += ../src/common/sae.o NEED_ECC=y NEED_DH_GROUPS=y endif ifdef CONFIG_WNM CFLAGS += -DCONFIG_WNM OBJS += ../src/ap/wnm_ap.o endif ifdef CONFIG_IEEE80211N CFLAGS += -DCONFIG_IEEE80211N endif ifdef CONFIG_IEEE80211AC CFLAGS += -DCONFIG_IEEE80211AC 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_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_SHA256=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 implementating 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_OMAC1=y 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 NEED_SHA256=y NEED_AES_OMAC1=y 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_SHA256=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_WPS ifdef CONFIG_WPS2 CFLAGS += -DCONFIG_WPS2 endif 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_SHA256=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_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 # 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 MS_FUNCS OBJS += ../src/crypto/ms_funcs.o NEED_DES=y NEED_MD4=y endif ifdef CHAP OBJS += ../src/eap_common/chap.o endif ifdef TLS_FUNCS NEED_DES=y # Shared TLS functions (needed for EAP_TLS, EAP_PEAP, and EAP_TTLS) CFLAGS += -DEAP_TLS_FUNCS OBJS += ../src/eap_server/eap_server_tls_common.o NEED_TLS_PRF=y endif ifndef CONFIG_TLS CONFIG_TLS=openssl endif ifdef CONFIG_TLSV11 CFLAGS += -DCONFIG_TLSV11 endif ifdef CONFIG_TLSV12 CFLAGS += -DCONFIG_TLSV12 NEED_SHA256=y endif ifeq ($(CONFIG_TLS), openssl) ifdef TLS_FUNCS OBJS += ../src/crypto/tls_openssl.o LIBS += -lssl endif OBJS += ../src/crypto/crypto_openssl.o HOBJS += ../src/crypto/crypto_openssl.o ifdef NEED_FIPS186_2_PRF OBJS += ../src/crypto/fips_prf_openssl.o endif LIBS += -lcrypto LIBS_h += -lcrypto endif ifeq ($(CONFIG_TLS), gnutls) ifdef TLS_FUNCS OBJS += ../src/crypto/tls_gnutls.o LIBS += -lgnutls -lgpg-error endif OBJS += ../src/crypto/crypto_gnutls.o HOBJS += ../src/crypto/crypto_gnutls.o ifdef NEED_FIPS186_2_PRF OBJS += ../src/crypto/fips_prf_gnutls.o endif LIBS += -lgcrypt LIBS_h += -lgcrypt CONFIG_INTERNAL_SHA256=y CONFIG_INTERNAL_RC4=y CONFIG_INTERNAL_DH_GROUP5=y endif ifeq ($(CONFIG_TLS), schannel) ifdef TLS_FUNCS OBJS += ../src/crypto/tls_schannel.o endif OBJS += ../src/crypto/crypto_cryptoapi.o OBJS_p += ../src/crypto/crypto_cryptoapi.o CONFIG_INTERNAL_SHA256=y CONFIG_INTERNAL_RC4=y CONFIG_INTERNAL_DH_GROUP5=y endif ifeq ($(CONFIG_TLS), nss) ifdef TLS_FUNCS OBJS += ../src/crypto/tls_nss.o LIBS += -lssl3 endif OBJS += ../src/crypto/crypto_nss.o ifdef NEED_FIPS186_2_PRF OBJS += ../src/crypto/fips_prf_nss.o endif LIBS += -lnss3 LIBS_h += -lnss3 CONFIG_INTERNAL_MD4=y CONFIG_INTERNAL_DH_GROUP5=y 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/asn1.o OBJS += ../src/tls/rsa.o OBJS += ../src/tls/x509v3.o OBJS += ../src/tls/pkcs1.o OBJS += ../src/tls/pkcs5.o OBJS += ../src/tls/pkcs8.o NEED_SHA256=y NEED_BASE64=y NEED_TLS_PRF=y ifdef CONFIG_TLSV12 NEED_TLS_PRF_SHA256=y endif NEED_MODEXP=y NEED_CIPHER=y CFLAGS += -DCONFIG_TLS_INTERNAL CFLAGS += -DCONFIG_TLS_INTERNAL_SERVER endif ifdef NEED_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_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), 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 AESOBJS += ../src/crypto/aes-wrap.o ifdef NEED_AES_EAX AESOBJS += ../src/crypto/aes-eax.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 ifdef NEED_AES_OMAC1 AESOBJS += ../src/crypto/aes-omac1.o endif ifdef NEED_AES_UNWRAP NEED_AES_DEC=y AESOBJS += ../src/crypto/aes-unwrap.o endif ifdef NEED_AES_CBC NEED_AES_DEC=y AESOBJS += ../src/crypto/aes-cbc.o 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) SHA1OBJS += ../src/crypto/sha1.o 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) SHA1OBJS += ../src/crypto/sha1-pbkdf2.o 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 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 ifdef CONFIG_INTERNAL_DES OBJS += ../src/crypto/des-internal.o endif endif ifdef NEED_RC4 ifdef CONFIG_INTERNAL_RC4 OBJS += ../src/crypto/rc4.o endif endif ifdef NEED_SHA256 CFLAGS += -DCONFIG_SHA256 ifneq ($(CONFIG_TLS), openssl) OBJS += ../src/crypto/sha256.o 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 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 OBJS += ../src/crypto/random.o HOBJS += ../src/crypto/random.o HOBJS += ../src/utils/eloop.o HOBJS += $(SHA1OBJS) HOBJS += ../src/crypto/md5.o 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 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 endif ifdef NEED_BASE64 OBJS += ../src/utils/base64.o endif ifdef NEED_AP_MLME OBJS += ../src/ap/wmm.o OBJS += ../src/ap/ap_list.o OBJS += ../src/ap/ieee802_11.o OBJS += ../src/ap/hw_features.o OBJS += ../src/ap/dfs.o CFLAGS += -DNEED_AP_MLME endif ifdef CONFIG_IEEE80211N OBJS += ../src/ap/ieee802_11_ht.o endif 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 OBJS += ../src/common/gas.o OBJS += ../src/ap/gas_serv.o 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_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 ALL=hostapd hostapd_cli all: verify_config $(ALL) Q=@ E=echo ifeq ($(V), 1) Q= E=true endif ifdef CONFIG_CODE_COVERAGE %.o: %.c @$(E) " CC " $< $(Q)cd $(dir $@); $(CC) -c -o $(notdir $@) $(CFLAGS) $(notdir $<) else %.o: %.c $(Q)$(CC) -c -o $@ $(CFLAGS) $< @$(E) " CC " $< endif verify_config: @if [ ! -r .config ]; then \ echo 'Building hostapd requires a configuration file'; \ echo '(.config). See README for more instructions. You can'; \ echo 'run "cp defconfig .config" to create an example'; \ echo 'configuration.'; \ exit 1; \ fi install: all mkdir -p $(DESTDIR)/usr/local/bin for i in $(ALL); do cp -f $$i $(DESTDIR)/usr/local/bin/$$i; done ../src/drivers/build.hostapd: @if [ -f ../src/drivers/build.wpa_supplicant ]; then \ $(MAKE) -C ../src/drivers clean; \ fi @touch ../src/drivers/build.hostapd BCHECK=../src/drivers/build.hostapd hostapd: $(BCHECK) $(OBJS) $(Q)$(CC) $(LDFLAGS) -o hostapd $(OBJS) $(LIBS) @$(E) " LD " $@ ifdef CONFIG_WPA_TRACE OBJS_c += ../src/utils/trace.o endif 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) ../src/crypto/md5.o ifdef NEED_RC4 ifdef CONFIG_INTERNAL_RC4 NOBJS += ../src/crypto/rc4.o endif endif ifdef CONFIG_INTERNAL_MD5 NOBJS += ../src/crypto/md5-internal.o endif NOBJS += ../src/crypto/crypto_openssl.o ../src/utils/os_$(CONFIG_OS).o NOBJS += ../src/utils/wpa_debug.o NOBJS += ../src/utils/wpabuf.o ifdef CONFIG_WPA_TRACE NOBJS += ../src/utils/trace.o LIBS_n += -lbfd endif ifdef TLS_FUNCS LIBS_n += -lcrypto endif HOBJS += hlr_auc_gw.o ../src/utils/common.o ../src/utils/wpa_debug.o ../src/utils/os_$(CONFIG_OS).o ../src/utils/wpabuf.o ../src/crypto/milenage.o HOBJS += ../src/crypto/aes-encblock.o ifdef CONFIG_INTERNAL_AES HOBJS += ../src/crypto/aes-internal.o HOBJS += ../src/crypto/aes-internal-enc.o endif 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 " $@ lcov-html: lcov -c -d .. > lcov.info genhtml lcov.info --output-directory lcov-html clean: $(MAKE) -C ../src clean rm -f core *~ *.o hostapd hostapd_cli nt_password_hash hlr_auc_gw rm -f *.d *.gcno *.gcda *.gcov rm -f lcov.info rm -rf lcov-html -include $(OBJS:%.o=%.d) wpa-2.1/hostapd/README000066400000000000000000000411621227414666700145100ustar00rootroot00000000000000hostapd - user space IEEE 802.11 AP and IEEE 802.1X/WPA/WPA2/EAP Authenticator and RADIUS authentication server ================================================================ Copyright (c) 2002-2014, 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://hostap.epitest.fi/) Please note that station firmware version needs to be 1.7.0 or newer to work in WPA mode. madwifi driver for cards based on Atheros chip set (ar521x) (http://sourceforge.net/projects/madwifi/) Please note that you will need to add the correct path for madwifi driver root directory in .config (see defconfig file for an example: CFLAGS += -I) 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 (with some kernel mods that have not yet been committed when hostapd v0.3.0 was released) 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 and Supplicant ------------------------------------ Any RADIUS server supporting EAP should be usable as an IEEE 802.1X Authentication Server with hostapd Authenticator. FreeRADIUS (http://www.freeradius.org/) has been successfully tested with hostapd Authenticator and both Xsupplicant (http://www.open1x.org) and Windows XP Supplicants. EAP/TLS was used with Xsupplicant and EAP/MD5-Challenge with Windows XP. http://www.missl.cs.umd.edu/wireless/eaptls/ has useful information about using EAP/TLS with FreeRADIUS and Xsupplicant (just replace Cisco access point with Host AP driver, hostapd daemon, and a Prism2 card ;-). http://www.freeradius.org/doc/EAP-MD5.html has information about using EAP/MD5 with FreeRADIUS, including instructions for WinXP configuration. http://www.denobula.com/EAPTLS.pdf has a HOWTO on EAP/TLS use with WinXP Supplicant. 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 is likely to be 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. Wi-Fi provides information about WPA at its web site (http://www.wi-fi.org/OpenSection/protected_access.asp). 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 wpa-2.1/hostapd/README-WPS000066400000000000000000000341111227414666700151530ustar00rootroot00000000000000hostapd 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 madwifi driver interface: CONFIG_DRIVER_MADWIFI=y CFLAGS += -I/usr/src/madwifi-0.9.3 CONFIG_WPS=y CONFIG_WPS2=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=madwifi interface=ath0 # 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. wpa-2.1/hostapd/android.config000066400000000000000000000127741227414666700164460ustar00rootroot00000000000000# 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 madwifi driver #CONFIG_DRIVER_MADWIFI=y #CFLAGS += -I../../madwifi # change to the madwifi source directory # 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 # 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 # IEEE 802.11F/IAPP #CONFIG_IAPP=y # WPA2/IEEE 802.11i RSN pre-authentication #CONFIG_RSN_PREAUTH=y # PeerKey handshake for Station to Station Link (IEEE 802.11e DLS) #CONFIG_PEERKEY=y # IEEE 802.11w (management frame protection) # This version is an experimental implementation based on IEEE 802.11w/D1.0 # draft and is subject to change since the standard has not yet been finalized. # Driver support is also needed for IEEE 802.11w. CONFIG_IEEE80211W=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 WSC 2.0 support CONFIG_WPS2=y # Enable UPnP support for external WPS Registrars #CONFIG_WPS_UPNP=y # EAP-IKEv2 #CONFIG_EAP_IKEV2=y # Trusted Network Connect (EAP-TNC) #CONFIG_EAP_TNC=y # PKCS#12 (PFX) support (used to read private key and certificate file from # a file that usually has extension .p12 or .pfx) CONFIG_PKCS12=y # RADIUS authentication server. This provides access to the integrated EAP # server from external hosts using RADIUS. #CONFIG_RADIUS_SERVER=y # Build IPv6 support for RADIUS operations CONFIG_IPV6=y # IEEE Std 802.11r-2008 (Fast BSS Transition) #CONFIG_IEEE80211R=y # Use the hostapd's IEEE 802.11 authentication (ACL), but without # the IEEE 802.11 Management capability (e.g., madwifi or FreeBSD/net80211) #CONFIG_DRIVER_RADIUS_ACL=y # IEEE 802.11n (High Throughput) support CONFIG_IEEE80211N=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 # Enable AP CONFIG_AP=y wpa-2.1/hostapd/config_file.c000066400000000000000000002423641227414666700162470ustar00rootroot00000000000000/* * hostapd / Configuration file parser * Copyright (c) 2003-2013, 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 "common/ieee802_11_defs.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; 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++; *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; } 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; os_strlcpy(vlan->ifname, pos, sizeof(vlan->ifname)); vlan->next = bss->vlan; bss->vlan = vlan; } fclose(f); return 0; } #endif /* CONFIG_NO_VLAN */ static int hostapd_acl_comp(const void *a, const void *b) { const struct mac_acl_entry *aa = a; const struct mac_acl_entry *bb = b; return os_memcmp(aa->addr, bb->addr, sizeof(macaddr)); } 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]; struct mac_acl_entry *newacl; int vlan_id; if (!fname) return 0; f = fopen(fname, "r"); if (!f) { wpa_printf(MSG_ERROR, "MAC list file '%s' not found.", fname); 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 (hwaddr_aton(buf, addr)) { wpa_printf(MSG_ERROR, "Invalid MAC address '%s' at " "line %d in '%s'", buf, line, fname); fclose(f); return -1; } vlan_id = 0; pos = buf; while (*pos != '\0' && *pos != ' ' && *pos != '\t') pos++; while (*pos == ' ' || *pos == '\t') pos++; if (*pos != '\0') vlan_id = atoi(pos); newacl = os_realloc_array(*acl, *num + 1, sizeof(**acl)); if (newacl == NULL) { wpa_printf(MSG_ERROR, "MAC list reallocation failed"); fclose(f); return -1; } *acl = newacl; os_memcpy((*acl)[*num].addr, addr, ETH_ALEN); (*acl)[*num].vlan_id = vlan_id; (*num)++; } fclose(f); qsort(*acl, *num, sizeof(**acl), hostapd_acl_comp); return 0; } #ifdef EAP_SERVER 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, *tail = NULL; if (!fname) return 0; if (os_strncmp(fname, "sqlite:", 7) == 0) { os_free(conf->eap_user_sqlite); conf->eap_user_sqlite = os_strdup(fname + 7); return 0; } 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; 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_malloc(pos - start); if (user->identity == NULL) { wpa_printf(MSG_ERROR, "Failed to allocate " "memory for EAP identity"); goto failed; } os_memcpy(user->identity, start, pos - start); user->identity_len = pos - start; if (pos[0] == '"' && pos[1] == '*') { 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; } 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) { 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_malloc(pos - start); if (user->password == NULL) { wpa_printf(MSG_ERROR, "Failed to allocate " "memory for EAP password"); goto failed; } os_memcpy(user->password, start, pos - start); user->password_len = pos - start; pos++; } 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 { 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 = conf->eap_user = user; } else { tail->next = user; tail = user; } continue; failed: if (user) { os_free(user->password); os_free(user->identity); os_free(user); } ret = -1; break; } fclose(f); 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 struct hostapd_radius_attr * hostapd_parse_radius_attr(const char *value) { const char *pos; char syntax; struct hostapd_radius_attr *attr; size_t len; attr = os_zalloc(sizeof(*attr)); if (attr == NULL) return NULL; attr->type = atoi(value); pos = os_strchr(value, ':'); if (pos == NULL) { attr->val = wpabuf_alloc(1); if (attr->val == NULL) { os_free(attr); return NULL; } wpabuf_put_u8(attr->val, 0); return attr; } pos++; if (pos[0] == '\0' || pos[1] != ':') { os_free(attr); return NULL; } syntax = *pos++; pos++; switch (syntax) { case 's': attr->val = wpabuf_alloc_copy(pos, os_strlen(pos)); break; case 'x': len = os_strlen(pos); if (len & 1) break; len /= 2; attr->val = wpabuf_alloc(len); if (attr->val == NULL) break; if (hexstr2bin(pos, wpabuf_put(attr->val, len), len) < 0) { wpabuf_free(attr->val); os_free(attr); return NULL; } break; case 'd': attr->val = wpabuf_alloc(4); if (attr->val) wpabuf_put_be32(attr->val, atoi(pos)); break; default: os_free(attr); return NULL; } if (attr->val == NULL) { os_free(attr); return NULL; } return attr; } static int hostapd_parse_das_client(struct hostapd_bss_config *bss, const char *val) { char *secret; secret = os_strchr(val, ' '); if (secret == NULL) return -1; secret++; 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 else if (os_strcmp(start, "FT-PSK") == 0) val |= WPA_KEY_MGMT_FT_PSK; else if (os_strcmp(start, "FT-EAP") == 0) val |= WPA_KEY_MGMT_FT_IEEE8021X; #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_IEEE80211W else if (os_strcmp(start, "WPA-PSK-SHA256") == 0) val |= WPA_KEY_MGMT_PSK_SHA256; else if (os_strcmp(start, "WPA-EAP-SHA256") == 0) val |= WPA_KEY_MGMT_IEEE8021X_SHA256; #endif /* CONFIG_IEEE80211W */ #ifdef CONFIG_SAE else if (os_strcmp(start, "SAE") == 0) val |= WPA_KEY_MGMT_SAE; else if (os_strcmp(start, "FT-SAE") == 0) val |= WPA_KEY_MGMT_FT_SAE; #endif /* CONFIG_SAE */ 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; } 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 || wep->key[keyidx] != NULL) return -1; if (val[0] == '"') { if (len < 2 || val[len - 1] != '"') return -1; len -= 2; wep->key[keyidx] = os_malloc(len); if (wep->key[keyidx] == NULL) return -1; os_memcpy(wep->key[keyidx], val + 1, len); 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; } 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; } /* convert floats with one decimal place to value*10 int, i.e., * "1.5" will return 15 */ static int hostapd_config_read_int10(const char *value) { int i, d; char *pos; i = atoi(value); pos = os_strchr(value, '.'); d = 0; if (pos) { pos++; if (*pos >= '0' && *pos <= '9') d = *pos - '0'; } return i * 10 + d; } static int valid_cw(int cw) { return (cw == 1 || cw == 3 || cw == 7 || cw == 15 || cw == 31 || cw == 63 || cw == 127 || cw == 255 || cw == 511 || cw == 1023); } enum { IEEE80211_TX_QUEUE_DATA0 = 0, /* used for EDCA AC_VO data */ IEEE80211_TX_QUEUE_DATA1 = 1, /* used for EDCA AC_VI data */ IEEE80211_TX_QUEUE_DATA2 = 2, /* used for EDCA AC_BE data */ IEEE80211_TX_QUEUE_DATA3 = 3 /* used for EDCA AC_BK data */ }; static int hostapd_config_tx_queue(struct hostapd_config *conf, char *name, char *val) { int num; char *pos; struct hostapd_tx_queue_params *queue; /* skip 'tx_queue_' prefix */ pos = name + 9; if (os_strncmp(pos, "data", 4) == 0 && pos[4] >= '0' && pos[4] <= '9' && pos[5] == '_') { num = pos[4] - '0'; pos += 6; } else if (os_strncmp(pos, "after_beacon_", 13) == 0 || os_strncmp(pos, "beacon_", 7) == 0) { wpa_printf(MSG_INFO, "DEPRECATED: '%s' not used", name); return 0; } else { wpa_printf(MSG_ERROR, "Unknown tx_queue name '%s'", pos); return -1; } if (num >= NUM_TX_QUEUES) { /* for backwards compatibility, do not trigger failure */ wpa_printf(MSG_INFO, "DEPRECATED: '%s' not used", name); return 0; } queue = &conf->tx_queue[num]; if (os_strcmp(pos, "aifs") == 0) { queue->aifs = atoi(val); if (queue->aifs < 0 || queue->aifs > 255) { wpa_printf(MSG_ERROR, "Invalid AIFS value %d", queue->aifs); return -1; } } else if (os_strcmp(pos, "cwmin") == 0) { queue->cwmin = atoi(val); if (!valid_cw(queue->cwmin)) { wpa_printf(MSG_ERROR, "Invalid cwMin value %d", queue->cwmin); return -1; } } else if (os_strcmp(pos, "cwmax") == 0) { queue->cwmax = atoi(val); if (!valid_cw(queue->cwmax)) { wpa_printf(MSG_ERROR, "Invalid cwMax value %d", queue->cwmax); return -1; } } else if (os_strcmp(pos, "burst") == 0) { queue->burst = hostapd_config_read_int10(val); } else { wpa_printf(MSG_ERROR, "Unknown tx_queue field '%s'", pos); return -1; } return 0; } #ifdef CONFIG_IEEE80211R 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 (hexstr2bin(pos, r0kh->key, sizeof(r0kh->key))) { 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 (hexstr2bin(pos, r1kh->key, sizeof(r1kh->key))) { 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; } #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_IEEE80211N 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, "[SMPS-STATIC]")) { conf->ht_capab &= ~HT_CAP_INFO_SMPS_MASK; conf->ht_capab |= HT_CAP_INFO_SMPS_STATIC; } if (os_strstr(capab, "[SMPS-DYNAMIC]")) { conf->ht_capab &= ~HT_CAP_INFO_SMPS_MASK; conf->ht_capab |= HT_CAP_INFO_SMPS_DYNAMIC; } if (os_strstr(capab, "[GF]")) conf->ht_capab |= HT_CAP_INFO_GREEN_FIELD; if (os_strstr(capab, "[SHORT-GI-20]")) 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, "[PSMP]")) conf->ht_capab |= HT_CAP_INFO_PSMP_SUPP; if (os_strstr(capab, "[LSIG-TXOP-PROT]")) conf->ht_capab |= HT_CAP_INFO_LSIG_TXOP_PROTECT_SUPPORT; return 0; } #endif /* CONFIG_IEEE80211N */ #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, "[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, "[SOUNDING-DIMENSION-2]") && (conf->vht_capab & VHT_CAP_SU_BEAMFORMER_CAPABLE)) conf->vht_capab |= (1 << VHT_CAP_SOUNDING_DIMENSION_OFFSET); if (os_strstr(capab, "[MU-BEAMFORMER]")) conf->vht_capab |= VHT_CAP_MU_BEAMFORMER_CAPABLE; if (os_strstr(capab, "[MU-BEAMFORMEE]")) conf->vht_capab |= VHT_CAP_MU_BEAMFORMEE_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-EXP0]")) conf->vht_capab |= VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT; 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_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_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_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; } #endif /* CONFIG_INTERWORKING */ #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, pos); 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; } #endif /* CONFIG_HS20 */ #ifdef CONFIG_WPS_NFC static struct wpabuf * hostapd_parse_bin(const char *buf) { size_t len; struct wpabuf *ret; len = os_strlen(buf); if (len & 0x01) return NULL; len /= 2; ret = wpabuf_alloc(len); if (ret == NULL) return NULL; if (hexstr2bin(buf, wpabuf_put(ret, len), len)) { wpabuf_free(ret); return NULL; } return ret; } #endif /* CONFIG_WPS_NFC */ static int hostapd_config_fill(struct hostapd_config *conf, struct hostapd_bss_config *bss, char *buf, char *pos, int line) { int errors = 0; { 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, "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; /* clear to get error below if setting is invalid */ conf->driver = NULL; for (j = 0; wpa_drivers[j]; j++) { if (os_strcmp(pos, wpa_drivers[j]->name) == 0) { conf->driver = wpa_drivers[j]; break; } } if (conf->driver == NULL) { wpa_printf(MSG_ERROR, "Line %d: invalid/" "unknown driver '%s'", line, pos); errors++; } } 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) { bss->ssid.ssid_len = os_strlen(pos); if (bss->ssid.ssid_len > HOSTAPD_MAX_SSID_LEN || bss->ssid.ssid_len < 1) { wpa_printf(MSG_ERROR, "Line %d: invalid SSID " "'%s'", line, pos); errors++; } else { os_memcpy(bss->ssid.ssid, pos, bss->ssid.ssid_len); bss->ssid.ssid_set = 1; } } else if (os_strcmp(buf, "ssid2") == 0) { size_t slen; char *str = wpa_config_parse_string(pos, &slen); if (str == NULL || slen < 1 || slen > HOSTAPD_MAX_SSID_LEN) { wpa_printf(MSG_ERROR, "Line %d: invalid SSID " "'%s'", line, pos); errors++; } else { os_memcpy(bss->ssid.ssid, str, slen); bss->ssid.ssid_len = slen; bss->ssid.ssid_set = 1; } 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) { bss->macaddr_acl = atoi(pos); if (bss->macaddr_acl != ACCEPT_UNLESS_DENIED && bss->macaddr_acl != DENY_UNLESS_ACCEPTED && bss->macaddr_acl != USE_EXTERNAL_RADIUS_AUTH) { wpa_printf(MSG_ERROR, "Line %d: unknown " "macaddr_acl %d", line, bss->macaddr_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); errors++; } } 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); errors++; } } 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, "country_code") == 0) { os_memcpy(conf->country, pos, 2); /* FIX: make this configurable */ conf->country[2] = ' '; } 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) { bss->eapol_version = atoi(pos); if (bss->eapol_version < 1 || bss->eapol_version > 2) { wpa_printf(MSG_ERROR, "Line %d: invalid EAPOL " "version (%d): '%s'.", line, bss->eapol_version, pos); errors++; } else 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)) errors++; } 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, "private_key") == 0) { os_free(bss->private_key); bss->private_key = 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, "check_crl") == 0) { bss->check_crl = 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, "dh_file") == 0) { os_free(bss->dh_file); bss->dh_file = 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); errors++; } else if (hexstr2bin(pos, bss->pac_opaque_encr_key, 16)) { wpa_printf(MSG_ERROR, "Line %d: Invalid " "pac_opaque_encr_key", line); errors++; } } 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); errors++; } else { 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); errors++; } 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_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_aka_result_ind") == 0) { bss->eap_sim_aka_result_ind = 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 */ #endif /* EAP_SERVER */ } else if (os_strcmp(buf, "eap_message") == 0) { char *term; 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); errors++; return errors; } 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, "wep_key_len_broadcast") == 0) { bss->default_wep_key_len = atoi(pos); if (bss->default_wep_key_len > 13) { wpa_printf(MSG_ERROR, "Line %d: invalid WEP " "key len %lu (= %lu bits)", line, (unsigned long) bss->default_wep_key_len, (unsigned long) bss->default_wep_key_len * 8); errors++; } } else if (os_strcmp(buf, "wep_key_len_unicast") == 0) { bss->individual_wep_key_len = atoi(pos); if (bss->individual_wep_key_len < 0 || bss->individual_wep_key_len > 13) { wpa_printf(MSG_ERROR, "Line %d: invalid WEP " "key len %d (= %d bits)", line, bss->individual_wep_key_len, bss->individual_wep_key_len * 8); errors++; } } 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); errors++; } } 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); errors++; } } 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) { bss->ieee802_11f = 1; os_strlcpy(bss->iapp_iface, pos, sizeof(bss->iapp_iface)); #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); errors++; } } else if (os_strcmp(buf, "nas_identifier") == 0) { bss->nas_identifier = os_strdup(pos); #ifndef CONFIG_NO_RADIUS } 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); errors++; } } 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); errors++; } bss->radius->auth_server->shared_secret = (u8 *) os_strdup(pos); bss->radius->auth_server->shared_secret_len = len; } 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); errors++; } } 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); errors++; } bss->radius->acct_server->shared_secret = (u8 *) os_strdup(pos); bss->radius->acct_server->shared_secret_len = len; } else if (os_strcmp(buf, "radius_retry_primary_interval") == 0) { bss->radius->retry_primary_interval = 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); errors++; } 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); errors++; } 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_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); errors++; } } 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); #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); errors++; } } 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); errors++; } } else if (os_strcmp(buf, "wpa") == 0) { bss->wpa = atoi(pos); } else if (os_strcmp(buf, "wpa_group_rekey") == 0) { bss->wpa_group_rekey = atoi(pos); } 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_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); errors++; } else { os_free(bss->ssid.wpa_passphrase); bss->ssid.wpa_passphrase = os_strdup(pos); if (bss->ssid.wpa_passphrase) { os_free(bss->ssid.wpa_psk); bss->ssid.wpa_psk = NULL; bss->ssid.wpa_passphrase_set = 1; } } } else if (os_strcmp(buf, "wpa_psk") == 0) { os_free(bss->ssid.wpa_psk); bss->ssid.wpa_psk = os_zalloc(sizeof(struct hostapd_wpa_psk)); if (bss->ssid.wpa_psk == NULL) errors++; else 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); errors++; } else { 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); errors++; } } 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) errors++; } 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) { wpa_printf(MSG_ERROR, "Line %d: unknown " "wpa_psk_radius %d", line, bss->wpa_psk_radius); errors++; } } 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) errors++; else if (bss->wpa_pairwise & (WPA_CIPHER_NONE | WPA_CIPHER_WEP40 | WPA_CIPHER_WEP104)) { wpa_printf(MSG_ERROR, "Line %d: unsupported " "pairwise cipher suite '%s'", bss->wpa_pairwise, pos); errors++; } } 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) errors++; else if (bss->rsn_pairwise & (WPA_CIPHER_NONE | WPA_CIPHER_WEP40 | WPA_CIPHER_WEP104)) { wpa_printf(MSG_ERROR, "Line %d: unsupported " "pairwise cipher suite '%s'", bss->rsn_pairwise, pos); errors++; } #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) { bss->rsn_preauth_interfaces = os_strdup(pos); #endif /* CONFIG_RSN_PREAUTH */ #ifdef CONFIG_PEERKEY } else if (os_strcmp(buf, "peerkey") == 0) { bss->peerkey = atoi(pos); #endif /* CONFIG_PEERKEY */ #ifdef CONFIG_IEEE80211R } 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_DEBUG, "Line %d: Invalid " "mobility_domain '%s'", line, pos); errors++; return errors; } } 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_DEBUG, "Line %d: Invalid " "r1_key_holder '%s'", line, pos); errors++; return errors; } } else if (os_strcmp(buf, "r0_key_lifetime") == 0) { bss->r0_key_lifetime = atoi(pos); } else if (os_strcmp(buf, "reassociation_deadline") == 0) { bss->reassociation_deadline = 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); errors++; return errors; } } else if (os_strcmp(buf, "r1kh") == 0) { if (add_r1kh(bss, pos) < 0) { wpa_printf(MSG_DEBUG, "Line %d: Invalid " "r1kh '%s'", line, pos); errors++; return errors; } } 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); #endif /* CONFIG_IEEE80211R */ #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 errors; } /* 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); errors++; return errors; } 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_ipv6") == 0) { bss->radius_server_ipv6 = atoi(pos); #endif /* RADIUS_SERVER */ } else if (os_strcmp(buf, "test_socket") == 0) { os_free(bss->test_socket); bss->test_socket = os_strdup(pos); } 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 { wpa_printf(MSG_ERROR, "Line %d: unknown " "hw_mode '%s'", line, pos); errors++; } } else if (os_strcmp(buf, "wps_rf_bands") == 0) { 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); errors++; } } 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); errors++; #endif /* CONFIG_ACS */ conf->channel = 0; } else conf->channel = atoi(pos); } 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 15 TU. */ if (val < 15 || val > 65535) { wpa_printf(MSG_ERROR, "Line %d: invalid " "beacon_int %d (expected " "15..65535)", line, val); errors++; } else 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); errors++; } else conf->acs_num_scans = val; #endif /* CONFIG_ACS */ } else if (os_strcmp(buf, "dtim_period") == 0) { bss->dtim_period = atoi(pos); if (bss->dtim_period < 1 || bss->dtim_period > 255) { wpa_printf(MSG_ERROR, "Line %d: invalid " "dtim_period %d", line, bss->dtim_period); errors++; } } else if (os_strcmp(buf, "rts_threshold") == 0) { conf->rts_threshold = atoi(pos); if (conf->rts_threshold < 0 || conf->rts_threshold > 2347) { wpa_printf(MSG_ERROR, "Line %d: invalid " "rts_threshold %d", line, conf->rts_threshold); errors++; } } else if (os_strcmp(buf, "fragm_threshold") == 0) { conf->fragm_threshold = atoi(pos); if (conf->fragm_threshold < 256 || conf->fragm_threshold > 2346) { wpa_printf(MSG_ERROR, "Line %d: invalid " "fragm_threshold %d", line, conf->fragm_threshold); errors++; } } 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); } else conf->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); errors++; } } 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); errors++; } } 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, "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); errors++; } } 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); errors++; } #ifndef CONFIG_NO_VLAN } else if (os_strcmp(buf, "dynamic_vlan") == 0) { bss->ssid.dynamic_vlan = 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); errors++; } } 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); errors++; } #ifdef CONFIG_FULL_DYNAMIC_VLAN } else if (os_strcmp(buf, "vlan_tagged_interface") == 0) { 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, buf, pos)) { wpa_printf(MSG_ERROR, "Line %d: invalid TX " "queue item", line); errors++; } } 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); errors++; } } else if (os_strcmp(buf, "bss") == 0) { if (hostapd_config_bss(conf, pos)) { wpa_printf(MSG_ERROR, "Line %d: invalid bss " "item", line); errors++; } } else if (os_strcmp(buf, "bssid") == 0) { if (hwaddr_aton(pos, bss->bssid)) { wpa_printf(MSG_ERROR, "Line %d: invalid bssid " "item", line); errors++; } #ifdef CONFIG_IEEE80211W } else if (os_strcmp(buf, "ieee80211w") == 0) { bss->ieee80211w = 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); errors++; } } 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); errors++; } #endif /* CONFIG_IEEE80211W */ #ifdef CONFIG_IEEE80211N } 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); errors++; } } else if (os_strcmp(buf, "require_ht") == 0) { conf->require_ht = atoi(pos); } else if (os_strcmp(buf, "obss_interval") == 0) { conf->obss_interval = atoi(pos); #endif /* CONFIG_IEEE80211N */ #ifdef CONFIG_IEEE80211AC } else if (os_strcmp(buf, "ieee80211ac") == 0) { conf->ieee80211ac = atoi(pos); } 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); errors++; } } 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); #endif /* CONFIG_IEEE80211AC */ } 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); errors++; } } 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); errors++; } } 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) > 32) { wpa_printf(MSG_ERROR, "Line %d: Too long " "device_name", line); errors++; } 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); errors++; } 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); errors++; } 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); errors++; } 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); errors++; } 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)) errors++; } 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); errors++; } } else if (os_strcmp(buf, "ap_pin") == 0) { os_free(bss->ap_pin); 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); errors++; } } else if (os_strcmp(buf, "wps_cred_processing") == 0) { bss->wps_cred_processing = 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); errors++; } } else if (os_strcmp(buf, "upnp_iface") == 0) { 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); #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); errors++; } 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 = hostapd_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 = hostapd_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 = hostapd_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) { int manage = atoi(pos); if (manage) 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) { int val = atoi(pos); if (val) bss->tdls |= TDLS_PROHIBIT; else bss->tdls &= ~TDLS_PROHIBIT; } else if (os_strcmp(buf, "tdls_prohibit_chan_switch") == 0) { int val = atoi(pos); if (val) 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); errors++; return errors; } os_free(bss->time_zone); bss->time_zone = os_strdup(pos); if (bss->time_zone == NULL) errors++; #ifdef CONFIG_WNM } else if (os_strcmp(buf, "wnm_sleep_mode") == 0) { bss->wnm_sleep_mode = atoi(pos); } else if (os_strcmp(buf, "bss_transition") == 0) { bss->bss_transition = atoi(pos); #endif /* CONFIG_WNM */ #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); errors++; } } 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); errors++; } } else if (os_strcmp(buf, "roaming_consortium") == 0) { if (parse_roaming_consortium(bss, pos, line) < 0) errors++; } else if (os_strcmp(buf, "venue_name") == 0) { if (parse_venue_name(bss, pos, line) < 0) errors++; } 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); errors++; return errors; } 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) { errors++; return errors; } *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; errors++; return errors; } 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) { errors++; return errors; } 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) errors++; } else if (os_strcmp(buf, "nai_realm") == 0) { if (parse_nai_realm(bss, pos, line) < 0) errors++; } else if (os_strcmp(buf, "gas_frag_limit") == 0) { bss->gas_frag_limit = atoi(pos); } else if (os_strcmp(buf, "gas_comeback_delay") == 0) { bss->gas_comeback_delay = atoi(pos); } else if (os_strcmp(buf, "qos_map_set") == 0) { if (parse_qos_map_set(bss, pos, line) < 0) errors++; #endif /* CONFIG_INTERWORKING */ #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_HS20 } else if (os_strcmp(buf, "hs20") == 0) { bss->hs20 = atoi(pos); } else if (os_strcmp(buf, "disable_dgaf") == 0) { bss->disable_dgaf = atoi(pos); } else if (os_strcmp(buf, "hs20_oper_friendly_name") == 0) { if (hs20_parse_oper_friendly_name(bss, pos, line) < 0) errors++; } else if (os_strcmp(buf, "hs20_wan_metrics") == 0) { if (hs20_parse_wan_metrics(bss, pos, line) < 0) { errors++; return errors; } } else if (os_strcmp(buf, "hs20_conn_capab") == 0) { if (hs20_parse_conn_capab(bss, pos, line) < 0) { errors++; return errors; } } 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); errors++; return errors; } oper_class_len /= 2; oper_class = os_malloc(oper_class_len); if (oper_class == NULL) { errors++; return errors; } 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); errors++; return errors; } os_free(bss->hs20_operating_class); bss->hs20_operating_class = oper_class; bss->hs20_operating_class_len = oper_class_len; #endif /* CONFIG_HS20 */ #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.0d || \ conf->_val > 1.0d) { \ wpa_printf(MSG_ERROR, \ "Line %d: Invalid value '%s'", \ line, pos); \ errors++; \ return errors; \ } 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, "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; #endif /* CONFIG_TESTING_OPTIONS */ } else if (os_strcmp(buf, "vendor_elements") == 0) { struct wpabuf *elems; size_t len = os_strlen(pos); if (len & 0x01) { wpa_printf(MSG_ERROR, "Line %d: Invalid " "vendor_elements '%s'", line, pos); return 1; } len /= 2; if (len == 0) { wpabuf_free(bss->vendor_elements); bss->vendor_elements = NULL; return 0; } elems = wpabuf_alloc(len); if (elems == NULL) return 1; if (hexstr2bin(pos, wpabuf_put(elems, len), len)) { wpabuf_free(elems); wpa_printf(MSG_ERROR, "Line %d: Invalid " "vendor_elements '%s'", line, pos); return 1; } wpabuf_free(bss->vendor_elements); bss->vendor_elements = elems; } else if (os_strcmp(buf, "sae_anti_clogging_threshold") == 0) { bss->sae_anti_clogging_threshold = 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 { wpa_printf(MSG_ERROR, "Line %d: unknown configuration " "item '%s'", line, buf); errors++; } } return errors; } /** * 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; struct hostapd_bss_config *bss; FILE *f; char buf[512], *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; } bss = conf->last_bss = conf->bss[0]; while (fgets(buf, sizeof(buf), f)) { 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]); 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, 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]); if (hostapd_config_check(conf, 0)) { wpa_printf(MSG_ERROR, "Configuration check failed"); return -1; } return 0; } wpa-2.1/hostapd/config_file.h000066400000000000000000000007251227414666700162450ustar00rootroot00000000000000/* * 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_set_iface(struct hostapd_config *conf, struct hostapd_bss_config *bss, char *field, char *value); #endif /* CONFIG_FILE_H */ wpa-2.1/hostapd/ctrl_iface.c000066400000000000000000001277571227414666700161060ustar00rootroot00000000000000/* * hostapd / UNIX domain socket -based control interface * Copyright (c) 2004-2014, 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 #include #include "utils/common.h" #include "utils/eloop.h" #include "common/version.h" #include "common/ieee802_11_defs.h" #include "drivers/driver.h" #include "radius/radius_client.h" #include "radius/radius_server.h" #include "ap/hostapd.h" #include "ap/ap_config.h" #include "ap/ieee802_1x.h" #include "ap/wpa_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/wnm_ap.h" #include "ap/wpa_auth.h" #include "wps/wps_defs.h" #include "wps/wps.h" #include "config_file.h" #include "ctrl_iface.h" struct wpa_ctrl_dst { struct wpa_ctrl_dst *next; struct sockaddr_un addr; socklen_t addrlen; int debug_level; int errors; }; static void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level, const char *buf, size_t len); static int hostapd_ctrl_iface_attach(struct hostapd_data *hapd, struct sockaddr_un *from, socklen_t fromlen) { struct wpa_ctrl_dst *dst; dst = os_zalloc(sizeof(*dst)); if (dst == NULL) return -1; os_memcpy(&dst->addr, from, sizeof(struct sockaddr_un)); dst->addrlen = fromlen; dst->debug_level = MSG_INFO; dst->next = hapd->ctrl_dst; hapd->ctrl_dst = dst; wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor attached", (u8 *) from->sun_path, fromlen - offsetof(struct sockaddr_un, sun_path)); return 0; } static int hostapd_ctrl_iface_detach(struct hostapd_data *hapd, struct sockaddr_un *from, socklen_t fromlen) { struct wpa_ctrl_dst *dst, *prev = NULL; dst = hapd->ctrl_dst; while (dst) { if (fromlen == dst->addrlen && os_memcmp(from->sun_path, dst->addr.sun_path, fromlen - offsetof(struct sockaddr_un, sun_path)) == 0) { wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor detached", (u8 *) from->sun_path, fromlen - offsetof(struct sockaddr_un, sun_path)); if (prev == NULL) hapd->ctrl_dst = dst->next; else prev->next = dst->next; os_free(dst); return 0; } prev = dst; dst = dst->next; } return -1; } static int hostapd_ctrl_iface_level(struct hostapd_data *hapd, struct sockaddr_un *from, socklen_t fromlen, char *level) { struct wpa_ctrl_dst *dst; wpa_printf(MSG_DEBUG, "CTRL_IFACE LEVEL %s", level); dst = hapd->ctrl_dst; while (dst) { if (fromlen == dst->addrlen && os_memcmp(from->sun_path, dst->addr.sun_path, fromlen - offsetof(struct sockaddr_un, sun_path)) == 0) { wpa_hexdump(MSG_DEBUG, "CTRL_IFACE changed monitor " "level", (u8 *) from->sun_path, fromlen - offsetof(struct sockaddr_un, sun_path)); dst->debug_level = atoi(level); return 0; } dst = dst->next; } return -1; } 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 CONFIG_IEEE80211W #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 */ #endif /* CONFIG_IEEE80211W */ #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 (ret < 0 || (size_t) ret >= buflen) return -1; return ret; } } ret = os_snprintf(buf, buflen, "%s", pin); if (ret < 0 || (size_t) ret >= buflen) 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 (ret < 0 || ret >= end - pos) 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 (ret < 0 || ret >= end - pos) 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 (ret < 0 || ret >= end - pos) 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 (ret < 0 || ret >= end - pos) return pos - buf; pos += ret; } return pos - buf; } #endif /* CONFIG_WPS */ #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 static int hostapd_ctrl_iface_disassoc_imminent(struct hostapd_data *hapd, const char *cmd) { u8 addr[ETH_ALEN]; int disassoc_timer; struct sta_info *sta; if (hwaddr_aton(cmd, addr)) return -1; if (cmd[17] != ' ') return -1; disassoc_timer = atoi(cmd + 17); sta = ap_get_sta(hapd, addr); if (sta == NULL) { wpa_printf(MSG_DEBUG, "Station " MACSTR " not found for disassociation imminent message", MAC2STR(addr)); return -1; } return wnm_send_disassoc_imminent(hapd, sta, disassoc_timer); } static int hostapd_ctrl_iface_ess_disassoc(struct hostapd_data *hapd, const char *cmd) { u8 addr[ETH_ALEN]; const char *url, *timerstr; int disassoc_timer; struct sta_info *sta; 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 ESS disassociation imminent message", MAC2STR(addr)); return -1; } timerstr = cmd + 17; if (*timerstr != ' ') return -1; timerstr++; disassoc_timer = atoi(timerstr); if (disassoc_timer < 0 || disassoc_timer > 65535) return -1; url = os_strchr(timerstr, ' '); if (url == NULL) return -1; url++; return wnm_send_ess_disassoc_imminent(hapd, sta, url, disassoc_timer); } #endif /* CONFIG_WNM */ 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 (ret < 0 || ret >= end - pos) 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 (ret < 0 || ret >= end - pos) 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 (ret < 0 || ret >= end - pos) 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 (ret < 0 || ret >= end - pos) return pos - buf; pos += ret; } #endif /* CONFIG_WPS */ if (hapd->conf->wpa && hapd->conf->wpa_key_mgmt) { ret = os_snprintf(pos, end - pos, "key_mgmt="); if (ret < 0 || ret >= end - pos) return pos - buf; pos += ret; if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) { ret = os_snprintf(pos, end - pos, "WPA-PSK "); if (ret < 0 || ret >= end - pos) return pos - buf; pos += ret; } if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) { ret = os_snprintf(pos, end - pos, "WPA-EAP "); if (ret < 0 || ret >= end - pos) return pos - buf; pos += ret; } #ifdef CONFIG_IEEE80211R if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_PSK) { ret = os_snprintf(pos, end - pos, "FT-PSK "); if (ret < 0 || ret >= end - pos) 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 (ret < 0 || ret >= end - pos) return pos - buf; pos += ret; } #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_IEEE80211W if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK_SHA256) { ret = os_snprintf(pos, end - pos, "WPA-PSK-SHA256 "); if (ret < 0 || ret >= end - pos) 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 (ret < 0 || ret >= end - pos) return pos - buf; pos += ret; } #endif /* CONFIG_IEEE80211W */ ret = os_snprintf(pos, end - pos, "\n"); if (ret < 0 || ret >= end - pos) 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 (ret < 0 || ret >= end - pos) 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 (ret < 0 || ret >= end - pos) 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 (ret < 0 || ret >= end - pos) 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 (ret < 0 || ret >= end - pos) 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 (ret < 0 || ret >= end - pos) return pos - buf; pos += ret; } return pos - buf; } 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_dummy_cred") == 0) { wps_testing_dummy_cred = atoi(value); wpa_printf(MSG_DEBUG, "WPS: Testing - dummy_cred=%d", wps_testing_dummy_cred); } else if (os_strcasecmp(cmd, "wps_corrupt_pkhash") == 0) { wps_corrupt_pkhash = atoi(value); wpa_printf(MSG_DEBUG, "WPS: Testing - wps_corrupt_pkhash=%d", wps_corrupt_pkhash); #endif /* CONFIG_WPS_TESTING */ #ifdef CONFIG_INTERWORKING } else if (os_strcasecmp(cmd, "gas_frag_limit") == 0) { int val = atoi(value); if (val <= 0) ret = -1; else hapd->gas_frag_limit = val; #endif /* CONFIG_INTERWORKING */ #ifdef CONFIG_TESTING_OPTIONS } else if (os_strcasecmp(cmd, "ext_mgmt_frame_handling") == 0) { hapd->ext_mgmt_frame_handling = atoi(value); #endif /* CONFIG_TESTING_OPTIONS */ } else { ret = hostapd_set_iface(hapd->iconf, hapd->conf, cmd, value); } 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 (res < 0 || (unsigned int) res >= buflen) 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_disable(struct hostapd_iface *iface) { if (hostapd_disable_iface(iface) < 0) { wpa_printf(MSG_ERROR, "Disabling of interface failed"); return -1; } return 0; } #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); os_free(buf); return res; } #endif /* CONFIG_TESTING_OPTIONS */ static int hostapd_ctrl_iface_chan_switch(struct hostapd_data *hapd, char *pos) { #ifdef NEED_AP_MLME struct csa_settings settings; int ret = hostapd_parse_csa_settings(pos, &settings); if (ret) return ret; return hostapd_switch_channel(hapd, &settings); #else /* NEED_AP_MLME */ return -1; #endif /* NEED_AP_MLME */ } 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 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_un from; socklen_t fromlen = sizeof(from); char *reply; const int reply_size = 4096; int reply_len; int level = MSG_DEBUG; res = recvfrom(sock, buf, sizeof(buf) - 1, 0, (struct sockaddr *) &from, &fromlen); if (res < 0) { perror("recvfrom(ctrl_iface)"); return; } buf[res] = '\0'; if (os_strcmp(buf, "PING") == 0) level = MSG_EXCESSIVE; wpa_hexdump_ascii(level, "RX ctrl_iface", (u8 *) buf, res); reply = os_malloc(reply_size); if (reply == NULL) { sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from, fromlen); return; } 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, "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)) 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_IEEE80211W #ifdef NEED_AP_MLME } else if (os_strncmp(buf, "SA_QUERY ", 9) == 0) { if (hostapd_ctrl_iface_sa_query(hapd, buf + 9)) reply_len = -1; #endif /* NEED_AP_MLME */ #endif /* CONFIG_IEEE80211W */ #ifdef CONFIG_WPS } else if (os_strncmp(buf, "WPS_PIN ", 8) == 0) { if (hostapd_ctrl_iface_wps_pin(hapd, buf + 8)) 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_WNM } 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; #endif /* CONFIG_WNM */ } 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_strncmp(buf, "ENABLE", 6) == 0) { if (hostapd_ctrl_iface_enable(hapd->iface)) reply_len = -1; } else if (os_strncmp(buf, "RELOAD", 6) == 0) { if (hostapd_ctrl_iface_reload(hapd->iface)) reply_len = -1; } else if (os_strncmp(buf, "DISABLE", 7) == 0) { if (hostapd_ctrl_iface_disable(hapd->iface)) 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; #endif /* CONFIG_TESTING_OPTIONS */ } else if (os_strncmp(buf, "CHAN_SWITCH ", 12) == 0) { if (hostapd_ctrl_iface_chan_switch(hapd, buf + 12)) reply_len = -1; } else { os_memcpy(reply, "UNKNOWN COMMAND\n", 16); reply_len = 16; } if (reply_len < 0) { os_memcpy(reply, "FAIL\n", 5); reply_len = 5; } sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from, fromlen); os_free(reply); } 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; } static void hostapd_ctrl_iface_msg_cb(void *ctx, int level, int global, const char *txt, size_t len) { struct hostapd_data *hapd = ctx; if (hapd == NULL) return; hostapd_ctrl_iface_send(hapd, level, txt, len); } int hostapd_ctrl_iface_init(struct hostapd_data *hapd) { 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; } 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 { perror("mkdir[ctrl_interface]"); goto fail; } } if (hapd->conf->ctrl_interface_gid_set && chown(hapd->conf->ctrl_interface, -1, hapd->conf->ctrl_interface_gid) < 0) { perror("chown[ctrl_interface]"); return -1; } if (!hapd->conf->ctrl_interface_gid_set && hapd->iface->interfaces->ctrl_iface_group && chown(hapd->conf->ctrl_interface, -1, hapd->iface->interfaces->ctrl_iface_group) < 0) { perror("chown[ctrl_interface]"); 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) { perror("socket(PF_UNIX)"); 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) { perror("unlink[ctrl_iface]"); wpa_printf(MSG_ERROR, "Could not unlink " "existing ctrl_iface socket '%s'", fname); goto fail; } if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { perror("hostapd-ctrl-iface: bind(PF_UNIX)"); 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 && chown(fname, -1, hapd->conf->ctrl_interface_gid) < 0) { perror("chown[ctrl_interface/ifname]"); goto fail; } if (!hapd->conf->ctrl_interface_gid_set && hapd->iface->interfaces->ctrl_iface_group && chown(fname, -1, hapd->iface->interfaces->ctrl_iface_group) < 0) { perror("chown[ctrl_interface/ifname]"); goto fail; } if (chmod(fname, S_IRWXU | S_IRWXG) < 0) { perror("chmod[ctrl_interface/ifname]"); goto fail; } os_free(fname); hapd->ctrl_sock = s; eloop_register_read_sock(s, hostapd_ctrl_iface_receive, hapd, NULL); 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; } void hostapd_ctrl_iface_deinit(struct hostapd_data *hapd) { struct wpa_ctrl_dst *dst, *prev; if (hapd->ctrl_sock > -1) { char *fname; eloop_unregister_read_sock(hapd->ctrl_sock); close(hapd->ctrl_sock); hapd->ctrl_sock = -1; 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)); } } } dst = hapd->ctrl_dst; while (dst) { prev = dst; dst = dst->next; os_free(prev); } } 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 void hostapd_ctrl_iface_flush(struct hapd_interfaces *interfaces) { #ifdef CONFIG_WPS_TESTING wps_version_number = 0x20; wps_testing_dummy_cred = 0; wps_corrupt_pkhash = 0; #endif /* CONFIG_WPS_TESTING */ } static void hostapd_global_ctrl_iface_receive(int sock, void *eloop_ctx, void *sock_ctx) { void *interfaces = eloop_ctx; char buf[256]; int res; struct sockaddr_un from; socklen_t fromlen = sizeof(from); char reply[24]; int reply_len; res = recvfrom(sock, buf, sizeof(buf) - 1, 0, (struct sockaddr *) &from, &fromlen); if (res < 0) { perror("recvfrom(ctrl_iface)"); return; } buf[res] = '\0'; wpa_printf(MSG_DEBUG, "Global ctrl_iface command: %s", buf); 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, "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 { wpa_printf(MSG_DEBUG, "Unrecognized global ctrl_iface command " "ignored"); reply_len = -1; } if (reply_len < 0) { os_memcpy(reply, "FAIL\n", 5); reply_len = 5; } sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from, fromlen); } 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; } int hostapd_global_ctrl_iface_init(struct hapd_interfaces *interface) { 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 { perror("mkdir[ctrl_interface]"); goto fail; } } else if (interface->ctrl_iface_group && chown(interface->global_iface_path, -1, interface->ctrl_iface_group) < 0) { perror("chown[ctrl_interface]"); 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) { perror("socket(PF_UNIX)"); 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) { perror("unlink[ctrl_iface]"); wpa_printf(MSG_ERROR, "Could not unlink " "existing ctrl_iface socket '%s'", fname); goto fail; } if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { perror("bind(PF_UNIX)"); 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 && chown(fname, -1, interface->ctrl_iface_group) < 0) { perror("chown[ctrl_interface]"); goto fail; } if (chmod(fname, S_IRWXU | S_IRWXG) < 0) { perror("chmod[ctrl_interface/ifname]"); goto fail; } os_free(fname); interface->global_ctrl_sock = s; eloop_register_read_sock(s, hostapd_global_ctrl_iface_receive, interface, NULL); return 0; fail: if (s >= 0) close(s); if (fname) { unlink(fname); os_free(fname); } return -1; } void hostapd_global_ctrl_iface_deinit(struct hapd_interfaces *interfaces) { char *fname = NULL; if (interfaces->global_ctrl_sock > -1) { eloop_unregister_read_sock(interfaces->global_ctrl_sock); close(interfaces->global_ctrl_sock); interfaces->global_ctrl_sock = -1; 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)); } } os_free(interfaces->global_iface_path); interfaces->global_iface_path = NULL; } } static void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level, const char *buf, size_t len) { struct wpa_ctrl_dst *dst, *next; struct msghdr msg; int idx; struct iovec io[2]; char levelstr[10]; dst = hapd->ctrl_dst; if (hapd->ctrl_sock < 0 || dst == NULL) return; os_snprintf(levelstr, sizeof(levelstr), "<%d>", level); io[0].iov_base = levelstr; io[0].iov_len = os_strlen(levelstr); io[1].iov_base = (char *) buf; io[1].iov_len = len; os_memset(&msg, 0, sizeof(msg)); msg.msg_iov = io; msg.msg_iovlen = 2; idx = 0; while (dst) { next = dst->next; if (level >= dst->debug_level) { wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor send", (u8 *) dst->addr.sun_path, dst->addrlen - offsetof(struct sockaddr_un, sun_path)); msg.msg_name = &dst->addr; msg.msg_namelen = dst->addrlen; if (sendmsg(hapd->ctrl_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) { hostapd_ctrl_iface_detach( hapd, &dst->addr, dst->addrlen); } } else dst->errors = 0; } idx++; dst = next; } } #endif /* CONFIG_NATIVE_WINDOWS */ wpa-2.1/hostapd/ctrl_iface.h000066400000000000000000000017461227414666700161000ustar00rootroot00000000000000/* * 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 */ wpa-2.1/hostapd/defconfig000066400000000000000000000252741227414666700155050ustar00rootroot00000000000000# 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 madwifi driver #CONFIG_DRIVER_MADWIFI=y #CFLAGS += -I../../madwifi # change to the madwifi source directory # Driver interface for drivers using the nl80211 kernel interface CONFIG_DRIVER_NL80211=y # driver_nl80211.c requires libnl. If you are compiling it yourself # you may need to point hostapd to your version of libnl. # #CFLAGS += -I$ #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 # IEEE 802.11F/IAPP CONFIG_IAPP=y # WPA2/IEEE 802.11i RSN pre-authentication CONFIG_RSN_PREAUTH=y # PeerKey handshake for Station to Station Link (IEEE 802.11e DLS) CONFIG_PEERKEY=y # IEEE 802.11w (management frame protection) # This version is an experimental implementation based on IEEE 802.11w/D1.0 # draft and is subject to change since the standard has not yet been finalized. # Driver support is also needed for IEEE 802.11w. #CONFIG_IEEE80211W=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-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 # Note: If OpenSSL is used as the TLS library, OpenSSL 1.0 or newer is needed # for EAP-FAST support. Older OpenSSL releases would need to be patched, e.g., # with openssl-0.9.8x-tls-extensions.patch, to add the needed functions. #CONFIG_EAP_FAST=y # Wi-Fi Protected Setup (WPS) #CONFIG_WPS=y # Enable WSC 2.0 support #CONFIG_WPS2=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 # 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., madwifi or FreeBSD/net80211) #CONFIG_DRIVER_RADIUS_ACL=y # IEEE 802.11n (High Throughput) support #CONFIG_IEEE80211N=y # Wireless Network Management (IEEE Std 802.11v-2011) # Note: This is experimental and not complete implementation. #CONFIG_WNM=y # IEEE 802.11ac (Very High Throughput) support #CONFIG_IEEE80211AC=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 # 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 # Select TLS implementation # openssl = OpenSSL (default) # gnutls = GnuTLS # internal = 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 # 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 # 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: # http://wireless.kernel.org/en/users/Documentation/acs # #CONFIG_ACS=y wpa-2.1/hostapd/eap_register.c000066400000000000000000000057551227414666700164550ustar00rootroot00000000000000/* * 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_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_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; } wpa-2.1/hostapd/eap_register.h000066400000000000000000000004651227414666700164530ustar00rootroot00000000000000/* * 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 */ wpa-2.1/hostapd/eap_testing.txt000066400000000000000000000050251227414666700166710ustar00rootroot00000000000000Interoperability 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?) wpa-2.1/hostapd/hlr_auc_gw.c000066400000000000000000000606111227414666700161060ustar00rootroot00000000000000/* * HLR/AuC testing gateway for hostapd EAP-SIM/AKA database/authenticator * Copyright (c) 2005-2007, 2012-2013, 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; }; 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_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" ");"; 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; } } 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 ki,opc,amf,sqn 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 tripler 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 = strchr(pos, ':'); if (pos2 == NULL) { printf("%s:%d - Invalid IMSI (%s)\n", fname, line, pos); ret = -1; break; } *pos2 = '\0'; if (strlen(pos) >= sizeof(g->imsi)) { printf("%s:%d - Too long IMSI (%s)\n", fname, line, pos); ret = -1; break; } os_strlcpy(g->imsi, pos, sizeof(g->imsi)); pos = pos2 + 1; /* Kc */ pos2 = strchr(pos, ':'); if (pos2 == NULL) { printf("%s:%d - Invalid Kc (%s)\n", fname, line, pos); ret = -1; break; } *pos2 = '\0'; if (strlen(pos) != 16 || hexstr2bin(pos, g->kc, 8)) { printf("%s:%d - Invalid Kc (%s)\n", fname, line, pos); ret = -1; break; } pos = pos2 + 1; /* SRES */ pos2 = strchr(pos, ':'); if (pos2 == NULL) { printf("%s:%d - Invalid SRES (%s)\n", fname, line, pos); ret = -1; break; } *pos2 = '\0'; if (strlen(pos) != 8 || hexstr2bin(pos, g->sres, 4)) { printf("%s:%d - Invalid SRES (%s)\n", fname, line, pos); ret = -1; break; } pos = pos2 + 1; /* RAND */ pos2 = strchr(pos, ':'); if (pos2) *pos2 = '\0'; if (strlen(pos) != 32 || hexstr2bin(pos, g->_rand, 16)) { printf("%s:%d - Invalid RAND (%s)\n", fname, line, pos); ret = -1; break; } pos = pos2 + 1; 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 */ 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 = strchr(pos, ' '); if (pos2 == NULL) { printf("%s:%d - Invalid IMSI (%s)\n", fname, line, pos); ret = -1; break; } *pos2 = '\0'; if (strlen(pos) >= sizeof(m->imsi)) { printf("%s:%d - Too long IMSI (%s)\n", fname, line, pos); ret = -1; break; } os_strlcpy(m->imsi, pos, sizeof(m->imsi)); pos = pos2 + 1; /* Ki */ pos2 = strchr(pos, ' '); if (pos2 == NULL) { printf("%s:%d - Invalid Ki (%s)\n", fname, line, pos); ret = -1; break; } *pos2 = '\0'; if (strlen(pos) != 32 || hexstr2bin(pos, m->ki, 16)) { printf("%s:%d - Invalid Ki (%s)\n", fname, line, pos); ret = -1; break; } pos = pos2 + 1; /* OPc */ pos2 = strchr(pos, ' '); if (pos2 == NULL) { printf("%s:%d - Invalid OPc (%s)\n", fname, line, pos); ret = -1; break; } *pos2 = '\0'; if (strlen(pos) != 32 || hexstr2bin(pos, m->opc, 16)) { printf("%s:%d - Invalid OPc (%s)\n", fname, line, pos); ret = -1; break; } pos = pos2 + 1; /* AMF */ pos2 = strchr(pos, ' '); if (pos2 == NULL) { printf("%s:%d - Invalid AMF (%s)\n", fname, line, pos); ret = -1; break; } *pos2 = '\0'; if (strlen(pos) != 4 || hexstr2bin(pos, m->amf, 2)) { printf("%s:%d - Invalid AMF (%s)\n", fname, line, pos); ret = -1; break; } pos = pos2 + 1; /* SQN */ pos2 = strchr(pos, ' '); if (pos2) *pos2 = '\0'; if (strlen(pos) != 12 || hexstr2bin(pos, m->sqn, 6)) { printf("%s:%d - Invalid SEQ (%s)\n", fname, line, pos); ret = -1; break; } pos = pos2 + 1; 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 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(buf, sizeof(buf), "%s.new", fname); f2 = fopen(buf, "w"); if (f2 == NULL) { printf("Could not write Milenage data file '%s'\n", buf); 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(buf, sizeof(buf), "%s.bak", fname); if (rename(fname, buf) < 0) { perror("rename"); return; } snprintf(buf, sizeof(buf), "%s.new", fname); if (rename(buf, 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 (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 < 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 (ret < 0 || ret >= rend - rpos) 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); } 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-2007, 2012-2013, 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; } wpa-2.1/hostapd/hlr_auc_gw.milenage_db000066400000000000000000000013361227414666700201110ustar00rootroot00000000000000# 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 # dummy values will need to be included in this file. # IMSI Ki OPc AMF SQN 232010000000000 90dca4eda45b53cf0f12d7c9c3bc6a89 cb9cccc4b9258e6dca4760379fb82581 61df 000000000000 # 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 wpa-2.1/hostapd/hlr_auc_gw.txt000066400000000000000000000062611227414666700165040ustar00rootroot00000000000000HLR/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' wpa-2.1/hostapd/hostapd.8000066400000000000000000000027521227414666700153650ustar00rootroot00000000000000.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, madwifi, 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). wpa-2.1/hostapd/hostapd.accept000066400000000000000000000004241227414666700164470ustar00rootroot00000000000000# 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 wpa-2.1/hostapd/hostapd.conf000066400000000000000000002045361227414666700161470ustar00rootroot00000000000000##### 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); ath0 for madwifi interface=wlan0 # In case of madwifi, 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/madwifi/test/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 # 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 5 (32) = IAPP # 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 contron 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. #country_code=US # 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 # Operation mode (a = IEEE 802.11a, b = IEEE 802.11b, g = IEEE 802.11g, # ad = IEEE 802.11ad (60 GHz); a/g options are used with IEEE 802.11n, too, to # specify band) # 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 # ACS tuning - Automatic Channel Selection # See: http://wireless.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. # # Defaults: #acs_num_scans=5 # 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; 2347 = disabled (default); range 0..2347 # 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=2347 # Fragmentation threshold; 2346 = disabled (default); range 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=2346 # 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 # 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=madwifi. # 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 # Additional vendor specfic 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 # TX queue parameters (EDCF / bursting) # tx_queue__ # queues: data0, data1, data2, data3, after_beacon, beacon # (data0 is the highest priority queue) # parameters: # aifs: AIFS (default 2) # cwmin: cwMin (1, 3, 7, 15, 31, 63, 127, 255, 511, 1023) # cwmax: cwMax (1, 3, 7, 15, 31, 63, 127, 255, 511, 1023); 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 # 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 # 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 # 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 # 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 ##### 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. #ieee80211n=1 # 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 below 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. # Spatial Multiplexing (SM) Power Save: [SMPS-STATIC] or [SMPS-DYNAMIC] # (SMPS disabled if neither is set) # HT-greenfield: [GF] (disabled if not set) # Short GI for 20 MHz: [SHORT-GI-20] (disabled if not set) # Short GI for 40 MHz: [SHORT-GI-40] (disabled if not set) # 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) # PSMP support: [PSMP] (disabled 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. This # is useful only for testing that stations properly set the OBSS interval, # since the other parameters in the OBSS scan parameters IE are set to 0. #obss_interval=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. #ieee80211ac=1 # 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] # 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] # 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 # # MU Beamformee Capable: [MU-BEAMFORMEE] # Indicates support for operation as an MU beamformee # 0 = Not supported or sent by AP (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 doesnt 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 ##### 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. #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). #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 ##### 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 # 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. # 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 # 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 # 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. In most cases, the default RSA authentication does # not use this configuration. However, it is possible setup RSA to use # ephemeral DH key exchange. In addition, ciphers with DSA keys always use # ephemeral DH keys. This can be used to achieve forward secrecy. If the file # is in DSA parameters format, it will be automatically converted into DH # params. This parameter is required if anonymous EAP-FAST is used. # You can generate DH parameters file with OpenSSL, e.g., # "openssl dhparam -out /etc/hostapd.dh.pem 1024" #dh_file=/etc/hostapd.dh.pem # 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 # 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 provid 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-SIM and EAP-AKA protected success/failure indication using AT_RESULT_IND # (default: 0 = disabled). #eap_sim_aka_result_ind=1 # 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 ##### IEEE 802.11f - Inter-Access Point Protocol (IAPP) ####################### # Interface to be used for IAPP broadcast packets #iapp_interface=eth0 ##### RADIUS client configuration ############################################# # for IEEE 802.1X with external Authentication Server, IEEE 802.11 # 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. # When using IEEE 802.11r, nas_identifier must be set and must be between 1 and # 48 octets long. #nas_identifier=ap.example.com # 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 # 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 # 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. # 0 = disabled (default) # 1 = option; 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 # 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. #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 # 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 #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 ##### 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 # 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) #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 (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) # 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 #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. # (dot11RSNAConfigAuthenticationSuitesTable) #wpa_key_mgmt=WPA-PSK WPA-EAP # Set of accepted cipher suites (encryption algorithms) for pairwise keys # (unicast packets). This is a space separated list of algorithms: # CCMP = AES in Counter mode with CBC-MAC [RFC 3610, IEEE 802.11i/D7.0] # TKIP = Temporal Key Integrity Protocol [IEEE 802.11i/D7.0] # 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. # (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 # Time interval for rekeying GTK (broadcast/multicast encryption keys) in # seconds. (dot11RSNAConfigGroupRekeyTime) #wpa_group_rekey=600 # Rekey GTK when any STA that possesses the current GTK is leaving the BSS. # (dot11RSNAConfigGroupRekeyStrict) #wpa_strict_rekey=1 # 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. #wpa_ptk_rekey=600 # 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 # peerkey: Whether PeerKey negotiation for direct links (IEEE 802.11e) is # allowed. This is only used with RSN/WPA2. # 0 = disabled (default) # 1 = enabled #peerkey=1 # ieee80211w: Whether management frame protection (MFP) is enabled # 0 = disabled (default) # 1 = optional # 2 = required #ieee80211w=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 # 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 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 # Enabled SAE finite cyclic groups # SAE implementation are required to support group 19 (ECC group defined over a # 256-bit prime order field). All groups that are supported by the # implementation 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-9 #sae_groups=19 20 21 25 26 ##### 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-RO in minutes; range 1..65535 # (dot11FTR0KeyLifetime) #r0_key_lifetime=10000 # PMK-R1 Key Holder identifier (dot11FTR1KeyHolderID) # 6-octet identifier as a hex string. #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: <128-bit key as hex string> # This list is used to map R0KH-ID (NAS Identifier) to a destination MAC # address when requesting PMK-R1 key from the R0KH that the STA used during the # Initial Mobility Domain Association. #r0kh=02:01:02:03:04:05 r0kh-1.example.com 000102030405060708090a0b0c0d0e0f #r0kh=02:01:02:03:04:06 r0kh-2.example.com 00112233445566778899aabbccddeeff # And so on.. One line per R0KH. # List of R1KHs in the same Mobility Domain # format: <128-bit key as hex string> # This list is used to map R1KH-ID to a destination MAC address when sending # PMK-R1 key from the R0KH. This is also the list of authorized R1KHs in the MD # that can request PMK-R1 keys. #r1kh=02:01:02:03:04:05 02:11:22:33:44:55 000102030405060708090a0b0c0d0e0f #r1kh=02:01:02:03:04:06 02:11:22:33:44:66 00112233445566778899aabbccddeeff # And so on.. One line per R1KH. # 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 ##### Neighbor table ########################################################## # Maximum number of entries kept in AP table (either for neigbor table or for # detecting Overlapping Legacy BSS Condition). The oldest entry will be # 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 ##### 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 # 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 # 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) # 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 ##### Wi-Fi Direct (P2P) ###################################################### # Enable P2P Device management #manage_p2p=1 # Allow cross connection #allow_cross_connection=1 #### 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 # BSS Transition Management # 0 = disabled (default) # 1 = enabled #bss_transition=1 ##### 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" # 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]>][...] # 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] # 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 # 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 ##### 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 ##### 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 # # 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 # ... wpa-2.1/hostapd/hostapd.deny000066400000000000000000000002201227414666700161410ustar00rootroot00000000000000# 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 wpa-2.1/hostapd/hostapd.eap_user000066400000000000000000000077251227414666700170260ustar00rootroot00000000000000# 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. # 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] wpa-2.1/hostapd/hostapd.eap_user_sqlite000066400000000000000000000007721227414666700204020ustar00rootroot00000000000000CREATE TABLE users( identity TEXT PRIMARY KEY, methods TEXT, password TEXT, phase2 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'); wpa-2.1/hostapd/hostapd.radius_clients000066400000000000000000000002161227414666700202170ustar00rootroot00000000000000# 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 wpa-2.1/hostapd/hostapd.sim_db000066400000000000000000000006311227414666700164450ustar00rootroot00000000000000# 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 wpa-2.1/hostapd/hostapd.vlan000066400000000000000000000004321227414666700161470ustar00rootroot00000000000000# 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# wpa-2.1/hostapd/hostapd.wpa_psk000066400000000000000000000010451227414666700166540ustar00rootroot00000000000000# 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). 00:00:00:00:00:00 secret passphrase 00:11:22:33:44:55 another passphrase 00:22:33:44:55:66 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef 00:00:00:00:00:00 another passphrase for all STAs wpa-2.1/hostapd/hostapd_cli.1000066400000000000000000000034751227414666700162100ustar00rootroot00000000000000.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). wpa-2.1/hostapd/hostapd_cli.c000066400000000000000000000730051227414666700162660ustar00rootroot00000000000000/* * hostapd - command line interface for hostapd daemon * Copyright (c) 2004-2014, 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 "utils/common.h" #include "utils/eloop.h" #include "utils/edit.h" #include "common/version.h" static const char *hostapd_cli_version = "hostapd_cli v" VERSION_STR "\n" "Copyright (c) 2004-2014, Jouni Malinen and contributors"; static const char *hostapd_cli_license = "This software may be distributed under the terms of the BSD license.\n" "See README for more details.\n"; static const char *hostapd_cli_full_license = "This software may be distributed under the terms of the BSD license.\n" "\n" "Redistribution and use in source and binary forms, with or without\n" "modification, are permitted provided that the following conditions are\n" "met:\n" "\n" "1. Redistributions of source code must retain the above copyright\n" " notice, this list of conditions and the following disclaimer.\n" "\n" "2. Redistributions in binary form must reproduce the above copyright\n" " notice, this list of conditions and the following disclaimer in the\n" " documentation and/or other materials provided with the distribution.\n" "\n" "3. Neither the name(s) of the above-listed copyright holder(s) nor the\n" " names of its contributors may be used to endorse or promote products\n" " derived from this software without specific prior written permission.\n" "\n" "THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n" "\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n" "LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n" "A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n" "OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n" "SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n" "LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n" "DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n" "THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n" "(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n" "OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n" "\n"; static const char *commands_help = "Commands:\n" " mib get MIB variables (dot1x, dot11, radius)\n" " sta get MIB variables for one station\n" " all_sta get MIB variables for all stations\n" " new_sta add a new station\n" " deauthenticate deauthenticate a station\n" " disassociate disassociate a station\n" #ifdef CONFIG_IEEE80211W " sa_query send SA Query to a station\n" #endif /* CONFIG_IEEE80211W */ #ifdef CONFIG_WPS " wps_pin [timeout] [addr] add WPS Enrollee PIN\n" " wps_check_pin verify PIN checksum\n" " wps_pbc indicate button pushed to initiate PBC\n" " wps_cancel cancel the pending WPS operation\n" #ifdef CONFIG_WPS_NFC " wps_nfc_tag_read report read NFC tag with WPS data\n" " wps_nfc_config_token build NFC configuration token\n" " wps_nfc_token manager NFC password token\n" #endif /* CONFIG_WPS_NFC */ " wps_ap_pin [params..] enable/disable AP PIN\n" " wps_config configure AP\n" " wps_get_status show current WPS status\n" #endif /* CONFIG_WPS */ " get_config show current configuration\n" " help show this usage help\n" " interface [ifname] show interfaces/select interface\n" " level change debug level\n" " license show full hostapd_cli license\n" " quit exit hostapd_cli\n"; 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 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 void usage(void) { fprintf(stderr, "%s\n", hostapd_cli_version); fprintf(stderr, "\n" "usage: hostapd_cli [-p] [-i] [-hvB] " "[-a] \\\n" " [-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" " -a run in daemon mode executing the action file " "based on events\n" " from hostapd\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" "%s", commands_help); } static struct wpa_ctrl * hostapd_cli_open_connection(const char *ifname) { char *cfile; int flen; if (ifname == NULL) return NULL; 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); ctrl_conn = wpa_ctrl_open(cfile); free(cfile); return ctrl_conn; } static void hostapd_cli_close_connection(void) { if (ctrl_conn == NULL) return; if (hostapd_cli_attached) { wpa_ctrl_detach(ctrl_conn); hostapd_cli_attached = 0; } wpa_ctrl_close(ctrl_conn); ctrl_conn = NULL; } static void hostapd_cli_msg_cb(char *msg, size_t len) { printf("%s\n", msg); } static int _wpa_ctrl_command(struct wpa_ctrl *ctrl, 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, char *cmd) { return _wpa_ctrl_command(ctrl, cmd, 1); } 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_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 *cmd; size_t len; int res; int ret = 0; len = os_strlen(program) + os_strlen(arg1) + os_strlen(arg2) + 3; cmd = os_malloc(len); if (cmd == NULL) return -1; res = os_snprintf(cmd, len, "%s %s %s", program, arg1, arg2); if (res < 0 || (size_t) res >= len) { os_free(cmd); return -1; } cmd[len - 1] = '\0'; #ifndef _WIN32_WCE if (system(cmd) < 0) ret = -1; #endif /* _WIN32_WCE */ os_free(cmd); return ret; } 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 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 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_IEEE80211W 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); } #endif /* CONFIG_IEEE80211W */ #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 (res < 0 || (size_t) res >= sizeof(cmd) - 1) { 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 (res < 0 || (size_t) res >= sizeof(cmd) - 1) { 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 (res < 0 || (size_t) res >= sizeof(cmd) - 1) { 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 (res < 0 || (size_t) res >= sizeof(cmd) - 1) { 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 * 32 + 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 < 32; 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 (res < 0 || res >= (int) sizeof(buf)) 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 (res < 0 || res >= (int) sizeof(buf)) return -1; 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, char *cmd, char *addr, size_t addr_len) { 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; 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))) return 0; do { snprintf(cmd, sizeof(cmd), "STA-NEXT %s", addr); } while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr)) == 0); return -1; } static int hostapd_cli_cmd_help(struct wpa_ctrl *ctrl, int argc, char *argv[]) { printf("%s", commands_help); return 0; } static int hostapd_cli_cmd_license(struct wpa_ctrl *ctrl, int argc, char *argv[]) { printf("%s\n\n%s\n", hostapd_cli_version, hostapd_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 (res < 0 || res >= (int) sizeof(buf)) 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 (res < 0 || res >= (int) sizeof(buf)) 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 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 " "openned.\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; } hostapd_cli_close_connection(); free(ctrl_ifname); ctrl_ifname = strdup(argv[0]); if (hostapd_cli_open_connection(ctrl_ifname)) { printf("Connected to interface '%s.\n", ctrl_ifname); if (wpa_ctrl_attach(ctrl_conn) == 0) { hostapd_cli_attached = 1; } else { printf("Warning: Failed to attach to " "hostapd.\n"); } } else { printf("Could not connect to interface '%s' - re-trying\n", ctrl_ifname); } return 0; } static int hostapd_cli_cmd_set(struct wpa_ctrl *ctrl, int argc, char *argv[]) { char cmd[256]; 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 (res < 0 || (size_t) res >= sizeof(cmd) - 1) { printf("Too long SET command.\n"); return -1; } return wpa_ctrl_command(ctrl, cmd); } 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 (res < 0 || (size_t) res >= sizeof(cmd) - 1) { printf("Too long GET command.\n"); return -1; } return wpa_ctrl_command(ctrl, cmd); } 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]\n"); return -1; } res = os_snprintf(cmd, sizeof(cmd), "CHAN_SWITCH %s %s", argv[0], argv[1]); if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { 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 (res < 0 || (size_t) res >= sizeof(cmd) - total - 1) { printf("Too long CHAN_SWITCH command.\n"); return -1; } total += res; } return wpa_ctrl_command(ctrl, cmd); } struct hostapd_cli_cmd { const char *cmd; int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]); }; static struct hostapd_cli_cmd hostapd_cli_commands[] = { { "ping", hostapd_cli_cmd_ping }, { "mib", hostapd_cli_cmd_mib }, { "relog", hostapd_cli_cmd_relog }, { "status", hostapd_cli_cmd_status }, { "sta", hostapd_cli_cmd_sta }, { "all_sta", hostapd_cli_cmd_all_sta }, { "new_sta", hostapd_cli_cmd_new_sta }, { "deauthenticate", hostapd_cli_cmd_deauthenticate }, { "disassociate", hostapd_cli_cmd_disassociate }, #ifdef CONFIG_IEEE80211W { "sa_query", hostapd_cli_cmd_sa_query }, #endif /* CONFIG_IEEE80211W */ #ifdef CONFIG_WPS { "wps_pin", hostapd_cli_cmd_wps_pin }, { "wps_check_pin", hostapd_cli_cmd_wps_check_pin }, { "wps_pbc", hostapd_cli_cmd_wps_pbc }, { "wps_cancel", hostapd_cli_cmd_wps_cancel }, #ifdef CONFIG_WPS_NFC { "wps_nfc_tag_read", hostapd_cli_cmd_wps_nfc_tag_read }, { "wps_nfc_config_token", hostapd_cli_cmd_wps_nfc_config_token }, { "wps_nfc_token", hostapd_cli_cmd_wps_nfc_token }, { "nfc_get_handover_sel", hostapd_cli_cmd_nfc_get_handover_sel }, #endif /* CONFIG_WPS_NFC */ { "wps_ap_pin", hostapd_cli_cmd_wps_ap_pin }, { "wps_config", hostapd_cli_cmd_wps_config }, { "wps_get_status", hostapd_cli_cmd_wps_get_status }, #endif /* CONFIG_WPS */ { "disassoc_imminent", hostapd_cli_cmd_disassoc_imminent }, { "ess_disassoc", hostapd_cli_cmd_ess_disassoc }, { "get_config", hostapd_cli_cmd_get_config }, { "help", hostapd_cli_cmd_help }, { "interface", hostapd_cli_cmd_interface }, { "level", hostapd_cli_cmd_level }, { "license", hostapd_cli_cmd_license }, { "quit", hostapd_cli_cmd_quit }, { "set", hostapd_cli_cmd_set }, { "get", hostapd_cli_cmd_get }, { "set_qos_map_set", hostapd_cli_cmd_set_qos_map_set }, { "send_qos_map_conf", hostapd_cli_cmd_send_qos_map_conf }, { "chan_switch", hostapd_cli_cmd_chan_switch }, { NULL, NULL } }; static void wpa_request(struct wpa_ctrl *ctrl, int argc, char *argv[]) { 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 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[256]; 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 { if (in_read && first) printf("\n"); first = 0; printf("%s\n", buf); } } else { printf("Could not read pending message.\n"); break; } } } #define max_args 10 static int tokenize_cmd(char *cmd, char *argv[]) { char *pos; int argc = 0; pos = cmd; for (;;) { while (*pos == ' ') pos++; if (*pos == '\0') break; argv[argc] = pos; argc++; if (argc == max_args) break; if (*pos == '"') { char *pos2 = os_strrchr(pos, '"'); if (pos2) pos = pos2 + 1; } while (*pos != '\0' && *pos != ' ') pos++; if (*pos == ' ') *pos++ = '\0'; } return argc; } 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) { ctrl_conn = hostapd_cli_open_connection(ctrl_ifname); if (ctrl_conn) { printf("Connection to hostapd re-established\n"); if (wpa_ctrl_attach(ctrl_conn) == 0) { hostapd_cli_attached = 1; } else { printf("Warning: Failed to attach to " "hostapd.\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 void hostapd_cli_interactive(void) { printf("\nInteractive mode\n\n"); eloop_register_signal_terminate(hostapd_cli_eloop_terminate, NULL); edit_init(hostapd_cli_edit_cmd_cb, hostapd_cli_edit_eof_cb, NULL, NULL, NULL, NULL); eloop_register_timeout(ping_interval, 0, hostapd_cli_ping, NULL, NULL); eloop_run(); edit_deinit(NULL, NULL); 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(struct wpa_ctrl *ctrl) { fd_set rfds; int fd, res; struct timeval tv; char buf[256]; size_t len; fd = wpa_ctrl_get_fd(ctrl); while (!hostapd_cli_quit) { FD_ZERO(&rfds); FD_SET(fd, &rfds); tv.tv_sec = ping_interval; tv.tv_usec = 0; res = select(fd + 1, &rfds, NULL, NULL, &tv); if (res < 0 && errno != EINTR) { perror("select"); break; } if (FD_ISSET(fd, &rfds)) hostapd_cli_recv_pending(ctrl, 0, 1); else { len = sizeof(buf) - 1; if (wpa_ctrl_request(ctrl, "PING", 4, buf, &len, hostapd_cli_action_process) < 0 || len < 4 || os_memcmp(buf, "PONG", 4) != 0) { printf("hostapd did not reply to PING " "command - exiting\n"); break; } } } } int main(int argc, char *argv[]) { int warning_displayed = 0; int c; int daemonize = 0; if (os_program_init()) return -1; for (;;) { c = getopt(argc, argv, "a:BhG:i:p: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; default: usage(); return -1; } } interactive = (argc == optind) && (action_file == NULL); if (interactive) { printf("%s\n\n%s\n\n", hostapd_cli_version, hostapd_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); } } ctrl_conn = hostapd_cli_open_connection(ctrl_ifname); if (ctrl_conn) { if (warning_displayed) printf("Connection established.\n"); break; } if (!interactive) { 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; } if (interactive || action_file) { if (wpa_ctrl_attach(ctrl_conn) == 0) { hostapd_cli_attached = 1; } else { printf("Warning: Failed to attach to hostapd.\n"); if (action_file) return -1; } } if (daemonize && os_daemonize(pid_file)) return -1; if (interactive) hostapd_cli_interactive(); else if (action_file) hostapd_cli_action(ctrl_conn); else wpa_request(ctrl_conn, argc - optind, &argv[optind]); os_free(ctrl_ifname); eloop_destroy(); hostapd_cli_cleanup(); return 0; } wpa-2.1/hostapd/logwatch/000077500000000000000000000000001227414666700154345ustar00rootroot00000000000000wpa-2.1/hostapd/logwatch/README000066400000000000000000000006511227414666700163160ustar00rootroot00000000000000Logwatch 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/ wpa-2.1/hostapd/logwatch/hostapd000077500000000000000000000030471227414666700170300ustar00rootroot00000000000000#!/usr/bin/perl -w # # Logwatch script 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 use strict; my $debug = $ENV{'LOGWATCH_DEBUG'} || 0; my $detail = $ENV{'LOGWATCH_DETAIL_LEVEL'} || 0; my $debugcounter = 1; my %hostapd; my @unmatched; if ($debug >= 5) { print STDERR "\n\nDEBUG: Inside HOSTAPD Filter\n\n"; } while (defined(my $line = )) { if ($debug >= 5) { print STDERR "DEBUG($debugcounter): $line"; $debugcounter++; } chomp($line); if (my ($iface,$mac,$layer,$details) = ($line =~ /(.*?): STA (.*?) (.*?): (.*?)$/i)) { unless ($detail == 10) { # collapse association events $details =~ s/^(associated) .*$/$1/i; } $hostapd{$iface}->{$mac}->{$layer}->{$details}++; } else { push @unmatched, "$line\n"; } } if (keys %hostapd) { foreach my $iface (sort keys %hostapd) { print "Interface $iface:\n"; foreach my $mac (sort keys %{$hostapd{$iface}}) { print " Client MAC Address $mac:\n"; foreach my $layer (sort keys %{$hostapd{$iface}->{$mac}}) { print " $layer:\n"; foreach my $details (sort keys %{$hostapd{$iface}->{$mac}->{$layer}}) { print " $details"; my $count = $hostapd{$iface}->{$mac}->{$layer}->{$details}; if ($count > 1) { print ": " . $count . " Times"; } print "\n"; } } } } } if ($#unmatched >= 0) { print "\n**Unmatched Entries**\n"; print @unmatched; } exit(0); wpa-2.1/hostapd/logwatch/hostapd.conf000066400000000000000000000004771227414666700177550ustar00rootroot00000000000000# 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 wpa-2.1/hostapd/main.c000066400000000000000000000423731227414666700147250ustar00rootroot00000000000000/* * hostapd / main() * Copyright (c) 2002-2011, 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 "crypto/random.h" #include "crypto/tls.h" #include "common/version.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 "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_IAPP: module_str = "IAPP"; 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, txt); else if (hapd && hapd->conf) os_snprintf(format, maxlen, "%s:%s%s %s", hapd->conf->iface, module_str ? " " : "", module_str, txt); else if (addr) os_snprintf(format, maxlen, "STA " MACSTR "%s%s: %s", MAC2STR(addr), module_str ? " " : "", module_str, txt); else os_snprintf(format, maxlen, "%s%s%s", module_str, module_str ? ": " : "", txt); 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; if (hapd->driver == NULL || hapd->driver->hapd_init == NULL) { wpa_printf(MSG_ERROR, "No hostapd driver wrapper available"); return -1; } /* Initialize the driver interface */ if (!(b[0] | b[1] | b[2] | b[3] | b[4] | b[5])) 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(); 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; params.ifname = hapd->conf->iface; params.ssid = hapd->conf->ssid.ssid; params.ssid_len = hapd->conf->ssid.ssid_len; params.test_socket = hapd->conf->test_socket; 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; } if (hapd->driver->get_capa && hapd->driver->get_capa(hapd->drv_priv, &capa) == 0) { iface->drv_flags = capa.flags; iface->probe_resp_offloads = capa.probe_resp_offloads; 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; } 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 actiual driver operations are started. */ static struct hostapd_iface * hostapd_interface_init(struct hapd_interfaces *interfaces, const char *config_fname, int debug) { struct hostapd_iface *iface; int k; wpa_printf(MSG_ERROR, "Configuration file: %s", config_fname); iface = hostapd_init(interfaces, config_fname); if (!iface) return NULL; 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", 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; } 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 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(); 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 && os_daemonize(pid_file)) { perror("daemon"); return -1; } eloop_run(); return 0; } static void show_version(void) { fprintf(stderr, "hostapd v" VERSION_STR "\n" "User space daemon for IEEE 802.11 AP management,\n" "IEEE 802.1X/WPA/WPA2/EAP/RADIUS Authenticator\n" "Copyright (c) 2002-2014, Jouni Malinen " "and contributors\n"); } static void usage(void) { show_version(); fprintf(stderr, "\n" "usage: hostapd [-hdBKtv] [-P ] [-e ] " "\\\n" " [-g ] [-G ] \\\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 */ " -t include timestamps in some debug messages\n" " -v show hostapd version\n"); exit(1); } static const char * hostapd_msg_ifname_cb(void *ctx) { struct hostapd_data *hapd = ctx; if (hapd && hapd->iconf && hapd->iconf->bss && hapd->iconf->num_bss > 0 && hapd->iconf->bss[0]) return hapd->iconf->bss[0]->iface; return NULL; } static int hostapd_get_global_ctrl_iface(struct hapd_interfaces *interfaces, const char *path) { char *pos; os_free(interfaces->global_iface_path); interfaces->global_iface_path = os_strdup(path); if (interfaces->global_iface_path == NULL) return -1; 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; 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; } 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 */ 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; for (;;) { c = getopt(argc, argv, "b:Bde:f:hKP:Ttvg:G:"); 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); break; 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; 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); #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 initilize global context"); return -1; } /* Allocate and parse configuration for full interface files */ for (i = 0; i < interfaces.count; i++) { interfaces.iface[i] = hostapd_interface_init(&interfaces, argv[optind + i], debug); if (!interfaces.iface[i]) { wpa_printf(MSG_ERROR, "Failed to initialize interface"); goto out; } } /* 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++) hostapd_interface_deinit_free(interfaces.iface[i]); os_free(interfaces.iface); hostapd_global_deinit(pid_file); os_free(pid_file); if (log_file) wpa_debug_close_file(); wpa_debug_close_linux_tracing(); os_free(bss_config); os_program_deinit(); return ret; } wpa-2.1/hostapd/nt_password_hash.c000066400000000000000000000016321227414666700173400ustar00rootroot00000000000000/* * 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; } wpa-2.1/hostapd/wired.conf000066400000000000000000000022001227414666700155770ustar00rootroot00000000000000##### 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 wpa-2.1/hostapd/wps-ap-nfc.py000077500000000000000000000175031227414666700161620ustar00rootroot00000000000000#!/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 def wpas_connect(): ifaces = [] if os.path.isdir(wpas_ctrl): try: ifaces = [os.path.join(wpas_ctrl, i) for i in os.listdir(wpas_ctrl)] except OSError, error: print "Could not find 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, e: pass return None def wpas_tag_read(message): wpas = wpas_connect() if (wpas == None): return 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 return wpas.request("WPS_NFC_CONFIG_TOKEN NDEF").rstrip().decode("hex") def wpas_get_password_token(): wpas = wpas_connect() if (wpas == None): return None return wpas.request("WPS_NFC_TOKEN NDEF").rstrip().decode("hex") def wpas_get_handover_sel(): wpas = wpas_connect() if (wpas == None): return None return wpas.request("NFC_GET_HANDOVER_SEL NDEF WPS-CR").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 def process_request(self, request): print "HandoverServer - request received" try: print "Parsed handover request: " + request.pretty() except Exception, 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": print "WPS carrier type match - add WPS carrier record" data = wpas_get_handover_sel() if data is None: print "Could not get handover select carrier record from hostapd" continue print "Handover select carrier record from hostapd:" print data.encode("hex") wpas_report_handover(carrier.record, data) message = nfc.ndef.Message(data); sel.add_carrier(message[0], "active", message[1:]) print "Handover select:" try: print sel.pretty() except Exception, e: print e print str(sel).encode("hex") print "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": print "WPS tag - send to hostapd" success = wpas_tag_read(tag.ndef.message) break else: print "Empty tag" return success def rdwr_connected_write(tag): print "Tag found - writing" global write_data tag.ndef.message = str(write_data) 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): print "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: print "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): print "Write WPS password token" global write_data, write_wait_remove write_wait_remove = wait_remove write_data = wpas_get_password_token() if write_data == None: print "Could not get WPS password token from hostapd" return print "Touch an NFC tag" clf.connect(rdwr={'on-connect': rdwr_connected_write}) def rdwr_connected(tag): global only_one, no_wait print "Tag connected: " + str(tag) if tag.ndef: print "NDEF tag: " + tag.type try: print tag.ndef.message.pretty() except Exception, e: print e success = wps_tag_read(tag) if only_one and success: global continue_loop continue_loop = False else: print "Not an NDEF tag - remove tag" 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('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 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, 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() wpa-2.1/patches/000077500000000000000000000000001227414666700136115ustar00rootroot00000000000000wpa-2.1/patches/openssl-0.9.8-tls-extensions.patch000066400000000000000000000352461227414666700217760ustar00rootroot00000000000000This patch is adding support for TLS hello extensions and externally generated pre-shared key material to OpenSSL 0.9.8. This is based on the patch from Alexey Kobozev (sent to openssl-dev mailing list on Tue, 07 Jun 2005 15:40:58 +0300). diff -uprN openssl-0.9.8.orig/include/openssl/ssl.h openssl-0.9.8/include/openssl/ssl.h --- openssl-0.9.8.orig/include/openssl/ssl.h 2005-06-10 12:51:16.000000000 -0700 +++ openssl-0.9.8/include/openssl/ssl.h 2005-07-19 20:02:15.000000000 -0700 @@ -340,6 +340,7 @@ extern "C" { * 'struct ssl_st *' function parameters used to prototype callbacks * in SSL_CTX. */ typedef struct ssl_st *ssl_crock_st; +typedef struct tls_extension_st TLS_EXTENSION; /* used to hold info on the particular ciphers used */ typedef struct ssl_cipher_st @@ -361,6 +362,8 @@ DECLARE_STACK_OF(SSL_CIPHER) typedef struct ssl_st SSL; typedef struct ssl_ctx_st SSL_CTX; +typedef int (*tls_session_secret_cb_fn)(SSL *s, void *secret, int *secret_len, STACK_OF(SSL_CIPHER) *peer_ciphers, SSL_CIPHER **cipher, void *arg); + /* Used to hold functions for SSLv2 or SSLv3/TLSv1 functions */ typedef struct ssl_method_st { @@ -968,6 +971,15 @@ struct ssl_st int first_packet; int client_version; /* what was passed, used for * SSLv3/TLS rollback check */ + + /* TLS externsions */ + TLS_EXTENSION *tls_extension; + int (*tls_extension_cb)(SSL *s, TLS_EXTENSION *tls_ext, void *arg); + void *tls_extension_cb_arg; + + /* TLS pre-shared secret session resumption */ + tls_session_secret_cb_fn tls_session_secret_cb; + void *tls_session_secret_cb_arg; }; #ifdef __cplusplus @@ -1533,6 +1545,13 @@ void *SSL_COMP_get_compression_methods(v int SSL_COMP_add_compression_method(int id,void *cm); #endif +/* TLS extensions functions */ +int SSL_set_hello_extension(SSL *s, int ext_type, void *ext_data, int ext_len); +int SSL_set_hello_extension_cb(SSL *s, int (*cb)(SSL *, TLS_EXTENSION *, void *), void *arg); + +/* Pre-shared secret session resumption functions */ +int SSL_set_session_secret_cb(SSL *s, tls_session_secret_cb_fn tls_session_secret_cb, void *arg); + /* BEGIN ERROR CODES */ /* The following lines are auto generated by the script mkerr.pl. Any changes * made after this point may be overwritten when the script is next run. @@ -1714,6 +1733,7 @@ void ERR_load_SSL_strings(void); #define SSL_F_TLS1_ENC 210 #define SSL_F_TLS1_SETUP_KEY_BLOCK 211 #define SSL_F_WRITE_PENDING 212 +#define SSL_F_SSL_SET_HELLO_EXTENSION 213 /* Reason codes. */ #define SSL_R_APP_DATA_IN_HANDSHAKE 100 diff -uprN openssl-0.9.8.orig/include/openssl/tls1.h openssl-0.9.8/include/openssl/tls1.h --- openssl-0.9.8.orig/include/openssl/tls1.h 2003-07-22 05:34:21.000000000 -0700 +++ openssl-0.9.8/include/openssl/tls1.h 2005-07-19 20:02:15.000000000 -0700 @@ -282,6 +282,14 @@ extern "C" { #define TLS_MD_MASTER_SECRET_CONST "\x6d\x61\x73\x74\x65\x72\x20\x73\x65\x63\x72\x65\x74" /*master secret*/ #endif +/* TLS extension struct */ +struct tls_extension_st +{ + unsigned short type; + unsigned short length; + void *data; +}; + #ifdef __cplusplus } #endif diff -uprN openssl-0.9.8.orig/ssl/Makefile openssl-0.9.8/ssl/Makefile --- openssl-0.9.8.orig/ssl/Makefile 2005-05-30 16:20:30.000000000 -0700 +++ openssl-0.9.8/ssl/Makefile 2005-07-19 20:02:15.000000000 -0700 @@ -24,7 +24,7 @@ LIBSRC= \ s2_meth.c s2_srvr.c s2_clnt.c s2_lib.c s2_enc.c s2_pkt.c \ s3_meth.c s3_srvr.c s3_clnt.c s3_lib.c s3_enc.c s3_pkt.c s3_both.c \ s23_meth.c s23_srvr.c s23_clnt.c s23_lib.c s23_pkt.c \ - t1_meth.c t1_srvr.c t1_clnt.c t1_lib.c t1_enc.c \ + t1_meth.c t1_srvr.c t1_clnt.c t1_lib.c t1_enc.c t1_ext.c \ d1_meth.c d1_srvr.c d1_clnt.c d1_lib.c d1_pkt.c \ d1_both.c d1_enc.c \ ssl_lib.c ssl_err2.c ssl_cert.c ssl_sess.c \ @@ -35,7 +35,7 @@ LIBOBJ= \ s2_meth.o s2_srvr.o s2_clnt.o s2_lib.o s2_enc.o s2_pkt.o \ s3_meth.o s3_srvr.o s3_clnt.o s3_lib.o s3_enc.o s3_pkt.o s3_both.o \ s23_meth.o s23_srvr.o s23_clnt.o s23_lib.o s23_pkt.o \ - t1_meth.o t1_srvr.o t1_clnt.o t1_lib.o t1_enc.o \ + t1_meth.o t1_srvr.o t1_clnt.o t1_lib.o t1_enc.o t1_ext.o \ d1_meth.o d1_srvr.o d1_clnt.o d1_lib.o d1_pkt.o \ d1_both.o d1_enc.o \ ssl_lib.o ssl_err2.o ssl_cert.o ssl_sess.o \ @@ -968,3 +968,4 @@ t1_srvr.o: ../include/openssl/ssl23.h .. t1_srvr.o: ../include/openssl/stack.h ../include/openssl/symhacks.h t1_srvr.o: ../include/openssl/tls1.h ../include/openssl/x509.h t1_srvr.o: ../include/openssl/x509_vfy.h ssl_locl.h t1_srvr.c +t1_ext.o: t1_ext.c ssl_locl.h diff -uprN openssl-0.9.8.orig/ssl/s3_clnt.c openssl-0.9.8/ssl/s3_clnt.c --- openssl-0.9.8.orig/ssl/s3_clnt.c 2005-05-16 03:11:03.000000000 -0700 +++ openssl-0.9.8/ssl/s3_clnt.c 2005-07-19 20:02:15.000000000 -0700 @@ -606,6 +606,20 @@ int ssl3_client_hello(SSL *s) } *(p++)=0; /* Add the NULL method */ + /* send client hello extensions if any */ + if (s->version >= TLS1_VERSION && s->tls_extension) + { + // set the total extensions length + s2n(s->tls_extension->length + 4, p); + + // put the extensions with type and length + s2n(s->tls_extension->type, p); + s2n(s->tls_extension->length, p); + + memcpy(p, s->tls_extension->data, s->tls_extension->length); + p+=s->tls_extension->length; + } + l=(p-d); d=buf; *(d++)=SSL3_MT_CLIENT_HELLO; @@ -628,7 +642,7 @@ int ssl3_get_server_hello(SSL *s) STACK_OF(SSL_CIPHER) *sk; SSL_CIPHER *c; unsigned char *p,*d; - int i,al,ok; + int i,al,ok,pre_shared; unsigned int j; long n; SSL_COMP *comp; @@ -693,7 +707,24 @@ int ssl3_get_server_hello(SSL *s) goto f_err; } - if (j != 0 && j == s->session->session_id_length + /* check if we want to resume the session based on external pre-shared secret */ + pre_shared = 0; + if (s->version >= TLS1_VERSION && s->tls_session_secret_cb) + { + SSL_CIPHER *pref_cipher=NULL; + s->session->master_key_length=sizeof(s->session->master_key); + if (s->tls_session_secret_cb(s, s->session->master_key, &s->session->master_key_length, + NULL, &pref_cipher, s->tls_session_secret_cb_arg)) + { + s->hit=1; + s->session->cipher=pref_cipher ? pref_cipher : ssl_get_cipher_by_char(s,p+j); + s->session->session_id_length = j; + memcpy(s->session->session_id, p, j); + pre_shared = 1; + } + } + + if ((pre_shared || j != 0) && j == s->session->session_id_length && memcmp(p,s->session->session_id,j) == 0) { if(s->sid_ctx_length != s->session->sid_ctx_length diff -uprN openssl-0.9.8.orig/ssl/s3_srvr.c openssl-0.9.8/ssl/s3_srvr.c --- openssl-0.9.8.orig/ssl/s3_srvr.c 2005-05-22 17:32:55.000000000 -0700 +++ openssl-0.9.8/ssl/s3_srvr.c 2005-07-19 20:02:15.000000000 -0700 @@ -955,6 +955,75 @@ int ssl3_get_client_hello(SSL *s) } #endif + /* Check for TLS client hello extension here */ + if (p < (d+n) && s->version >= TLS1_VERSION) + { + if (s->tls_extension_cb) + { + TLS_EXTENSION tls_ext; + unsigned short ext_total_len; + + n2s(p, ext_total_len); + n2s(p, tls_ext.type); + n2s(p, tls_ext.length); + + // sanity check in TLS extension len + if (tls_ext.length > (d+n) - p) + { + // just cut the lenth to packet border + tls_ext.length = (d+n) - p; + } + + tls_ext.data = p; + + // returns an alert code or 0 + al = s->tls_extension_cb(s, &tls_ext, s->tls_extension_cb_arg); + if (al != 0) + { + SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO,SSL_R_PEER_ERROR); + goto f_err; + } + } + } + + /* Check if we want to use external pre-shared secret for this handshake */ + /* for not reused session only */ + if (!s->hit && s->version >= TLS1_VERSION && s->tls_session_secret_cb) + { + SSL_CIPHER *pref_cipher=NULL; + + s->session->master_key_length=sizeof(s->session->master_key); + if(s->tls_session_secret_cb(s, s->session->master_key, &s->session->master_key_length, + ciphers, &pref_cipher, s->tls_session_secret_cb_arg)) + { + s->hit=1; + s->session->ciphers=ciphers; + s->session->verify_result=X509_V_OK; + + ciphers=NULL; + + /* check if some cipher was preferred by call back */ + pref_cipher=pref_cipher ? pref_cipher : ssl3_choose_cipher(s, s->session->ciphers, SSL_get_ciphers(s)); + if (pref_cipher == NULL) + { + al=SSL_AD_HANDSHAKE_FAILURE; + SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO,SSL_R_NO_SHARED_CIPHER); + goto f_err; + } + + s->session->cipher=pref_cipher; + + if (s->cipher_list) + sk_SSL_CIPHER_free(s->cipher_list); + + if (s->cipher_list_by_id) + sk_SSL_CIPHER_free(s->cipher_list_by_id); + + s->cipher_list = sk_SSL_CIPHER_dup(s->session->ciphers); + s->cipher_list_by_id = sk_SSL_CIPHER_dup(s->session->ciphers); + } + } + /* Given s->session->ciphers and SSL_get_ciphers, we must * pick a cipher */ diff -uprN openssl-0.9.8.orig/ssl/ssl_err.c openssl-0.9.8/ssl/ssl_err.c --- openssl-0.9.8.orig/ssl/ssl_err.c 2005-06-10 12:51:16.000000000 -0700 +++ openssl-0.9.8/ssl/ssl_err.c 2005-07-19 20:02:15.000000000 -0700 @@ -242,6 +242,7 @@ static ERR_STRING_DATA SSL_str_functs[]= {ERR_FUNC(SSL_F_TLS1_ENC), "TLS1_ENC"}, {ERR_FUNC(SSL_F_TLS1_SETUP_KEY_BLOCK), "TLS1_SETUP_KEY_BLOCK"}, {ERR_FUNC(SSL_F_WRITE_PENDING), "WRITE_PENDING"}, +{ERR_FUNC(SSL_F_SSL_SET_HELLO_EXTENSION), "SSL_set_hello_extension"}, {0,NULL} }; diff -uprN openssl-0.9.8.orig/ssl/ssl.h openssl-0.9.8/ssl/ssl.h --- openssl-0.9.8.orig/ssl/ssl.h 2005-06-10 12:51:16.000000000 -0700 +++ openssl-0.9.8/ssl/ssl.h 2005-07-19 20:02:15.000000000 -0700 @@ -340,6 +340,7 @@ extern "C" { * 'struct ssl_st *' function parameters used to prototype callbacks * in SSL_CTX. */ typedef struct ssl_st *ssl_crock_st; +typedef struct tls_extension_st TLS_EXTENSION; /* used to hold info on the particular ciphers used */ typedef struct ssl_cipher_st @@ -361,6 +362,8 @@ DECLARE_STACK_OF(SSL_CIPHER) typedef struct ssl_st SSL; typedef struct ssl_ctx_st SSL_CTX; +typedef int (*tls_session_secret_cb_fn)(SSL *s, void *secret, int *secret_len, STACK_OF(SSL_CIPHER) *peer_ciphers, SSL_CIPHER **cipher, void *arg); + /* Used to hold functions for SSLv2 or SSLv3/TLSv1 functions */ typedef struct ssl_method_st { @@ -968,6 +971,15 @@ struct ssl_st int first_packet; int client_version; /* what was passed, used for * SSLv3/TLS rollback check */ + + /* TLS externsions */ + TLS_EXTENSION *tls_extension; + int (*tls_extension_cb)(SSL *s, TLS_EXTENSION *tls_ext, void *arg); + void *tls_extension_cb_arg; + + /* TLS pre-shared secret session resumption */ + tls_session_secret_cb_fn tls_session_secret_cb; + void *tls_session_secret_cb_arg; }; #ifdef __cplusplus @@ -1533,6 +1545,13 @@ void *SSL_COMP_get_compression_methods(v int SSL_COMP_add_compression_method(int id,void *cm); #endif +/* TLS extensions functions */ +int SSL_set_hello_extension(SSL *s, int ext_type, void *ext_data, int ext_len); +int SSL_set_hello_extension_cb(SSL *s, int (*cb)(SSL *, TLS_EXTENSION *, void *), void *arg); + +/* Pre-shared secret session resumption functions */ +int SSL_set_session_secret_cb(SSL *s, tls_session_secret_cb_fn tls_session_secret_cb, void *arg); + /* BEGIN ERROR CODES */ /* The following lines are auto generated by the script mkerr.pl. Any changes * made after this point may be overwritten when the script is next run. @@ -1714,6 +1733,7 @@ void ERR_load_SSL_strings(void); #define SSL_F_TLS1_ENC 210 #define SSL_F_TLS1_SETUP_KEY_BLOCK 211 #define SSL_F_WRITE_PENDING 212 +#define SSL_F_SSL_SET_HELLO_EXTENSION 213 /* Reason codes. */ #define SSL_R_APP_DATA_IN_HANDSHAKE 100 diff -uprN openssl-0.9.8.orig/ssl/ssl_sess.c openssl-0.9.8/ssl/ssl_sess.c --- openssl-0.9.8.orig/ssl/ssl_sess.c 2005-04-29 13:10:06.000000000 -0700 +++ openssl-0.9.8/ssl/ssl_sess.c 2005-07-19 20:02:15.000000000 -0700 @@ -656,6 +656,15 @@ long SSL_CTX_get_timeout(const SSL_CTX * return(s->session_timeout); } +int SSL_set_session_secret_cb(SSL *s, int (*tls_session_secret_cb)(SSL *s, void *secret, int *secret_len, + STACK_OF(SSL_CIPHER) *peer_ciphers, SSL_CIPHER **cipher, void *arg), void *arg) +{ + if (s == NULL) return(0); + s->tls_session_secret_cb = tls_session_secret_cb; + s->tls_session_secret_cb_arg = arg; + return(1); +} + typedef struct timeout_param_st { SSL_CTX *ctx; diff -uprN openssl-0.9.8.orig/ssl/t1_ext.c openssl-0.9.8/ssl/t1_ext.c --- openssl-0.9.8.orig/ssl/t1_ext.c 1969-12-31 16:00:00.000000000 -0800 +++ openssl-0.9.8/ssl/t1_ext.c 2005-07-19 20:03:29.000000000 -0700 @@ -0,0 +1,48 @@ + +#include +#include "ssl_locl.h" + + +int SSL_set_hello_extension(SSL *s, int ext_type, void *ext_data, int ext_len) +{ + if(s->version >= TLS1_VERSION) + { + if(s->tls_extension) + { + OPENSSL_free(s->tls_extension); + s->tls_extension = NULL; + } + + if(ext_data) + { + s->tls_extension = OPENSSL_malloc(sizeof(TLS_EXTENSION) + ext_len); + if(!s->tls_extension) + { + SSLerr(SSL_F_SSL_SET_HELLO_EXTENSION, ERR_R_MALLOC_FAILURE); + return 0; + } + + s->tls_extension->type = ext_type; + s->tls_extension->length = ext_len; + s->tls_extension->data = s->tls_extension + 1; + memcpy(s->tls_extension->data, ext_data, ext_len); + } + + return 1; + } + + return 0; +} + +int SSL_set_hello_extension_cb(SSL *s, int (*cb)(SSL *, TLS_EXTENSION *, void *), void *arg) +{ + if(s->version >= TLS1_VERSION) + { + s->tls_extension_cb = cb; + s->tls_extension_cb_arg = arg; + + return 1; + } + + return 0; +} diff -uprN openssl-0.9.8.orig/ssl/t1_lib.c openssl-0.9.8/ssl/t1_lib.c --- openssl-0.9.8.orig/ssl/t1_lib.c 2005-04-26 09:02:40.000000000 -0700 +++ openssl-0.9.8/ssl/t1_lib.c 2005-07-19 20:02:15.000000000 -0700 @@ -131,6 +131,10 @@ int tls1_new(SSL *s) void tls1_free(SSL *s) { + if(s->tls_extension) + { + OPENSSL_free(s->tls_extension); + } ssl3_free(s); } diff -uprN openssl-0.9.8.orig/ssl/tls1.h openssl-0.9.8/ssl/tls1.h --- openssl-0.9.8.orig/ssl/tls1.h 2003-07-22 05:34:21.000000000 -0700 +++ openssl-0.9.8/ssl/tls1.h 2005-07-19 20:02:15.000000000 -0700 @@ -282,6 +282,14 @@ extern "C" { #define TLS_MD_MASTER_SECRET_CONST "\x6d\x61\x73\x74\x65\x72\x20\x73\x65\x63\x72\x65\x74" /*master secret*/ #endif +/* TLS extension struct */ +struct tls_extension_st +{ + unsigned short type; + unsigned short length; + void *data; +}; + #ifdef __cplusplus } #endif diff -uprN openssl-0.9.8.orig/util/ssleay.num openssl-0.9.8/util/ssleay.num --- openssl-0.9.8.orig/util/ssleay.num 2005-05-08 17:22:02.000000000 -0700 +++ openssl-0.9.8/util/ssleay.num 2005-07-19 20:02:15.000000000 -0700 @@ -226,3 +226,6 @@ DTLSv1_server_method SSL_COMP_get_compression_methods 276 EXIST:!VMS:FUNCTION:COMP SSL_COMP_get_compress_methods 276 EXIST:VMS:FUNCTION:COMP SSL_SESSION_get_id 277 EXIST::FUNCTION: +SSL_set_hello_extension 278 EXIST::FUNCTION: +SSL_set_hello_extension_cb 279 EXIST::FUNCTION: +SSL_set_session_secret_cb 280 EXIST::FUNCTION: wpa-2.1/patches/openssl-0.9.8d-tls-extensions.patch000066400000000000000000000353361227414666700221420ustar00rootroot00000000000000This patch is adding support for TLS hello extensions and externally generated pre-shared key material to OpenSSL 0.9.8d. This is based on the patch from Alexey Kobozev (sent to openssl-dev mailing list on Tue, 07 Jun 2005 15:40:58 +0300). diff -uprN openssl-0.9.8d.orig/include/openssl/ssl.h openssl-0.9.8d/include/openssl/ssl.h --- openssl-0.9.8d.orig/include/openssl/ssl.h 2006-06-14 06:52:49.000000000 -0700 +++ openssl-0.9.8d/include/openssl/ssl.h 2006-12-10 08:20:02.000000000 -0800 @@ -345,6 +345,7 @@ extern "C" { * 'struct ssl_st *' function parameters used to prototype callbacks * in SSL_CTX. */ typedef struct ssl_st *ssl_crock_st; +typedef struct tls_extension_st TLS_EXTENSION; /* used to hold info on the particular ciphers used */ typedef struct ssl_cipher_st @@ -366,6 +367,8 @@ DECLARE_STACK_OF(SSL_CIPHER) typedef struct ssl_st SSL; typedef struct ssl_ctx_st SSL_CTX; +typedef int (*tls_session_secret_cb_fn)(SSL *s, void *secret, int *secret_len, STACK_OF(SSL_CIPHER) *peer_ciphers, SSL_CIPHER **cipher, void *arg); + /* Used to hold functions for SSLv2 or SSLv3/TLSv1 functions */ typedef struct ssl_method_st { @@ -973,6 +976,15 @@ struct ssl_st int first_packet; int client_version; /* what was passed, used for * SSLv3/TLS rollback check */ + + /* TLS externsions */ + TLS_EXTENSION *tls_extension; + int (*tls_extension_cb)(SSL *s, TLS_EXTENSION *tls_ext, void *arg); + void *tls_extension_cb_arg; + + /* TLS pre-shared secret session resumption */ + tls_session_secret_cb_fn tls_session_secret_cb; + void *tls_session_secret_cb_arg; }; #ifdef __cplusplus @@ -1538,6 +1550,13 @@ void *SSL_COMP_get_compression_methods(v int SSL_COMP_add_compression_method(int id,void *cm); #endif +/* TLS extensions functions */ +int SSL_set_hello_extension(SSL *s, int ext_type, void *ext_data, int ext_len); +int SSL_set_hello_extension_cb(SSL *s, int (*cb)(SSL *, TLS_EXTENSION *, void *), void *arg); + +/* Pre-shared secret session resumption functions */ +int SSL_set_session_secret_cb(SSL *s, tls_session_secret_cb_fn tls_session_secret_cb, void *arg); + /* BEGIN ERROR CODES */ /* The following lines are auto generated by the script mkerr.pl. Any changes * made after this point may be overwritten when the script is next run. @@ -1719,6 +1738,7 @@ void ERR_load_SSL_strings(void); #define SSL_F_TLS1_ENC 210 #define SSL_F_TLS1_SETUP_KEY_BLOCK 211 #define SSL_F_WRITE_PENDING 212 +#define SSL_F_SSL_SET_HELLO_EXTENSION 213 /* Reason codes. */ #define SSL_R_APP_DATA_IN_HANDSHAKE 100 diff -uprN openssl-0.9.8d.orig/include/openssl/tls1.h openssl-0.9.8d/include/openssl/tls1.h --- openssl-0.9.8d.orig/include/openssl/tls1.h 2006-06-14 10:52:01.000000000 -0700 +++ openssl-0.9.8d/include/openssl/tls1.h 2006-12-10 08:20:02.000000000 -0800 @@ -296,6 +296,14 @@ extern "C" { #define TLS_MD_MASTER_SECRET_CONST "\x6d\x61\x73\x74\x65\x72\x20\x73\x65\x63\x72\x65\x74" /*master secret*/ #endif +/* TLS extension struct */ +struct tls_extension_st +{ + unsigned short type; + unsigned short length; + void *data; +}; + #ifdef __cplusplus } #endif diff -uprN openssl-0.9.8d.orig/ssl/Makefile openssl-0.9.8d/ssl/Makefile --- openssl-0.9.8d.orig/ssl/Makefile 2006-02-03 17:49:35.000000000 -0800 +++ openssl-0.9.8d/ssl/Makefile 2006-12-10 08:20:02.000000000 -0800 @@ -24,7 +24,7 @@ LIBSRC= \ s2_meth.c s2_srvr.c s2_clnt.c s2_lib.c s2_enc.c s2_pkt.c \ s3_meth.c s3_srvr.c s3_clnt.c s3_lib.c s3_enc.c s3_pkt.c s3_both.c \ s23_meth.c s23_srvr.c s23_clnt.c s23_lib.c s23_pkt.c \ - t1_meth.c t1_srvr.c t1_clnt.c t1_lib.c t1_enc.c \ + t1_meth.c t1_srvr.c t1_clnt.c t1_lib.c t1_enc.c t1_ext.c \ d1_meth.c d1_srvr.c d1_clnt.c d1_lib.c d1_pkt.c \ d1_both.c d1_enc.c \ ssl_lib.c ssl_err2.c ssl_cert.c ssl_sess.c \ @@ -35,7 +35,7 @@ LIBOBJ= \ s2_meth.o s2_srvr.o s2_clnt.o s2_lib.o s2_enc.o s2_pkt.o \ s3_meth.o s3_srvr.o s3_clnt.o s3_lib.o s3_enc.o s3_pkt.o s3_both.o \ s23_meth.o s23_srvr.o s23_clnt.o s23_lib.o s23_pkt.o \ - t1_meth.o t1_srvr.o t1_clnt.o t1_lib.o t1_enc.o \ + t1_meth.o t1_srvr.o t1_clnt.o t1_lib.o t1_enc.o t1_ext.o \ d1_meth.o d1_srvr.o d1_clnt.o d1_lib.o d1_pkt.o \ d1_both.o d1_enc.o \ ssl_lib.o ssl_err2.o ssl_cert.o ssl_sess.o \ @@ -968,3 +968,4 @@ t1_srvr.o: ../include/openssl/ssl23.h .. t1_srvr.o: ../include/openssl/stack.h ../include/openssl/symhacks.h t1_srvr.o: ../include/openssl/tls1.h ../include/openssl/x509.h t1_srvr.o: ../include/openssl/x509_vfy.h ssl_locl.h t1_srvr.c +t1_ext.o: t1_ext.c ssl_locl.h diff -uprN openssl-0.9.8d.orig/ssl/s3_clnt.c openssl-0.9.8d/ssl/s3_clnt.c --- openssl-0.9.8d.orig/ssl/s3_clnt.c 2005-12-12 23:41:46.000000000 -0800 +++ openssl-0.9.8d/ssl/s3_clnt.c 2006-12-10 08:20:02.000000000 -0800 @@ -601,6 +601,20 @@ int ssl3_client_hello(SSL *s) #endif *(p++)=0; /* Add the NULL method */ + /* send client hello extensions if any */ + if (s->version >= TLS1_VERSION && s->tls_extension) + { + // set the total extensions length + s2n(s->tls_extension->length + 4, p); + + // put the extensions with type and length + s2n(s->tls_extension->type, p); + s2n(s->tls_extension->length, p); + + memcpy(p, s->tls_extension->data, s->tls_extension->length); + p+=s->tls_extension->length; + } + l=(p-d); d=buf; *(d++)=SSL3_MT_CLIENT_HELLO; @@ -623,7 +637,7 @@ int ssl3_get_server_hello(SSL *s) STACK_OF(SSL_CIPHER) *sk; SSL_CIPHER *c; unsigned char *p,*d; - int i,al,ok; + int i,al,ok,pre_shared; unsigned int j; long n; #ifndef OPENSSL_NO_COMP @@ -690,7 +704,24 @@ int ssl3_get_server_hello(SSL *s) goto f_err; } - if (j != 0 && j == s->session->session_id_length + /* check if we want to resume the session based on external pre-shared secret */ + pre_shared = 0; + if (s->version >= TLS1_VERSION && s->tls_session_secret_cb) + { + SSL_CIPHER *pref_cipher=NULL; + s->session->master_key_length=sizeof(s->session->master_key); + if (s->tls_session_secret_cb(s, s->session->master_key, &s->session->master_key_length, + NULL, &pref_cipher, s->tls_session_secret_cb_arg)) + { + s->hit=1; + s->session->cipher=pref_cipher ? pref_cipher : ssl_get_cipher_by_char(s,p+j); + s->session->session_id_length = j; + memcpy(s->session->session_id, p, j); + pre_shared = 1; + } + } + + if ((pre_shared || j != 0) && j == s->session->session_id_length && memcmp(p,s->session->session_id,j) == 0) { if(s->sid_ctx_length != s->session->sid_ctx_length diff -uprN openssl-0.9.8d.orig/ssl/s3_srvr.c openssl-0.9.8d/ssl/s3_srvr.c --- openssl-0.9.8d.orig/ssl/s3_srvr.c 2006-09-28 04:29:03.000000000 -0700 +++ openssl-0.9.8d/ssl/s3_srvr.c 2006-12-10 08:20:02.000000000 -0800 @@ -943,6 +943,75 @@ int ssl3_get_client_hello(SSL *s) } #endif + /* Check for TLS client hello extension here */ + if (p < (d+n) && s->version >= TLS1_VERSION) + { + if (s->tls_extension_cb) + { + TLS_EXTENSION tls_ext; + unsigned short ext_total_len; + + n2s(p, ext_total_len); + n2s(p, tls_ext.type); + n2s(p, tls_ext.length); + + // sanity check in TLS extension len + if (tls_ext.length > (d+n) - p) + { + // just cut the lenth to packet border + tls_ext.length = (d+n) - p; + } + + tls_ext.data = p; + + // returns an alert code or 0 + al = s->tls_extension_cb(s, &tls_ext, s->tls_extension_cb_arg); + if (al != 0) + { + SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO,SSL_R_PEER_ERROR); + goto f_err; + } + } + } + + /* Check if we want to use external pre-shared secret for this handshake */ + /* for not reused session only */ + if (!s->hit && s->version >= TLS1_VERSION && s->tls_session_secret_cb) + { + SSL_CIPHER *pref_cipher=NULL; + + s->session->master_key_length=sizeof(s->session->master_key); + if(s->tls_session_secret_cb(s, s->session->master_key, &s->session->master_key_length, + ciphers, &pref_cipher, s->tls_session_secret_cb_arg)) + { + s->hit=1; + s->session->ciphers=ciphers; + s->session->verify_result=X509_V_OK; + + ciphers=NULL; + + /* check if some cipher was preferred by call back */ + pref_cipher=pref_cipher ? pref_cipher : ssl3_choose_cipher(s, s->session->ciphers, SSL_get_ciphers(s)); + if (pref_cipher == NULL) + { + al=SSL_AD_HANDSHAKE_FAILURE; + SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO,SSL_R_NO_SHARED_CIPHER); + goto f_err; + } + + s->session->cipher=pref_cipher; + + if (s->cipher_list) + sk_SSL_CIPHER_free(s->cipher_list); + + if (s->cipher_list_by_id) + sk_SSL_CIPHER_free(s->cipher_list_by_id); + + s->cipher_list = sk_SSL_CIPHER_dup(s->session->ciphers); + s->cipher_list_by_id = sk_SSL_CIPHER_dup(s->session->ciphers); + } + } + /* Given s->session->ciphers and SSL_get_ciphers, we must * pick a cipher */ diff -uprN openssl-0.9.8d.orig/ssl/ssl.h openssl-0.9.8d/ssl/ssl.h --- openssl-0.9.8d.orig/ssl/ssl.h 2006-06-14 06:52:49.000000000 -0700 +++ openssl-0.9.8d/ssl/ssl.h 2006-12-10 08:20:02.000000000 -0800 @@ -345,6 +345,7 @@ extern "C" { * 'struct ssl_st *' function parameters used to prototype callbacks * in SSL_CTX. */ typedef struct ssl_st *ssl_crock_st; +typedef struct tls_extension_st TLS_EXTENSION; /* used to hold info on the particular ciphers used */ typedef struct ssl_cipher_st @@ -366,6 +367,8 @@ DECLARE_STACK_OF(SSL_CIPHER) typedef struct ssl_st SSL; typedef struct ssl_ctx_st SSL_CTX; +typedef int (*tls_session_secret_cb_fn)(SSL *s, void *secret, int *secret_len, STACK_OF(SSL_CIPHER) *peer_ciphers, SSL_CIPHER **cipher, void *arg); + /* Used to hold functions for SSLv2 or SSLv3/TLSv1 functions */ typedef struct ssl_method_st { @@ -973,6 +976,15 @@ struct ssl_st int first_packet; int client_version; /* what was passed, used for * SSLv3/TLS rollback check */ + + /* TLS externsions */ + TLS_EXTENSION *tls_extension; + int (*tls_extension_cb)(SSL *s, TLS_EXTENSION *tls_ext, void *arg); + void *tls_extension_cb_arg; + + /* TLS pre-shared secret session resumption */ + tls_session_secret_cb_fn tls_session_secret_cb; + void *tls_session_secret_cb_arg; }; #ifdef __cplusplus @@ -1538,6 +1550,13 @@ void *SSL_COMP_get_compression_methods(v int SSL_COMP_add_compression_method(int id,void *cm); #endif +/* TLS extensions functions */ +int SSL_set_hello_extension(SSL *s, int ext_type, void *ext_data, int ext_len); +int SSL_set_hello_extension_cb(SSL *s, int (*cb)(SSL *, TLS_EXTENSION *, void *), void *arg); + +/* Pre-shared secret session resumption functions */ +int SSL_set_session_secret_cb(SSL *s, tls_session_secret_cb_fn tls_session_secret_cb, void *arg); + /* BEGIN ERROR CODES */ /* The following lines are auto generated by the script mkerr.pl. Any changes * made after this point may be overwritten when the script is next run. @@ -1719,6 +1738,7 @@ void ERR_load_SSL_strings(void); #define SSL_F_TLS1_ENC 210 #define SSL_F_TLS1_SETUP_KEY_BLOCK 211 #define SSL_F_WRITE_PENDING 212 +#define SSL_F_SSL_SET_HELLO_EXTENSION 213 /* Reason codes. */ #define SSL_R_APP_DATA_IN_HANDSHAKE 100 diff -uprN openssl-0.9.8d.orig/ssl/ssl_err.c openssl-0.9.8d/ssl/ssl_err.c --- openssl-0.9.8d.orig/ssl/ssl_err.c 2006-01-08 13:52:46.000000000 -0800 +++ openssl-0.9.8d/ssl/ssl_err.c 2006-12-10 08:20:02.000000000 -0800 @@ -242,6 +242,7 @@ static ERR_STRING_DATA SSL_str_functs[]= {ERR_FUNC(SSL_F_TLS1_ENC), "TLS1_ENC"}, {ERR_FUNC(SSL_F_TLS1_SETUP_KEY_BLOCK), "TLS1_SETUP_KEY_BLOCK"}, {ERR_FUNC(SSL_F_WRITE_PENDING), "WRITE_PENDING"}, +{ERR_FUNC(SSL_F_SSL_SET_HELLO_EXTENSION), "SSL_set_hello_extension"}, {0,NULL} }; diff -uprN openssl-0.9.8d.orig/ssl/ssl_sess.c openssl-0.9.8d/ssl/ssl_sess.c --- openssl-0.9.8d.orig/ssl/ssl_sess.c 2005-12-30 15:51:57.000000000 -0800 +++ openssl-0.9.8d/ssl/ssl_sess.c 2006-12-10 08:20:02.000000000 -0800 @@ -656,6 +656,15 @@ long SSL_CTX_get_timeout(const SSL_CTX * return(s->session_timeout); } +int SSL_set_session_secret_cb(SSL *s, int (*tls_session_secret_cb)(SSL *s, void *secret, int *secret_len, + STACK_OF(SSL_CIPHER) *peer_ciphers, SSL_CIPHER **cipher, void *arg), void *arg) +{ + if (s == NULL) return(0); + s->tls_session_secret_cb = tls_session_secret_cb; + s->tls_session_secret_cb_arg = arg; + return(1); +} + typedef struct timeout_param_st { SSL_CTX *ctx; diff -uprN openssl-0.9.8d.orig/ssl/t1_ext.c openssl-0.9.8d/ssl/t1_ext.c --- openssl-0.9.8d.orig/ssl/t1_ext.c 1969-12-31 16:00:00.000000000 -0800 +++ openssl-0.9.8d/ssl/t1_ext.c 2006-12-10 08:20:02.000000000 -0800 @@ -0,0 +1,48 @@ + +#include +#include "ssl_locl.h" + + +int SSL_set_hello_extension(SSL *s, int ext_type, void *ext_data, int ext_len) +{ + if(s->version >= TLS1_VERSION) + { + if(s->tls_extension) + { + OPENSSL_free(s->tls_extension); + s->tls_extension = NULL; + } + + if(ext_data) + { + s->tls_extension = OPENSSL_malloc(sizeof(TLS_EXTENSION) + ext_len); + if(!s->tls_extension) + { + SSLerr(SSL_F_SSL_SET_HELLO_EXTENSION, ERR_R_MALLOC_FAILURE); + return 0; + } + + s->tls_extension->type = ext_type; + s->tls_extension->length = ext_len; + s->tls_extension->data = s->tls_extension + 1; + memcpy(s->tls_extension->data, ext_data, ext_len); + } + + return 1; + } + + return 0; +} + +int SSL_set_hello_extension_cb(SSL *s, int (*cb)(SSL *, TLS_EXTENSION *, void *), void *arg) +{ + if(s->version >= TLS1_VERSION) + { + s->tls_extension_cb = cb; + s->tls_extension_cb_arg = arg; + + return 1; + } + + return 0; +} diff -uprN openssl-0.9.8d.orig/ssl/t1_lib.c openssl-0.9.8d/ssl/t1_lib.c --- openssl-0.9.8d.orig/ssl/t1_lib.c 2005-08-05 16:52:07.000000000 -0700 +++ openssl-0.9.8d/ssl/t1_lib.c 2006-12-10 08:20:02.000000000 -0800 @@ -97,6 +97,10 @@ int tls1_new(SSL *s) void tls1_free(SSL *s) { + if(s->tls_extension) + { + OPENSSL_free(s->tls_extension); + } ssl3_free(s); } diff -uprN openssl-0.9.8d.orig/ssl/tls1.h openssl-0.9.8d/ssl/tls1.h --- openssl-0.9.8d.orig/ssl/tls1.h 2006-06-14 10:52:01.000000000 -0700 +++ openssl-0.9.8d/ssl/tls1.h 2006-12-10 08:20:02.000000000 -0800 @@ -296,6 +296,14 @@ extern "C" { #define TLS_MD_MASTER_SECRET_CONST "\x6d\x61\x73\x74\x65\x72\x20\x73\x65\x63\x72\x65\x74" /*master secret*/ #endif +/* TLS extension struct */ +struct tls_extension_st +{ + unsigned short type; + unsigned short length; + void *data; +}; + #ifdef __cplusplus } #endif diff -uprN openssl-0.9.8d.orig/util/ssleay.num openssl-0.9.8d/util/ssleay.num --- openssl-0.9.8d.orig/util/ssleay.num 2005-05-08 17:22:02.000000000 -0700 +++ openssl-0.9.8d/util/ssleay.num 2006-12-10 08:20:02.000000000 -0800 @@ -226,3 +226,6 @@ DTLSv1_server_method SSL_COMP_get_compression_methods 276 EXIST:!VMS:FUNCTION:COMP SSL_COMP_get_compress_methods 276 EXIST:VMS:FUNCTION:COMP SSL_SESSION_get_id 277 EXIST::FUNCTION: +SSL_set_hello_extension 278 EXIST::FUNCTION: +SSL_set_hello_extension_cb 279 EXIST::FUNCTION: +SSL_set_session_secret_cb 280 EXIST::FUNCTION: wpa-2.1/patches/openssl-0.9.8e-tls-extensions.patch000066400000000000000000000276051227414666700221430ustar00rootroot00000000000000This patch is adding support for TLS hello extensions and externally generated pre-shared key material to OpenSSL 0.9.8e. This is based on the patch from Alexey Kobozev (sent to openssl-dev mailing list on Tue, 07 Jun 2005 15:40:58 +0300). diff -uprN openssl-0.9.8e.orig/ssl/Makefile openssl-0.9.8e/ssl/Makefile --- openssl-0.9.8e.orig/ssl/Makefile 2006-02-03 17:49:35.000000000 -0800 +++ openssl-0.9.8e/ssl/Makefile 2007-03-22 20:23:19.000000000 -0700 @@ -24,7 +24,7 @@ LIBSRC= \ s2_meth.c s2_srvr.c s2_clnt.c s2_lib.c s2_enc.c s2_pkt.c \ s3_meth.c s3_srvr.c s3_clnt.c s3_lib.c s3_enc.c s3_pkt.c s3_both.c \ s23_meth.c s23_srvr.c s23_clnt.c s23_lib.c s23_pkt.c \ - t1_meth.c t1_srvr.c t1_clnt.c t1_lib.c t1_enc.c \ + t1_meth.c t1_srvr.c t1_clnt.c t1_lib.c t1_enc.c t1_ext.c \ d1_meth.c d1_srvr.c d1_clnt.c d1_lib.c d1_pkt.c \ d1_both.c d1_enc.c \ ssl_lib.c ssl_err2.c ssl_cert.c ssl_sess.c \ @@ -35,7 +35,7 @@ LIBOBJ= \ s2_meth.o s2_srvr.o s2_clnt.o s2_lib.o s2_enc.o s2_pkt.o \ s3_meth.o s3_srvr.o s3_clnt.o s3_lib.o s3_enc.o s3_pkt.o s3_both.o \ s23_meth.o s23_srvr.o s23_clnt.o s23_lib.o s23_pkt.o \ - t1_meth.o t1_srvr.o t1_clnt.o t1_lib.o t1_enc.o \ + t1_meth.o t1_srvr.o t1_clnt.o t1_lib.o t1_enc.o t1_ext.o \ d1_meth.o d1_srvr.o d1_clnt.o d1_lib.o d1_pkt.o \ d1_both.o d1_enc.o \ ssl_lib.o ssl_err2.o ssl_cert.o ssl_sess.o \ @@ -968,3 +968,4 @@ t1_srvr.o: ../include/openssl/ssl23.h .. t1_srvr.o: ../include/openssl/stack.h ../include/openssl/symhacks.h t1_srvr.o: ../include/openssl/tls1.h ../include/openssl/x509.h t1_srvr.o: ../include/openssl/x509_vfy.h ssl_locl.h t1_srvr.c +t1_ext.o: t1_ext.c ssl_locl.h diff -uprN openssl-0.9.8e.orig/ssl/s3_clnt.c openssl-0.9.8e/ssl/s3_clnt.c --- openssl-0.9.8e.orig/ssl/s3_clnt.c 2006-09-28 05:23:15.000000000 -0700 +++ openssl-0.9.8e/ssl/s3_clnt.c 2007-03-22 20:23:19.000000000 -0700 @@ -601,6 +601,20 @@ int ssl3_client_hello(SSL *s) #endif *(p++)=0; /* Add the NULL method */ + /* send client hello extensions if any */ + if (s->version >= TLS1_VERSION && s->tls_extension) + { + // set the total extensions length + s2n(s->tls_extension->length + 4, p); + + // put the extensions with type and length + s2n(s->tls_extension->type, p); + s2n(s->tls_extension->length, p); + + memcpy(p, s->tls_extension->data, s->tls_extension->length); + p+=s->tls_extension->length; + } + l=(p-d); d=buf; *(d++)=SSL3_MT_CLIENT_HELLO; @@ -623,7 +637,7 @@ int ssl3_get_server_hello(SSL *s) STACK_OF(SSL_CIPHER) *sk; SSL_CIPHER *c; unsigned char *p,*d; - int i,al,ok; + int i,al,ok,pre_shared; unsigned int j; long n; #ifndef OPENSSL_NO_COMP @@ -690,7 +704,24 @@ int ssl3_get_server_hello(SSL *s) goto f_err; } - if (j != 0 && j == s->session->session_id_length + /* check if we want to resume the session based on external pre-shared secret */ + pre_shared = 0; + if (s->version >= TLS1_VERSION && s->tls_session_secret_cb) + { + SSL_CIPHER *pref_cipher=NULL; + s->session->master_key_length=sizeof(s->session->master_key); + if (s->tls_session_secret_cb(s, s->session->master_key, &s->session->master_key_length, + NULL, &pref_cipher, s->tls_session_secret_cb_arg)) + { + s->hit=1; + s->session->cipher=pref_cipher ? pref_cipher : ssl_get_cipher_by_char(s,p+j); + s->session->session_id_length = j; + memcpy(s->session->session_id, p, j); + pre_shared = 1; + } + } + + if ((pre_shared || j != 0) && j == s->session->session_id_length && memcmp(p,s->session->session_id,j) == 0) { if(s->sid_ctx_length != s->session->sid_ctx_length diff -uprN openssl-0.9.8e.orig/ssl/s3_srvr.c openssl-0.9.8e/ssl/s3_srvr.c --- openssl-0.9.8e.orig/ssl/s3_srvr.c 2007-02-07 12:36:40.000000000 -0800 +++ openssl-0.9.8e/ssl/s3_srvr.c 2007-03-22 20:23:19.000000000 -0700 @@ -945,6 +945,75 @@ int ssl3_get_client_hello(SSL *s) } #endif + /* Check for TLS client hello extension here */ + if (p < (d+n) && s->version >= TLS1_VERSION) + { + if (s->tls_extension_cb) + { + TLS_EXTENSION tls_ext; + unsigned short ext_total_len; + + n2s(p, ext_total_len); + n2s(p, tls_ext.type); + n2s(p, tls_ext.length); + + // sanity check in TLS extension len + if (tls_ext.length > (d+n) - p) + { + // just cut the lenth to packet border + tls_ext.length = (d+n) - p; + } + + tls_ext.data = p; + + // returns an alert code or 0 + al = s->tls_extension_cb(s, &tls_ext, s->tls_extension_cb_arg); + if (al != 0) + { + SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO,SSL_R_PEER_ERROR); + goto f_err; + } + } + } + + /* Check if we want to use external pre-shared secret for this handshake */ + /* for not reused session only */ + if (!s->hit && s->version >= TLS1_VERSION && s->tls_session_secret_cb) + { + SSL_CIPHER *pref_cipher=NULL; + + s->session->master_key_length=sizeof(s->session->master_key); + if(s->tls_session_secret_cb(s, s->session->master_key, &s->session->master_key_length, + ciphers, &pref_cipher, s->tls_session_secret_cb_arg)) + { + s->hit=1; + s->session->ciphers=ciphers; + s->session->verify_result=X509_V_OK; + + ciphers=NULL; + + /* check if some cipher was preferred by call back */ + pref_cipher=pref_cipher ? pref_cipher : ssl3_choose_cipher(s, s->session->ciphers, SSL_get_ciphers(s)); + if (pref_cipher == NULL) + { + al=SSL_AD_HANDSHAKE_FAILURE; + SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO,SSL_R_NO_SHARED_CIPHER); + goto f_err; + } + + s->session->cipher=pref_cipher; + + if (s->cipher_list) + sk_SSL_CIPHER_free(s->cipher_list); + + if (s->cipher_list_by_id) + sk_SSL_CIPHER_free(s->cipher_list_by_id); + + s->cipher_list = sk_SSL_CIPHER_dup(s->session->ciphers); + s->cipher_list_by_id = sk_SSL_CIPHER_dup(s->session->ciphers); + } + } + /* Given s->session->ciphers and SSL_get_ciphers, we must * pick a cipher */ diff -uprN openssl-0.9.8e.orig/ssl/ssl.h openssl-0.9.8e/ssl/ssl.h --- openssl-0.9.8e.orig/ssl/ssl.h 2007-02-19 09:55:07.000000000 -0800 +++ openssl-0.9.8e/ssl/ssl.h 2007-03-22 20:23:19.000000000 -0700 @@ -345,6 +345,7 @@ extern "C" { * 'struct ssl_st *' function parameters used to prototype callbacks * in SSL_CTX. */ typedef struct ssl_st *ssl_crock_st; +typedef struct tls_extension_st TLS_EXTENSION; /* used to hold info on the particular ciphers used */ typedef struct ssl_cipher_st @@ -366,6 +367,8 @@ DECLARE_STACK_OF(SSL_CIPHER) typedef struct ssl_st SSL; typedef struct ssl_ctx_st SSL_CTX; +typedef int (*tls_session_secret_cb_fn)(SSL *s, void *secret, int *secret_len, STACK_OF(SSL_CIPHER) *peer_ciphers, SSL_CIPHER **cipher, void *arg); + /* Used to hold functions for SSLv2 or SSLv3/TLSv1 functions */ typedef struct ssl_method_st { @@ -973,6 +976,15 @@ struct ssl_st int first_packet; int client_version; /* what was passed, used for * SSLv3/TLS rollback check */ + + /* TLS externsions */ + TLS_EXTENSION *tls_extension; + int (*tls_extension_cb)(SSL *s, TLS_EXTENSION *tls_ext, void *arg); + void *tls_extension_cb_arg; + + /* TLS pre-shared secret session resumption */ + tls_session_secret_cb_fn tls_session_secret_cb; + void *tls_session_secret_cb_arg; }; #ifdef __cplusplus @@ -1538,6 +1550,13 @@ void *SSL_COMP_get_compression_methods(v int SSL_COMP_add_compression_method(int id,void *cm); #endif +/* TLS extensions functions */ +int SSL_set_hello_extension(SSL *s, int ext_type, void *ext_data, int ext_len); +int SSL_set_hello_extension_cb(SSL *s, int (*cb)(SSL *, TLS_EXTENSION *, void *), void *arg); + +/* Pre-shared secret session resumption functions */ +int SSL_set_session_secret_cb(SSL *s, tls_session_secret_cb_fn tls_session_secret_cb, void *arg); + /* BEGIN ERROR CODES */ /* The following lines are auto generated by the script mkerr.pl. Any changes * made after this point may be overwritten when the script is next run. @@ -1719,6 +1738,7 @@ void ERR_load_SSL_strings(void); #define SSL_F_TLS1_ENC 210 #define SSL_F_TLS1_SETUP_KEY_BLOCK 211 #define SSL_F_WRITE_PENDING 212 +#define SSL_F_SSL_SET_HELLO_EXTENSION 213 /* Reason codes. */ #define SSL_R_APP_DATA_IN_HANDSHAKE 100 diff -uprN openssl-0.9.8e.orig/ssl/ssl_err.c openssl-0.9.8e/ssl/ssl_err.c --- openssl-0.9.8e.orig/ssl/ssl_err.c 2006-11-21 12:14:46.000000000 -0800 +++ openssl-0.9.8e/ssl/ssl_err.c 2007-03-22 20:23:19.000000000 -0700 @@ -242,6 +242,7 @@ static ERR_STRING_DATA SSL_str_functs[]= {ERR_FUNC(SSL_F_TLS1_ENC), "TLS1_ENC"}, {ERR_FUNC(SSL_F_TLS1_SETUP_KEY_BLOCK), "TLS1_SETUP_KEY_BLOCK"}, {ERR_FUNC(SSL_F_WRITE_PENDING), "WRITE_PENDING"}, +{ERR_FUNC(SSL_F_SSL_SET_HELLO_EXTENSION), "SSL_set_hello_extension"}, {0,NULL} }; diff -uprN openssl-0.9.8e.orig/ssl/ssl_sess.c openssl-0.9.8e/ssl/ssl_sess.c --- openssl-0.9.8e.orig/ssl/ssl_sess.c 2007-02-10 02:40:24.000000000 -0800 +++ openssl-0.9.8e/ssl/ssl_sess.c 2007-03-22 20:23:19.000000000 -0700 @@ -656,6 +656,15 @@ long SSL_CTX_get_timeout(const SSL_CTX * return(s->session_timeout); } +int SSL_set_session_secret_cb(SSL *s, int (*tls_session_secret_cb)(SSL *s, void *secret, int *secret_len, + STACK_OF(SSL_CIPHER) *peer_ciphers, SSL_CIPHER **cipher, void *arg), void *arg) +{ + if (s == NULL) return(0); + s->tls_session_secret_cb = tls_session_secret_cb; + s->tls_session_secret_cb_arg = arg; + return(1); +} + typedef struct timeout_param_st { SSL_CTX *ctx; diff -uprN openssl-0.9.8e.orig/ssl/t1_ext.c openssl-0.9.8e/ssl/t1_ext.c --- openssl-0.9.8e.orig/ssl/t1_ext.c 1969-12-31 16:00:00.000000000 -0800 +++ openssl-0.9.8e/ssl/t1_ext.c 2007-03-22 20:23:19.000000000 -0700 @@ -0,0 +1,48 @@ + +#include +#include "ssl_locl.h" + + +int SSL_set_hello_extension(SSL *s, int ext_type, void *ext_data, int ext_len) +{ + if(s->version >= TLS1_VERSION) + { + if(s->tls_extension) + { + OPENSSL_free(s->tls_extension); + s->tls_extension = NULL; + } + + if(ext_data) + { + s->tls_extension = OPENSSL_malloc(sizeof(TLS_EXTENSION) + ext_len); + if(!s->tls_extension) + { + SSLerr(SSL_F_SSL_SET_HELLO_EXTENSION, ERR_R_MALLOC_FAILURE); + return 0; + } + + s->tls_extension->type = ext_type; + s->tls_extension->length = ext_len; + s->tls_extension->data = s->tls_extension + 1; + memcpy(s->tls_extension->data, ext_data, ext_len); + } + + return 1; + } + + return 0; +} + +int SSL_set_hello_extension_cb(SSL *s, int (*cb)(SSL *, TLS_EXTENSION *, void *), void *arg) +{ + if(s->version >= TLS1_VERSION) + { + s->tls_extension_cb = cb; + s->tls_extension_cb_arg = arg; + + return 1; + } + + return 0; +} diff -uprN openssl-0.9.8e.orig/ssl/t1_lib.c openssl-0.9.8e/ssl/t1_lib.c --- openssl-0.9.8e.orig/ssl/t1_lib.c 2007-01-21 08:07:25.000000000 -0800 +++ openssl-0.9.8e/ssl/t1_lib.c 2007-03-22 20:23:19.000000000 -0700 @@ -97,6 +97,10 @@ int tls1_new(SSL *s) void tls1_free(SSL *s) { + if(s->tls_extension) + { + OPENSSL_free(s->tls_extension); + } ssl3_free(s); } diff -uprN openssl-0.9.8e.orig/ssl/tls1.h openssl-0.9.8e/ssl/tls1.h --- openssl-0.9.8e.orig/ssl/tls1.h 2006-06-14 10:52:01.000000000 -0700 +++ openssl-0.9.8e/ssl/tls1.h 2007-03-22 20:23:19.000000000 -0700 @@ -296,6 +296,14 @@ extern "C" { #define TLS_MD_MASTER_SECRET_CONST "\x6d\x61\x73\x74\x65\x72\x20\x73\x65\x63\x72\x65\x74" /*master secret*/ #endif +/* TLS extension struct */ +struct tls_extension_st +{ + unsigned short type; + unsigned short length; + void *data; +}; + #ifdef __cplusplus } #endif diff -uprN openssl-0.9.8e.orig/util/ssleay.num openssl-0.9.8e/util/ssleay.num --- openssl-0.9.8e.orig/util/ssleay.num 2006-11-30 05:04:43.000000000 -0800 +++ openssl-0.9.8e/util/ssleay.num 2007-03-22 20:24:07.000000000 -0700 @@ -238,3 +238,6 @@ SSL_CTX_set_info_callback SSL_CTX_sess_get_new_cb 287 EXIST::FUNCTION: SSL_CTX_get_client_cert_cb 288 EXIST::FUNCTION: SSL_CTX_sess_get_remove_cb 289 EXIST::FUNCTION: +SSL_set_hello_extension 290 EXIST::FUNCTION: +SSL_set_hello_extension_cb 291 EXIST::FUNCTION: +SSL_set_session_secret_cb 292 EXIST::FUNCTION: wpa-2.1/patches/openssl-0.9.8g-tls-extensions.patch000066400000000000000000000256011227414666700221370ustar00rootroot00000000000000This patch adds support for TLS SessionTicket extension (RFC 5077) for the parts used by EAP-FAST (RFC 4851). This is based on the patch from Alexey Kobozev (sent to openssl-dev mailing list on Tue, 07 Jun 2005 15:40:58 +0300). OpenSSL 0.9.8g does not enable TLS extension support by default, so it will need to be enabled by adding enable-tlsext to config script command line. diff -upr openssl-0.9.8g.orig/ssl/s3_clnt.c openssl-0.9.8g/ssl/s3_clnt.c --- openssl-0.9.8g.orig/ssl/s3_clnt.c 2007-08-31 03:28:51.000000000 +0300 +++ openssl-0.9.8g/ssl/s3_clnt.c 2008-04-15 17:11:46.000000000 +0300 @@ -727,6 +727,20 @@ int ssl3_get_server_hello(SSL *s) goto f_err; } +#ifndef OPENSSL_NO_TLSEXT + /* check if we want to resume the session based on external pre-shared secret */ + if (s->version >= TLS1_VERSION && s->tls_session_secret_cb) + { + SSL_CIPHER *pref_cipher=NULL; + s->session->master_key_length=sizeof(s->session->master_key); + if (s->tls_session_secret_cb(s, s->session->master_key, &s->session->master_key_length, + NULL, &pref_cipher, s->tls_session_secret_cb_arg)) + { + s->session->cipher=pref_cipher ? pref_cipher : ssl_get_cipher_by_char(s,p+j); + } + } +#endif /* OPENSSL_NO_TLSEXT */ + if (j != 0 && j == s->session->session_id_length && memcmp(p,s->session->session_id,j) == 0) { diff -upr openssl-0.9.8g.orig/ssl/s3_srvr.c openssl-0.9.8g/ssl/s3_srvr.c --- openssl-0.9.8g.orig/ssl/s3_srvr.c 2007-09-30 21:55:59.000000000 +0300 +++ openssl-0.9.8g/ssl/s3_srvr.c 2008-04-15 17:10:37.000000000 +0300 @@ -928,6 +928,59 @@ int ssl3_get_client_hello(SSL *s) SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO,SSL_R_CLIENTHELLO_TLSEXT); goto err; } + + /* Check if we want to use external pre-shared secret for this + * handshake for not reused session only. We need to generate + * server_random before calling tls_session_secret_cb in order to allow + * SessionTicket processing to use it in key derivation. */ + { + unsigned long Time; + unsigned char *pos; + Time=(unsigned long)time(NULL); /* Time */ + pos=s->s3->server_random; + l2n(Time,pos); + if (RAND_pseudo_bytes(pos,SSL3_RANDOM_SIZE-4) <= 0) + { + al=SSL_AD_INTERNAL_ERROR; + goto f_err; + } + } + + if (!s->hit && s->version >= TLS1_VERSION && s->tls_session_secret_cb) + { + SSL_CIPHER *pref_cipher=NULL; + + s->session->master_key_length=sizeof(s->session->master_key); + if(s->tls_session_secret_cb(s, s->session->master_key, &s->session->master_key_length, + ciphers, &pref_cipher, s->tls_session_secret_cb_arg)) + { + s->hit=1; + s->session->ciphers=ciphers; + s->session->verify_result=X509_V_OK; + + ciphers=NULL; + + /* check if some cipher was preferred by call back */ + pref_cipher=pref_cipher ? pref_cipher : ssl3_choose_cipher(s, s->session->ciphers, SSL_get_ciphers(s)); + if (pref_cipher == NULL) + { + al=SSL_AD_HANDSHAKE_FAILURE; + SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO,SSL_R_NO_SHARED_CIPHER); + goto f_err; + } + + s->session->cipher=pref_cipher; + + if (s->cipher_list) + sk_SSL_CIPHER_free(s->cipher_list); + + if (s->cipher_list_by_id) + sk_SSL_CIPHER_free(s->cipher_list_by_id); + + s->cipher_list = sk_SSL_CIPHER_dup(s->session->ciphers); + s->cipher_list_by_id = sk_SSL_CIPHER_dup(s->session->ciphers); + } + } #endif /* Worst case, we will use the NULL compression, but if we have other * options, we will now look for them. We have i-1 compression @@ -1066,16 +1119,22 @@ int ssl3_send_server_hello(SSL *s) unsigned char *buf; unsigned char *p,*d; int i,sl; - unsigned long l,Time; + unsigned long l; +#ifdef OPENSSL_NO_TLSEXT + unsigned long Time; +#endif if (s->state == SSL3_ST_SW_SRVR_HELLO_A) { buf=(unsigned char *)s->init_buf->data; +#ifdef OPENSSL_NO_TLSEXT p=s->s3->server_random; + /* Generate server_random if it was not needed previously */ Time=(unsigned long)time(NULL); /* Time */ l2n(Time,p); if (RAND_pseudo_bytes(p,SSL3_RANDOM_SIZE-4) <= 0) return -1; +#endif /* Do the message type and length last */ d=p= &(buf[4]); diff -upr openssl-0.9.8g.orig/ssl/ssl.h openssl-0.9.8g/ssl/ssl.h --- openssl-0.9.8g.orig/ssl/ssl.h 2007-10-19 10:42:38.000000000 +0300 +++ openssl-0.9.8g/ssl/ssl.h 2008-04-15 17:10:37.000000000 +0300 @@ -342,6 +342,7 @@ extern "C" { * 'struct ssl_st *' function parameters used to prototype callbacks * in SSL_CTX. */ typedef struct ssl_st *ssl_crock_st; +typedef struct tls_extension_st TLS_EXTENSION; /* used to hold info on the particular ciphers used */ typedef struct ssl_cipher_st @@ -363,6 +364,8 @@ DECLARE_STACK_OF(SSL_CIPHER) typedef struct ssl_st SSL; typedef struct ssl_ctx_st SSL_CTX; +typedef int (*tls_session_secret_cb_fn)(SSL *s, void *secret, int *secret_len, STACK_OF(SSL_CIPHER) *peer_ciphers, SSL_CIPHER **cipher, void *arg); + /* Used to hold functions for SSLv2 or SSLv3/TLSv1 functions */ typedef struct ssl_method_st { @@ -1004,6 +1007,14 @@ struct ssl_st */ /* RFC4507 session ticket expected to be received or sent */ int tlsext_ticket_expected; + + /* TLS extensions */ + TLS_EXTENSION *tls_extension; + + /* TLS pre-shared secret session resumption */ + tls_session_secret_cb_fn tls_session_secret_cb; + void *tls_session_secret_cb_arg; + SSL_CTX * initial_ctx; /* initial ctx, used to store sessions */ #define session_ctx initial_ctx #else @@ -1589,6 +1600,12 @@ void *SSL_COMP_get_compression_methods(v int SSL_COMP_add_compression_method(int id,void *cm); #endif +/* TLS extensions functions */ +int SSL_set_hello_extension(SSL *s, int ext_type, void *ext_data, int ext_len); + +/* Pre-shared secret session resumption functions */ +int SSL_set_session_secret_cb(SSL *s, tls_session_secret_cb_fn tls_session_secret_cb, void *arg); + /* BEGIN ERROR CODES */ /* The following lines are auto generated by the script mkerr.pl. Any changes * made after this point may be overwritten when the script is next run. @@ -1778,6 +1795,7 @@ void ERR_load_SSL_strings(void); #define SSL_F_TLS1_ENC 210 #define SSL_F_TLS1_SETUP_KEY_BLOCK 211 #define SSL_F_WRITE_PENDING 212 +#define SSL_F_SSL_SET_HELLO_EXTENSION 213 /* Reason codes. */ #define SSL_R_APP_DATA_IN_HANDSHAKE 100 diff -upr openssl-0.9.8g.orig/ssl/ssl_err.c openssl-0.9.8g/ssl/ssl_err.c --- openssl-0.9.8g.orig/ssl/ssl_err.c 2007-10-11 17:36:59.000000000 +0300 +++ openssl-0.9.8g/ssl/ssl_err.c 2008-04-15 17:10:37.000000000 +0300 @@ -250,6 +250,7 @@ static ERR_STRING_DATA SSL_str_functs[]= {ERR_FUNC(SSL_F_TLS1_ENC), "TLS1_ENC"}, {ERR_FUNC(SSL_F_TLS1_SETUP_KEY_BLOCK), "TLS1_SETUP_KEY_BLOCK"}, {ERR_FUNC(SSL_F_WRITE_PENDING), "WRITE_PENDING"}, +{ERR_FUNC(SSL_F_SSL_SET_HELLO_EXTENSION), "SSL_set_hello_extension"}, {0,NULL} }; diff -upr openssl-0.9.8g.orig/ssl/ssl_sess.c openssl-0.9.8g/ssl/ssl_sess.c --- openssl-0.9.8g.orig/ssl/ssl_sess.c 2007-10-19 10:36:34.000000000 +0300 +++ openssl-0.9.8g/ssl/ssl_sess.c 2008-04-15 17:10:37.000000000 +0300 @@ -704,6 +704,52 @@ long SSL_CTX_get_timeout(const SSL_CTX * return(s->session_timeout); } +#ifndef OPENSSL_NO_TLSEXT +int SSL_set_session_secret_cb(SSL *s, int (*tls_session_secret_cb)(SSL *s, void *secret, int *secret_len, + STACK_OF(SSL_CIPHER) *peer_ciphers, SSL_CIPHER **cipher, void *arg), void *arg) +{ + if (s == NULL) return(0); + s->tls_session_secret_cb = tls_session_secret_cb; + s->tls_session_secret_cb_arg = arg; + return(1); +} + +int SSL_set_hello_extension(SSL *s, int ext_type, void *ext_data, int ext_len) +{ + if(s->version >= TLS1_VERSION) + { + if(s->tls_extension) + { + OPENSSL_free(s->tls_extension); + s->tls_extension = NULL; + } + + s->tls_extension = OPENSSL_malloc(sizeof(TLS_EXTENSION) + ext_len); + if(!s->tls_extension) + { + SSLerr(SSL_F_SSL_SET_HELLO_EXTENSION, ERR_R_MALLOC_FAILURE); + return 0; + } + + s->tls_extension->type = ext_type; + + if(ext_data) + { + s->tls_extension->length = ext_len; + s->tls_extension->data = s->tls_extension + 1; + memcpy(s->tls_extension->data, ext_data, ext_len); + } else { + s->tls_extension->length = 0; + s->tls_extension->data = NULL; + } + + return 1; + } + + return 0; +} +#endif /* OPENSSL_NO_TLSEXT */ + typedef struct timeout_param_st { SSL_CTX *ctx; diff -upr openssl-0.9.8g.orig/ssl/t1_lib.c openssl-0.9.8g/ssl/t1_lib.c --- openssl-0.9.8g.orig/ssl/t1_lib.c 2007-10-19 10:44:10.000000000 +0300 +++ openssl-0.9.8g/ssl/t1_lib.c 2008-04-15 17:10:37.000000000 +0300 @@ -105,6 +105,12 @@ int tls1_new(SSL *s) void tls1_free(SSL *s) { +#ifndef OPENSSL_NO_TLSEXT + if(s->tls_extension) + { + OPENSSL_free(s->tls_extension); + } +#endif ssl3_free(s); } @@ -174,8 +180,24 @@ unsigned char *ssl_add_clienthello_tlsex int ticklen; if (s->session && s->session->tlsext_tick) ticklen = s->session->tlsext_ticklen; + else if (s->session && s->tls_extension && + s->tls_extension->type == TLSEXT_TYPE_session_ticket && + s->tls_extension->data) + { + ticklen = s->tls_extension->length; + s->session->tlsext_tick = OPENSSL_malloc(ticklen); + if (!s->session->tlsext_tick) + return NULL; + memcpy(s->session->tlsext_tick, s->tls_extension->data, + ticklen); + s->session->tlsext_ticklen = ticklen; + } else ticklen = 0; + if (ticklen == 0 && s->tls_extension && + s->tls_extension->type == TLSEXT_TYPE_session_ticket && + s->tls_extension->data == NULL) + goto skip_ext; /* Check for enough room 2 for extension type, 2 for len * rest for ticket */ @@ -189,6 +211,7 @@ unsigned char *ssl_add_clienthello_tlsex ret += ticklen; } } + skip_ext: if ((extdatalen = ret-p-2)== 0) return p; @@ -543,6 +566,8 @@ int tls1_process_ticket(SSL *s, unsigned s->tlsext_ticket_expected = 1; return 0; /* Cache miss */ } + if (s->tls_session_secret_cb) + return 0; return tls_decrypt_ticket(s, p, size, session_id, len, ret); } diff -upr openssl-0.9.8g.orig/ssl/tls1.h openssl-0.9.8g/ssl/tls1.h --- openssl-0.9.8g.orig/ssl/tls1.h 2007-08-28 04:12:44.000000000 +0300 +++ openssl-0.9.8g/ssl/tls1.h 2008-04-15 17:10:37.000000000 +0300 @@ -365,6 +365,14 @@ SSL_CTX_ctrl(ctx,SSL_CTRL_SET_TLSEXT_SER #define TLS_MD_MASTER_SECRET_CONST "\x6d\x61\x73\x74\x65\x72\x20\x73\x65\x63\x72\x65\x74" /*master secret*/ #endif +/* TLS extension struct */ +struct tls_extension_st +{ + unsigned short type; + unsigned short length; + void *data; +}; + #ifdef __cplusplus } #endif diff -upr openssl-0.9.8g.orig/util/ssleay.num openssl-0.9.8g/util/ssleay.num --- openssl-0.9.8g.orig/util/ssleay.num 2007-08-13 01:31:16.000000000 +0300 +++ openssl-0.9.8g/util/ssleay.num 2008-04-15 17:10:37.000000000 +0300 @@ -241,3 +241,5 @@ SSL_CTX_sess_get_remove_cb SSL_set_SSL_CTX 290 EXIST::FUNCTION: SSL_get_servername 291 EXIST::FUNCTION:TLSEXT SSL_get_servername_type 292 EXIST::FUNCTION:TLSEXT +SSL_set_hello_extension 305 EXIST::FUNCTION:TLSEXT +SSL_set_session_secret_cb 306 EXIST::FUNCTION:TLSEXT wpa-2.1/patches/openssl-0.9.8h-tls-extensions.patch000066400000000000000000000266241227414666700221460ustar00rootroot00000000000000This patch adds support for TLS SessionTicket extension (RFC 5077) for the parts used by EAP-FAST (RFC 4851). This is based on the patch from Alexey Kobozev (sent to openssl-dev mailing list on Tue, 07 Jun 2005 15:40:58 +0300). OpenSSL 0.9.8h does not enable TLS extension support by default, so it will need to be enabled by adding enable-tlsext to config script command line. diff -upr openssl-0.9.8h.orig/ssl/s3_clnt.c openssl-0.9.8h/ssl/s3_clnt.c --- openssl-0.9.8h.orig/ssl/s3_clnt.c 2008-05-28 10:29:27.000000000 +0300 +++ openssl-0.9.8h/ssl/s3_clnt.c 2008-05-29 10:44:25.000000000 +0300 @@ -752,6 +752,20 @@ int ssl3_get_server_hello(SSL *s) goto f_err; } +#ifndef OPENSSL_NO_TLSEXT + /* check if we want to resume the session based on external pre-shared secret */ + if (s->version >= TLS1_VERSION && s->tls_session_secret_cb) + { + SSL_CIPHER *pref_cipher=NULL; + s->session->master_key_length=sizeof(s->session->master_key); + if (s->tls_session_secret_cb(s, s->session->master_key, &s->session->master_key_length, + NULL, &pref_cipher, s->tls_session_secret_cb_arg)) + { + s->session->cipher=pref_cipher ? pref_cipher : ssl_get_cipher_by_char(s,p+j); + } + } +#endif /* OPENSSL_NO_TLSEXT */ + if (j != 0 && j == s->session->session_id_length && memcmp(p,s->session->session_id,j) == 0) { @@ -2693,11 +2707,8 @@ static int ssl3_check_finished(SSL *s) { int ok; long n; - /* If we have no ticket or session ID is non-zero length (a match of - * a non-zero session length would never reach here) it cannot be a - * resumed session. - */ - if (!s->session->tlsext_tick || s->session->session_id_length) + /* If we have no ticket it cannot be a resumed session. */ + if (!s->session->tlsext_tick) return 1; /* this function is called when we really expect a Certificate * message, so permit appropriate message length */ diff -upr openssl-0.9.8h.orig/ssl/s3_srvr.c openssl-0.9.8h/ssl/s3_srvr.c --- openssl-0.9.8h.orig/ssl/s3_srvr.c 2008-04-30 19:11:32.000000000 +0300 +++ openssl-0.9.8h/ssl/s3_srvr.c 2008-05-28 18:49:34.000000000 +0300 @@ -959,6 +959,59 @@ int ssl3_get_client_hello(SSL *s) SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO,SSL_R_CLIENTHELLO_TLSEXT); goto err; } + + /* Check if we want to use external pre-shared secret for this + * handshake for not reused session only. We need to generate + * server_random before calling tls_session_secret_cb in order to allow + * SessionTicket processing to use it in key derivation. */ + { + unsigned long Time; + unsigned char *pos; + Time=(unsigned long)time(NULL); /* Time */ + pos=s->s3->server_random; + l2n(Time,pos); + if (RAND_pseudo_bytes(pos,SSL3_RANDOM_SIZE-4) <= 0) + { + al=SSL_AD_INTERNAL_ERROR; + goto f_err; + } + } + + if (!s->hit && s->version >= TLS1_VERSION && s->tls_session_secret_cb) + { + SSL_CIPHER *pref_cipher=NULL; + + s->session->master_key_length=sizeof(s->session->master_key); + if(s->tls_session_secret_cb(s, s->session->master_key, &s->session->master_key_length, + ciphers, &pref_cipher, s->tls_session_secret_cb_arg)) + { + s->hit=1; + s->session->ciphers=ciphers; + s->session->verify_result=X509_V_OK; + + ciphers=NULL; + + /* check if some cipher was preferred by call back */ + pref_cipher=pref_cipher ? pref_cipher : ssl3_choose_cipher(s, s->session->ciphers, SSL_get_ciphers(s)); + if (pref_cipher == NULL) + { + al=SSL_AD_HANDSHAKE_FAILURE; + SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO,SSL_R_NO_SHARED_CIPHER); + goto f_err; + } + + s->session->cipher=pref_cipher; + + if (s->cipher_list) + sk_SSL_CIPHER_free(s->cipher_list); + + if (s->cipher_list_by_id) + sk_SSL_CIPHER_free(s->cipher_list_by_id); + + s->cipher_list = sk_SSL_CIPHER_dup(s->session->ciphers); + s->cipher_list_by_id = sk_SSL_CIPHER_dup(s->session->ciphers); + } + } #endif /* Worst case, we will use the NULL compression, but if we have other * options, we will now look for them. We have i-1 compression @@ -1097,16 +1150,22 @@ int ssl3_send_server_hello(SSL *s) unsigned char *buf; unsigned char *p,*d; int i,sl; - unsigned long l,Time; + unsigned long l; +#ifdef OPENSSL_NO_TLSEXT + unsigned long Time; +#endif if (s->state == SSL3_ST_SW_SRVR_HELLO_A) { buf=(unsigned char *)s->init_buf->data; +#ifdef OPENSSL_NO_TLSEXT p=s->s3->server_random; + /* Generate server_random if it was not needed previously */ Time=(unsigned long)time(NULL); /* Time */ l2n(Time,p); if (RAND_pseudo_bytes(p,SSL3_RANDOM_SIZE-4) <= 0) return -1; +#endif /* Do the message type and length last */ d=p= &(buf[4]); diff -upr openssl-0.9.8h.orig/ssl/ssl.h openssl-0.9.8h/ssl/ssl.h --- openssl-0.9.8h.orig/ssl/ssl.h 2008-04-30 19:11:32.000000000 +0300 +++ openssl-0.9.8h/ssl/ssl.h 2008-05-28 18:49:34.000000000 +0300 @@ -343,6 +343,7 @@ extern "C" { * 'struct ssl_st *' function parameters used to prototype callbacks * in SSL_CTX. */ typedef struct ssl_st *ssl_crock_st; +typedef struct tls_extension_st TLS_EXTENSION; /* used to hold info on the particular ciphers used */ typedef struct ssl_cipher_st @@ -364,6 +365,8 @@ DECLARE_STACK_OF(SSL_CIPHER) typedef struct ssl_st SSL; typedef struct ssl_ctx_st SSL_CTX; +typedef int (*tls_session_secret_cb_fn)(SSL *s, void *secret, int *secret_len, STACK_OF(SSL_CIPHER) *peer_ciphers, SSL_CIPHER **cipher, void *arg); + /* Used to hold functions for SSLv2 or SSLv3/TLSv1 functions */ typedef struct ssl_method_st { @@ -1027,6 +1030,14 @@ struct ssl_st /* RFC4507 session ticket expected to be received or sent */ int tlsext_ticket_expected; + + /* TLS extensions */ + TLS_EXTENSION *tls_extension; + + /* TLS pre-shared secret session resumption */ + tls_session_secret_cb_fn tls_session_secret_cb; + void *tls_session_secret_cb_arg; + SSL_CTX * initial_ctx; /* initial ctx, used to store sessions */ #define session_ctx initial_ctx #else @@ -1625,6 +1636,12 @@ void *SSL_COMP_get_compression_methods(v int SSL_COMP_add_compression_method(int id,void *cm); #endif +/* TLS extensions functions */ +int SSL_set_hello_extension(SSL *s, int ext_type, void *ext_data, int ext_len); + +/* Pre-shared secret session resumption functions */ +int SSL_set_session_secret_cb(SSL *s, tls_session_secret_cb_fn tls_session_secret_cb, void *arg); + /* BEGIN ERROR CODES */ /* The following lines are auto generated by the script mkerr.pl. Any changes * made after this point may be overwritten when the script is next run. @@ -1815,6 +1832,7 @@ void ERR_load_SSL_strings(void); #define SSL_F_TLS1_ENC 210 #define SSL_F_TLS1_SETUP_KEY_BLOCK 211 #define SSL_F_WRITE_PENDING 212 +#define SSL_F_SSL_SET_HELLO_EXTENSION 213 /* Reason codes. */ #define SSL_R_APP_DATA_IN_HANDSHAKE 100 diff -upr openssl-0.9.8h.orig/ssl/ssl_err.c openssl-0.9.8h/ssl/ssl_err.c --- openssl-0.9.8h.orig/ssl/ssl_err.c 2007-10-12 03:00:30.000000000 +0300 +++ openssl-0.9.8h/ssl/ssl_err.c 2008-05-28 18:49:34.000000000 +0300 @@ -251,6 +251,7 @@ static ERR_STRING_DATA SSL_str_functs[]= {ERR_FUNC(SSL_F_TLS1_ENC), "TLS1_ENC"}, {ERR_FUNC(SSL_F_TLS1_SETUP_KEY_BLOCK), "TLS1_SETUP_KEY_BLOCK"}, {ERR_FUNC(SSL_F_WRITE_PENDING), "WRITE_PENDING"}, +{ERR_FUNC(SSL_F_SSL_SET_HELLO_EXTENSION), "SSL_set_hello_extension"}, {0,NULL} }; diff -upr openssl-0.9.8h.orig/ssl/ssl_sess.c openssl-0.9.8h/ssl/ssl_sess.c --- openssl-0.9.8h.orig/ssl/ssl_sess.c 2007-10-17 20:30:15.000000000 +0300 +++ openssl-0.9.8h/ssl/ssl_sess.c 2008-05-28 18:49:34.000000000 +0300 @@ -704,6 +704,52 @@ long SSL_CTX_get_timeout(const SSL_CTX * return(s->session_timeout); } +#ifndef OPENSSL_NO_TLSEXT +int SSL_set_session_secret_cb(SSL *s, int (*tls_session_secret_cb)(SSL *s, void *secret, int *secret_len, + STACK_OF(SSL_CIPHER) *peer_ciphers, SSL_CIPHER **cipher, void *arg), void *arg) +{ + if (s == NULL) return(0); + s->tls_session_secret_cb = tls_session_secret_cb; + s->tls_session_secret_cb_arg = arg; + return(1); +} + +int SSL_set_hello_extension(SSL *s, int ext_type, void *ext_data, int ext_len) +{ + if(s->version >= TLS1_VERSION) + { + if(s->tls_extension) + { + OPENSSL_free(s->tls_extension); + s->tls_extension = NULL; + } + + s->tls_extension = OPENSSL_malloc(sizeof(TLS_EXTENSION) + ext_len); + if(!s->tls_extension) + { + SSLerr(SSL_F_SSL_SET_HELLO_EXTENSION, ERR_R_MALLOC_FAILURE); + return 0; + } + + s->tls_extension->type = ext_type; + + if(ext_data) + { + s->tls_extension->length = ext_len; + s->tls_extension->data = s->tls_extension + 1; + memcpy(s->tls_extension->data, ext_data, ext_len); + } else { + s->tls_extension->length = 0; + s->tls_extension->data = NULL; + } + + return 1; + } + + return 0; +} +#endif /* OPENSSL_NO_TLSEXT */ + typedef struct timeout_param_st { SSL_CTX *ctx; diff -upr openssl-0.9.8h.orig/ssl/t1_lib.c openssl-0.9.8h/ssl/t1_lib.c --- openssl-0.9.8h.orig/ssl/t1_lib.c 2008-05-28 10:26:33.000000000 +0300 +++ openssl-0.9.8h/ssl/t1_lib.c 2008-05-28 18:49:34.000000000 +0300 @@ -106,6 +106,12 @@ int tls1_new(SSL *s) void tls1_free(SSL *s) { +#ifndef OPENSSL_NO_TLSEXT + if(s->tls_extension) + { + OPENSSL_free(s->tls_extension); + } +#endif ssl3_free(s); } @@ -175,8 +181,24 @@ unsigned char *ssl_add_clienthello_tlsex int ticklen; if (s->session && s->session->tlsext_tick) ticklen = s->session->tlsext_ticklen; + else if (s->session && s->tls_extension && + s->tls_extension->type == TLSEXT_TYPE_session_ticket && + s->tls_extension->data) + { + ticklen = s->tls_extension->length; + s->session->tlsext_tick = OPENSSL_malloc(ticklen); + if (!s->session->tlsext_tick) + return NULL; + memcpy(s->session->tlsext_tick, s->tls_extension->data, + ticklen); + s->session->tlsext_ticklen = ticklen; + } else ticklen = 0; + if (ticklen == 0 && s->tls_extension && + s->tls_extension->type == TLSEXT_TYPE_session_ticket && + s->tls_extension->data == NULL) + goto skip_ext; /* Check for enough room 2 for extension type, 2 for len * rest for ticket */ @@ -190,6 +212,7 @@ unsigned char *ssl_add_clienthello_tlsex ret += ticklen; } } + skip_ext: if (s->tlsext_status_type == TLSEXT_STATUSTYPE_ocsp) { @@ -774,6 +797,8 @@ int tls1_process_ticket(SSL *s, unsigned s->tlsext_ticket_expected = 1; return 0; /* Cache miss */ } + if (s->tls_session_secret_cb) + return 0; return tls_decrypt_ticket(s, p, size, session_id, len, ret); } diff -upr openssl-0.9.8h.orig/ssl/tls1.h openssl-0.9.8h/ssl/tls1.h --- openssl-0.9.8h.orig/ssl/tls1.h 2008-04-30 19:11:33.000000000 +0300 +++ openssl-0.9.8h/ssl/tls1.h 2008-05-28 18:49:34.000000000 +0300 @@ -398,6 +398,14 @@ SSL_CTX_callback_ctrl(ssl,SSL_CTRL_SET_T #define TLS_MD_MASTER_SECRET_CONST "\x6d\x61\x73\x74\x65\x72\x20\x73\x65\x63\x72\x65\x74" /*master secret*/ #endif +/* TLS extension struct */ +struct tls_extension_st +{ + unsigned short type; + unsigned short length; + void *data; +}; + #ifdef __cplusplus } #endif diff -upr openssl-0.9.8h.orig/util/ssleay.num openssl-0.9.8h/util/ssleay.num --- openssl-0.9.8h.orig/util/ssleay.num 2007-08-13 01:31:16.000000000 +0300 +++ openssl-0.9.8h/util/ssleay.num 2008-05-28 18:49:34.000000000 +0300 @@ -241,3 +241,5 @@ SSL_CTX_sess_get_remove_cb SSL_set_SSL_CTX 290 EXIST::FUNCTION: SSL_get_servername 291 EXIST::FUNCTION:TLSEXT SSL_get_servername_type 292 EXIST::FUNCTION:TLSEXT +SSL_set_hello_extension 305 EXIST::FUNCTION:TLSEXT +SSL_set_session_secret_cb 306 EXIST::FUNCTION:TLSEXT wpa-2.1/patches/openssl-0.9.8i-tls-extensions.patch000066400000000000000000000312031227414666700221340ustar00rootroot00000000000000This patch adds support for TLS SessionTicket extension (RFC 5077) for the parts used by EAP-FAST (RFC 4851). This is based on the patch from Alexey Kobozev (sent to openssl-dev mailing list on Tue, 07 Jun 2005 15:40:58 +0300). OpenSSL 0.9.8i does not enable TLS extension support by default, so it will need to be enabled by adding enable-tlsext to config script command line. Index: openssl-0.9.8i/ssl/s3_clnt.c =================================================================== --- openssl-0.9.8i.orig/ssl/s3_clnt.c 2008-06-16 19:56:41.000000000 +0300 +++ openssl-0.9.8i/ssl/s3_clnt.c 2008-11-23 20:39:40.000000000 +0200 @@ -759,6 +759,21 @@ goto f_err; } +#ifndef OPENSSL_NO_TLSEXT + /* check if we want to resume the session based on external pre-shared secret */ + if (s->version >= TLS1_VERSION && s->tls_session_secret_cb) + { + SSL_CIPHER *pref_cipher=NULL; + s->session->master_key_length=sizeof(s->session->master_key); + if (s->tls_session_secret_cb(s, s->session->master_key, &s->session->master_key_length, + NULL, &pref_cipher, s->tls_session_secret_cb_arg)) + { + s->session->cipher=pref_cipher ? + pref_cipher : ssl_get_cipher_by_char(s,p+j); + } + } +#endif /* OPENSSL_NO_TLSEXT */ + if (j != 0 && j == s->session->session_id_length && memcmp(p,s->session->session_id,j) == 0) { @@ -2701,11 +2716,8 @@ { int ok; long n; - /* If we have no ticket or session ID is non-zero length (a match of - * a non-zero session length would never reach here) it cannot be a - * resumed session. - */ - if (!s->session->tlsext_tick || s->session->session_id_length) + /* If we have no ticket it cannot be a resumed session. */ + if (!s->session->tlsext_tick) return 1; /* this function is called when we really expect a Certificate * message, so permit appropriate message length */ Index: openssl-0.9.8i/ssl/s3_srvr.c =================================================================== --- openssl-0.9.8i.orig/ssl/s3_srvr.c 2008-09-14 21:16:09.000000000 +0300 +++ openssl-0.9.8i/ssl/s3_srvr.c 2008-11-23 20:37:40.000000000 +0200 @@ -959,6 +959,59 @@ SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO,SSL_R_CLIENTHELLO_TLSEXT); goto err; } + + /* Check if we want to use external pre-shared secret for this + * handshake for not reused session only. We need to generate + * server_random before calling tls_session_secret_cb in order to allow + * SessionTicket processing to use it in key derivation. */ + { + unsigned long Time; + unsigned char *pos; + Time=(unsigned long)time(NULL); /* Time */ + pos=s->s3->server_random; + l2n(Time,pos); + if (RAND_pseudo_bytes(pos,SSL3_RANDOM_SIZE-4) <= 0) + { + al=SSL_AD_INTERNAL_ERROR; + goto f_err; + } + } + + if (!s->hit && s->version >= TLS1_VERSION && s->tls_session_secret_cb) + { + SSL_CIPHER *pref_cipher=NULL; + + s->session->master_key_length=sizeof(s->session->master_key); + if(s->tls_session_secret_cb(s, s->session->master_key, &s->session->master_key_length, + ciphers, &pref_cipher, s->tls_session_secret_cb_arg)) + { + s->hit=1; + s->session->ciphers=ciphers; + s->session->verify_result=X509_V_OK; + + ciphers=NULL; + + /* check if some cipher was preferred by call back */ + pref_cipher=pref_cipher ? pref_cipher : ssl3_choose_cipher(s, s->session->ciphers, SSL_get_ciphers(s)); + if (pref_cipher == NULL) + { + al=SSL_AD_HANDSHAKE_FAILURE; + SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO,SSL_R_NO_SHARED_CIPHER); + goto f_err; + } + + s->session->cipher=pref_cipher; + + if (s->cipher_list) + sk_SSL_CIPHER_free(s->cipher_list); + + if (s->cipher_list_by_id) + sk_SSL_CIPHER_free(s->cipher_list_by_id); + + s->cipher_list = sk_SSL_CIPHER_dup(s->session->ciphers); + s->cipher_list_by_id = sk_SSL_CIPHER_dup(s->session->ciphers); + } + } #endif /* Worst case, we will use the NULL compression, but if we have other * options, we will now look for them. We have i-1 compression @@ -1097,16 +1150,22 @@ unsigned char *buf; unsigned char *p,*d; int i,sl; - unsigned long l,Time; + unsigned long l; +#ifdef OPENSSL_NO_TLSEXT + unsigned long Time; +#endif if (s->state == SSL3_ST_SW_SRVR_HELLO_A) { buf=(unsigned char *)s->init_buf->data; +#ifdef OPENSSL_NO_TLSEXT p=s->s3->server_random; + /* Generate server_random if it was not needed previously */ Time=(unsigned long)time(NULL); /* Time */ l2n(Time,p); if (RAND_pseudo_bytes(p,SSL3_RANDOM_SIZE-4) <= 0) return -1; +#endif /* Do the message type and length last */ d=p= &(buf[4]); Index: openssl-0.9.8i/ssl/ssl_err.c =================================================================== --- openssl-0.9.8i.orig/ssl/ssl_err.c 2008-08-13 22:44:44.000000000 +0300 +++ openssl-0.9.8i/ssl/ssl_err.c 2008-11-23 20:33:43.000000000 +0200 @@ -253,6 +253,7 @@ {ERR_FUNC(SSL_F_TLS1_ENC), "TLS1_ENC"}, {ERR_FUNC(SSL_F_TLS1_SETUP_KEY_BLOCK), "TLS1_SETUP_KEY_BLOCK"}, {ERR_FUNC(SSL_F_WRITE_PENDING), "WRITE_PENDING"}, +{ERR_FUNC(SSL_F_SSL_SET_SESSION_TICKET_EXT), "SSL_set_session_ticket_ext"}, {0,NULL} }; Index: openssl-0.9.8i/ssl/ssl.h =================================================================== --- openssl-0.9.8i.orig/ssl/ssl.h 2008-08-13 22:44:44.000000000 +0300 +++ openssl-0.9.8i/ssl/ssl.h 2008-11-23 20:35:41.000000000 +0200 @@ -344,6 +344,7 @@ * 'struct ssl_st *' function parameters used to prototype callbacks * in SSL_CTX. */ typedef struct ssl_st *ssl_crock_st; +typedef struct tls_session_ticket_ext_st TLS_SESSION_TICKET_EXT; /* used to hold info on the particular ciphers used */ typedef struct ssl_cipher_st @@ -362,6 +363,9 @@ DECLARE_STACK_OF(SSL_CIPHER) +typedef int (*tls_session_ticket_ext_cb_fn)(SSL *s, const unsigned char *data, int len, void *arg); +typedef int (*tls_session_secret_cb_fn)(SSL *s, void *secret, int *secret_len, STACK_OF(SSL_CIPHER) *peer_ciphers, SSL_CIPHER **cipher, void *arg); + /* Used to hold functions for SSLv2 or SSLv3/TLSv1 functions */ typedef struct ssl_method_st { @@ -1034,6 +1038,18 @@ /* RFC4507 session ticket expected to be received or sent */ int tlsext_ticket_expected; + + /* TLS Session Ticket extension override */ + TLS_SESSION_TICKET_EXT *tlsext_session_ticket; + + /* TLS Session Ticket extension callback */ + tls_session_ticket_ext_cb_fn tls_session_ticket_ext_cb; + void *tls_session_ticket_ext_cb_arg; + + /* TLS pre-shared secret session resumption */ + tls_session_secret_cb_fn tls_session_secret_cb; + void *tls_session_secret_cb_arg; + SSL_CTX * initial_ctx; /* initial ctx, used to store sessions */ #define session_ctx initial_ctx #else @@ -1632,6 +1648,15 @@ int SSL_COMP_add_compression_method(int id,void *cm); #endif +/* TLS extensions functions */ +int SSL_set_session_ticket_ext(SSL *s, void *ext_data, int ext_len); + +int SSL_set_session_ticket_ext_cb(SSL *s, tls_session_ticket_ext_cb_fn cb, + void *arg); + +/* Pre-shared secret session resumption functions */ +int SSL_set_session_secret_cb(SSL *s, tls_session_secret_cb_fn tls_session_secret_cb, void *arg); + /* BEGIN ERROR CODES */ /* The following lines are auto generated by the script mkerr.pl. Any changes * made after this point may be overwritten when the script is next run. @@ -1824,6 +1849,7 @@ #define SSL_F_TLS1_ENC 210 #define SSL_F_TLS1_SETUP_KEY_BLOCK 211 #define SSL_F_WRITE_PENDING 212 +#define SSL_F_SSL_SET_SESSION_TICKET_EXT 213 /* Reason codes. */ #define SSL_R_APP_DATA_IN_HANDSHAKE 100 Index: openssl-0.9.8i/ssl/ssl_sess.c =================================================================== --- openssl-0.9.8i.orig/ssl/ssl_sess.c 2008-06-04 21:35:27.000000000 +0300 +++ openssl-0.9.8i/ssl/ssl_sess.c 2008-11-23 20:32:24.000000000 +0200 @@ -707,6 +707,61 @@ return(s->session_timeout); } +#ifndef OPENSSL_NO_TLSEXT +int SSL_set_session_secret_cb(SSL *s, int (*tls_session_secret_cb)(SSL *s, void *secret, int *secret_len, + STACK_OF(SSL_CIPHER) *peer_ciphers, SSL_CIPHER **cipher, void *arg), void *arg) + { + if (s == NULL) return(0); + s->tls_session_secret_cb = tls_session_secret_cb; + s->tls_session_secret_cb_arg = arg; + return(1); + } + +int SSL_set_session_ticket_ext_cb(SSL *s, tls_session_ticket_ext_cb_fn cb, + void *arg) + { + if (s == NULL) return(0); + s->tls_session_ticket_ext_cb = cb; + s->tls_session_ticket_ext_cb_arg = arg; + return(1); + } + +int SSL_set_session_ticket_ext(SSL *s, void *ext_data, int ext_len) + { + if (s->version >= TLS1_VERSION) + { + if (s->tlsext_session_ticket) + { + OPENSSL_free(s->tlsext_session_ticket); + s->tlsext_session_ticket = NULL; + } + + s->tlsext_session_ticket = OPENSSL_malloc(sizeof(TLS_SESSION_TICKET_EXT) + ext_len); + if (!s->tlsext_session_ticket) + { + SSLerr(SSL_F_SSL_SET_SESSION_TICKET_EXT, ERR_R_MALLOC_FAILURE); + return 0; + } + + if (ext_data) + { + s->tlsext_session_ticket->length = ext_len; + s->tlsext_session_ticket->data = s->tlsext_session_ticket + 1; + memcpy(s->tlsext_session_ticket->data, ext_data, ext_len); + } + else + { + s->tlsext_session_ticket->length = 0; + s->tlsext_session_ticket->data = NULL; + } + + return 1; + } + + return 0; + } +#endif /* OPENSSL_NO_TLSEXT */ + typedef struct timeout_param_st { SSL_CTX *ctx; Index: openssl-0.9.8i/ssl/t1_lib.c =================================================================== --- openssl-0.9.8i.orig/ssl/t1_lib.c 2008-09-04 01:13:04.000000000 +0300 +++ openssl-0.9.8i/ssl/t1_lib.c 2008-11-23 20:31:20.000000000 +0200 @@ -106,6 +106,12 @@ void tls1_free(SSL *s) { +#ifndef OPENSSL_NO_TLSEXT + if (s->tlsext_session_ticket) + { + OPENSSL_free(s->tlsext_session_ticket); + } +#endif ssl3_free(s); } @@ -175,8 +181,23 @@ int ticklen; if (s->session && s->session->tlsext_tick) ticklen = s->session->tlsext_ticklen; + else if (s->session && s->tlsext_session_ticket && + s->tlsext_session_ticket->data) + { + ticklen = s->tlsext_session_ticket->length; + s->session->tlsext_tick = OPENSSL_malloc(ticklen); + if (!s->session->tlsext_tick) + return NULL; + memcpy(s->session->tlsext_tick, + s->tlsext_session_ticket->data, + ticklen); + s->session->tlsext_ticklen = ticklen; + } else ticklen = 0; + if (ticklen == 0 && s->tlsext_session_ticket && + s->tlsext_session_ticket->data == NULL) + goto skip_ext; /* Check for enough room 2 for extension type, 2 for len * rest for ticket */ @@ -190,6 +211,7 @@ ret += ticklen; } } + skip_ext: if (s->tlsext_status_type == TLSEXT_STATUSTYPE_ocsp) { @@ -407,6 +429,15 @@ } } + else if (type == TLSEXT_TYPE_session_ticket) + { + if (s->tls_session_ticket_ext_cb && + !s->tls_session_ticket_ext_cb(s, data, size, s->tls_session_ticket_ext_cb_arg)) + { + *al = TLS1_AD_INTERNAL_ERROR; + return 0; + } + } else if (type == TLSEXT_TYPE_status_request && s->ctx->tlsext_status_cb) { @@ -553,6 +584,12 @@ } else if (type == TLSEXT_TYPE_session_ticket) { + if (s->tls_session_ticket_ext_cb && + !s->tls_session_ticket_ext_cb(s, data, size, s->tls_session_ticket_ext_cb_arg)) + { + *al = TLS1_AD_INTERNAL_ERROR; + return 0; + } if ((SSL_get_options(s) & SSL_OP_NO_TICKET) || (size > 0)) { @@ -776,6 +813,15 @@ s->tlsext_ticket_expected = 1; return 0; /* Cache miss */ } + if (s->tls_session_secret_cb) + { + /* Indicate cache miss here and instead of + * generating the session from ticket now, + * trigger abbreviated handshake based on + * external mechanism to calculate the master + * secret later. */ + return 0; + } return tls_decrypt_ticket(s, p, size, session_id, len, ret); } Index: openssl-0.9.8i/ssl/tls1.h =================================================================== --- openssl-0.9.8i.orig/ssl/tls1.h 2008-04-30 19:11:33.000000000 +0300 +++ openssl-0.9.8i/ssl/tls1.h 2008-11-23 20:22:38.000000000 +0200 @@ -398,6 +398,13 @@ #define TLS_MD_MASTER_SECRET_CONST "\x6d\x61\x73\x74\x65\x72\x20\x73\x65\x63\x72\x65\x74" /*master secret*/ #endif +/* TLS extension struct */ +struct tls_session_ticket_ext_st + { + unsigned short length; + void *data; + }; + #ifdef __cplusplus } #endif Index: openssl-0.9.8i/util/ssleay.num =================================================================== --- openssl-0.9.8i.orig/util/ssleay.num 2008-06-05 13:57:21.000000000 +0300 +++ openssl-0.9.8i/util/ssleay.num 2008-11-23 20:22:05.000000000 +0200 @@ -242,3 +242,5 @@ SSL_get_servername 291 EXIST::FUNCTION:TLSEXT SSL_get_servername_type 292 EXIST::FUNCTION:TLSEXT SSL_CTX_set_client_cert_engine 293 EXIST::FUNCTION:ENGINE +SSL_set_session_ticket_ext 306 EXIST::FUNCTION:TLSEXT +SSL_set_session_secret_cb 307 EXIST::FUNCTION:TLSEXT wpa-2.1/patches/openssl-0.9.8x-tls-extensions.patch000066400000000000000000000321651227414666700221630ustar00rootroot00000000000000This patch adds support for TLS SessionTicket extension (RFC 5077) for the parts used by EAP-FAST (RFC 4851). This is based on the patch from Alexey Kobozev (sent to openssl-dev mailing list on Tue, 07 Jun 2005 15:40:58 +0300). OpenSSL 0.9.8x does not enable TLS extension support by default, so it will need to be enabled by adding enable-tlsext to config script command line. diff -upr openssl-0.9.8x.orig/ssl/s3_clnt.c openssl-0.9.8x/ssl/s3_clnt.c --- openssl-0.9.8x.orig/ssl/s3_clnt.c 2011-12-26 21:38:28.000000000 +0200 +++ openssl-0.9.8x/ssl/s3_clnt.c 2012-07-07 10:46:31.501140621 +0300 @@ -757,6 +757,21 @@ int ssl3_get_server_hello(SSL *s) goto f_err; } +#ifndef OPENSSL_NO_TLSEXT + /* check if we want to resume the session based on external pre-shared secret */ + if (s->version >= TLS1_VERSION && s->tls_session_secret_cb) + { + SSL_CIPHER *pref_cipher=NULL; + s->session->master_key_length=sizeof(s->session->master_key); + if (s->tls_session_secret_cb(s, s->session->master_key, &s->session->master_key_length, + NULL, &pref_cipher, s->tls_session_secret_cb_arg)) + { + s->session->cipher=pref_cipher ? + pref_cipher : ssl_get_cipher_by_char(s,p+j); + } + } +#endif /* OPENSSL_NO_TLSEXT */ + if (j != 0 && j == s->session->session_id_length && memcmp(p,s->session->session_id,j) == 0) { @@ -2725,11 +2740,8 @@ int ssl3_check_finished(SSL *s) { int ok; long n; - /* If we have no ticket or session ID is non-zero length (a match of - * a non-zero session length would never reach here) it cannot be a - * resumed session. - */ - if (!s->session->tlsext_tick || s->session->session_id_length) + /* If we have no ticket it cannot be a resumed session. */ + if (!s->session->tlsext_tick) return 1; /* this function is called when we really expect a Certificate * message, so permit appropriate message length */ diff -upr openssl-0.9.8x.orig/ssl/s3_srvr.c openssl-0.9.8x/ssl/s3_srvr.c --- openssl-0.9.8x.orig/ssl/s3_srvr.c 2012-02-16 17:21:17.000000000 +0200 +++ openssl-0.9.8x/ssl/s3_srvr.c 2012-07-07 10:46:31.501140621 +0300 @@ -1009,6 +1009,59 @@ int ssl3_get_client_hello(SSL *s) SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO,SSL_R_CLIENTHELLO_TLSEXT); goto err; } + + /* Check if we want to use external pre-shared secret for this + * handshake for not reused session only. We need to generate + * server_random before calling tls_session_secret_cb in order to allow + * SessionTicket processing to use it in key derivation. */ + { + unsigned long Time; + unsigned char *pos; + Time=(unsigned long)time(NULL); /* Time */ + pos=s->s3->server_random; + l2n(Time,pos); + if (RAND_pseudo_bytes(pos,SSL3_RANDOM_SIZE-4) <= 0) + { + al=SSL_AD_INTERNAL_ERROR; + goto f_err; + } + } + + if (!s->hit && s->version >= TLS1_VERSION && s->tls_session_secret_cb) + { + SSL_CIPHER *pref_cipher=NULL; + + s->session->master_key_length=sizeof(s->session->master_key); + if(s->tls_session_secret_cb(s, s->session->master_key, &s->session->master_key_length, + ciphers, &pref_cipher, s->tls_session_secret_cb_arg)) + { + s->hit=1; + s->session->ciphers=ciphers; + s->session->verify_result=X509_V_OK; + + ciphers=NULL; + + /* check if some cipher was preferred by call back */ + pref_cipher=pref_cipher ? pref_cipher : ssl3_choose_cipher(s, s->session->ciphers, SSL_get_ciphers(s)); + if (pref_cipher == NULL) + { + al=SSL_AD_HANDSHAKE_FAILURE; + SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO,SSL_R_NO_SHARED_CIPHER); + goto f_err; + } + + s->session->cipher=pref_cipher; + + if (s->cipher_list) + sk_SSL_CIPHER_free(s->cipher_list); + + if (s->cipher_list_by_id) + sk_SSL_CIPHER_free(s->cipher_list_by_id); + + s->cipher_list = sk_SSL_CIPHER_dup(s->session->ciphers); + s->cipher_list_by_id = sk_SSL_CIPHER_dup(s->session->ciphers); + } + } #endif /* Worst case, we will use the NULL compression, but if we have other * options, we will now look for them. We have i-1 compression @@ -1147,16 +1200,22 @@ int ssl3_send_server_hello(SSL *s) unsigned char *buf; unsigned char *p,*d; int i,sl; - unsigned long l,Time; + unsigned long l; +#ifdef OPENSSL_NO_TLSEXT + unsigned long Time; +#endif if (s->state == SSL3_ST_SW_SRVR_HELLO_A) { buf=(unsigned char *)s->init_buf->data; +#ifdef OPENSSL_NO_TLSEXT p=s->s3->server_random; + /* Generate server_random if it was not needed previously */ Time=(unsigned long)time(NULL); /* Time */ l2n(Time,p); if (RAND_pseudo_bytes(p,SSL3_RANDOM_SIZE-4) <= 0) return -1; +#endif /* Do the message type and length last */ d=p= &(buf[4]); diff -upr openssl-0.9.8x.orig/ssl/ssl_err.c openssl-0.9.8x/ssl/ssl_err.c --- openssl-0.9.8x.orig/ssl/ssl_err.c 2012-03-12 16:50:55.000000000 +0200 +++ openssl-0.9.8x/ssl/ssl_err.c 2012-07-07 10:46:31.501140621 +0300 @@ -264,6 +264,7 @@ static ERR_STRING_DATA SSL_str_functs[]= {ERR_FUNC(SSL_F_TLS1_ENC), "TLS1_ENC"}, {ERR_FUNC(SSL_F_TLS1_SETUP_KEY_BLOCK), "TLS1_SETUP_KEY_BLOCK"}, {ERR_FUNC(SSL_F_WRITE_PENDING), "WRITE_PENDING"}, +{ERR_FUNC(SSL_F_SSL_SET_SESSION_TICKET_EXT), "SSL_set_session_ticket_ext"}, {0,NULL} }; diff -upr openssl-0.9.8x.orig/ssl/ssl.h openssl-0.9.8x/ssl/ssl.h --- openssl-0.9.8x.orig/ssl/ssl.h 2012-03-12 16:50:55.000000000 +0200 +++ openssl-0.9.8x/ssl/ssl.h 2012-07-07 10:46:31.501140621 +0300 @@ -344,6 +344,7 @@ extern "C" { * 'struct ssl_st *' function parameters used to prototype callbacks * in SSL_CTX. */ typedef struct ssl_st *ssl_crock_st; +typedef struct tls_session_ticket_ext_st TLS_SESSION_TICKET_EXT; /* used to hold info on the particular ciphers used */ typedef struct ssl_cipher_st @@ -362,6 +363,9 @@ typedef struct ssl_cipher_st DECLARE_STACK_OF(SSL_CIPHER) +typedef int (*tls_session_ticket_ext_cb_fn)(SSL *s, const unsigned char *data, int len, void *arg); +typedef int (*tls_session_secret_cb_fn)(SSL *s, void *secret, int *secret_len, STACK_OF(SSL_CIPHER) *peer_ciphers, SSL_CIPHER **cipher, void *arg); + /* Used to hold functions for SSLv2 or SSLv3/TLSv1 functions */ typedef struct ssl_method_st { @@ -1050,6 +1054,18 @@ struct ssl_st /* RFC4507 session ticket expected to be received or sent */ int tlsext_ticket_expected; + + /* TLS Session Ticket extension override */ + TLS_SESSION_TICKET_EXT *tlsext_session_ticket; + + /* TLS Session Ticket extension callback */ + tls_session_ticket_ext_cb_fn tls_session_ticket_ext_cb; + void *tls_session_ticket_ext_cb_arg; + + /* TLS pre-shared secret session resumption */ + tls_session_secret_cb_fn tls_session_secret_cb; + void *tls_session_secret_cb_arg; + SSL_CTX * initial_ctx; /* initial ctx, used to store sessions */ #define session_ctx initial_ctx #else @@ -1663,6 +1679,15 @@ void *SSL_COMP_get_compression_methods(v int SSL_COMP_add_compression_method(int id,void *cm); #endif +/* TLS extensions functions */ +int SSL_set_session_ticket_ext(SSL *s, void *ext_data, int ext_len); + +int SSL_set_session_ticket_ext_cb(SSL *s, tls_session_ticket_ext_cb_fn cb, + void *arg); + +/* Pre-shared secret session resumption functions */ +int SSL_set_session_secret_cb(SSL *s, tls_session_secret_cb_fn tls_session_secret_cb, void *arg); + /* BEGIN ERROR CODES */ /* The following lines are auto generated by the script mkerr.pl. Any changes * made after this point may be overwritten when the script is next run. @@ -1866,6 +1891,7 @@ void ERR_load_SSL_strings(void); #define SSL_F_TLS1_ENC 210 #define SSL_F_TLS1_SETUP_KEY_BLOCK 211 #define SSL_F_WRITE_PENDING 212 +#define SSL_F_SSL_SET_SESSION_TICKET_EXT 213 /* Reason codes. */ #define SSL_R_APP_DATA_IN_HANDSHAKE 100 diff -upr openssl-0.9.8x.orig/ssl/ssl_sess.c openssl-0.9.8x/ssl/ssl_sess.c --- openssl-0.9.8x.orig/ssl/ssl_sess.c 2010-02-01 18:48:40.000000000 +0200 +++ openssl-0.9.8x/ssl/ssl_sess.c 2012-07-07 10:46:31.501140621 +0300 @@ -712,6 +712,61 @@ long SSL_CTX_get_timeout(const SSL_CTX * return(s->session_timeout); } +#ifndef OPENSSL_NO_TLSEXT +int SSL_set_session_secret_cb(SSL *s, int (*tls_session_secret_cb)(SSL *s, void *secret, int *secret_len, + STACK_OF(SSL_CIPHER) *peer_ciphers, SSL_CIPHER **cipher, void *arg), void *arg) + { + if (s == NULL) return(0); + s->tls_session_secret_cb = tls_session_secret_cb; + s->tls_session_secret_cb_arg = arg; + return(1); + } + +int SSL_set_session_ticket_ext_cb(SSL *s, tls_session_ticket_ext_cb_fn cb, + void *arg) + { + if (s == NULL) return(0); + s->tls_session_ticket_ext_cb = cb; + s->tls_session_ticket_ext_cb_arg = arg; + return(1); + } + +int SSL_set_session_ticket_ext(SSL *s, void *ext_data, int ext_len) + { + if (s->version >= TLS1_VERSION) + { + if (s->tlsext_session_ticket) + { + OPENSSL_free(s->tlsext_session_ticket); + s->tlsext_session_ticket = NULL; + } + + s->tlsext_session_ticket = OPENSSL_malloc(sizeof(TLS_SESSION_TICKET_EXT) + ext_len); + if (!s->tlsext_session_ticket) + { + SSLerr(SSL_F_SSL_SET_SESSION_TICKET_EXT, ERR_R_MALLOC_FAILURE); + return 0; + } + + if (ext_data) + { + s->tlsext_session_ticket->length = ext_len; + s->tlsext_session_ticket->data = s->tlsext_session_ticket + 1; + memcpy(s->tlsext_session_ticket->data, ext_data, ext_len); + } + else + { + s->tlsext_session_ticket->length = 0; + s->tlsext_session_ticket->data = NULL; + } + + return 1; + } + + return 0; + } +#endif /* OPENSSL_NO_TLSEXT */ + typedef struct timeout_param_st { SSL_CTX *ctx; diff -upr openssl-0.9.8x.orig/ssl/t1_lib.c openssl-0.9.8x/ssl/t1_lib.c --- openssl-0.9.8x.orig/ssl/t1_lib.c 2012-01-04 16:25:10.000000000 +0200 +++ openssl-0.9.8x/ssl/t1_lib.c 2012-07-07 10:47:31.153140501 +0300 @@ -106,6 +106,12 @@ int tls1_new(SSL *s) void tls1_free(SSL *s) { +#ifndef OPENSSL_NO_TLSEXT + if (s->tlsext_session_ticket) + { + OPENSSL_free(s->tlsext_session_ticket); + } +#endif ssl3_free(s); } @@ -206,8 +212,23 @@ unsigned char *ssl_add_clienthello_tlsex int ticklen; if (!s->new_session && s->session && s->session->tlsext_tick) ticklen = s->session->tlsext_ticklen; + else if (s->session && s->tlsext_session_ticket && + s->tlsext_session_ticket->data) + { + ticklen = s->tlsext_session_ticket->length; + s->session->tlsext_tick = OPENSSL_malloc(ticklen); + if (!s->session->tlsext_tick) + return NULL; + memcpy(s->session->tlsext_tick, + s->tlsext_session_ticket->data, + ticklen); + s->session->tlsext_ticklen = ticklen; + } else ticklen = 0; + if (ticklen == 0 && s->tlsext_session_ticket && + s->tlsext_session_ticket->data == NULL) + goto skip_ext; /* Check for enough room 2 for extension type, 2 for len * rest for ticket */ @@ -221,6 +242,7 @@ unsigned char *ssl_add_clienthello_tlsex ret += ticklen; } } + skip_ext: if (s->tlsext_status_type == TLSEXT_STATUSTYPE_ocsp && s->version != DTLS1_VERSION) @@ -486,6 +508,15 @@ int ssl_parse_clienthello_tlsext(SSL *s, return 0; renegotiate_seen = 1; } + else if (type == TLSEXT_TYPE_session_ticket) + { + if (s->tls_session_ticket_ext_cb && + !s->tls_session_ticket_ext_cb(s, data, size, s->tls_session_ticket_ext_cb_arg)) + { + *al = TLS1_AD_INTERNAL_ERROR; + return 0; + } + } else if (type == TLSEXT_TYPE_status_request && s->version != DTLS1_VERSION && s->ctx->tlsext_status_cb) { @@ -663,6 +694,12 @@ int ssl_parse_serverhello_tlsext(SSL *s, } else if (type == TLSEXT_TYPE_session_ticket) { + if (s->tls_session_ticket_ext_cb && + !s->tls_session_ticket_ext_cb(s, data, size, s->tls_session_ticket_ext_cb_arg)) + { + *al = TLS1_AD_INTERNAL_ERROR; + return 0; + } if ((SSL_get_options(s) & SSL_OP_NO_TICKET) || (size > 0)) { @@ -920,6 +957,15 @@ int tls1_process_ticket(SSL *s, unsigned s->tlsext_ticket_expected = 1; return 0; /* Cache miss */ } + if (s->tls_session_secret_cb) + { + /* Indicate cache miss here and instead of + * generating the session from ticket now, + * trigger abbreviated handshake based on + * external mechanism to calculate the master + * secret later. */ + return 0; + } return tls_decrypt_ticket(s, p, size, session_id, len, ret); } diff -upr openssl-0.9.8x.orig/ssl/tls1.h openssl-0.9.8x/ssl/tls1.h --- openssl-0.9.8x.orig/ssl/tls1.h 2009-11-08 16:51:54.000000000 +0200 +++ openssl-0.9.8x/ssl/tls1.h 2012-07-07 10:46:31.501140621 +0300 @@ -401,6 +401,13 @@ SSL_CTX_callback_ctrl(ssl,SSL_CTRL_SET_T #define TLS_MD_MASTER_SECRET_CONST "\x6d\x61\x73\x74\x65\x72\x20\x73\x65\x63\x72\x65\x74" /*master secret*/ #endif +/* TLS extension struct */ +struct tls_session_ticket_ext_st + { + unsigned short length; + void *data; + }; + #ifdef __cplusplus } #endif diff -upr openssl-0.9.8x.orig/util/ssleay.num openssl-0.9.8x/util/ssleay.num --- openssl-0.9.8x.orig/util/ssleay.num 2008-06-05 13:57:21.000000000 +0300 +++ openssl-0.9.8x/util/ssleay.num 2012-07-07 10:46:31.505140623 +0300 @@ -242,3 +242,5 @@ SSL_set_SSL_CTX SSL_get_servername 291 EXIST::FUNCTION:TLSEXT SSL_get_servername_type 292 EXIST::FUNCTION:TLSEXT SSL_CTX_set_client_cert_engine 293 EXIST::FUNCTION:ENGINE +SSL_set_session_ticket_ext 306 EXIST::FUNCTION:TLSEXT +SSL_set_session_secret_cb 307 EXIST::FUNCTION:TLSEXT wpa-2.1/patches/openssl-0.9.9-session-ticket.patch000066400000000000000000000301301227414666700217270ustar00rootroot00000000000000This patch adds support for TLS SessionTicket extension (RFC 5077) for the parts used by EAP-FAST (RFC 4851). This is based on the patch from Alexey Kobozev (sent to openssl-dev mailing list on Tue, 07 Jun 2005 15:40:58 +0300). NOTE: This patch (without SSL_set_hello_extension() wrapper) was merged into the upstream OpenSSL 0.9.9 tree and as such, an external patch for EAP-FAST support is not needed anymore. Index: openssl-SNAP-20081111/ssl/s3_clnt.c =================================================================== --- openssl-SNAP-20081111.orig/ssl/s3_clnt.c +++ openssl-SNAP-20081111/ssl/s3_clnt.c @@ -788,6 +788,23 @@ int ssl3_get_server_hello(SSL *s) goto f_err; } +#ifndef OPENSSL_NO_TLSEXT + /* check if we want to resume the session based on external pre-shared secret */ + if (s->version >= TLS1_VERSION && s->tls_session_secret_cb) + { + SSL_CIPHER *pref_cipher=NULL; + s->session->master_key_length=sizeof(s->session->master_key); + if (s->tls_session_secret_cb(s, s->session->master_key, + &s->session->master_key_length, + NULL, &pref_cipher, + s->tls_session_secret_cb_arg)) + { + s->session->cipher = pref_cipher ? + pref_cipher : ssl_get_cipher_by_char(s, p+j); + } + } +#endif /* OPENSSL_NO_TLSEXT */ + if (j != 0 && j == s->session->session_id_length && memcmp(p,s->session->session_id,j) == 0) { @@ -2927,11 +2944,8 @@ static int ssl3_check_finished(SSL *s) { int ok; long n; - /* If we have no ticket or session ID is non-zero length (a match of - * a non-zero session length would never reach here) it cannot be a - * resumed session. - */ - if (!s->session->tlsext_tick || s->session->session_id_length) + /* If we have no ticket it cannot be a resumed session. */ + if (!s->session->tlsext_tick) return 1; /* this function is called when we really expect a Certificate * message, so permit appropriate message length */ Index: openssl-SNAP-20081111/ssl/s3_srvr.c =================================================================== --- openssl-SNAP-20081111.orig/ssl/s3_srvr.c +++ openssl-SNAP-20081111/ssl/s3_srvr.c @@ -1010,6 +1010,59 @@ int ssl3_get_client_hello(SSL *s) SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO,SSL_R_CLIENTHELLO_TLSEXT); goto err; } + + /* Check if we want to use external pre-shared secret for this + * handshake for not reused session only. We need to generate + * server_random before calling tls_session_secret_cb in order to allow + * SessionTicket processing to use it in key derivation. */ + { + unsigned long Time; + unsigned char *pos; + Time=(unsigned long)time(NULL); /* Time */ + pos=s->s3->server_random; + l2n(Time,pos); + if (RAND_pseudo_bytes(pos,SSL3_RANDOM_SIZE-4) <= 0) + { + al=SSL_AD_INTERNAL_ERROR; + goto f_err; + } + } + + if (!s->hit && s->version >= TLS1_VERSION && s->tls_session_secret_cb) + { + SSL_CIPHER *pref_cipher=NULL; + + s->session->master_key_length=sizeof(s->session->master_key); + if(s->tls_session_secret_cb(s, s->session->master_key, &s->session->master_key_length, + ciphers, &pref_cipher, s->tls_session_secret_cb_arg)) + { + s->hit=1; + s->session->ciphers=ciphers; + s->session->verify_result=X509_V_OK; + + ciphers=NULL; + + /* check if some cipher was preferred by call back */ + pref_cipher=pref_cipher ? pref_cipher : ssl3_choose_cipher(s, s->session->ciphers, SSL_get_ciphers(s)); + if (pref_cipher == NULL) + { + al=SSL_AD_HANDSHAKE_FAILURE; + SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO,SSL_R_NO_SHARED_CIPHER); + goto f_err; + } + + s->session->cipher=pref_cipher; + + if (s->cipher_list) + sk_SSL_CIPHER_free(s->cipher_list); + + if (s->cipher_list_by_id) + sk_SSL_CIPHER_free(s->cipher_list_by_id); + + s->cipher_list = sk_SSL_CIPHER_dup(s->session->ciphers); + s->cipher_list_by_id = sk_SSL_CIPHER_dup(s->session->ciphers); + } + } #endif /* Worst case, we will use the NULL compression, but if we have other @@ -1134,16 +1187,22 @@ int ssl3_send_server_hello(SSL *s) unsigned char *buf; unsigned char *p,*d; int i,sl; - unsigned long l,Time; + unsigned long l; +#ifdef OPENSSL_NO_TLSEXT + unsigned long Time; +#endif if (s->state == SSL3_ST_SW_SRVR_HELLO_A) { buf=(unsigned char *)s->init_buf->data; +#ifdef OPENSSL_NO_TLSEXT p=s->s3->server_random; + /* Generate server_random if it was not needed previously */ Time=(unsigned long)time(NULL); /* Time */ l2n(Time,p); if (RAND_pseudo_bytes(p,SSL3_RANDOM_SIZE-4) <= 0) return -1; +#endif /* Do the message type and length last */ d=p= &(buf[4]); Index: openssl-SNAP-20081111/ssl/ssl_err.c =================================================================== --- openssl-SNAP-20081111.orig/ssl/ssl_err.c +++ openssl-SNAP-20081111/ssl/ssl_err.c @@ -263,6 +263,7 @@ static ERR_STRING_DATA SSL_str_functs[]= {ERR_FUNC(SSL_F_TLS1_PRF), "tls1_prf"}, {ERR_FUNC(SSL_F_TLS1_SETUP_KEY_BLOCK), "TLS1_SETUP_KEY_BLOCK"}, {ERR_FUNC(SSL_F_WRITE_PENDING), "WRITE_PENDING"}, +{ERR_FUNC(SSL_F_SSL_SET_SESSION_TICKET_EXT), "SSL_set_session_ticket_ext"}, {0,NULL} }; Index: openssl-SNAP-20081111/ssl/ssl.h =================================================================== --- openssl-SNAP-20081111.orig/ssl/ssl.h +++ openssl-SNAP-20081111/ssl/ssl.h @@ -355,6 +355,7 @@ extern "C" { * 'struct ssl_st *' function parameters used to prototype callbacks * in SSL_CTX. */ typedef struct ssl_st *ssl_crock_st; +typedef struct tls_session_ticket_ext_st TLS_SESSION_TICKET_EXT; /* used to hold info on the particular ciphers used */ typedef struct ssl_cipher_st @@ -378,6 +379,8 @@ typedef struct ssl_cipher_st DECLARE_STACK_OF(SSL_CIPHER) +typedef int (*tls_session_secret_cb_fn)(SSL *s, void *secret, int *secret_len, STACK_OF(SSL_CIPHER) *peer_ciphers, SSL_CIPHER **cipher, void *arg); + /* Used to hold functions for SSLv2 or SSLv3/TLSv1 functions */ typedef struct ssl_method_st { @@ -1145,6 +1148,13 @@ struct ssl_st void *tlsext_opaque_prf_input; size_t tlsext_opaque_prf_input_len; + /* TLS Session Ticket extension override */ + TLS_SESSION_TICKET_EXT *tlsext_session_ticket; + + /* TLS pre-shared secret session resumption */ + tls_session_secret_cb_fn tls_session_secret_cb; + void *tls_session_secret_cb_arg; + SSL_CTX * initial_ctx; /* initial ctx, used to store sessions */ #define session_ctx initial_ctx #else @@ -1746,6 +1756,16 @@ void *SSL_COMP_get_compression_methods(v int SSL_COMP_add_compression_method(int id,void *cm); #endif +/* NOTE: This function will be removed; it is only here for backwards + * compatibility for the API during testing. */ +int SSL_set_hello_extension(SSL *s, int ext_type, void *ext_data, int ext_len); + +/* TLS extensions functions */ +int SSL_set_session_ticket_ext(SSL *s, void *ext_data, int ext_len); + +/* Pre-shared secret session resumption functions */ +int SSL_set_session_secret_cb(SSL *s, tls_session_secret_cb_fn tls_session_secret_cb, void *arg); + /* BEGIN ERROR CODES */ /* The following lines are auto generated by the script mkerr.pl. Any changes * made after this point may be overwritten when the script is next run. @@ -1948,6 +1968,7 @@ void ERR_load_SSL_strings(void); #define SSL_F_TLS1_PRF 284 #define SSL_F_TLS1_SETUP_KEY_BLOCK 211 #define SSL_F_WRITE_PENDING 212 +#define SSL_F_SSL_SET_SESSION_TICKET_EXT 213 /* Reason codes. */ #define SSL_R_APP_DATA_IN_HANDSHAKE 100 Index: openssl-SNAP-20081111/ssl/ssl_sess.c =================================================================== --- openssl-SNAP-20081111.orig/ssl/ssl_sess.c +++ openssl-SNAP-20081111/ssl/ssl_sess.c @@ -834,6 +834,62 @@ long SSL_CTX_get_timeout(const SSL_CTX * return(s->session_timeout); } +#ifndef OPENSSL_NO_TLSEXT +int SSL_set_session_secret_cb(SSL *s, int (*tls_session_secret_cb)(SSL *s, void *secret, int *secret_len, + STACK_OF(SSL_CIPHER) *peer_ciphers, SSL_CIPHER **cipher, void *arg), void *arg) + { + if (s == NULL) return(0); + s->tls_session_secret_cb = tls_session_secret_cb; + s->tls_session_secret_cb_arg = arg; + return(1); + } + +int SSL_set_session_ticket_ext(SSL *s, void *ext_data, int ext_len) + { + if (s->version >= TLS1_VERSION) + { + if (s->tlsext_session_ticket) + { + OPENSSL_free(s->tlsext_session_ticket); + s->tlsext_session_ticket = NULL; + } + + s->tlsext_session_ticket = OPENSSL_malloc(sizeof(TLS_SESSION_TICKET_EXT) + ext_len); + if (!s->tlsext_session_ticket) + { + SSLerr(SSL_F_SSL_SET_SESSION_TICKET_EXT, ERR_R_MALLOC_FAILURE); + return 0; + } + + if (ext_data) + { + s->tlsext_session_ticket->length = ext_len; + s->tlsext_session_ticket->data = s->tlsext_session_ticket + 1; + memcpy(s->tlsext_session_ticket->data, ext_data, ext_len); + } + else + { + s->tlsext_session_ticket->length = 0; + s->tlsext_session_ticket->data = NULL; + } + + return 1; + } + + return 0; + } + +/* NOTE: This function will be removed; it is only here for backwards + * compatibility for the API during testing. */ +int SSL_set_hello_extension(SSL *s, int ext_type, void *ext_data, int ext_len) + { + if (ext_type != TLSEXT_TYPE_session_ticket) + return 0; + + return SSL_set_session_ticket_ext(s, ext_data, ext_len); + } +#endif /* OPENSSL_NO_TLSEXT */ + typedef struct timeout_param_st { SSL_CTX *ctx; Index: openssl-SNAP-20081111/ssl/t1_lib.c =================================================================== --- openssl-SNAP-20081111.orig/ssl/t1_lib.c +++ openssl-SNAP-20081111/ssl/t1_lib.c @@ -154,6 +154,12 @@ int tls1_new(SSL *s) void tls1_free(SSL *s) { +#ifndef OPENSSL_NO_TLSEXT + if (s->tlsext_session_ticket) + { + OPENSSL_free(s->tlsext_session_ticket); + } +#endif /* OPENSSL_NO_TLSEXT */ ssl3_free(s); } @@ -357,8 +363,23 @@ unsigned char *ssl_add_clienthello_tlsex int ticklen; if (s->session && s->session->tlsext_tick) ticklen = s->session->tlsext_ticklen; + else if (s->session && s->tlsext_session_ticket && + s->tlsext_session_ticket->data) + { + ticklen = s->tlsext_session_ticket->length; + s->session->tlsext_tick = OPENSSL_malloc(ticklen); + if (!s->session->tlsext_tick) + return NULL; + memcpy(s->session->tlsext_tick, + s->tlsext_session_ticket->data, + ticklen); + s->session->tlsext_ticklen = ticklen; + } else ticklen = 0; + if (ticklen == 0 && s->tlsext_session_ticket && + s->tlsext_session_ticket->data == NULL) + goto skip_ext; /* Check for enough room 2 for extension type, 2 for len * rest for ticket */ @@ -371,6 +392,7 @@ unsigned char *ssl_add_clienthello_tlsex ret += ticklen; } } + skip_ext: #ifdef TLSEXT_TYPE_opaque_prf_input if (s->s3->client_opaque_prf_input != NULL) @@ -1435,6 +1457,15 @@ int tls1_process_ticket(SSL *s, unsigned s->tlsext_ticket_expected = 1; return 0; /* Cache miss */ } + if (s->tls_session_secret_cb) + { + /* Indicate cache miss here and instead of + * generating the session from ticket now, + * trigger abbreviated handshake based on + * external mechanism to calculate the master + * secret later. */ + return 0; + } return tls_decrypt_ticket(s, p, size, session_id, len, ret); } Index: openssl-SNAP-20081111/ssl/tls1.h =================================================================== --- openssl-SNAP-20081111.orig/ssl/tls1.h +++ openssl-SNAP-20081111/ssl/tls1.h @@ -512,6 +512,13 @@ SSL_CTX_callback_ctrl(ssl,SSL_CTRL_SET_T #define TLS_MD_MASTER_SECRET_CONST "\x6d\x61\x73\x74\x65\x72\x20\x73\x65\x63\x72\x65\x74" /*master secret*/ #endif +/* TLS Session Ticket extension struct */ +struct tls_session_ticket_ext_st + { + unsigned short length; + void *data; + }; + #ifdef __cplusplus } #endif Index: openssl-SNAP-20081111/util/ssleay.num =================================================================== --- openssl-SNAP-20081111.orig/util/ssleay.num +++ openssl-SNAP-20081111/util/ssleay.num @@ -254,3 +254,5 @@ PEM_read_bio_SSL_SESSION SSL_CTX_set_psk_server_callback 303 EXIST::FUNCTION:PSK SSL_get_psk_identity 304 EXIST::FUNCTION:PSK PEM_write_SSL_SESSION 305 EXIST:!WIN16:FUNCTION: +SSL_set_session_ticket_ext 306 EXIST::FUNCTION:TLSEXT +SSL_set_session_secret_cb 307 EXIST::FUNCTION:TLSEXT wpa-2.1/src/000077500000000000000000000000001227414666700127515ustar00rootroot00000000000000wpa-2.1/src/Makefile000066400000000000000000000005461227414666700144160ustar00rootroot00000000000000SUBDIRS=ap common crypto drivers eapol_auth eapol_supp eap_common eap_peer eap_server l2_packet p2p radius rsn_supp tls utils wps all: for d in $(SUBDIRS); do [ -d $$d ] && $(MAKE) -C $$d; done clean: for d in $(SUBDIRS); do [ -d $$d ] && $(MAKE) -C $$d clean; done rm -f *~ install: for d in $(SUBDIRS); do [ -d $$d ] && $(MAKE) -C $$d install; done wpa-2.1/src/ap/000077500000000000000000000000001227414666700133515ustar00rootroot00000000000000wpa-2.1/src/ap/Makefile000066400000000000000000000001641227414666700150120ustar00rootroot00000000000000all: @echo Nothing to be made. clean: rm -f *~ *.o *.d *.gcno *.gcda *.gcov install: @echo Nothing to be made. wpa-2.1/src/ap/accounting.c000066400000000000000000000305151227414666700156530ustar00rootroot00000000000000/* * hostapd / RADIUS Accounting * Copyright (c) 2002-2009, 2012, 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/eloop.h" #include "radius/radius.h" #include "radius/radius_client.h" #include "hostapd.h" #include "ieee802_1x.h" #include "ap_config.h" #include "sta_info.h" #include "ap_drv_ops.h" #include "accounting.h" /* Default interval in seconds for polling TX/RX octets from the driver if * STA is not using interim accounting. This detects wrap arounds for * input/output octets and updates Acct-{Input,Output}-Gigawords. */ #define ACCT_DEFAULT_UPDATE_INTERVAL 300 static void accounting_sta_interim(struct hostapd_data *hapd, struct sta_info *sta); static struct radius_msg * accounting_msg(struct hostapd_data *hapd, struct sta_info *sta, int status_type) { struct radius_msg *msg; char buf[128]; u8 *val; size_t len; int i; struct wpabuf *b; msg = radius_msg_new(RADIUS_CODE_ACCOUNTING_REQUEST, radius_client_get_id(hapd->radius)); if (msg == NULL) { wpa_printf(MSG_INFO, "Could not create new RADIUS packet"); return NULL; } if (sta) { radius_msg_make_authenticator(msg, (u8 *) sta, sizeof(*sta)); os_snprintf(buf, sizeof(buf), "%08X-%08X", sta->acct_session_id_hi, sta->acct_session_id_lo); if (!radius_msg_add_attr(msg, RADIUS_ATTR_ACCT_SESSION_ID, (u8 *) buf, os_strlen(buf))) { wpa_printf(MSG_INFO, "Could not add Acct-Session-Id"); goto fail; } } else { radius_msg_make_authenticator(msg, (u8 *) hapd, sizeof(*hapd)); } if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_STATUS_TYPE, status_type)) { wpa_printf(MSG_INFO, "Could not add Acct-Status-Type"); goto fail; } if (!hostapd_config_get_radius_attr(hapd->conf->radius_acct_req_attr, RADIUS_ATTR_ACCT_AUTHENTIC) && !radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_AUTHENTIC, hapd->conf->ieee802_1x ? RADIUS_ACCT_AUTHENTIC_RADIUS : RADIUS_ACCT_AUTHENTIC_LOCAL)) { wpa_printf(MSG_INFO, "Could not add Acct-Authentic"); goto fail; } if (sta) { /* Use 802.1X identity if available */ val = ieee802_1x_get_identity(sta->eapol_sm, &len); /* Use RADIUS ACL identity if 802.1X provides no identity */ if (!val && sta->identity) { val = (u8 *) sta->identity; len = os_strlen(sta->identity); } /* Use STA MAC if neither 802.1X nor RADIUS ACL provided * identity */ if (!val) { os_snprintf(buf, sizeof(buf), RADIUS_ADDR_FORMAT, MAC2STR(sta->addr)); val = (u8 *) buf; len = os_strlen(buf); } if (!radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME, val, len)) { wpa_printf(MSG_INFO, "Could not add User-Name"); goto fail; } } if (add_common_radius_attr(hapd, hapd->conf->radius_acct_req_attr, sta, msg) < 0) goto fail; if (sta) { for (i = 0; ; i++) { val = ieee802_1x_get_radius_class(sta->eapol_sm, &len, i); if (val == NULL) break; if (!radius_msg_add_attr(msg, RADIUS_ATTR_CLASS, val, len)) { wpa_printf(MSG_INFO, "Could not add Class"); goto fail; } } b = ieee802_1x_get_radius_cui(sta->eapol_sm); if (b && !radius_msg_add_attr(msg, RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, wpabuf_head(b), wpabuf_len(b))) { wpa_printf(MSG_ERROR, "Could not add CUI"); goto fail; } if (!b && sta->radius_cui && !radius_msg_add_attr(msg, RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, (u8 *) sta->radius_cui, os_strlen(sta->radius_cui))) { wpa_printf(MSG_ERROR, "Could not add CUI from ACL"); goto fail; } } return msg; fail: radius_msg_free(msg); return NULL; } static int accounting_sta_update_stats(struct hostapd_data *hapd, struct sta_info *sta, struct hostap_sta_driver_data *data) { if (hostapd_drv_read_sta_data(hapd, data, sta->addr)) return -1; if (sta->last_rx_bytes > data->rx_bytes) sta->acct_input_gigawords++; if (sta->last_tx_bytes > data->tx_bytes) sta->acct_output_gigawords++; sta->last_rx_bytes = data->rx_bytes; sta->last_tx_bytes = data->tx_bytes; hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_DEBUG, "updated TX/RX stats: " "Acct-Input-Octets=%lu Acct-Input-Gigawords=%u " "Acct-Output-Octets=%lu Acct-Output-Gigawords=%u", sta->last_rx_bytes, sta->acct_input_gigawords, sta->last_tx_bytes, sta->acct_output_gigawords); return 0; } static void accounting_interim_update(void *eloop_ctx, void *timeout_ctx) { struct hostapd_data *hapd = eloop_ctx; struct sta_info *sta = timeout_ctx; int interval; if (sta->acct_interim_interval) { accounting_sta_interim(hapd, sta); interval = sta->acct_interim_interval; } else { struct hostap_sta_driver_data data; accounting_sta_update_stats(hapd, sta, &data); interval = ACCT_DEFAULT_UPDATE_INTERVAL; } eloop_register_timeout(interval, 0, accounting_interim_update, hapd, sta); } /** * accounting_sta_start - Start STA accounting * @hapd: hostapd BSS data * @sta: The station */ void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta) { struct radius_msg *msg; int interval; if (sta->acct_session_started) return; hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_INFO, "starting accounting session %08X-%08X", sta->acct_session_id_hi, sta->acct_session_id_lo); os_get_reltime(&sta->acct_session_start); sta->last_rx_bytes = sta->last_tx_bytes = 0; sta->acct_input_gigawords = sta->acct_output_gigawords = 0; hostapd_drv_sta_clear_stats(hapd, sta->addr); if (!hapd->conf->radius->acct_server) return; if (sta->acct_interim_interval) interval = sta->acct_interim_interval; else interval = ACCT_DEFAULT_UPDATE_INTERVAL; eloop_register_timeout(interval, 0, accounting_interim_update, hapd, sta); msg = accounting_msg(hapd, sta, RADIUS_ACCT_STATUS_TYPE_START); if (msg && radius_client_send(hapd->radius, msg, RADIUS_ACCT, sta->addr) < 0) radius_msg_free(msg); sta->acct_session_started = 1; } static void accounting_sta_report(struct hostapd_data *hapd, struct sta_info *sta, int stop) { struct radius_msg *msg; int cause = sta->acct_terminate_cause; struct hostap_sta_driver_data data; struct os_reltime now_r, diff; struct os_time now; u32 gigawords; if (!hapd->conf->radius->acct_server) return; msg = accounting_msg(hapd, sta, stop ? RADIUS_ACCT_STATUS_TYPE_STOP : RADIUS_ACCT_STATUS_TYPE_INTERIM_UPDATE); if (!msg) { wpa_printf(MSG_INFO, "Could not create RADIUS Accounting message"); return; } os_get_reltime(&now_r); os_get_time(&now); os_reltime_sub(&now_r, &sta->acct_session_start, &diff); if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_SESSION_TIME, diff.sec)) { wpa_printf(MSG_INFO, "Could not add Acct-Session-Time"); goto fail; } if (accounting_sta_update_stats(hapd, sta, &data) == 0) { if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_INPUT_PACKETS, data.rx_packets)) { wpa_printf(MSG_INFO, "Could not add Acct-Input-Packets"); goto fail; } if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_OUTPUT_PACKETS, data.tx_packets)) { wpa_printf(MSG_INFO, "Could not add Acct-Output-Packets"); goto fail; } if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_INPUT_OCTETS, data.rx_bytes)) { wpa_printf(MSG_INFO, "Could not add Acct-Input-Octets"); goto fail; } gigawords = sta->acct_input_gigawords; #if __WORDSIZE == 64 gigawords += data.rx_bytes >> 32; #endif if (gigawords && !radius_msg_add_attr_int32( msg, RADIUS_ATTR_ACCT_INPUT_GIGAWORDS, gigawords)) { wpa_printf(MSG_INFO, "Could not add Acct-Input-Gigawords"); goto fail; } if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_OUTPUT_OCTETS, data.tx_bytes)) { wpa_printf(MSG_INFO, "Could not add Acct-Output-Octets"); goto fail; } gigawords = sta->acct_output_gigawords; #if __WORDSIZE == 64 gigawords += data.tx_bytes >> 32; #endif if (gigawords && !radius_msg_add_attr_int32( msg, RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS, gigawords)) { wpa_printf(MSG_INFO, "Could not add Acct-Output-Gigawords"); goto fail; } } if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_EVENT_TIMESTAMP, now.sec)) { wpa_printf(MSG_INFO, "Could not add Event-Timestamp"); goto fail; } if (eloop_terminated()) cause = RADIUS_ACCT_TERMINATE_CAUSE_ADMIN_REBOOT; if (stop && cause && !radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_TERMINATE_CAUSE, cause)) { wpa_printf(MSG_INFO, "Could not add Acct-Terminate-Cause"); goto fail; } if (radius_client_send(hapd->radius, msg, stop ? RADIUS_ACCT : RADIUS_ACCT_INTERIM, sta->addr) < 0) goto fail; return; fail: radius_msg_free(msg); } /** * accounting_sta_interim - Send a interim STA accounting report * @hapd: hostapd BSS data * @sta: The station */ static void accounting_sta_interim(struct hostapd_data *hapd, struct sta_info *sta) { if (sta->acct_session_started) accounting_sta_report(hapd, sta, 0); } /** * accounting_sta_stop - Stop STA accounting * @hapd: hostapd BSS data * @sta: The station */ void accounting_sta_stop(struct hostapd_data *hapd, struct sta_info *sta) { if (sta->acct_session_started) { accounting_sta_report(hapd, sta, 1); eloop_cancel_timeout(accounting_interim_update, hapd, sta); hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_INFO, "stopped accounting session %08X-%08X", sta->acct_session_id_hi, sta->acct_session_id_lo); sta->acct_session_started = 0; } } void accounting_sta_get_id(struct hostapd_data *hapd, struct sta_info *sta) { sta->acct_session_id_lo = hapd->acct_session_id_lo++; if (hapd->acct_session_id_lo == 0) { hapd->acct_session_id_hi++; } sta->acct_session_id_hi = hapd->acct_session_id_hi; } /** * accounting_receive - Process the RADIUS frames from Accounting Server * @msg: RADIUS response message * @req: RADIUS request message * @shared_secret: RADIUS shared secret * @shared_secret_len: Length of shared_secret in octets * @data: Context data (struct hostapd_data *) * Returns: Processing status */ static RadiusRxResult accounting_receive(struct radius_msg *msg, struct radius_msg *req, const u8 *shared_secret, size_t shared_secret_len, void *data) { if (radius_msg_get_hdr(msg)->code != RADIUS_CODE_ACCOUNTING_RESPONSE) { wpa_printf(MSG_INFO, "Unknown RADIUS message code"); return RADIUS_RX_UNKNOWN; } if (radius_msg_verify(msg, shared_secret, shared_secret_len, req, 0)) { wpa_printf(MSG_INFO, "Incoming RADIUS packet did not have correct Authenticator - dropped"); return RADIUS_RX_INVALID_AUTHENTICATOR; } return RADIUS_RX_PROCESSED; } static void accounting_report_state(struct hostapd_data *hapd, int on) { struct radius_msg *msg; if (!hapd->conf->radius->acct_server || hapd->radius == NULL) return; /* Inform RADIUS server that accounting will start/stop so that the * server can close old accounting sessions. */ msg = accounting_msg(hapd, NULL, on ? RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_ON : RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_OFF); if (!msg) return; if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_TERMINATE_CAUSE, RADIUS_ACCT_TERMINATE_CAUSE_NAS_REBOOT)) { wpa_printf(MSG_INFO, "Could not add Acct-Terminate-Cause"); radius_msg_free(msg); return; } if (radius_client_send(hapd->radius, msg, RADIUS_ACCT, NULL) < 0) radius_msg_free(msg); } /** * accounting_init: Initialize accounting * @hapd: hostapd BSS data * Returns: 0 on success, -1 on failure */ int accounting_init(struct hostapd_data *hapd) { struct os_time now; /* Acct-Session-Id should be unique over reboots. If reliable clock is * not available, this could be replaced with reboot counter, etc. */ os_get_time(&now); hapd->acct_session_id_hi = now.sec; if (radius_client_register(hapd->radius, RADIUS_ACCT, accounting_receive, hapd)) return -1; accounting_report_state(hapd, 1); return 0; } /** * accounting_deinit: Deinitilize accounting * @hapd: hostapd BSS data */ void accounting_deinit(struct hostapd_data *hapd) { accounting_report_state(hapd, 0); } wpa-2.1/src/ap/accounting.h000066400000000000000000000021631227414666700156560ustar00rootroot00000000000000/* * hostapd / RADIUS Accounting * Copyright (c) 2002-2005, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef ACCOUNTING_H #define ACCOUNTING_H #ifdef CONFIG_NO_ACCOUNTING static inline void accounting_sta_get_id(struct hostapd_data *hapd, struct sta_info *sta) { } static inline void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta) { } static inline void accounting_sta_stop(struct hostapd_data *hapd, struct sta_info *sta) { } static inline int accounting_init(struct hostapd_data *hapd) { return 0; } static inline void accounting_deinit(struct hostapd_data *hapd) { } #else /* CONFIG_NO_ACCOUNTING */ void accounting_sta_get_id(struct hostapd_data *hapd, struct sta_info *sta); void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta); void accounting_sta_stop(struct hostapd_data *hapd, struct sta_info *sta); int accounting_init(struct hostapd_data *hapd); void accounting_deinit(struct hostapd_data *hapd); #endif /* CONFIG_NO_ACCOUNTING */ #endif /* ACCOUNTING_H */ wpa-2.1/src/ap/acs.c000066400000000000000000000626261227414666700142770ustar00rootroot00000000000000/* * ACS - Automatic Channel Selection module * Copyright (c) 2011, Atheros Communications * Copyright (c) 2013, Qualcomm Atheros, Inc. * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "utils/includes.h" #include #include "utils/common.h" #include "utils/list.h" #include "common/ieee802_11_defs.h" #include "common/wpa_ctrl.h" #include "drivers/driver.h" #include "hostapd.h" #include "ap_drv_ops.h" #include "ap_config.h" #include "hw_features.h" #include "acs.h" /* * Automatic Channel Selection * =========================== * * More info at * ------------ * http://wireless.kernel.org/en/users/Documentation/acs * * How to use * ---------- * - make sure you have CONFIG_ACS=y in hostapd's .config * - use channel=0 or channel=acs to enable ACS * * How does it work * ---------------- * 1. passive scans are used to collect survey data * (it is assumed that scan trigger collection of survey data in driver) * 2. interference factor is calculated for each channel * 3. ideal channel is picked depending on channel width by using adjacent * channel interference factors * * Known limitations * ----------------- * - Current implementation depends heavily on the amount of time willing to * spend gathering survey data during hostapd startup. Short traffic bursts * may be missed and a suboptimal channel may be picked. * - Ideal channel may end up overlapping a channel with 40 MHz intolerant BSS * * Todo / Ideas * ------------ * - implement other interference computation methods * - BSS/RSSI based * - spectral scan based * (should be possibly to hook this up with current ACS scans) * - add wpa_supplicant support (for P2P) * - collect a histogram of interference over time allowing more educated * guess about an ideal channel (perhaps CSA could be used to migrate AP to a * new "better" channel while running) * - include neighboring BSS scan to avoid conflicts with 40 MHz intolerant BSSs * when choosing the ideal channel * * Survey interference factor implementation details * ------------------------------------------------- * Generic interference_factor in struct hostapd_channel_data is used. * * The survey interference factor is defined as the ratio of the * observed busy time over the time we spent on the channel, * this value is then amplified by the observed noise floor on * the channel in comparison to the lowest noise floor observed * on the entire band. * * This corresponds to: * --- * (busy time - tx time) / (active time - tx time) * 2^(chan_nf + band_min_nf) * --- * * The coefficient of 2 reflects the way power in "far-field" * radiation decreases as the square of distance from the antenna [1]. * What this does is it decreases the observed busy time ratio if the * noise observed was low but increases it if the noise was high, * proportionally to the way "far field" radiation changes over * distance. * * If channel busy time is not available the fallback is to use channel RX time. * * Since noise floor is in dBm it is necessary to convert it into Watts so that * combined channel interference (e.g., HT40, which uses two channels) can be * calculated easily. * --- * (busy time - tx time) / (active time - tx time) * * 2^(10^(chan_nf/10) + 10^(band_min_nf/10)) * --- * * However to account for cases where busy/rx time is 0 (channel load is then * 0%) channel noise floor signal power is combined into the equation so a * channel with lower noise floor is preferred. The equation becomes: * --- * 10^(chan_nf/5) + (busy time - tx time) / (active time - tx time) * * 2^(10^(chan_nf/10) + 10^(band_min_nf/10)) * --- * * All this "interference factor" is purely subjective and only time * will tell how usable this is. By using the minimum noise floor we * remove any possible issues due to card calibration. The computation * of the interference factor then is dependent on what the card itself * picks up as the minimum noise, not an actual real possible card * noise value. * * Total interference computation details * -------------------------------------- * The above channel interference factor is calculated with no respect to * target operational bandwidth. * * To find an ideal channel the above data is combined by taking into account * the target operational bandwidth and selected band. E.g., on 2.4 GHz channels * overlap with 20 MHz bandwidth, but there is no overlap for 20 MHz bandwidth * on 5 GHz. * * Each valid and possible channel spec (i.e., channel + width) is taken and its * interference factor is computed by summing up interferences of each channel * it overlaps. The one with least total interference is picked up. * * Note: This implies base channel interference factor must be non-negative * allowing easy summing up. * * Example ACS analysis printout * ----------------------------- * * ACS: Trying survey-based ACS * ACS: Survey analysis for channel 1 (2412 MHz) * ACS: 1: min_nf=-113 interference_factor=0.0802469 nf=-113 time=162 busy=0 rx=13 * ACS: 2: min_nf=-113 interference_factor=0.0745342 nf=-113 time=161 busy=0 rx=12 * ACS: 3: min_nf=-113 interference_factor=0.0679012 nf=-113 time=162 busy=0 rx=11 * ACS: 4: min_nf=-113 interference_factor=0.0310559 nf=-113 time=161 busy=0 rx=5 * ACS: 5: min_nf=-113 interference_factor=0.0248447 nf=-113 time=161 busy=0 rx=4 * ACS: * interference factor average: 0.0557166 * ACS: Survey analysis for channel 2 (2417 MHz) * ACS: 1: min_nf=-113 interference_factor=0.0185185 nf=-113 time=162 busy=0 rx=3 * ACS: 2: min_nf=-113 interference_factor=0.0246914 nf=-113 time=162 busy=0 rx=4 * ACS: 3: min_nf=-113 interference_factor=0.037037 nf=-113 time=162 busy=0 rx=6 * ACS: 4: min_nf=-113 interference_factor=0.149068 nf=-113 time=161 busy=0 rx=24 * ACS: 5: min_nf=-113 interference_factor=0.0248447 nf=-113 time=161 busy=0 rx=4 * ACS: * interference factor average: 0.050832 * ACS: Survey analysis for channel 3 (2422 MHz) * ACS: 1: min_nf=-113 interference_factor=2.51189e-23 nf=-113 time=162 busy=0 rx=0 * ACS: 2: min_nf=-113 interference_factor=0.0185185 nf=-113 time=162 busy=0 rx=3 * ACS: 3: min_nf=-113 interference_factor=0.0186335 nf=-113 time=161 busy=0 rx=3 * ACS: 4: min_nf=-113 interference_factor=0.0186335 nf=-113 time=161 busy=0 rx=3 * ACS: 5: min_nf=-113 interference_factor=0.0186335 nf=-113 time=161 busy=0 rx=3 * ACS: * interference factor average: 0.0148838 * ACS: Survey analysis for channel 4 (2427 MHz) * ACS: 1: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0 * ACS: 2: min_nf=-114 interference_factor=0.0555556 nf=-114 time=162 busy=0 rx=9 * ACS: 3: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=161 busy=0 rx=0 * ACS: 4: min_nf=-114 interference_factor=0.0186335 nf=-114 time=161 busy=0 rx=3 * ACS: 5: min_nf=-114 interference_factor=0.00621118 nf=-114 time=161 busy=0 rx=1 * ACS: * interference factor average: 0.0160801 * ACS: Survey analysis for channel 5 (2432 MHz) * ACS: 1: min_nf=-114 interference_factor=0.409938 nf=-113 time=161 busy=0 rx=66 * ACS: 2: min_nf=-114 interference_factor=0.0432099 nf=-113 time=162 busy=0 rx=7 * ACS: 3: min_nf=-114 interference_factor=0.0124224 nf=-113 time=161 busy=0 rx=2 * ACS: 4: min_nf=-114 interference_factor=0.677019 nf=-113 time=161 busy=0 rx=109 * ACS: 5: min_nf=-114 interference_factor=0.0186335 nf=-114 time=161 busy=0 rx=3 * ACS: * interference factor average: 0.232244 * ACS: Survey analysis for channel 6 (2437 MHz) * ACS: 1: min_nf=-113 interference_factor=0.552795 nf=-113 time=161 busy=0 rx=89 * ACS: 2: min_nf=-113 interference_factor=0.0807453 nf=-112 time=161 busy=0 rx=13 * ACS: 3: min_nf=-113 interference_factor=0.0310559 nf=-113 time=161 busy=0 rx=5 * ACS: 4: min_nf=-113 interference_factor=0.434783 nf=-112 time=161 busy=0 rx=70 * ACS: 5: min_nf=-113 interference_factor=0.0621118 nf=-113 time=161 busy=0 rx=10 * ACS: * interference factor average: 0.232298 * ACS: Survey analysis for channel 7 (2442 MHz) * ACS: 1: min_nf=-113 interference_factor=0.440994 nf=-112 time=161 busy=0 rx=71 * ACS: 2: min_nf=-113 interference_factor=0.385093 nf=-113 time=161 busy=0 rx=62 * ACS: 3: min_nf=-113 interference_factor=0.0372671 nf=-113 time=161 busy=0 rx=6 * ACS: 4: min_nf=-113 interference_factor=0.0372671 nf=-113 time=161 busy=0 rx=6 * ACS: 5: min_nf=-113 interference_factor=0.0745342 nf=-113 time=161 busy=0 rx=12 * ACS: * interference factor average: 0.195031 * ACS: Survey analysis for channel 8 (2447 MHz) * ACS: 1: min_nf=-114 interference_factor=0.0496894 nf=-112 time=161 busy=0 rx=8 * ACS: 2: min_nf=-114 interference_factor=0.0496894 nf=-114 time=161 busy=0 rx=8 * ACS: 3: min_nf=-114 interference_factor=0.0372671 nf=-113 time=161 busy=0 rx=6 * ACS: 4: min_nf=-114 interference_factor=0.12963 nf=-113 time=162 busy=0 rx=21 * ACS: 5: min_nf=-114 interference_factor=0.166667 nf=-114 time=162 busy=0 rx=27 * ACS: * interference factor average: 0.0865885 * ACS: Survey analysis for channel 9 (2452 MHz) * ACS: 1: min_nf=-114 interference_factor=0.0124224 nf=-114 time=161 busy=0 rx=2 * ACS: 2: min_nf=-114 interference_factor=0.0310559 nf=-114 time=161 busy=0 rx=5 * ACS: 3: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=161 busy=0 rx=0 * ACS: 4: min_nf=-114 interference_factor=0.00617284 nf=-114 time=162 busy=0 rx=1 * ACS: 5: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0 * ACS: * interference factor average: 0.00993022 * ACS: Survey analysis for channel 10 (2457 MHz) * ACS: 1: min_nf=-114 interference_factor=0.00621118 nf=-114 time=161 busy=0 rx=1 * ACS: 2: min_nf=-114 interference_factor=0.00621118 nf=-114 time=161 busy=0 rx=1 * ACS: 3: min_nf=-114 interference_factor=0.00621118 nf=-114 time=161 busy=0 rx=1 * ACS: 4: min_nf=-114 interference_factor=0.0493827 nf=-114 time=162 busy=0 rx=8 * ACS: 5: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0 * ACS: * interference factor average: 0.0136033 * ACS: Survey analysis for channel 11 (2462 MHz) * ACS: 1: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=161 busy=0 rx=0 * ACS: 2: min_nf=-114 interference_factor=2.51189e-23 nf=-113 time=161 busy=0 rx=0 * ACS: 3: min_nf=-114 interference_factor=2.51189e-23 nf=-113 time=161 busy=0 rx=0 * ACS: 4: min_nf=-114 interference_factor=0.0432099 nf=-114 time=162 busy=0 rx=7 * ACS: 5: min_nf=-114 interference_factor=0.0925926 nf=-114 time=162 busy=0 rx=15 * ACS: * interference factor average: 0.0271605 * ACS: Survey analysis for channel 12 (2467 MHz) * ACS: 1: min_nf=-114 interference_factor=0.0621118 nf=-113 time=161 busy=0 rx=10 * ACS: 2: min_nf=-114 interference_factor=0.00621118 nf=-114 time=161 busy=0 rx=1 * ACS: 3: min_nf=-114 interference_factor=2.51189e-23 nf=-113 time=162 busy=0 rx=0 * ACS: 4: min_nf=-114 interference_factor=2.51189e-23 nf=-113 time=162 busy=0 rx=0 * ACS: 5: min_nf=-114 interference_factor=0.00617284 nf=-113 time=162 busy=0 rx=1 * ACS: * interference factor average: 0.0148992 * ACS: Survey analysis for channel 13 (2472 MHz) * ACS: 1: min_nf=-114 interference_factor=0.0745342 nf=-114 time=161 busy=0 rx=12 * ACS: 2: min_nf=-114 interference_factor=0.0555556 nf=-114 time=162 busy=0 rx=9 * ACS: 3: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0 * ACS: 4: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0 * ACS: 5: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0 * ACS: * interference factor average: 0.0260179 * ACS: Survey analysis for selected bandwidth 20MHz * ACS: * channel 1: total interference = 0.121432 * ACS: * channel 2: total interference = 0.137512 * ACS: * channel 3: total interference = 0.369757 * ACS: * channel 4: total interference = 0.546338 * ACS: * channel 5: total interference = 0.690538 * ACS: * channel 6: total interference = 0.762242 * ACS: * channel 7: total interference = 0.756092 * ACS: * channel 8: total interference = 0.537451 * ACS: * channel 9: total interference = 0.332313 * ACS: * channel 10: total interference = 0.152182 * ACS: * channel 11: total interference = 0.0916111 * ACS: * channel 12: total interference = 0.0816809 * ACS: * channel 13: total interference = 0.0680776 * ACS: Ideal channel is 13 (2472 MHz) with total interference factor of 0.0680776 * * [1] http://en.wikipedia.org/wiki/Near_and_far_field */ static int acs_request_scan(struct hostapd_iface *iface); static void acs_clean_chan_surveys(struct hostapd_channel_data *chan) { struct freq_survey *survey, *tmp; if (dl_list_empty(&chan->survey_list)) return; dl_list_for_each_safe(survey, tmp, &chan->survey_list, struct freq_survey, list) { dl_list_del(&survey->list); os_free(survey); } } static void acs_cleanup(struct hostapd_iface *iface) { int i; struct hostapd_channel_data *chan; for (i = 0; i < iface->current_mode->num_channels; i++) { chan = &iface->current_mode->channels[i]; if (chan->flag & HOSTAPD_CHAN_SURVEY_LIST_INITIALIZED) acs_clean_chan_surveys(chan); dl_list_init(&chan->survey_list); chan->flag |= HOSTAPD_CHAN_SURVEY_LIST_INITIALIZED; chan->min_nf = 0; } iface->chans_surveyed = 0; iface->acs_num_completed_scans = 0; } static void acs_fail(struct hostapd_iface *iface) { wpa_printf(MSG_ERROR, "ACS: Failed to start"); acs_cleanup(iface); } static long double acs_survey_interference_factor(struct freq_survey *survey, s8 min_nf) { long double factor, busy, total; if (survey->filled & SURVEY_HAS_CHAN_TIME_BUSY) busy = survey->channel_time_busy; else if (survey->filled & SURVEY_HAS_CHAN_TIME_RX) busy = survey->channel_time_rx; else { /* This shouldn't really happen as survey data is checked in * acs_sanity_check() */ wpa_printf(MSG_ERROR, "ACS: Survey data missing"); return 0; } total = survey->channel_time; if (survey->filled & SURVEY_HAS_CHAN_TIME_TX) { busy -= survey->channel_time_tx; total -= survey->channel_time_tx; } /* TODO: figure out the best multiplier for noise floor base */ factor = pow(10, survey->nf / 5.0L) + (busy / total) * pow(2, pow(10, (long double) survey->nf / 10.0L) - pow(10, (long double) min_nf / 10.0L)); return factor; } static void acs_survey_chan_interference_factor(struct hostapd_iface *iface, struct hostapd_channel_data *chan) { struct freq_survey *survey; unsigned int i = 0; long double int_factor = 0; if (dl_list_empty(&chan->survey_list)) return; if (chan->flag & HOSTAPD_CHAN_DISABLED) return; chan->interference_factor = 0; dl_list_for_each(survey, &chan->survey_list, struct freq_survey, list) { int_factor = acs_survey_interference_factor(survey, iface->lowest_nf); chan->interference_factor += int_factor; wpa_printf(MSG_DEBUG, "ACS: %d: min_nf=%d interference_factor=%Lg nf=%d time=%lu busy=%lu rx=%lu", ++i, chan->min_nf, int_factor, survey->nf, (unsigned long) survey->channel_time, (unsigned long) survey->channel_time_busy, (unsigned long) survey->channel_time_rx); } chan->interference_factor = chan->interference_factor / dl_list_len(&chan->survey_list); } static int acs_usable_ht40_chan(struct hostapd_channel_data *chan) { const int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157, 184, 192 }; unsigned int i; for (i = 0; i < ARRAY_SIZE(allowed); i++) if (chan->chan == allowed[i]) return 1; return 0; } static int acs_survey_is_sufficient(struct freq_survey *survey) { if (!(survey->filled & SURVEY_HAS_NF)) { wpa_printf(MSG_ERROR, "ACS: Survey is missing noise floor"); return 0; } if (!(survey->filled & SURVEY_HAS_CHAN_TIME)) { wpa_printf(MSG_ERROR, "ACS: Survey is missing channel time"); return 0; } if (!(survey->filled & SURVEY_HAS_CHAN_TIME_BUSY) && !(survey->filled & SURVEY_HAS_CHAN_TIME_RX)) { wpa_printf(MSG_ERROR, "ACS: Survey is missing RX and busy time (at least one is required)"); return 0; } return 1; } static int acs_survey_list_is_sufficient(struct hostapd_channel_data *chan) { struct freq_survey *survey; dl_list_for_each(survey, &chan->survey_list, struct freq_survey, list) { if (!acs_survey_is_sufficient(survey)) { wpa_printf(MSG_ERROR, "ACS: Channel %d has insufficient survey data", chan->chan); return 0; } } return 1; } static int acs_surveys_are_sufficient(struct hostapd_iface *iface) { int i; struct hostapd_channel_data *chan; int valid = 0; for (i = 0; i < iface->current_mode->num_channels; i++) { chan = &iface->current_mode->channels[i]; if (chan->flag & HOSTAPD_CHAN_DISABLED) continue; if (!acs_survey_list_is_sufficient(chan)) continue; valid++; } /* We need at least survey data for one channel */ return !!valid; } static int acs_usable_chan(struct hostapd_channel_data *chan) { if (dl_list_empty(&chan->survey_list)) return 0; if (chan->flag & HOSTAPD_CHAN_DISABLED) return 0; if (!acs_survey_list_is_sufficient(chan)) return 0; return 1; } static void acs_survey_all_chans_intereference_factor( struct hostapd_iface *iface) { int i; struct hostapd_channel_data *chan; for (i = 0; i < iface->current_mode->num_channels; i++) { chan = &iface->current_mode->channels[i]; if (!acs_usable_chan(chan)) continue; wpa_printf(MSG_DEBUG, "ACS: Survey analysis for channel %d (%d MHz)", chan->chan, chan->freq); acs_survey_chan_interference_factor(iface, chan); wpa_printf(MSG_DEBUG, "ACS: * interference factor average: %Lg", chan->interference_factor); } } static struct hostapd_channel_data *acs_find_chan(struct hostapd_iface *iface, int freq) { struct hostapd_channel_data *chan; int i; for (i = 0; i < iface->current_mode->num_channels; i++) { chan = &iface->current_mode->channels[i]; if (chan->flag & HOSTAPD_CHAN_DISABLED) continue; if (chan->freq == freq) return chan; } return NULL; } /* * At this point it's assumed chan->interface_factor has been computed. * This function should be reusable regardless of interference computation * option (survey, BSS, spectral, ...). chan->interference factor must be * summable (i.e., must be always greater than zero). */ static struct hostapd_channel_data * acs_find_ideal_chan(struct hostapd_iface *iface) { struct hostapd_channel_data *chan, *adj_chan, *ideal_chan = NULL, *rand_chan = NULL; long double factor, ideal_factor = 0; int i, j; int n_chans = 1; /* TODO: HT40- support */ if (iface->conf->ieee80211n && iface->conf->secondary_channel == -1) { wpa_printf(MSG_ERROR, "ACS: HT40- is not supported yet. Please try HT40+"); return NULL; } if (iface->conf->ieee80211n && iface->conf->secondary_channel) n_chans = 2; if (iface->conf->ieee80211ac && iface->conf->vht_oper_chwidth == 1) n_chans = 4; /* TODO: VHT80+80, VHT160. Update acs_adjust_vht_center_freq() too. */ wpa_printf(MSG_DEBUG, "ACS: Survey analysis for selected bandwidth %d MHz", n_chans == 1 ? 20 : n_chans == 2 ? 40 : n_chans == 4 ? 80 : -1); for (i = 0; i < iface->current_mode->num_channels; i++) { chan = &iface->current_mode->channels[i]; if (chan->flag & HOSTAPD_CHAN_DISABLED) continue; /* HT40 on 5 GHz has a limited set of primary channels as per * 11n Annex J */ if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A && iface->conf->ieee80211n && iface->conf->secondary_channel && !acs_usable_ht40_chan(chan)) { wpa_printf(MSG_DEBUG, "ACS: Channel %d: not allowed as primary channel for HT40", chan->chan); continue; } factor = 0; if (acs_usable_chan(chan)) factor = chan->interference_factor; for (j = 1; j < n_chans; j++) { adj_chan = acs_find_chan(iface, chan->freq + (j * 20)); if (!adj_chan) break; if (acs_usable_chan(adj_chan)) factor += adj_chan->interference_factor; } if (j != n_chans) { wpa_printf(MSG_DEBUG, "ACS: Channel %d: not enough bandwidth", chan->chan); continue; } /* 2.4 GHz has overlapping 20 MHz channels. Include adjacent * channel interference factor. */ if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211B || iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G) { for (j = 0; j < n_chans; j++) { /* TODO: perhaps a multiplier should be used * here? */ adj_chan = acs_find_chan(iface, chan->freq + (j * 20) - 5); if (adj_chan && acs_usable_chan(adj_chan)) factor += adj_chan->interference_factor; adj_chan = acs_find_chan(iface, chan->freq + (j * 20) - 10); if (adj_chan && acs_usable_chan(adj_chan)) factor += adj_chan->interference_factor; adj_chan = acs_find_chan(iface, chan->freq + (j * 20) + 5); if (adj_chan && acs_usable_chan(adj_chan)) factor += adj_chan->interference_factor; adj_chan = acs_find_chan(iface, chan->freq + (j * 20) + 10); if (adj_chan && acs_usable_chan(adj_chan)) factor += adj_chan->interference_factor; } } wpa_printf(MSG_DEBUG, "ACS: * channel %d: total interference = %Lg", chan->chan, factor); if (acs_usable_chan(chan) && (!ideal_chan || factor < ideal_factor)) { ideal_factor = factor; ideal_chan = chan; } /* This channel would at least be usable */ if (!rand_chan) rand_chan = chan; } if (ideal_chan) { wpa_printf(MSG_DEBUG, "ACS: Ideal channel is %d (%d MHz) with total interference factor of %Lg", ideal_chan->chan, ideal_chan->freq, ideal_factor); return ideal_chan; } return rand_chan; } static void acs_adjust_vht_center_freq(struct hostapd_iface *iface) { wpa_printf(MSG_DEBUG, "ACS: Adjusting VHT center frequency"); switch (iface->conf->vht_oper_chwidth) { case VHT_CHANWIDTH_USE_HT: iface->conf->vht_oper_centr_freq_seg0_idx = iface->conf->channel + 2; break; case VHT_CHANWIDTH_80MHZ: iface->conf->vht_oper_centr_freq_seg0_idx = iface->conf->channel + 6; break; default: /* TODO: How can this be calculated? Adjust * acs_find_ideal_chan() */ wpa_printf(MSG_INFO, "ACS: Only VHT20/40/80 is supported now"); break; } } static int acs_study_survey_based(struct hostapd_iface *iface) { wpa_printf(MSG_DEBUG, "ACS: Trying survey-based ACS"); if (!iface->chans_surveyed) { wpa_printf(MSG_ERROR, "ACS: Unable to collect survey data"); return -1; } if (!acs_surveys_are_sufficient(iface)) { wpa_printf(MSG_ERROR, "ACS: Surveys have insufficient data"); return -1; } acs_survey_all_chans_intereference_factor(iface); return 0; } static int acs_study_options(struct hostapd_iface *iface) { int err; err = acs_study_survey_based(iface); if (err == 0) return 0; /* TODO: If no surveys are available/sufficient this is a good * place to fallback to BSS-based ACS */ return -1; } static void acs_study(struct hostapd_iface *iface) { struct hostapd_channel_data *ideal_chan; int err; err = acs_study_options(iface); if (err < 0) { wpa_printf(MSG_ERROR, "ACS: All study options have failed"); goto fail; } ideal_chan = acs_find_ideal_chan(iface); if (!ideal_chan) { wpa_printf(MSG_ERROR, "ACS: Failed to compute ideal channel"); err = -1; goto fail; } iface->conf->channel = ideal_chan->chan; if (iface->conf->ieee80211ac) acs_adjust_vht_center_freq(iface); err = 0; fail: /* * hostapd_setup_interface_complete() will return -1 on failure, * 0 on success and 0 is HOSTAPD_CHAN_VALID :) */ if (hostapd_acs_completed(iface, err) == HOSTAPD_CHAN_VALID) { acs_cleanup(iface); return; } /* This can possibly happen if channel parameters (secondary * channel, center frequencies) are misconfigured */ wpa_printf(MSG_ERROR, "ACS: Possibly channel configuration is invalid, please report this along with your config file."); acs_fail(iface); } static void acs_scan_complete(struct hostapd_iface *iface) { int err; iface->scan_cb = NULL; wpa_printf(MSG_DEBUG, "ACS: Using survey based algorithm (acs_num_scans=%d)", iface->conf->acs_num_scans); err = hostapd_drv_get_survey(iface->bss[0], 0); if (err) { wpa_printf(MSG_ERROR, "ACS: Failed to get survey data"); acs_fail(iface); } if (++iface->acs_num_completed_scans < iface->conf->acs_num_scans) { err = acs_request_scan(iface); if (err) { wpa_printf(MSG_ERROR, "ACS: Failed to request scan"); goto fail; } return; } acs_study(iface); return; fail: hostapd_acs_completed(iface, 1); acs_fail(iface); } static int acs_request_scan(struct hostapd_iface *iface) { struct wpa_driver_scan_params params; struct hostapd_channel_data *chan; int i, *freq; os_memset(¶ms, 0, sizeof(params)); params.freqs = os_calloc(iface->current_mode->num_channels + 1, sizeof(params.freqs[0])); if (params.freqs == NULL) return -1; freq = params.freqs; for (i = 0; i < iface->current_mode->num_channels; i++) { chan = &iface->current_mode->channels[i]; if (chan->flag & HOSTAPD_CHAN_DISABLED) continue; *freq++ = chan->freq; } *freq = 0; iface->scan_cb = acs_scan_complete; wpa_printf(MSG_DEBUG, "ACS: Scanning %d / %d", iface->acs_num_completed_scans + 1, iface->conf->acs_num_scans); if (hostapd_driver_scan(iface->bss[0], ¶ms) < 0) { wpa_printf(MSG_ERROR, "ACS: Failed to request initial scan"); acs_cleanup(iface); return -1; } os_free(params.freqs); return 0; } enum hostapd_chan_status acs_init(struct hostapd_iface *iface) { int err; wpa_printf(MSG_INFO, "ACS: Automatic channel selection started, this may take a bit"); acs_cleanup(iface); err = acs_request_scan(iface); if (err < 0) return HOSTAPD_CHAN_INVALID; hostapd_set_state(iface, HAPD_IFACE_ACS); wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, ACS_EVENT_STARTED); return HOSTAPD_CHAN_ACS; } wpa-2.1/src/ap/acs.h000066400000000000000000000012141227414666700142660ustar00rootroot00000000000000/* * ACS - Automatic Channel Selection module * Copyright (c) 2011, Atheros Communications * Copyright (c) 2013, Qualcomm Atheros, Inc. * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef ACS_H #define ACS_H #ifdef CONFIG_ACS enum hostapd_chan_status acs_init(struct hostapd_iface *iface); #else /* CONFIG_ACS */ static inline enum hostapd_chan_status acs_init(struct hostapd_iface *iface) { wpa_printf(MSG_ERROR, "ACS was disabled on your build, rebuild hostapd with CONFIG_ACS=y or set channel"); return HOSTAPD_CHAN_INVALID; } #endif /* CONFIG_ACS */ #endif /* ACS_H */ wpa-2.1/src/ap/ap_config.c000066400000000000000000000522161227414666700154500ustar00rootroot00000000000000/* * hostapd / Configuration helper functions * Copyright (c) 2003-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 "crypto/sha1.h" #include "radius/radius_client.h" #include "common/ieee802_11_defs.h" #include "common/eapol_common.h" #include "eap_common/eap_wsc_common.h" #include "eap_server/eap.h" #include "wpa_auth.h" #include "sta_info.h" #include "ap_config.h" static void hostapd_config_free_vlan(struct hostapd_bss_config *bss) { struct hostapd_vlan *vlan, *prev; vlan = bss->vlan; prev = NULL; while (vlan) { prev = vlan; vlan = vlan->next; os_free(prev); } bss->vlan = NULL; } void hostapd_config_defaults_bss(struct hostapd_bss_config *bss) { bss->logger_syslog_level = HOSTAPD_LEVEL_INFO; bss->logger_stdout_level = HOSTAPD_LEVEL_INFO; bss->logger_syslog = (unsigned int) -1; bss->logger_stdout = (unsigned int) -1; bss->auth_algs = WPA_AUTH_ALG_OPEN | WPA_AUTH_ALG_SHARED; bss->wep_rekeying_period = 300; /* use key0 in individual key and key1 in broadcast key */ bss->broadcast_key_idx_min = 1; bss->broadcast_key_idx_max = 2; bss->eap_reauth_period = 3600; bss->wpa_group_rekey = 600; bss->wpa_gmk_rekey = 86400; bss->wpa_key_mgmt = WPA_KEY_MGMT_PSK; bss->wpa_pairwise = WPA_CIPHER_TKIP; bss->wpa_group = WPA_CIPHER_TKIP; bss->rsn_pairwise = 0; bss->max_num_sta = MAX_STA_COUNT; bss->dtim_period = 2; bss->radius_server_auth_port = 1812; bss->ap_max_inactivity = AP_MAX_INACTIVITY; bss->eapol_version = EAPOL_VERSION; bss->max_listen_interval = 65535; bss->pwd_group = 19; /* ECC: GF(p=256) */ #ifdef CONFIG_IEEE80211W bss->assoc_sa_query_max_timeout = 1000; bss->assoc_sa_query_retry_timeout = 201; #endif /* CONFIG_IEEE80211W */ #ifdef EAP_SERVER_FAST /* both anonymous and authenticated provisioning */ bss->eap_fast_prov = 3; bss->pac_key_lifetime = 7 * 24 * 60 * 60; bss->pac_key_refresh_time = 1 * 24 * 60 * 60; #endif /* EAP_SERVER_FAST */ /* Set to -1 as defaults depends on HT in setup */ bss->wmm_enabled = -1; #ifdef CONFIG_IEEE80211R bss->ft_over_ds = 1; #endif /* CONFIG_IEEE80211R */ bss->radius_das_time_window = 300; bss->sae_anti_clogging_threshold = 5; } struct hostapd_config * hostapd_config_defaults(void) { #define ecw2cw(ecw) ((1 << (ecw)) - 1) struct hostapd_config *conf; struct hostapd_bss_config *bss; const int aCWmin = 4, aCWmax = 10; const struct hostapd_wmm_ac_params ac_bk = { aCWmin, aCWmax, 7, 0, 0 }; /* background traffic */ const struct hostapd_wmm_ac_params ac_be = { aCWmin, aCWmax, 3, 0, 0 }; /* best effort traffic */ const struct hostapd_wmm_ac_params ac_vi = /* video traffic */ { aCWmin - 1, aCWmin, 2, 3000 / 32, 0 }; const struct hostapd_wmm_ac_params ac_vo = /* voice traffic */ { aCWmin - 2, aCWmin - 1, 2, 1500 / 32, 0 }; const struct hostapd_tx_queue_params txq_bk = { 7, ecw2cw(aCWmin), ecw2cw(aCWmax), 0 }; const struct hostapd_tx_queue_params txq_be = { 3, ecw2cw(aCWmin), 4 * (ecw2cw(aCWmin) + 1) - 1, 0}; const struct hostapd_tx_queue_params txq_vi = { 1, (ecw2cw(aCWmin) + 1) / 2 - 1, ecw2cw(aCWmin), 30}; const struct hostapd_tx_queue_params txq_vo = { 1, (ecw2cw(aCWmin) + 1) / 4 - 1, (ecw2cw(aCWmin) + 1) / 2 - 1, 15}; #undef ecw2cw conf = os_zalloc(sizeof(*conf)); bss = os_zalloc(sizeof(*bss)); if (conf == NULL || bss == NULL) { wpa_printf(MSG_ERROR, "Failed to allocate memory for " "configuration data."); os_free(conf); os_free(bss); return NULL; } conf->bss = os_calloc(1, sizeof(struct hostapd_bss_config *)); if (conf->bss == NULL) { os_free(conf); os_free(bss); return NULL; } conf->bss[0] = bss; bss->radius = os_zalloc(sizeof(*bss->radius)); if (bss->radius == NULL) { os_free(conf->bss); os_free(conf); os_free(bss); return NULL; } hostapd_config_defaults_bss(bss); conf->num_bss = 1; conf->beacon_int = 100; conf->rts_threshold = -1; /* use driver default: 2347 */ conf->fragm_threshold = -1; /* user driver default: 2346 */ conf->send_probe_response = 1; conf->wmm_ac_params[0] = ac_be; conf->wmm_ac_params[1] = ac_bk; conf->wmm_ac_params[2] = ac_vi; conf->wmm_ac_params[3] = ac_vo; conf->tx_queue[0] = txq_vo; conf->tx_queue[1] = txq_vi; conf->tx_queue[2] = txq_be; conf->tx_queue[3] = txq_bk; conf->ht_capab = HT_CAP_INFO_SMPS_DISABLED; conf->ap_table_max_size = 255; conf->ap_table_expiration_time = 60; #ifdef CONFIG_TESTING_OPTIONS conf->ignore_probe_probability = 0.0d; conf->ignore_auth_probability = 0.0d; conf->ignore_assoc_probability = 0.0d; conf->ignore_reassoc_probability = 0.0d; conf->corrupt_gtk_rekey_mic_probability = 0.0d; #endif /* CONFIG_TESTING_OPTIONS */ #ifdef CONFIG_ACS conf->acs_num_scans = 5; #endif /* CONFIG_ACS */ return conf; } int hostapd_mac_comp(const void *a, const void *b) { return os_memcmp(a, b, sizeof(macaddr)); } int hostapd_mac_comp_empty(const void *a) { macaddr empty = { 0 }; return os_memcmp(a, empty, sizeof(macaddr)); } static int hostapd_config_read_wpa_psk(const char *fname, struct hostapd_ssid *ssid) { FILE *f; char buf[128], *pos; int line = 0, ret = 0, len, ok; u8 addr[ETH_ALEN]; struct hostapd_wpa_psk *psk; if (!fname) return 0; f = fopen(fname, "r"); if (!f) { wpa_printf(MSG_ERROR, "WPA PSK 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; if (hwaddr_aton(buf, addr)) { wpa_printf(MSG_ERROR, "Invalid MAC address '%s' on " "line %d in '%s'", buf, line, fname); ret = -1; break; } psk = os_zalloc(sizeof(*psk)); if (psk == NULL) { wpa_printf(MSG_ERROR, "WPA PSK allocation failed"); ret = -1; break; } if (is_zero_ether_addr(addr)) psk->group = 1; else os_memcpy(psk->addr, addr, ETH_ALEN); pos = buf + 17; if (*pos == '\0') { wpa_printf(MSG_ERROR, "No PSK on line %d in '%s'", line, fname); os_free(psk); ret = -1; break; } pos++; ok = 0; len = os_strlen(pos); if (len == 64 && hexstr2bin(pos, psk->psk, PMK_LEN) == 0) ok = 1; else if (len >= 8 && len < 64) { pbkdf2_sha1(pos, ssid->ssid, ssid->ssid_len, 4096, psk->psk, PMK_LEN); ok = 1; } if (!ok) { wpa_printf(MSG_ERROR, "Invalid PSK '%s' on line %d in " "'%s'", pos, line, fname); os_free(psk); ret = -1; break; } psk->next = ssid->wpa_psk; ssid->wpa_psk = psk; } fclose(f); return ret; } static int hostapd_derive_psk(struct hostapd_ssid *ssid) { ssid->wpa_psk = os_zalloc(sizeof(struct hostapd_wpa_psk)); if (ssid->wpa_psk == NULL) { wpa_printf(MSG_ERROR, "Unable to alloc space for PSK"); return -1; } wpa_hexdump_ascii(MSG_DEBUG, "SSID", (u8 *) ssid->ssid, ssid->ssid_len); wpa_hexdump_ascii_key(MSG_DEBUG, "PSK (ASCII passphrase)", (u8 *) ssid->wpa_passphrase, os_strlen(ssid->wpa_passphrase)); pbkdf2_sha1(ssid->wpa_passphrase, ssid->ssid, ssid->ssid_len, 4096, ssid->wpa_psk->psk, PMK_LEN); wpa_hexdump_key(MSG_DEBUG, "PSK (from passphrase)", ssid->wpa_psk->psk, PMK_LEN); return 0; } int hostapd_setup_wpa_psk(struct hostapd_bss_config *conf) { struct hostapd_ssid *ssid = &conf->ssid; if (ssid->wpa_passphrase != NULL) { if (ssid->wpa_psk != NULL) { wpa_printf(MSG_DEBUG, "Using pre-configured WPA PSK " "instead of passphrase"); } else { wpa_printf(MSG_DEBUG, "Deriving WPA PSK based on " "passphrase"); if (hostapd_derive_psk(ssid) < 0) return -1; } ssid->wpa_psk->group = 1; } if (ssid->wpa_psk_file) { if (hostapd_config_read_wpa_psk(ssid->wpa_psk_file, &conf->ssid)) return -1; } return 0; } int hostapd_wep_key_cmp(struct hostapd_wep_keys *a, struct hostapd_wep_keys *b) { int i; if (a->idx != b->idx || a->default_len != b->default_len) return 1; for (i = 0; i < NUM_WEP_KEYS; i++) if (a->len[i] != b->len[i] || os_memcmp(a->key[i], b->key[i], a->len[i]) != 0) return 1; return 0; } static void hostapd_config_free_radius(struct hostapd_radius_server *servers, int num_servers) { int i; for (i = 0; i < num_servers; i++) { os_free(servers[i].shared_secret); } os_free(servers); } struct hostapd_radius_attr * hostapd_config_get_radius_attr(struct hostapd_radius_attr *attr, u8 type) { for (; attr; attr = attr->next) { if (attr->type == type) return attr; } return NULL; } static void hostapd_config_free_radius_attr(struct hostapd_radius_attr *attr) { struct hostapd_radius_attr *prev; while (attr) { prev = attr; attr = attr->next; wpabuf_free(prev->val); os_free(prev); } } static void hostapd_config_free_eap_user(struct hostapd_eap_user *user) { os_free(user->identity); os_free(user->password); os_free(user); } static void hostapd_config_free_wep(struct hostapd_wep_keys *keys) { int i; for (i = 0; i < NUM_WEP_KEYS; i++) { os_free(keys->key[i]); keys->key[i] = NULL; } } void hostapd_config_free_bss(struct hostapd_bss_config *conf) { struct hostapd_wpa_psk *psk, *prev; struct hostapd_eap_user *user, *prev_user; if (conf == NULL) return; psk = conf->ssid.wpa_psk; while (psk) { prev = psk; psk = psk->next; os_free(prev); } os_free(conf->ssid.wpa_passphrase); os_free(conf->ssid.wpa_psk_file); hostapd_config_free_wep(&conf->ssid.wep); #ifdef CONFIG_FULL_DYNAMIC_VLAN os_free(conf->ssid.vlan_tagged_interface); #endif /* CONFIG_FULL_DYNAMIC_VLAN */ user = conf->eap_user; while (user) { prev_user = user; user = user->next; hostapd_config_free_eap_user(prev_user); } os_free(conf->eap_user_sqlite); os_free(conf->eap_req_id_text); os_free(conf->accept_mac); os_free(conf->deny_mac); os_free(conf->nas_identifier); if (conf->radius) { hostapd_config_free_radius(conf->radius->auth_servers, conf->radius->num_auth_servers); hostapd_config_free_radius(conf->radius->acct_servers, conf->radius->num_acct_servers); } hostapd_config_free_radius_attr(conf->radius_auth_req_attr); hostapd_config_free_radius_attr(conf->radius_acct_req_attr); os_free(conf->rsn_preauth_interfaces); os_free(conf->ctrl_interface); os_free(conf->ca_cert); os_free(conf->server_cert); os_free(conf->private_key); os_free(conf->private_key_passwd); os_free(conf->ocsp_stapling_response); os_free(conf->dh_file); os_free(conf->pac_opaque_encr_key); os_free(conf->eap_fast_a_id); os_free(conf->eap_fast_a_id_info); os_free(conf->eap_sim_db); os_free(conf->radius_server_clients); os_free(conf->test_socket); os_free(conf->radius); os_free(conf->radius_das_shared_secret); hostapd_config_free_vlan(conf); os_free(conf->time_zone); #ifdef CONFIG_IEEE80211R { struct ft_remote_r0kh *r0kh, *r0kh_prev; struct ft_remote_r1kh *r1kh, *r1kh_prev; r0kh = conf->r0kh_list; conf->r0kh_list = NULL; while (r0kh) { r0kh_prev = r0kh; r0kh = r0kh->next; os_free(r0kh_prev); } r1kh = conf->r1kh_list; conf->r1kh_list = NULL; while (r1kh) { r1kh_prev = r1kh; r1kh = r1kh->next; os_free(r1kh_prev); } } #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_WPS os_free(conf->wps_pin_requests); os_free(conf->device_name); os_free(conf->manufacturer); os_free(conf->model_name); os_free(conf->model_number); os_free(conf->serial_number); os_free(conf->config_methods); os_free(conf->ap_pin); os_free(conf->extra_cred); os_free(conf->ap_settings); os_free(conf->upnp_iface); os_free(conf->friendly_name); os_free(conf->manufacturer_url); os_free(conf->model_description); os_free(conf->model_url); os_free(conf->upc); wpabuf_free(conf->wps_nfc_dh_pubkey); wpabuf_free(conf->wps_nfc_dh_privkey); wpabuf_free(conf->wps_nfc_dev_pw); #endif /* CONFIG_WPS */ os_free(conf->roaming_consortium); os_free(conf->venue_name); os_free(conf->nai_realm_data); os_free(conf->network_auth_type); os_free(conf->anqp_3gpp_cell_net); os_free(conf->domain_name); #ifdef CONFIG_RADIUS_TEST os_free(conf->dump_msk_file); #endif /* CONFIG_RADIUS_TEST */ #ifdef CONFIG_HS20 os_free(conf->hs20_oper_friendly_name); os_free(conf->hs20_wan_metrics); os_free(conf->hs20_connection_capability); os_free(conf->hs20_operating_class); #endif /* CONFIG_HS20 */ wpabuf_free(conf->vendor_elements); os_free(conf->sae_groups); os_free(conf->server_id); os_free(conf); } /** * hostapd_config_free - Free hostapd configuration * @conf: Configuration data from hostapd_config_read(). */ void hostapd_config_free(struct hostapd_config *conf) { size_t i; if (conf == NULL) return; for (i = 0; i < conf->num_bss; i++) hostapd_config_free_bss(conf->bss[i]); os_free(conf->bss); os_free(conf->supported_rates); os_free(conf->basic_rates); os_free(conf); } /** * hostapd_maclist_found - Find a MAC address from a list * @list: MAC address list * @num_entries: Number of addresses in the list * @addr: Address to search for * @vlan_id: Buffer for returning VLAN ID or %NULL if not needed * Returns: 1 if address is in the list or 0 if not. * * Perform a binary search for given MAC address from a pre-sorted list. */ int hostapd_maclist_found(struct mac_acl_entry *list, int num_entries, const u8 *addr, int *vlan_id) { int start, end, middle, res; start = 0; end = num_entries - 1; while (start <= end) { middle = (start + end) / 2; res = os_memcmp(list[middle].addr, addr, ETH_ALEN); if (res == 0) { if (vlan_id) *vlan_id = list[middle].vlan_id; return 1; } if (res < 0) start = middle + 1; else end = middle - 1; } return 0; } int hostapd_rate_found(int *list, int rate) { int i; if (list == NULL) return 0; for (i = 0; list[i] >= 0; i++) if (list[i] == rate) return 1; return 0; } int hostapd_vlan_id_valid(struct hostapd_vlan *vlan, int vlan_id) { struct hostapd_vlan *v = vlan; while (v) { if (v->vlan_id == vlan_id || v->vlan_id == VLAN_ID_WILDCARD) return 1; v = v->next; } return 0; } const char * hostapd_get_vlan_id_ifname(struct hostapd_vlan *vlan, int vlan_id) { struct hostapd_vlan *v = vlan; while (v) { if (v->vlan_id == vlan_id) return v->ifname; v = v->next; } return NULL; } const u8 * hostapd_get_psk(const struct hostapd_bss_config *conf, const u8 *addr, const u8 *p2p_dev_addr, const u8 *prev_psk) { struct hostapd_wpa_psk *psk; int next_ok = prev_psk == NULL; if (p2p_dev_addr) { wpa_printf(MSG_DEBUG, "Searching a PSK for " MACSTR " p2p_dev_addr=" MACSTR " prev_psk=%p", MAC2STR(addr), MAC2STR(p2p_dev_addr), prev_psk); if (!is_zero_ether_addr(p2p_dev_addr)) addr = NULL; /* Use P2P Device Address for matching */ } else { wpa_printf(MSG_DEBUG, "Searching a PSK for " MACSTR " prev_psk=%p", MAC2STR(addr), prev_psk); } for (psk = conf->ssid.wpa_psk; psk != NULL; psk = psk->next) { if (next_ok && (psk->group || (addr && os_memcmp(psk->addr, addr, ETH_ALEN) == 0) || (!addr && p2p_dev_addr && os_memcmp(psk->p2p_dev_addr, p2p_dev_addr, ETH_ALEN) == 0))) return psk->psk; if (psk->psk == prev_psk) next_ok = 1; } return NULL; } static int hostapd_config_check_bss(struct hostapd_bss_config *bss, struct hostapd_config *conf, int full_config) { if (full_config && bss->ieee802_1x && !bss->eap_server && !bss->radius->auth_servers) { wpa_printf(MSG_ERROR, "Invalid IEEE 802.1X configuration (no " "EAP authenticator configured)."); return -1; } if (bss->wpa) { int wep, i; wep = bss->default_wep_key_len > 0 || bss->individual_wep_key_len > 0; for (i = 0; i < NUM_WEP_KEYS; i++) { if (bss->ssid.wep.keys_set) { wep = 1; break; } } if (wep) { wpa_printf(MSG_ERROR, "WEP configuration in a WPA network is not supported"); return -1; } } if (full_config && bss->wpa && bss->wpa_psk_radius != PSK_RADIUS_IGNORED && bss->macaddr_acl != USE_EXTERNAL_RADIUS_AUTH) { wpa_printf(MSG_ERROR, "WPA-PSK using RADIUS enabled, but no " "RADIUS checking (macaddr_acl=2) enabled."); return -1; } if (full_config && bss->wpa && (bss->wpa_key_mgmt & WPA_KEY_MGMT_PSK) && bss->ssid.wpa_psk == NULL && bss->ssid.wpa_passphrase == NULL && bss->ssid.wpa_psk_file == NULL && (bss->wpa_psk_radius != PSK_RADIUS_REQUIRED || bss->macaddr_acl != USE_EXTERNAL_RADIUS_AUTH)) { wpa_printf(MSG_ERROR, "WPA-PSK enabled, but PSK or passphrase " "is not configured."); return -1; } if (full_config && hostapd_mac_comp_empty(bss->bssid) != 0) { size_t i; for (i = 0; i < conf->num_bss; i++) { if (conf->bss[i] != bss && (hostapd_mac_comp(conf->bss[i]->bssid, bss->bssid) == 0)) { wpa_printf(MSG_ERROR, "Duplicate BSSID " MACSTR " on interface '%s' and '%s'.", MAC2STR(bss->bssid), conf->bss[i]->iface, bss->iface); return -1; } } } #ifdef CONFIG_IEEE80211R if (full_config && wpa_key_mgmt_ft(bss->wpa_key_mgmt) && (bss->nas_identifier == NULL || os_strlen(bss->nas_identifier) < 1 || os_strlen(bss->nas_identifier) > FT_R0KH_ID_MAX_LEN)) { wpa_printf(MSG_ERROR, "FT (IEEE 802.11r) requires " "nas_identifier to be configured as a 1..48 octet " "string"); return -1; } #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_IEEE80211N if (full_config && conf->ieee80211n && conf->hw_mode == HOSTAPD_MODE_IEEE80211B) { bss->disable_11n = 1; wpa_printf(MSG_ERROR, "HT (IEEE 802.11n) in 11b mode is not " "allowed, disabling HT capabilites"); } if (full_config && conf->ieee80211n && bss->ssid.security_policy == SECURITY_STATIC_WEP) { bss->disable_11n = 1; wpa_printf(MSG_ERROR, "HT (IEEE 802.11n) with WEP is not " "allowed, disabling HT capabilities"); } if (full_config && conf->ieee80211n && bss->wpa && !(bss->wpa_pairwise & WPA_CIPHER_CCMP) && !(bss->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | WPA_CIPHER_CCMP_256 | WPA_CIPHER_GCMP_256))) { bss->disable_11n = 1; wpa_printf(MSG_ERROR, "HT (IEEE 802.11n) with WPA/WPA2 " "requires CCMP/GCMP to be enabled, disabling HT " "capabilities"); } #endif /* CONFIG_IEEE80211N */ #ifdef CONFIG_WPS2 if (full_config && bss->wps_state && bss->ignore_broadcast_ssid) { wpa_printf(MSG_INFO, "WPS: ignore_broadcast_ssid " "configuration forced WPS to be disabled"); bss->wps_state = 0; } if (full_config && bss->wps_state && bss->ssid.wep.keys_set && bss->wpa == 0) { wpa_printf(MSG_INFO, "WPS: WEP configuration forced WPS to be " "disabled"); bss->wps_state = 0; } if (full_config && bss->wps_state && bss->wpa && (!(bss->wpa & 2) || !(bss->rsn_pairwise & WPA_CIPHER_CCMP))) { wpa_printf(MSG_INFO, "WPS: WPA/TKIP configuration without " "WPA2/CCMP forced WPS to be disabled"); bss->wps_state = 0; } #endif /* CONFIG_WPS2 */ #ifdef CONFIG_HS20 if (full_config && bss->hs20 && (!(bss->wpa & 2) || !(bss->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | WPA_CIPHER_CCMP_256 | WPA_CIPHER_GCMP_256)))) { wpa_printf(MSG_ERROR, "HS 2.0: WPA2-Enterprise/CCMP " "configuration is required for Hotspot 2.0 " "functionality"); return -1; } #endif /* CONFIG_HS20 */ return 0; } int hostapd_config_check(struct hostapd_config *conf, int full_config) { size_t i; if (full_config && conf->ieee80211d && (!conf->country[0] || !conf->country[1])) { wpa_printf(MSG_ERROR, "Cannot enable IEEE 802.11d without " "setting the country_code"); return -1; } if (full_config && conf->ieee80211h && !conf->ieee80211d) { wpa_printf(MSG_ERROR, "Cannot enable IEEE 802.11h without " "IEEE 802.11d enabled"); return -1; } for (i = 0; i < conf->num_bss; i++) { if (hostapd_config_check_bss(conf->bss[i], conf, full_config)) return -1; } return 0; } void hostapd_set_security_params(struct hostapd_bss_config *bss) { if (bss->individual_wep_key_len == 0) { /* individual keys are not use; can use key idx0 for * broadcast keys */ bss->broadcast_key_idx_min = 0; } if ((bss->wpa & 2) && bss->rsn_pairwise == 0) bss->rsn_pairwise = bss->wpa_pairwise; bss->wpa_group = wpa_select_ap_group_cipher(bss->wpa, bss->wpa_pairwise, bss->rsn_pairwise); bss->radius->auth_server = bss->radius->auth_servers; bss->radius->acct_server = bss->radius->acct_servers; if (bss->wpa && bss->ieee802_1x) { bss->ssid.security_policy = SECURITY_WPA; } else if (bss->wpa) { bss->ssid.security_policy = SECURITY_WPA_PSK; } else if (bss->ieee802_1x) { int cipher = WPA_CIPHER_NONE; bss->ssid.security_policy = SECURITY_IEEE_802_1X; bss->ssid.wep.default_len = bss->default_wep_key_len; if (bss->default_wep_key_len) cipher = bss->default_wep_key_len >= 13 ? WPA_CIPHER_WEP104 : WPA_CIPHER_WEP40; bss->wpa_group = cipher; bss->wpa_pairwise = cipher; bss->rsn_pairwise = cipher; } else if (bss->ssid.wep.keys_set) { int cipher = WPA_CIPHER_WEP40; if (bss->ssid.wep.len[0] >= 13) cipher = WPA_CIPHER_WEP104; bss->ssid.security_policy = SECURITY_STATIC_WEP; bss->wpa_group = cipher; bss->wpa_pairwise = cipher; bss->rsn_pairwise = cipher; } else { bss->ssid.security_policy = SECURITY_PLAINTEXT; bss->wpa_group = WPA_CIPHER_NONE; bss->wpa_pairwise = WPA_CIPHER_NONE; bss->rsn_pairwise = WPA_CIPHER_NONE; } } wpa-2.1/src/ap/ap_config.h000066400000000000000000000333141227414666700154530ustar00rootroot00000000000000/* * hostapd / Configuration definitions and helpers functions * Copyright (c) 2003-2012, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef HOSTAPD_CONFIG_H #define HOSTAPD_CONFIG_H #include "common/defs.h" #include "ip_addr.h" #include "common/wpa_common.h" #include "common/ieee802_11_common.h" #include "wps/wps.h" #define MAX_STA_COUNT 2007 #define MAX_VLAN_ID 4094 typedef u8 macaddr[ETH_ALEN]; struct mac_acl_entry { macaddr addr; int vlan_id; }; struct hostapd_radius_servers; struct ft_remote_r0kh; struct ft_remote_r1kh; #define HOSTAPD_MAX_SSID_LEN 32 #define NUM_WEP_KEYS 4 struct hostapd_wep_keys { u8 idx; u8 *key[NUM_WEP_KEYS]; size_t len[NUM_WEP_KEYS]; int keys_set; size_t default_len; /* key length used for dynamic key generation */ }; typedef enum hostap_security_policy { SECURITY_PLAINTEXT = 0, SECURITY_STATIC_WEP = 1, SECURITY_IEEE_802_1X = 2, SECURITY_WPA_PSK = 3, SECURITY_WPA = 4 } secpolicy; struct hostapd_ssid { u8 ssid[HOSTAPD_MAX_SSID_LEN]; size_t ssid_len; unsigned int ssid_set:1; unsigned int utf8_ssid:1; unsigned int wpa_passphrase_set:1; unsigned int wpa_psk_set:1; char vlan[IFNAMSIZ + 1]; secpolicy security_policy; struct hostapd_wpa_psk *wpa_psk; char *wpa_passphrase; char *wpa_psk_file; struct hostapd_wep_keys wep; #define DYNAMIC_VLAN_DISABLED 0 #define DYNAMIC_VLAN_OPTIONAL 1 #define DYNAMIC_VLAN_REQUIRED 2 int dynamic_vlan; #define DYNAMIC_VLAN_NAMING_WITHOUT_DEVICE 0 #define DYNAMIC_VLAN_NAMING_WITH_DEVICE 1 #define DYNAMIC_VLAN_NAMING_END 2 int vlan_naming; #ifdef CONFIG_FULL_DYNAMIC_VLAN char *vlan_tagged_interface; #endif /* CONFIG_FULL_DYNAMIC_VLAN */ }; #define VLAN_ID_WILDCARD -1 struct hostapd_vlan { struct hostapd_vlan *next; int vlan_id; /* VLAN ID or -1 (VLAN_ID_WILDCARD) for wildcard entry */ char ifname[IFNAMSIZ + 1]; int dynamic_vlan; #ifdef CONFIG_FULL_DYNAMIC_VLAN #define DVLAN_CLEAN_BR 0x1 #define DVLAN_CLEAN_VLAN 0x2 #define DVLAN_CLEAN_VLAN_PORT 0x4 #define DVLAN_CLEAN_WLAN_PORT 0x8 int clean; #endif /* CONFIG_FULL_DYNAMIC_VLAN */ }; #define PMK_LEN 32 struct hostapd_sta_wpa_psk_short { struct hostapd_sta_wpa_psk_short *next; u8 psk[PMK_LEN]; }; struct hostapd_wpa_psk { struct hostapd_wpa_psk *next; int group; u8 psk[PMK_LEN]; u8 addr[ETH_ALEN]; u8 p2p_dev_addr[ETH_ALEN]; }; struct hostapd_eap_user { struct hostapd_eap_user *next; u8 *identity; size_t identity_len; struct { int vendor; u32 method; } methods[EAP_MAX_METHODS]; u8 *password; size_t password_len; int phase2; int force_version; unsigned int wildcard_prefix:1; unsigned int password_hash:1; /* whether password is hashed with * nt_password_hash() */ int ttls_auth; /* EAP_TTLS_AUTH_* bitfield */ }; struct hostapd_radius_attr { u8 type; struct wpabuf *val; struct hostapd_radius_attr *next; }; #define NUM_TX_QUEUES 4 struct hostapd_tx_queue_params { int aifs; int cwmin; int cwmax; int burst; /* maximum burst time in 0.1 ms, i.e., 10 = 1 ms */ }; #define MAX_ROAMING_CONSORTIUM_LEN 15 struct hostapd_roaming_consortium { u8 len; u8 oi[MAX_ROAMING_CONSORTIUM_LEN]; }; struct hostapd_lang_string { u8 lang[3]; u8 name_len; u8 name[252]; }; #define MAX_NAI_REALMS 10 #define MAX_NAI_REALMLEN 255 #define MAX_NAI_EAP_METHODS 5 #define MAX_NAI_AUTH_TYPES 4 struct hostapd_nai_realm_data { u8 encoding; char realm_buf[MAX_NAI_REALMLEN + 1]; char *realm[MAX_NAI_REALMS]; u8 eap_method_count; struct hostapd_nai_realm_eap { u8 eap_method; u8 num_auths; u8 auth_id[MAX_NAI_AUTH_TYPES]; u8 auth_val[MAX_NAI_AUTH_TYPES]; } eap_method[MAX_NAI_EAP_METHODS]; }; /** * struct hostapd_bss_config - Per-BSS configuration */ struct hostapd_bss_config { char iface[IFNAMSIZ + 1]; char bridge[IFNAMSIZ + 1]; char vlan_bridge[IFNAMSIZ + 1]; char wds_bridge[IFNAMSIZ + 1]; enum hostapd_logger_level logger_syslog_level, logger_stdout_level; unsigned int logger_syslog; /* module bitfield */ unsigned int logger_stdout; /* module bitfield */ int max_num_sta; /* maximum number of STAs in station table */ int dtim_period; int ieee802_1x; /* use IEEE 802.1X */ int eapol_version; int eap_server; /* Use internal EAP server instead of external * RADIUS server */ struct hostapd_eap_user *eap_user; char *eap_user_sqlite; char *eap_sim_db; struct hostapd_ip_addr own_ip_addr; char *nas_identifier; struct hostapd_radius_servers *radius; int acct_interim_interval; int radius_request_cui; struct hostapd_radius_attr *radius_auth_req_attr; struct hostapd_radius_attr *radius_acct_req_attr; int radius_das_port; unsigned int radius_das_time_window; int radius_das_require_event_timestamp; struct hostapd_ip_addr radius_das_client_addr; u8 *radius_das_shared_secret; size_t radius_das_shared_secret_len; struct hostapd_ssid ssid; char *eap_req_id_text; /* optional displayable message sent with * EAP Request-Identity */ size_t eap_req_id_text_len; int eapol_key_index_workaround; size_t default_wep_key_len; int individual_wep_key_len; int wep_rekeying_period; int broadcast_key_idx_min, broadcast_key_idx_max; int eap_reauth_period; int ieee802_11f; /* use IEEE 802.11f (IAPP) */ char iapp_iface[IFNAMSIZ + 1]; /* interface used with IAPP broadcast * frames */ enum { ACCEPT_UNLESS_DENIED = 0, DENY_UNLESS_ACCEPTED = 1, USE_EXTERNAL_RADIUS_AUTH = 2 } macaddr_acl; struct mac_acl_entry *accept_mac; int num_accept_mac; struct mac_acl_entry *deny_mac; int num_deny_mac; int wds_sta; int isolate; int start_disabled; int auth_algs; /* bitfield of allowed IEEE 802.11 authentication * algorithms, WPA_AUTH_ALG_{OPEN,SHARED,LEAP} */ int wpa; /* bitfield of WPA_PROTO_WPA, WPA_PROTO_RSN */ int wpa_key_mgmt; #ifdef CONFIG_IEEE80211W enum mfp_options ieee80211w; /* dot11AssociationSAQueryMaximumTimeout (in TUs) */ unsigned int assoc_sa_query_max_timeout; /* dot11AssociationSAQueryRetryTimeout (in TUs) */ int assoc_sa_query_retry_timeout; #endif /* CONFIG_IEEE80211W */ enum { PSK_RADIUS_IGNORED = 0, PSK_RADIUS_ACCEPTED = 1, PSK_RADIUS_REQUIRED = 2 } wpa_psk_radius; int wpa_pairwise; int wpa_group; int wpa_group_rekey; int wpa_strict_rekey; int wpa_gmk_rekey; int wpa_ptk_rekey; int rsn_pairwise; int rsn_preauth; char *rsn_preauth_interfaces; int peerkey; #ifdef CONFIG_IEEE80211R /* IEEE 802.11r - Fast BSS Transition */ u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN]; u8 r1_key_holder[FT_R1KH_ID_LEN]; u32 r0_key_lifetime; u32 reassociation_deadline; struct ft_remote_r0kh *r0kh_list; struct ft_remote_r1kh *r1kh_list; int pmk_r1_push; int ft_over_ds; #endif /* CONFIG_IEEE80211R */ char *ctrl_interface; /* directory for UNIX domain sockets */ #ifndef CONFIG_NATIVE_WINDOWS gid_t ctrl_interface_gid; #endif /* CONFIG_NATIVE_WINDOWS */ int ctrl_interface_gid_set; char *ca_cert; char *server_cert; char *private_key; char *private_key_passwd; int check_crl; char *ocsp_stapling_response; char *dh_file; u8 *pac_opaque_encr_key; u8 *eap_fast_a_id; size_t eap_fast_a_id_len; char *eap_fast_a_id_info; int eap_fast_prov; int pac_key_lifetime; int pac_key_refresh_time; int eap_sim_aka_result_ind; int tnc; int fragment_size; u16 pwd_group; char *radius_server_clients; int radius_server_auth_port; int radius_server_ipv6; char *test_socket; /* UNIX domain socket path for driver_test */ int use_pae_group_addr; /* Whether to send EAPOL frames to PAE group * address instead of individual address * (for driver_wired.c). */ int ap_max_inactivity; int ignore_broadcast_ssid; int wmm_enabled; int wmm_uapsd; struct hostapd_vlan *vlan; macaddr bssid; /* * Maximum listen interval that STAs can use when associating with this * BSS. If a STA tries to use larger value, the association will be * denied with status code 51. */ u16 max_listen_interval; int disable_pmksa_caching; int okc; /* Opportunistic Key Caching */ int wps_state; #ifdef CONFIG_WPS int wps_independent; int ap_setup_locked; u8 uuid[16]; char *wps_pin_requests; char *device_name; char *manufacturer; char *model_name; char *model_number; char *serial_number; u8 device_type[WPS_DEV_TYPE_LEN]; char *config_methods; u8 os_version[4]; char *ap_pin; int skip_cred_build; u8 *extra_cred; size_t extra_cred_len; int wps_cred_processing; int force_per_enrollee_psk; u8 *ap_settings; size_t ap_settings_len; char *upnp_iface; char *friendly_name; char *manufacturer_url; char *model_description; char *model_url; char *upc; struct wpabuf *wps_vendor_ext[MAX_WPS_VENDOR_EXTENSIONS]; int wps_nfc_pw_from_config; int wps_nfc_dev_pw_id; struct wpabuf *wps_nfc_dh_pubkey; struct wpabuf *wps_nfc_dh_privkey; struct wpabuf *wps_nfc_dev_pw; #endif /* CONFIG_WPS */ int pbc_in_m1; char *server_id; #define P2P_ENABLED BIT(0) #define P2P_GROUP_OWNER BIT(1) #define P2P_GROUP_FORMATION BIT(2) #define P2P_MANAGE BIT(3) #define P2P_ALLOW_CROSS_CONNECTION BIT(4) int p2p; #ifdef CONFIG_P2P u8 ip_addr_go[4]; u8 ip_addr_mask[4]; u8 ip_addr_start[4]; u8 ip_addr_end[4]; #endif /* CONFIG_P2P */ int disassoc_low_ack; int skip_inactivity_poll; #define TDLS_PROHIBIT BIT(0) #define TDLS_PROHIBIT_CHAN_SWITCH BIT(1) int tdls; int disable_11n; int disable_11ac; /* IEEE 802.11v */ int time_advertisement; char *time_zone; int wnm_sleep_mode; int bss_transition; /* IEEE 802.11u - Interworking */ int interworking; int access_network_type; int internet; int asra; int esr; int uesa; int venue_info_set; u8 venue_group; u8 venue_type; u8 hessid[ETH_ALEN]; /* IEEE 802.11u - Roaming Consortium list */ unsigned int roaming_consortium_count; struct hostapd_roaming_consortium *roaming_consortium; /* IEEE 802.11u - Venue Name duples */ unsigned int venue_name_count; struct hostapd_lang_string *venue_name; /* IEEE 802.11u - Network Authentication Type */ u8 *network_auth_type; size_t network_auth_type_len; /* IEEE 802.11u - IP Address Type Availability */ u8 ipaddr_type_availability; u8 ipaddr_type_configured; /* IEEE 802.11u - 3GPP Cellular Network */ u8 *anqp_3gpp_cell_net; size_t anqp_3gpp_cell_net_len; /* IEEE 802.11u - Domain Name */ u8 *domain_name; size_t domain_name_len; unsigned int nai_realm_count; struct hostapd_nai_realm_data *nai_realm_data; u16 gas_comeback_delay; int gas_frag_limit; u8 qos_map_set[16 + 2 * 21]; unsigned int qos_map_set_len; #ifdef CONFIG_HS20 int hs20; int disable_dgaf; unsigned int hs20_oper_friendly_name_count; struct hostapd_lang_string *hs20_oper_friendly_name; u8 *hs20_wan_metrics; u8 *hs20_connection_capability; size_t hs20_connection_capability_len; u8 *hs20_operating_class; u8 hs20_operating_class_len; #endif /* CONFIG_HS20 */ u8 wps_rf_bands; /* RF bands for WPS (WPS_RF_*) */ #ifdef CONFIG_RADIUS_TEST char *dump_msk_file; #endif /* CONFIG_RADIUS_TEST */ struct wpabuf *vendor_elements; unsigned int sae_anti_clogging_threshold; int *sae_groups; #ifdef CONFIG_TESTING_OPTIONS u8 bss_load_test[5]; u8 bss_load_test_set; #endif /* CONFIG_TESTING_OPTIONS */ }; /** * struct hostapd_config - Per-radio interface configuration */ struct hostapd_config { struct hostapd_bss_config **bss, *last_bss; size_t num_bss; u16 beacon_int; int rts_threshold; int fragm_threshold; u8 send_probe_response; u8 channel; enum hostapd_hw_mode hw_mode; /* HOSTAPD_MODE_IEEE80211A, .. */ enum { LONG_PREAMBLE = 0, SHORT_PREAMBLE = 1 } preamble; int *supported_rates; int *basic_rates; const struct wpa_driver_ops *driver; int ap_table_max_size; int ap_table_expiration_time; char country[3]; /* first two octets: country code as described in * ISO/IEC 3166-1. Third octet: * ' ' (ascii 32): all environments * 'O': Outdoor environemnt only * 'I': Indoor environment only */ int ieee80211d; int ieee80211h; /* DFS */ struct hostapd_tx_queue_params tx_queue[NUM_TX_QUEUES]; /* * WMM AC parameters, in same order as 802.1D, i.e. * 0 = BE (best effort) * 1 = BK (background) * 2 = VI (video) * 3 = VO (voice) */ struct hostapd_wmm_ac_params wmm_ac_params[4]; int ht_op_mode_fixed; u16 ht_capab; int ieee80211n; int secondary_channel; int require_ht; int obss_interval; u32 vht_capab; int ieee80211ac; int require_vht; u8 vht_oper_chwidth; u8 vht_oper_centr_freq_seg0_idx; u8 vht_oper_centr_freq_seg1_idx; #ifdef CONFIG_TESTING_OPTIONS double ignore_probe_probability; double ignore_auth_probability; double ignore_assoc_probability; double ignore_reassoc_probability; double corrupt_gtk_rekey_mic_probability; #endif /* CONFIG_TESTING_OPTIONS */ #ifdef CONFIG_ACS unsigned int acs_num_scans; #endif /* CONFIG_ACS */ }; int hostapd_mac_comp(const void *a, const void *b); int hostapd_mac_comp_empty(const void *a); struct hostapd_config * hostapd_config_defaults(void); void hostapd_config_defaults_bss(struct hostapd_bss_config *bss); void hostapd_config_free_bss(struct hostapd_bss_config *conf); void hostapd_config_free(struct hostapd_config *conf); int hostapd_maclist_found(struct mac_acl_entry *list, int num_entries, const u8 *addr, int *vlan_id); int hostapd_rate_found(int *list, int rate); int hostapd_wep_key_cmp(struct hostapd_wep_keys *a, struct hostapd_wep_keys *b); const u8 * hostapd_get_psk(const struct hostapd_bss_config *conf, const u8 *addr, const u8 *p2p_dev_addr, const u8 *prev_psk); int hostapd_setup_wpa_psk(struct hostapd_bss_config *conf); int hostapd_vlan_id_valid(struct hostapd_vlan *vlan, int vlan_id); const char * hostapd_get_vlan_id_ifname(struct hostapd_vlan *vlan, int vlan_id); struct hostapd_radius_attr * hostapd_config_get_radius_attr(struct hostapd_radius_attr *attr, u8 type); int hostapd_config_check(struct hostapd_config *conf, int full_config); void hostapd_set_security_params(struct hostapd_bss_config *bss); #endif /* HOSTAPD_CONFIG_H */ wpa-2.1/src/ap/ap_drv_ops.c000066400000000000000000000506561227414666700156650ustar00rootroot00000000000000/* * hostapd - Driver operations * Copyright (c) 2009-2010, 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 "common/ieee802_11_defs.h" #include "wps/wps.h" #include "p2p/p2p.h" #include "hostapd.h" #include "ieee802_11.h" #include "sta_info.h" #include "ap_config.h" #include "p2p_hostapd.h" #include "hs20.h" #include "ap_drv_ops.h" u32 hostapd_sta_flags_to_drv(u32 flags) { int res = 0; if (flags & WLAN_STA_AUTHORIZED) res |= WPA_STA_AUTHORIZED; if (flags & WLAN_STA_WMM) res |= WPA_STA_WMM; if (flags & WLAN_STA_SHORT_PREAMBLE) res |= WPA_STA_SHORT_PREAMBLE; if (flags & WLAN_STA_MFP) res |= WPA_STA_MFP; return res; } int hostapd_build_ap_extra_ies(struct hostapd_data *hapd, struct wpabuf **beacon_ret, struct wpabuf **proberesp_ret, struct wpabuf **assocresp_ret) { struct wpabuf *beacon = NULL, *proberesp = NULL, *assocresp = NULL; u8 buf[200], *pos; *beacon_ret = *proberesp_ret = *assocresp_ret = NULL; pos = buf; pos = hostapd_eid_time_adv(hapd, pos); if (pos != buf) { if (wpabuf_resize(&beacon, pos - buf) != 0) goto fail; wpabuf_put_data(beacon, buf, pos - buf); } pos = hostapd_eid_time_zone(hapd, pos); if (pos != buf) { if (wpabuf_resize(&proberesp, pos - buf) != 0) goto fail; wpabuf_put_data(proberesp, buf, pos - buf); } pos = buf; pos = hostapd_eid_ext_capab(hapd, pos); if (pos != buf) { if (wpabuf_resize(&assocresp, pos - buf) != 0) goto fail; wpabuf_put_data(assocresp, buf, pos - buf); } pos = hostapd_eid_interworking(hapd, pos); pos = hostapd_eid_adv_proto(hapd, pos); pos = hostapd_eid_roaming_consortium(hapd, pos); if (pos != buf) { if (wpabuf_resize(&beacon, pos - buf) != 0) goto fail; wpabuf_put_data(beacon, buf, pos - buf); if (wpabuf_resize(&proberesp, pos - buf) != 0) goto fail; wpabuf_put_data(proberesp, buf, pos - buf); } if (hapd->wps_beacon_ie) { if (wpabuf_resize(&beacon, wpabuf_len(hapd->wps_beacon_ie)) < 0) goto fail; wpabuf_put_buf(beacon, hapd->wps_beacon_ie); } if (hapd->wps_probe_resp_ie) { if (wpabuf_resize(&proberesp, wpabuf_len(hapd->wps_probe_resp_ie)) < 0) goto fail; wpabuf_put_buf(proberesp, hapd->wps_probe_resp_ie); } #ifdef CONFIG_P2P if (hapd->p2p_beacon_ie) { if (wpabuf_resize(&beacon, wpabuf_len(hapd->p2p_beacon_ie)) < 0) goto fail; wpabuf_put_buf(beacon, hapd->p2p_beacon_ie); } if (hapd->p2p_probe_resp_ie) { if (wpabuf_resize(&proberesp, wpabuf_len(hapd->p2p_probe_resp_ie)) < 0) goto fail; wpabuf_put_buf(proberesp, hapd->p2p_probe_resp_ie); } #endif /* CONFIG_P2P */ #ifdef CONFIG_P2P_MANAGER if (hapd->conf->p2p & P2P_MANAGE) { if (wpabuf_resize(&beacon, 100) == 0) { u8 *start, *p; start = wpabuf_put(beacon, 0); p = hostapd_eid_p2p_manage(hapd, start); wpabuf_put(beacon, p - start); } if (wpabuf_resize(&proberesp, 100) == 0) { u8 *start, *p; start = wpabuf_put(proberesp, 0); p = hostapd_eid_p2p_manage(hapd, start); wpabuf_put(proberesp, p - start); } } #endif /* CONFIG_P2P_MANAGER */ #ifdef CONFIG_WPS2 if (hapd->conf->wps_state) { struct wpabuf *a = wps_build_assoc_resp_ie(); if (a && wpabuf_resize(&assocresp, wpabuf_len(a)) == 0) wpabuf_put_buf(assocresp, a); wpabuf_free(a); } #endif /* CONFIG_WPS2 */ #ifdef CONFIG_P2P_MANAGER if (hapd->conf->p2p & P2P_MANAGE) { if (wpabuf_resize(&assocresp, 100) == 0) { u8 *start, *p; start = wpabuf_put(assocresp, 0); p = hostapd_eid_p2p_manage(hapd, start); wpabuf_put(assocresp, p - start); } } #endif /* CONFIG_P2P_MANAGER */ #ifdef CONFIG_WIFI_DISPLAY if (hapd->p2p_group) { struct wpabuf *a; a = p2p_group_assoc_resp_ie(hapd->p2p_group, P2P_SC_SUCCESS); if (a && wpabuf_resize(&assocresp, wpabuf_len(a)) == 0) wpabuf_put_buf(assocresp, a); wpabuf_free(a); } #endif /* CONFIG_WIFI_DISPLAY */ #ifdef CONFIG_HS20 pos = buf; pos = hostapd_eid_hs20_indication(hapd, pos); if (pos != buf) { if (wpabuf_resize(&beacon, pos - buf) != 0) goto fail; wpabuf_put_data(beacon, buf, pos - buf); if (wpabuf_resize(&proberesp, pos - buf) != 0) goto fail; wpabuf_put_data(proberesp, buf, pos - buf); } #endif /* CONFIG_HS20 */ if (hapd->conf->vendor_elements) { size_t add = wpabuf_len(hapd->conf->vendor_elements); if (wpabuf_resize(&beacon, add) == 0) wpabuf_put_buf(beacon, hapd->conf->vendor_elements); if (wpabuf_resize(&proberesp, add) == 0) wpabuf_put_buf(proberesp, hapd->conf->vendor_elements); } *beacon_ret = beacon; *proberesp_ret = proberesp; *assocresp_ret = assocresp; return 0; fail: wpabuf_free(beacon); wpabuf_free(proberesp); wpabuf_free(assocresp); return -1; } void hostapd_free_ap_extra_ies(struct hostapd_data *hapd, struct wpabuf *beacon, struct wpabuf *proberesp, struct wpabuf *assocresp) { wpabuf_free(beacon); wpabuf_free(proberesp); wpabuf_free(assocresp); } int hostapd_set_ap_wps_ie(struct hostapd_data *hapd) { struct wpabuf *beacon, *proberesp, *assocresp; int ret; if (hapd->driver == NULL || hapd->driver->set_ap_wps_ie == NULL) return 0; if (hostapd_build_ap_extra_ies(hapd, &beacon, &proberesp, &assocresp) < 0) return -1; ret = hapd->driver->set_ap_wps_ie(hapd->drv_priv, beacon, proberesp, assocresp); hostapd_free_ap_extra_ies(hapd, beacon, proberesp, assocresp); return ret; } int hostapd_set_authorized(struct hostapd_data *hapd, struct sta_info *sta, int authorized) { if (authorized) { return hostapd_sta_set_flags(hapd, sta->addr, hostapd_sta_flags_to_drv( sta->flags), WPA_STA_AUTHORIZED, ~0); } return hostapd_sta_set_flags(hapd, sta->addr, hostapd_sta_flags_to_drv(sta->flags), 0, ~WPA_STA_AUTHORIZED); } int hostapd_set_sta_flags(struct hostapd_data *hapd, struct sta_info *sta) { int set_flags, total_flags, flags_and, flags_or; total_flags = hostapd_sta_flags_to_drv(sta->flags); set_flags = WPA_STA_SHORT_PREAMBLE | WPA_STA_WMM | WPA_STA_MFP; if (((!hapd->conf->ieee802_1x && !hapd->conf->wpa) || sta->auth_alg == WLAN_AUTH_FT) && sta->flags & WLAN_STA_AUTHORIZED) set_flags |= WPA_STA_AUTHORIZED; flags_or = total_flags & set_flags; flags_and = total_flags | ~set_flags; return hostapd_sta_set_flags(hapd, sta->addr, total_flags, flags_or, flags_and); } int hostapd_set_drv_ieee8021x(struct hostapd_data *hapd, const char *ifname, int enabled) { struct wpa_bss_params params; os_memset(¶ms, 0, sizeof(params)); params.ifname = ifname; params.enabled = enabled; if (enabled) { params.wpa = hapd->conf->wpa; params.ieee802_1x = hapd->conf->ieee802_1x; params.wpa_group = hapd->conf->wpa_group; params.wpa_pairwise = hapd->conf->wpa_pairwise; params.wpa_key_mgmt = hapd->conf->wpa_key_mgmt; params.rsn_preauth = hapd->conf->rsn_preauth; #ifdef CONFIG_IEEE80211W params.ieee80211w = hapd->conf->ieee80211w; #endif /* CONFIG_IEEE80211W */ } return hostapd_set_ieee8021x(hapd, ¶ms); } int hostapd_vlan_if_add(struct hostapd_data *hapd, const char *ifname) { char force_ifname[IFNAMSIZ]; u8 if_addr[ETH_ALEN]; return hostapd_if_add(hapd, WPA_IF_AP_VLAN, ifname, hapd->own_addr, NULL, NULL, force_ifname, if_addr, NULL, 0); } int hostapd_vlan_if_remove(struct hostapd_data *hapd, const char *ifname) { return hostapd_if_remove(hapd, WPA_IF_AP_VLAN, ifname); } int hostapd_set_wds_sta(struct hostapd_data *hapd, char *ifname_wds, const u8 *addr, int aid, int val) { const char *bridge = NULL; if (hapd->driver == NULL || hapd->driver->set_wds_sta == NULL) return -1; if (hapd->conf->wds_bridge[0]) bridge = hapd->conf->wds_bridge; else if (hapd->conf->bridge[0]) bridge = hapd->conf->bridge; return hapd->driver->set_wds_sta(hapd->drv_priv, addr, aid, val, bridge, ifname_wds); } int hostapd_add_sta_node(struct hostapd_data *hapd, const u8 *addr, u16 auth_alg) { if (hapd->driver == NULL || hapd->driver->add_sta_node == NULL) return 0; return hapd->driver->add_sta_node(hapd->drv_priv, addr, auth_alg); } int hostapd_sta_auth(struct hostapd_data *hapd, const u8 *addr, u16 seq, u16 status, const u8 *ie, size_t len) { if (hapd->driver == NULL || hapd->driver->sta_auth == NULL) return 0; return hapd->driver->sta_auth(hapd->drv_priv, hapd->own_addr, addr, seq, status, ie, len); } int hostapd_sta_assoc(struct hostapd_data *hapd, const u8 *addr, int reassoc, u16 status, const u8 *ie, size_t len) { if (hapd->driver == NULL || hapd->driver->sta_assoc == NULL) return 0; return hapd->driver->sta_assoc(hapd->drv_priv, hapd->own_addr, addr, reassoc, status, ie, len); } int hostapd_sta_add(struct hostapd_data *hapd, const u8 *addr, u16 aid, u16 capability, const u8 *supp_rates, size_t supp_rates_len, u16 listen_interval, const struct ieee80211_ht_capabilities *ht_capab, const struct ieee80211_vht_capabilities *vht_capab, u32 flags, u8 qosinfo) { struct hostapd_sta_add_params params; if (hapd->driver == NULL) return 0; if (hapd->driver->sta_add == NULL) return 0; os_memset(¶ms, 0, sizeof(params)); params.addr = addr; params.aid = aid; params.capability = capability; params.supp_rates = supp_rates; params.supp_rates_len = supp_rates_len; params.listen_interval = listen_interval; params.ht_capabilities = ht_capab; params.vht_capabilities = vht_capab; params.flags = hostapd_sta_flags_to_drv(flags); params.qosinfo = qosinfo; return hapd->driver->sta_add(hapd->drv_priv, ¶ms); } int hostapd_add_tspec(struct hostapd_data *hapd, const u8 *addr, u8 *tspec_ie, size_t tspec_ielen) { if (hapd->driver == NULL || hapd->driver->add_tspec == NULL) return 0; return hapd->driver->add_tspec(hapd->drv_priv, addr, tspec_ie, tspec_ielen); } int hostapd_set_privacy(struct hostapd_data *hapd, int enabled) { if (hapd->driver == NULL || hapd->driver->set_privacy == NULL) return 0; return hapd->driver->set_privacy(hapd->drv_priv, enabled); } int hostapd_set_generic_elem(struct hostapd_data *hapd, const u8 *elem, size_t elem_len) { if (hapd->driver == NULL || hapd->driver->set_generic_elem == NULL) return 0; return hapd->driver->set_generic_elem(hapd->drv_priv, elem, elem_len); } int hostapd_get_ssid(struct hostapd_data *hapd, u8 *buf, size_t len) { if (hapd->driver == NULL || hapd->driver->hapd_get_ssid == NULL) return 0; return hapd->driver->hapd_get_ssid(hapd->drv_priv, buf, len); } int hostapd_set_ssid(struct hostapd_data *hapd, const u8 *buf, size_t len) { if (hapd->driver == NULL || hapd->driver->hapd_set_ssid == NULL) return 0; return hapd->driver->hapd_set_ssid(hapd->drv_priv, buf, len); } int hostapd_if_add(struct hostapd_data *hapd, enum wpa_driver_if_type type, const char *ifname, const u8 *addr, void *bss_ctx, void **drv_priv, char *force_ifname, u8 *if_addr, const char *bridge, int use_existing) { if (hapd->driver == NULL || hapd->driver->if_add == NULL) return -1; return hapd->driver->if_add(hapd->drv_priv, type, ifname, addr, bss_ctx, drv_priv, force_ifname, if_addr, bridge, use_existing); } int hostapd_if_remove(struct hostapd_data *hapd, enum wpa_driver_if_type type, const char *ifname) { if (hapd->driver == NULL || hapd->drv_priv == NULL || hapd->driver->if_remove == NULL) return -1; return hapd->driver->if_remove(hapd->drv_priv, type, ifname); } int hostapd_set_ieee8021x(struct hostapd_data *hapd, struct wpa_bss_params *params) { if (hapd->driver == NULL || hapd->driver->set_ieee8021x == NULL) return 0; return hapd->driver->set_ieee8021x(hapd->drv_priv, params); } int hostapd_get_seqnum(const char *ifname, struct hostapd_data *hapd, const u8 *addr, int idx, u8 *seq) { if (hapd->driver == NULL || hapd->driver->get_seqnum == NULL) return 0; return hapd->driver->get_seqnum(ifname, hapd->drv_priv, addr, idx, seq); } int hostapd_flush(struct hostapd_data *hapd) { if (hapd->driver == NULL || hapd->driver->flush == NULL) return 0; return hapd->driver->flush(hapd->drv_priv); } int hostapd_set_freq_params(struct hostapd_freq_params *data, int mode, int freq, int channel, int ht_enabled, int vht_enabled, int sec_channel_offset, int vht_oper_chwidth, int center_segment0, int center_segment1, u32 vht_caps) { int tmp; os_memset(data, 0, sizeof(*data)); data->mode = mode; data->freq = freq; data->channel = channel; data->ht_enabled = ht_enabled; data->vht_enabled = vht_enabled; data->sec_channel_offset = sec_channel_offset; data->center_freq1 = freq + sec_channel_offset * 10; data->center_freq2 = 0; data->bandwidth = sec_channel_offset ? 40 : 20; /* * This validation code is probably misplaced, maybe it should be * in src/ap/hw_features.c and check the hardware support as well. */ if (data->vht_enabled) switch (vht_oper_chwidth) { case VHT_CHANWIDTH_USE_HT: if (center_segment1) return -1; if (5000 + center_segment0 * 5 != data->center_freq1 && 2407 + center_segment0 * 5 != data->center_freq1) return -1; break; case VHT_CHANWIDTH_80P80MHZ: if (!(vht_caps & VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ)) { wpa_printf(MSG_ERROR, "80+80 channel width is not supported!"); return -1; } if (center_segment1 == center_segment0 + 4 || center_segment1 == center_segment0 - 4) return -1; data->center_freq2 = 5000 + center_segment1 * 5; /* fall through */ case VHT_CHANWIDTH_80MHZ: data->bandwidth = 80; if (vht_oper_chwidth == 1 && center_segment1) return -1; if (vht_oper_chwidth == 3 && !center_segment1) return -1; if (!sec_channel_offset) return -1; /* primary 40 part must match the HT configuration */ tmp = (30 + freq - 5000 - center_segment0 * 5)/20; tmp /= 2; if (data->center_freq1 != 5000 + center_segment0 * 5 - 20 + 40 * tmp) return -1; data->center_freq1 = 5000 + center_segment0 * 5; break; case VHT_CHANWIDTH_160MHZ: data->bandwidth = 160; if (!(vht_caps & (VHT_CAP_SUPP_CHAN_WIDTH_160MHZ | VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ))) { wpa_printf(MSG_ERROR, "160MHZ channel width is not supported!"); return -1; } if (center_segment1) return -1; if (!sec_channel_offset) return -1; /* primary 40 part must match the HT configuration */ tmp = (70 + freq - 5000 - center_segment0 * 5)/20; tmp /= 2; if (data->center_freq1 != 5000 + center_segment0 * 5 - 60 + 40 * tmp) return -1; data->center_freq1 = 5000 + center_segment0 * 5; break; } return 0; } int hostapd_set_freq(struct hostapd_data *hapd, int mode, int freq, int channel, int ht_enabled, int vht_enabled, int sec_channel_offset, int vht_oper_chwidth, int center_segment0, int center_segment1) { struct hostapd_freq_params data; if (hostapd_set_freq_params(&data, mode, freq, channel, ht_enabled, vht_enabled, sec_channel_offset, vht_oper_chwidth, center_segment0, center_segment1, hapd->iface->current_mode->vht_capab)) return -1; if (hapd->driver == NULL) return 0; if (hapd->driver->set_freq == NULL) return 0; return hapd->driver->set_freq(hapd->drv_priv, &data); } int hostapd_set_rts(struct hostapd_data *hapd, int rts) { if (hapd->driver == NULL || hapd->driver->set_rts == NULL) return 0; return hapd->driver->set_rts(hapd->drv_priv, rts); } int hostapd_set_frag(struct hostapd_data *hapd, int frag) { if (hapd->driver == NULL || hapd->driver->set_frag == NULL) return 0; return hapd->driver->set_frag(hapd->drv_priv, frag); } int hostapd_sta_set_flags(struct hostapd_data *hapd, u8 *addr, int total_flags, int flags_or, int flags_and) { if (hapd->driver == NULL || hapd->driver->sta_set_flags == NULL) return 0; return hapd->driver->sta_set_flags(hapd->drv_priv, addr, total_flags, flags_or, flags_and); } int hostapd_set_country(struct hostapd_data *hapd, const char *country) { if (hapd->driver == NULL || hapd->driver->set_country == NULL) return 0; return hapd->driver->set_country(hapd->drv_priv, country); } int hostapd_set_tx_queue_params(struct hostapd_data *hapd, int queue, int aifs, int cw_min, int cw_max, int burst_time) { if (hapd->driver == NULL || hapd->driver->set_tx_queue_params == NULL) return 0; return hapd->driver->set_tx_queue_params(hapd->drv_priv, queue, aifs, cw_min, cw_max, burst_time); } struct hostapd_hw_modes * hostapd_get_hw_feature_data(struct hostapd_data *hapd, u16 *num_modes, u16 *flags) { if (hapd->driver == NULL || hapd->driver->get_hw_feature_data == NULL) return NULL; return hapd->driver->get_hw_feature_data(hapd->drv_priv, num_modes, flags); } int hostapd_driver_commit(struct hostapd_data *hapd) { if (hapd->driver == NULL || hapd->driver->commit == NULL) return 0; return hapd->driver->commit(hapd->drv_priv); } int hostapd_drv_none(struct hostapd_data *hapd) { return hapd->driver && os_strcmp(hapd->driver->name, "none") == 0; } int hostapd_driver_scan(struct hostapd_data *hapd, struct wpa_driver_scan_params *params) { if (hapd->driver && hapd->driver->scan2) return hapd->driver->scan2(hapd->drv_priv, params); return -1; } struct wpa_scan_results * hostapd_driver_get_scan_results( struct hostapd_data *hapd) { if (hapd->driver && hapd->driver->get_scan_results2) return hapd->driver->get_scan_results2(hapd->drv_priv); return NULL; } int hostapd_driver_set_noa(struct hostapd_data *hapd, u8 count, int start, int duration) { if (hapd->driver && hapd->driver->set_noa) return hapd->driver->set_noa(hapd->drv_priv, count, start, duration); return -1; } int hostapd_drv_set_key(const char *ifname, struct hostapd_data *hapd, enum wpa_alg alg, const u8 *addr, int key_idx, int set_tx, const u8 *seq, size_t seq_len, const u8 *key, size_t key_len) { if (hapd->driver == NULL || hapd->driver->set_key == NULL) return 0; return hapd->driver->set_key(ifname, hapd->drv_priv, alg, addr, key_idx, set_tx, seq, seq_len, key, key_len); } int hostapd_drv_send_mlme(struct hostapd_data *hapd, const void *msg, size_t len, int noack) { if (hapd->driver == NULL || hapd->driver->send_mlme == NULL) return 0; return hapd->driver->send_mlme(hapd->drv_priv, msg, len, noack); } int hostapd_drv_sta_deauth(struct hostapd_data *hapd, const u8 *addr, int reason) { if (hapd->driver == NULL || hapd->driver->sta_deauth == NULL) return 0; return hapd->driver->sta_deauth(hapd->drv_priv, hapd->own_addr, addr, reason); } int hostapd_drv_sta_disassoc(struct hostapd_data *hapd, const u8 *addr, int reason) { if (hapd->driver == NULL || hapd->driver->sta_disassoc == NULL) return 0; return hapd->driver->sta_disassoc(hapd->drv_priv, hapd->own_addr, addr, reason); } int hostapd_drv_wnm_oper(struct hostapd_data *hapd, enum wnm_oper oper, const u8 *peer, u8 *buf, u16 *buf_len) { if (hapd->driver == NULL || hapd->driver->wnm_oper == NULL) return -1; return hapd->driver->wnm_oper(hapd->drv_priv, oper, peer, buf, buf_len); } int hostapd_drv_send_action(struct hostapd_data *hapd, unsigned int freq, unsigned int wait, const u8 *dst, const u8 *data, size_t len) { if (hapd->driver == NULL || hapd->driver->send_action == NULL) return 0; return hapd->driver->send_action(hapd->drv_priv, freq, wait, dst, hapd->own_addr, hapd->own_addr, data, len, 0); } int hostapd_start_dfs_cac(struct hostapd_iface *iface, int mode, int freq, int channel, int ht_enabled, int vht_enabled, int sec_channel_offset, int vht_oper_chwidth, int center_segment0, int center_segment1) { struct hostapd_data *hapd = iface->bss[0]; struct hostapd_freq_params data; int res; if (!hapd->driver || !hapd->driver->start_dfs_cac) return 0; if (!iface->conf->ieee80211h) { wpa_printf(MSG_ERROR, "Can't start DFS CAC, DFS functionality " "is not enabled"); return -1; } if (hostapd_set_freq_params(&data, mode, freq, channel, ht_enabled, vht_enabled, sec_channel_offset, vht_oper_chwidth, center_segment0, center_segment1, iface->current_mode->vht_capab)) return -1; res = hapd->driver->start_dfs_cac(hapd->drv_priv, &data); if (!res) iface->cac_started = 1; return res; } int hostapd_drv_set_qos_map(struct hostapd_data *hapd, const u8 *qos_map_set, u8 qos_map_set_len) { if (hapd->driver == NULL || hapd->driver->set_qos_map == NULL) return 0; return hapd->driver->set_qos_map(hapd->drv_priv, qos_map_set, qos_map_set_len); } wpa-2.1/src/ap/ap_drv_ops.h000066400000000000000000000241251227414666700156620ustar00rootroot00000000000000/* * hostapd - Driver operations * Copyright (c) 2009-2014, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef AP_DRV_OPS #define AP_DRV_OPS enum wpa_driver_if_type; struct wpa_bss_params; struct wpa_driver_scan_params; struct ieee80211_ht_capabilities; struct ieee80211_vht_capabilities; struct hostapd_freq_params; u32 hostapd_sta_flags_to_drv(u32 flags); int hostapd_build_ap_extra_ies(struct hostapd_data *hapd, struct wpabuf **beacon, struct wpabuf **proberesp, struct wpabuf **assocresp); void hostapd_free_ap_extra_ies(struct hostapd_data *hapd, struct wpabuf *beacon, struct wpabuf *proberesp, struct wpabuf *assocresp); int hostapd_set_ap_wps_ie(struct hostapd_data *hapd); int hostapd_set_authorized(struct hostapd_data *hapd, struct sta_info *sta, int authorized); int hostapd_set_sta_flags(struct hostapd_data *hapd, struct sta_info *sta); int hostapd_set_drv_ieee8021x(struct hostapd_data *hapd, const char *ifname, int enabled); int hostapd_vlan_if_add(struct hostapd_data *hapd, const char *ifname); int hostapd_vlan_if_remove(struct hostapd_data *hapd, const char *ifname); int hostapd_set_wds_sta(struct hostapd_data *hapd, char *ifname_wds, const u8 *addr, int aid, int val); int hostapd_sta_add(struct hostapd_data *hapd, const u8 *addr, u16 aid, u16 capability, const u8 *supp_rates, size_t supp_rates_len, u16 listen_interval, const struct ieee80211_ht_capabilities *ht_capab, const struct ieee80211_vht_capabilities *vht_capab, u32 flags, u8 qosinfo); int hostapd_set_privacy(struct hostapd_data *hapd, int enabled); int hostapd_set_generic_elem(struct hostapd_data *hapd, const u8 *elem, size_t elem_len); int hostapd_get_ssid(struct hostapd_data *hapd, u8 *buf, size_t len); int hostapd_set_ssid(struct hostapd_data *hapd, const u8 *buf, size_t len); int hostapd_if_add(struct hostapd_data *hapd, enum wpa_driver_if_type type, const char *ifname, const u8 *addr, void *bss_ctx, void **drv_priv, char *force_ifname, u8 *if_addr, const char *bridge, int use_existing); int hostapd_if_remove(struct hostapd_data *hapd, enum wpa_driver_if_type type, const char *ifname); int hostapd_set_ieee8021x(struct hostapd_data *hapd, struct wpa_bss_params *params); int hostapd_get_seqnum(const char *ifname, struct hostapd_data *hapd, const u8 *addr, int idx, u8 *seq); int hostapd_flush(struct hostapd_data *hapd); int hostapd_set_freq(struct hostapd_data *hapd, int mode, int freq, int channel, int ht_enabled, int vht_enabled, int sec_channel_offset, int vht_oper_chwidth, int center_segment0, int center_segment1); int hostapd_set_rts(struct hostapd_data *hapd, int rts); int hostapd_set_frag(struct hostapd_data *hapd, int frag); int hostapd_sta_set_flags(struct hostapd_data *hapd, u8 *addr, int total_flags, int flags_or, int flags_and); int hostapd_set_country(struct hostapd_data *hapd, const char *country); int hostapd_set_tx_queue_params(struct hostapd_data *hapd, int queue, int aifs, int cw_min, int cw_max, int burst_time); struct hostapd_hw_modes * hostapd_get_hw_feature_data(struct hostapd_data *hapd, u16 *num_modes, u16 *flags); int hostapd_driver_commit(struct hostapd_data *hapd); int hostapd_drv_none(struct hostapd_data *hapd); int hostapd_driver_scan(struct hostapd_data *hapd, struct wpa_driver_scan_params *params); struct wpa_scan_results * hostapd_driver_get_scan_results( struct hostapd_data *hapd); int hostapd_driver_set_noa(struct hostapd_data *hapd, u8 count, int start, int duration); int hostapd_drv_set_key(const char *ifname, struct hostapd_data *hapd, enum wpa_alg alg, const u8 *addr, int key_idx, int set_tx, const u8 *seq, size_t seq_len, const u8 *key, size_t key_len); int hostapd_drv_send_mlme(struct hostapd_data *hapd, const void *msg, size_t len, int noack); int hostapd_drv_sta_deauth(struct hostapd_data *hapd, const u8 *addr, int reason); int hostapd_drv_sta_disassoc(struct hostapd_data *hapd, const u8 *addr, int reason); int hostapd_drv_send_action(struct hostapd_data *hapd, unsigned int freq, unsigned int wait, const u8 *dst, const u8 *data, size_t len); int hostapd_add_sta_node(struct hostapd_data *hapd, const u8 *addr, u16 auth_alg); int hostapd_sta_auth(struct hostapd_data *hapd, const u8 *addr, u16 seq, u16 status, const u8 *ie, size_t len); int hostapd_sta_assoc(struct hostapd_data *hapd, const u8 *addr, int reassoc, u16 status, const u8 *ie, size_t len); int hostapd_add_tspec(struct hostapd_data *hapd, const u8 *addr, u8 *tspec_ie, size_t tspec_ielen); int hostapd_start_dfs_cac(struct hostapd_iface *iface, int mode, int freq, int channel, int ht_enabled, int vht_enabled, int sec_channel_offset, int vht_oper_chwidth, int center_segment0, int center_segment1); int hostapd_set_freq_params(struct hostapd_freq_params *data, int mode, int freq, int channel, int ht_enabled, int vht_enabled, int sec_channel_offset, int vht_oper_chwidth, int center_segment0, int center_segment1, u32 vht_caps); #include "drivers/driver.h" int hostapd_drv_wnm_oper(struct hostapd_data *hapd, enum wnm_oper oper, const u8 *peer, u8 *buf, u16 *buf_len); int hostapd_drv_set_qos_map(struct hostapd_data *hapd, const u8 *qos_map_set, u8 qos_map_set_len); static inline int hostapd_drv_set_countermeasures(struct hostapd_data *hapd, int enabled) { if (hapd->driver == NULL || hapd->driver->hapd_set_countermeasures == NULL) return 0; return hapd->driver->hapd_set_countermeasures(hapd->drv_priv, enabled); } static inline int hostapd_drv_set_sta_vlan(const char *ifname, struct hostapd_data *hapd, const u8 *addr, int vlan_id) { if (hapd->driver == NULL || hapd->driver->set_sta_vlan == NULL) return 0; return hapd->driver->set_sta_vlan(hapd->drv_priv, addr, ifname, vlan_id); } static inline int hostapd_drv_get_inact_sec(struct hostapd_data *hapd, const u8 *addr) { if (hapd->driver == NULL || hapd->driver->get_inact_sec == NULL) return 0; return hapd->driver->get_inact_sec(hapd->drv_priv, addr); } static inline int hostapd_drv_sta_remove(struct hostapd_data *hapd, const u8 *addr) { if (hapd->driver == NULL || hapd->driver->sta_remove == NULL) return 0; return hapd->driver->sta_remove(hapd->drv_priv, addr); } static inline int hostapd_drv_hapd_send_eapol(struct hostapd_data *hapd, const u8 *addr, const u8 *data, size_t data_len, int encrypt, u32 flags) { if (hapd->driver == NULL || hapd->driver->hapd_send_eapol == NULL) return 0; return hapd->driver->hapd_send_eapol(hapd->drv_priv, addr, data, data_len, encrypt, hapd->own_addr, flags); } static inline int hostapd_drv_read_sta_data( struct hostapd_data *hapd, struct hostap_sta_driver_data *data, const u8 *addr) { if (hapd->driver == NULL || hapd->driver->read_sta_data == NULL) return -1; return hapd->driver->read_sta_data(hapd->drv_priv, data, addr); } static inline int hostapd_drv_sta_clear_stats(struct hostapd_data *hapd, const u8 *addr) { if (hapd->driver == NULL || hapd->driver->sta_clear_stats == NULL) return 0; return hapd->driver->sta_clear_stats(hapd->drv_priv, addr); } static inline int hostapd_drv_set_acl(struct hostapd_data *hapd, struct hostapd_acl_params *params) { if (hapd->driver == NULL || hapd->driver->set_acl == NULL) return 0; return hapd->driver->set_acl(hapd->drv_priv, params); } static inline int hostapd_drv_set_ap(struct hostapd_data *hapd, struct wpa_driver_ap_params *params) { if (hapd->driver == NULL || hapd->driver->set_ap == NULL) return 0; return hapd->driver->set_ap(hapd->drv_priv, params); } static inline int hostapd_drv_set_radius_acl_auth(struct hostapd_data *hapd, const u8 *mac, int accepted, u32 session_timeout) { if (hapd->driver == NULL || hapd->driver->set_radius_acl_auth == NULL) return 0; return hapd->driver->set_radius_acl_auth(hapd->drv_priv, mac, accepted, session_timeout); } static inline int hostapd_drv_set_radius_acl_expire(struct hostapd_data *hapd, const u8 *mac) { if (hapd->driver == NULL || hapd->driver->set_radius_acl_expire == NULL) return 0; return hapd->driver->set_radius_acl_expire(hapd->drv_priv, mac); } static inline int hostapd_drv_set_authmode(struct hostapd_data *hapd, int auth_algs) { if (hapd->driver == NULL || hapd->driver->set_authmode == NULL) return 0; return hapd->driver->set_authmode(hapd->drv_priv, auth_algs); } static inline void hostapd_drv_poll_client(struct hostapd_data *hapd, const u8 *own_addr, const u8 *addr, int qos) { if (hapd->driver == NULL || hapd->driver->poll_client == NULL) return; hapd->driver->poll_client(hapd->drv_priv, own_addr, addr, qos); } static inline int hostapd_drv_get_survey(struct hostapd_data *hapd, unsigned int freq) { if (hapd->driver == NULL) return -1; if (!hapd->driver->get_survey) return -1; return hapd->driver->get_survey(hapd->drv_priv, freq); } static inline int hostapd_get_country(struct hostapd_data *hapd, char *alpha2) { if (hapd->driver == NULL || hapd->driver->get_country == NULL) return -1; return hapd->driver->get_country(hapd->drv_priv, alpha2); } static inline const char * hostapd_drv_get_radio_name(struct hostapd_data *hapd) { if (hapd->driver == NULL || hapd->drv_priv == NULL || hapd->driver->get_radio_name == NULL) return NULL; return hapd->driver->get_radio_name(hapd->drv_priv); } static inline int hostapd_drv_switch_channel(struct hostapd_data *hapd, struct csa_settings *settings) { if (hapd->driver == NULL || hapd->driver->switch_channel == NULL) return -ENOTSUP; return hapd->driver->switch_channel(hapd->drv_priv, settings); } static inline int hostapd_drv_status(struct hostapd_data *hapd, char *buf, size_t buflen) { if (hapd->driver == NULL || hapd->driver->status == NULL) return -1; return hapd->driver->status(hapd->drv_priv, buf, buflen); } #endif /* AP_DRV_OPS */ wpa-2.1/src/ap/ap_list.c000066400000000000000000000161031227414666700151510ustar00rootroot00000000000000/* * hostapd / AP table * Copyright (c) 2002-2009, Jouni Malinen * Copyright (c) 2003-2004, Instant802 Networks, Inc. * Copyright (c) 2006, Devicescape Software, Inc. * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "utils/includes.h" #include "utils/common.h" #include "utils/eloop.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" #include "hostapd.h" #include "ap_config.h" #include "ieee802_11.h" #include "sta_info.h" #include "beacon.h" #include "ap_list.h" /* AP list is a double linked list with head->prev pointing to the end of the * list and tail->next = NULL. Entries are moved to the head of the list * whenever a beacon has been received from the AP in question. The tail entry * in this link will thus be the least recently used entry. */ static int ap_list_beacon_olbc(struct hostapd_iface *iface, struct ap_info *ap) { int i; if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G || iface->conf->channel != ap->channel) return 0; if (ap->erp != -1 && (ap->erp & ERP_INFO_NON_ERP_PRESENT)) return 1; for (i = 0; i < WLAN_SUPP_RATES_MAX; i++) { int rate = (ap->supported_rates[i] & 0x7f) * 5; if (rate == 60 || rate == 90 || rate > 110) return 0; } return 1; } static struct ap_info * ap_get_ap(struct hostapd_iface *iface, const u8 *ap) { struct ap_info *s; s = iface->ap_hash[STA_HASH(ap)]; while (s != NULL && os_memcmp(s->addr, ap, ETH_ALEN) != 0) s = s->hnext; return s; } static void ap_ap_list_add(struct hostapd_iface *iface, struct ap_info *ap) { if (iface->ap_list) { ap->prev = iface->ap_list->prev; iface->ap_list->prev = ap; } else ap->prev = ap; ap->next = iface->ap_list; iface->ap_list = ap; } static void ap_ap_list_del(struct hostapd_iface *iface, struct ap_info *ap) { if (iface->ap_list == ap) iface->ap_list = ap->next; else ap->prev->next = ap->next; if (ap->next) ap->next->prev = ap->prev; else if (iface->ap_list) iface->ap_list->prev = ap->prev; } static void ap_ap_hash_add(struct hostapd_iface *iface, struct ap_info *ap) { ap->hnext = iface->ap_hash[STA_HASH(ap->addr)]; iface->ap_hash[STA_HASH(ap->addr)] = ap; } static void ap_ap_hash_del(struct hostapd_iface *iface, struct ap_info *ap) { struct ap_info *s; s = iface->ap_hash[STA_HASH(ap->addr)]; if (s == NULL) return; if (os_memcmp(s->addr, ap->addr, ETH_ALEN) == 0) { iface->ap_hash[STA_HASH(ap->addr)] = s->hnext; return; } while (s->hnext != NULL && os_memcmp(s->hnext->addr, ap->addr, ETH_ALEN) != 0) s = s->hnext; if (s->hnext != NULL) s->hnext = s->hnext->hnext; else printf("AP: could not remove AP " MACSTR " from hash table\n", MAC2STR(ap->addr)); } static void ap_free_ap(struct hostapd_iface *iface, struct ap_info *ap) { ap_ap_hash_del(iface, ap); ap_ap_list_del(iface, ap); iface->num_ap--; os_free(ap); } static void hostapd_free_aps(struct hostapd_iface *iface) { struct ap_info *ap, *prev; ap = iface->ap_list; while (ap) { prev = ap; ap = ap->next; ap_free_ap(iface, prev); } iface->ap_list = NULL; } static struct ap_info * ap_ap_add(struct hostapd_iface *iface, const u8 *addr) { struct ap_info *ap; ap = os_zalloc(sizeof(struct ap_info)); if (ap == NULL) return NULL; /* initialize AP info data */ os_memcpy(ap->addr, addr, ETH_ALEN); ap_ap_list_add(iface, ap); iface->num_ap++; ap_ap_hash_add(iface, ap); if (iface->num_ap > iface->conf->ap_table_max_size && ap != ap->prev) { wpa_printf(MSG_DEBUG, "Removing the least recently used AP " MACSTR " from AP table", MAC2STR(ap->prev->addr)); ap_free_ap(iface, ap->prev); } return ap; } void ap_list_process_beacon(struct hostapd_iface *iface, const struct ieee80211_mgmt *mgmt, struct ieee802_11_elems *elems, struct hostapd_frame_info *fi) { struct ap_info *ap; int new_ap = 0; int set_beacon = 0; if (iface->conf->ap_table_max_size < 1) return; ap = ap_get_ap(iface, mgmt->bssid); if (!ap) { ap = ap_ap_add(iface, mgmt->bssid); if (!ap) { printf("Failed to allocate AP information entry\n"); return; } new_ap = 1; } merge_byte_arrays(ap->supported_rates, WLAN_SUPP_RATES_MAX, elems->supp_rates, elems->supp_rates_len, elems->ext_supp_rates, elems->ext_supp_rates_len); if (elems->erp_info && elems->erp_info_len == 1) ap->erp = elems->erp_info[0]; else ap->erp = -1; if (elems->ds_params && elems->ds_params_len == 1) ap->channel = elems->ds_params[0]; else if (elems->ht_operation && elems->ht_operation_len >= 1) ap->channel = elems->ht_operation[0]; else if (fi) ap->channel = fi->channel; if (elems->ht_capabilities) ap->ht_support = 1; else ap->ht_support = 0; os_get_reltime(&ap->last_beacon); if (!new_ap && ap != iface->ap_list) { /* move AP entry into the beginning of the list so that the * oldest entry is always in the end of the list */ ap_ap_list_del(iface, ap); ap_ap_list_add(iface, ap); } if (!iface->olbc && ap_list_beacon_olbc(iface, ap)) { iface->olbc = 1; wpa_printf(MSG_DEBUG, "OLBC AP detected: " MACSTR " (channel %d) - enable protection", MAC2STR(ap->addr), ap->channel); set_beacon++; } #ifdef CONFIG_IEEE80211N if (!iface->olbc_ht && !ap->ht_support && (ap->channel == 0 || ap->channel == iface->conf->channel || ap->channel == iface->conf->channel + iface->conf->secondary_channel * 4)) { iface->olbc_ht = 1; hostapd_ht_operation_update(iface); wpa_printf(MSG_DEBUG, "OLBC HT AP detected: " MACSTR " (channel %d) - enable protection", MAC2STR(ap->addr), ap->channel); set_beacon++; } #endif /* CONFIG_IEEE80211N */ if (set_beacon) ieee802_11_update_beacons(iface); } static void ap_list_timer(void *eloop_ctx, void *timeout_ctx) { struct hostapd_iface *iface = eloop_ctx; struct os_reltime now; struct ap_info *ap; int set_beacon = 0; eloop_register_timeout(10, 0, ap_list_timer, iface, NULL); if (!iface->ap_list) return; os_get_reltime(&now); while (iface->ap_list) { ap = iface->ap_list->prev; if (!os_reltime_expired(&now, &ap->last_beacon, iface->conf->ap_table_expiration_time)) break; ap_free_ap(iface, ap); } if (iface->olbc || iface->olbc_ht) { int olbc = 0; int olbc_ht = 0; ap = iface->ap_list; while (ap && (olbc == 0 || olbc_ht == 0)) { if (ap_list_beacon_olbc(iface, ap)) olbc = 1; if (!ap->ht_support) olbc_ht = 1; ap = ap->next; } if (!olbc && iface->olbc) { wpa_printf(MSG_DEBUG, "OLBC not detected anymore"); iface->olbc = 0; set_beacon++; } #ifdef CONFIG_IEEE80211N if (!olbc_ht && iface->olbc_ht) { wpa_printf(MSG_DEBUG, "OLBC HT not detected anymore"); iface->olbc_ht = 0; hostapd_ht_operation_update(iface); set_beacon++; } #endif /* CONFIG_IEEE80211N */ } if (set_beacon) ieee802_11_update_beacons(iface); } int ap_list_init(struct hostapd_iface *iface) { eloop_register_timeout(10, 0, ap_list_timer, iface, NULL); return 0; } void ap_list_deinit(struct hostapd_iface *iface) { eloop_cancel_timeout(ap_list_timer, iface, NULL); hostapd_free_aps(iface); } wpa-2.1/src/ap/ap_list.h000066400000000000000000000026101227414666700151540ustar00rootroot00000000000000/* * hostapd / AP table * Copyright (c) 2002-2003, Jouni Malinen * Copyright (c) 2003-2004, Instant802 Networks, Inc. * Copyright (c) 2006, Devicescape Software, Inc. * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef AP_LIST_H #define AP_LIST_H struct ap_info { /* Note: next/prev pointers are updated whenever a new beacon is * received because these are used to find the least recently used * entries. */ struct ap_info *next; /* next entry in AP list */ struct ap_info *prev; /* previous entry in AP list */ struct ap_info *hnext; /* next entry in hash table list */ u8 addr[6]; u8 supported_rates[WLAN_SUPP_RATES_MAX]; int erp; /* ERP Info or -1 if ERP info element not present */ int channel; int ht_support; struct os_reltime last_beacon; }; struct ieee802_11_elems; struct hostapd_frame_info; void ap_list_process_beacon(struct hostapd_iface *iface, const struct ieee80211_mgmt *mgmt, struct ieee802_11_elems *elems, struct hostapd_frame_info *fi); #ifdef NEED_AP_MLME int ap_list_init(struct hostapd_iface *iface); void ap_list_deinit(struct hostapd_iface *iface); #else /* NEED_AP_MLME */ static inline int ap_list_init(struct hostapd_iface *iface) { return 0; } static inline void ap_list_deinit(struct hostapd_iface *iface) { } #endif /* NEED_AP_MLME */ #endif /* AP_LIST_H */ wpa-2.1/src/ap/ap_mlme.c000066400000000000000000000122031227414666700151250ustar00rootroot00000000000000/* * hostapd / IEEE 802.11 MLME * Copyright 2003-2006, Jouni Malinen * Copyright 2003-2004, Instant802 Networks, Inc. * Copyright 2005-2006, Devicescape Software, Inc. * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "utils/includes.h" #include "utils/common.h" #include "common/ieee802_11_defs.h" #include "ieee802_11.h" #include "wpa_auth.h" #include "sta_info.h" #include "ap_mlme.h" #ifndef CONFIG_NO_HOSTAPD_LOGGER static const char * mlme_auth_alg_str(int alg) { switch (alg) { case WLAN_AUTH_OPEN: return "OPEN_SYSTEM"; case WLAN_AUTH_SHARED_KEY: return "SHARED_KEY"; case WLAN_AUTH_FT: return "FT"; } return "unknown"; } #endif /* CONFIG_NO_HOSTAPD_LOGGER */ /** * mlme_authenticate_indication - Report the establishment of an authentication * relationship with a specific peer MAC entity * @hapd: BSS data * @sta: peer STA data * * MLME calls this function as a result of the establishment of an * authentication relationship with a specific peer MAC entity that * resulted from an authentication procedure that was initiated by * that specific peer MAC entity. * * PeerSTAAddress = sta->addr * AuthenticationType = sta->auth_alg (WLAN_AUTH_OPEN / WLAN_AUTH_SHARED_KEY) */ void mlme_authenticate_indication(struct hostapd_data *hapd, struct sta_info *sta) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_MLME, HOSTAPD_LEVEL_DEBUG, "MLME-AUTHENTICATE.indication(" MACSTR ", %s)", MAC2STR(sta->addr), mlme_auth_alg_str(sta->auth_alg)); if (sta->auth_alg != WLAN_AUTH_FT && !(sta->flags & WLAN_STA_MFP)) mlme_deletekeys_request(hapd, sta); } /** * mlme_deauthenticate_indication - Report the invalidation of an * authentication relationship with a specific peer MAC entity * @hapd: BSS data * @sta: Peer STA data * @reason_code: ReasonCode from Deauthentication frame * * MLME calls this function as a result of the invalidation of an * authentication relationship with a specific peer MAC entity. * * PeerSTAAddress = sta->addr */ void mlme_deauthenticate_indication(struct hostapd_data *hapd, struct sta_info *sta, u16 reason_code) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_MLME, HOSTAPD_LEVEL_DEBUG, "MLME-DEAUTHENTICATE.indication(" MACSTR ", %d)", MAC2STR(sta->addr), reason_code); mlme_deletekeys_request(hapd, sta); } /** * mlme_associate_indication - Report the establishment of an association with * a specific peer MAC entity * @hapd: BSS data * @sta: peer STA data * * MLME calls this function as a result of the establishment of an * association with a specific peer MAC entity that resulted from an * association procedure that was initiated by that specific peer MAC entity. * * PeerSTAAddress = sta->addr */ void mlme_associate_indication(struct hostapd_data *hapd, struct sta_info *sta) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_MLME, HOSTAPD_LEVEL_DEBUG, "MLME-ASSOCIATE.indication(" MACSTR ")", MAC2STR(sta->addr)); if (sta->auth_alg != WLAN_AUTH_FT) mlme_deletekeys_request(hapd, sta); } /** * mlme_reassociate_indication - Report the establishment of an reassociation * with a specific peer MAC entity * @hapd: BSS data * @sta: peer STA data * * MLME calls this function as a result of the establishment of an * reassociation with a specific peer MAC entity that resulted from a * reassociation procedure that was initiated by that specific peer MAC entity. * * PeerSTAAddress = sta->addr * * sta->previous_ap contains the "Current AP" information from ReassocReq. */ void mlme_reassociate_indication(struct hostapd_data *hapd, struct sta_info *sta) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_MLME, HOSTAPD_LEVEL_DEBUG, "MLME-REASSOCIATE.indication(" MACSTR ")", MAC2STR(sta->addr)); if (sta->auth_alg != WLAN_AUTH_FT) mlme_deletekeys_request(hapd, sta); } /** * mlme_disassociate_indication - Report disassociation with a specific peer * MAC entity * @hapd: BSS data * @sta: Peer STA data * @reason_code: ReasonCode from Disassociation frame * * MLME calls this function as a result of the invalidation of an association * relationship with a specific peer MAC entity. * * PeerSTAAddress = sta->addr */ void mlme_disassociate_indication(struct hostapd_data *hapd, struct sta_info *sta, u16 reason_code) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_MLME, HOSTAPD_LEVEL_DEBUG, "MLME-DISASSOCIATE.indication(" MACSTR ", %d)", MAC2STR(sta->addr), reason_code); mlme_deletekeys_request(hapd, sta); } void mlme_michaelmicfailure_indication(struct hostapd_data *hapd, const u8 *addr) { hostapd_logger(hapd, addr, HOSTAPD_MODULE_MLME, HOSTAPD_LEVEL_DEBUG, "MLME-MichaelMICFailure.indication(" MACSTR ")", MAC2STR(addr)); } void mlme_deletekeys_request(struct hostapd_data *hapd, struct sta_info *sta) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_MLME, HOSTAPD_LEVEL_DEBUG, "MLME-DELETEKEYS.request(" MACSTR ")", MAC2STR(sta->addr)); if (sta->wpa_sm) wpa_remove_ptk(sta->wpa_sm); } wpa-2.1/src/ap/ap_mlme.h000066400000000000000000000017571227414666700151460ustar00rootroot00000000000000/* * hostapd / IEEE 802.11 MLME * Copyright 2003, Jouni Malinen * Copyright 2003-2004, Instant802 Networks, Inc. * Copyright 2005-2006, Devicescape Software, Inc. * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef MLME_H #define MLME_H void mlme_authenticate_indication(struct hostapd_data *hapd, struct sta_info *sta); void mlme_deauthenticate_indication(struct hostapd_data *hapd, struct sta_info *sta, u16 reason_code); void mlme_associate_indication(struct hostapd_data *hapd, struct sta_info *sta); void mlme_reassociate_indication(struct hostapd_data *hapd, struct sta_info *sta); void mlme_disassociate_indication(struct hostapd_data *hapd, struct sta_info *sta, u16 reason_code); void mlme_michaelmicfailure_indication(struct hostapd_data *hapd, const u8 *addr); void mlme_deletekeys_request(struct hostapd_data *hapd, struct sta_info *sta); #endif /* MLME_H */ wpa-2.1/src/ap/authsrv.c000066400000000000000000000127571227414666700152250ustar00rootroot00000000000000/* * Authentication server setup * Copyright (c) 2002-2009, 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 "crypto/tls.h" #include "eap_server/eap.h" #include "eap_server/eap_sim_db.h" #include "eapol_auth/eapol_auth_sm.h" #include "radius/radius_server.h" #include "hostapd.h" #include "ap_config.h" #include "sta_info.h" #include "authsrv.h" #if defined(EAP_SERVER_SIM) || defined(EAP_SERVER_AKA) #define EAP_SIM_DB #endif /* EAP_SERVER_SIM || EAP_SERVER_AKA */ #ifdef EAP_SIM_DB static int hostapd_sim_db_cb_sta(struct hostapd_data *hapd, struct sta_info *sta, void *ctx) { if (eapol_auth_eap_pending_cb(sta->eapol_sm, ctx) == 0) return 1; return 0; } static void hostapd_sim_db_cb(void *ctx, void *session_ctx) { struct hostapd_data *hapd = ctx; if (ap_for_each_sta(hapd, hostapd_sim_db_cb_sta, session_ctx) == 0) { #ifdef RADIUS_SERVER radius_server_eap_pending_cb(hapd->radius_srv, session_ctx); #endif /* RADIUS_SERVER */ } } #endif /* EAP_SIM_DB */ #ifdef RADIUS_SERVER static int hostapd_radius_get_eap_user(void *ctx, const u8 *identity, size_t identity_len, int phase2, struct eap_user *user) { const struct hostapd_eap_user *eap_user; int i; eap_user = hostapd_get_eap_user(ctx, identity, identity_len, phase2); if (eap_user == NULL) return -1; if (user == NULL) return 0; os_memset(user, 0, sizeof(*user)); for (i = 0; i < EAP_MAX_METHODS; i++) { user->methods[i].vendor = eap_user->methods[i].vendor; user->methods[i].method = eap_user->methods[i].method; } if (eap_user->password) { user->password = os_malloc(eap_user->password_len); if (user->password == NULL) return -1; os_memcpy(user->password, eap_user->password, eap_user->password_len); user->password_len = eap_user->password_len; user->password_hash = eap_user->password_hash; } user->force_version = eap_user->force_version; user->ttls_auth = eap_user->ttls_auth; return 0; } static int hostapd_setup_radius_srv(struct hostapd_data *hapd) { struct radius_server_conf srv; struct hostapd_bss_config *conf = hapd->conf; os_memset(&srv, 0, sizeof(srv)); srv.client_file = conf->radius_server_clients; srv.auth_port = conf->radius_server_auth_port; srv.conf_ctx = hapd; srv.eap_sim_db_priv = hapd->eap_sim_db_priv; srv.ssl_ctx = hapd->ssl_ctx; srv.msg_ctx = hapd->msg_ctx; srv.pac_opaque_encr_key = conf->pac_opaque_encr_key; srv.eap_fast_a_id = conf->eap_fast_a_id; srv.eap_fast_a_id_len = conf->eap_fast_a_id_len; srv.eap_fast_a_id_info = conf->eap_fast_a_id_info; srv.eap_fast_prov = conf->eap_fast_prov; srv.pac_key_lifetime = conf->pac_key_lifetime; srv.pac_key_refresh_time = conf->pac_key_refresh_time; srv.eap_sim_aka_result_ind = conf->eap_sim_aka_result_ind; srv.tnc = conf->tnc; srv.wps = hapd->wps; srv.ipv6 = conf->radius_server_ipv6; srv.get_eap_user = hostapd_radius_get_eap_user; srv.eap_req_id_text = conf->eap_req_id_text; srv.eap_req_id_text_len = conf->eap_req_id_text_len; srv.pwd_group = conf->pwd_group; srv.server_id = conf->server_id ? conf->server_id : "hostapd"; #ifdef CONFIG_RADIUS_TEST srv.dump_msk_file = conf->dump_msk_file; #endif /* CONFIG_RADIUS_TEST */ hapd->radius_srv = radius_server_init(&srv); if (hapd->radius_srv == NULL) { wpa_printf(MSG_ERROR, "RADIUS server initialization failed."); return -1; } return 0; } #endif /* RADIUS_SERVER */ int authsrv_init(struct hostapd_data *hapd) { #ifdef EAP_TLS_FUNCS if (hapd->conf->eap_server && (hapd->conf->ca_cert || hapd->conf->server_cert || hapd->conf->private_key || hapd->conf->dh_file)) { struct tls_connection_params params; hapd->ssl_ctx = tls_init(NULL); if (hapd->ssl_ctx == NULL) { wpa_printf(MSG_ERROR, "Failed to initialize TLS"); authsrv_deinit(hapd); return -1; } os_memset(¶ms, 0, sizeof(params)); params.ca_cert = hapd->conf->ca_cert; params.client_cert = hapd->conf->server_cert; params.private_key = hapd->conf->private_key; params.private_key_passwd = hapd->conf->private_key_passwd; params.dh_file = hapd->conf->dh_file; params.ocsp_stapling_response = hapd->conf->ocsp_stapling_response; if (tls_global_set_params(hapd->ssl_ctx, ¶ms)) { wpa_printf(MSG_ERROR, "Failed to set TLS parameters"); authsrv_deinit(hapd); return -1; } if (tls_global_set_verify(hapd->ssl_ctx, hapd->conf->check_crl)) { wpa_printf(MSG_ERROR, "Failed to enable check_crl"); authsrv_deinit(hapd); return -1; } } #endif /* EAP_TLS_FUNCS */ #ifdef EAP_SIM_DB if (hapd->conf->eap_sim_db) { hapd->eap_sim_db_priv = eap_sim_db_init(hapd->conf->eap_sim_db, hostapd_sim_db_cb, hapd); if (hapd->eap_sim_db_priv == NULL) { wpa_printf(MSG_ERROR, "Failed to initialize EAP-SIM " "database interface"); authsrv_deinit(hapd); return -1; } } #endif /* EAP_SIM_DB */ #ifdef RADIUS_SERVER if (hapd->conf->radius_server_clients && hostapd_setup_radius_srv(hapd)) return -1; #endif /* RADIUS_SERVER */ return 0; } void authsrv_deinit(struct hostapd_data *hapd) { #ifdef RADIUS_SERVER radius_server_deinit(hapd->radius_srv); hapd->radius_srv = NULL; #endif /* RADIUS_SERVER */ #ifdef EAP_TLS_FUNCS if (hapd->ssl_ctx) { tls_deinit(hapd->ssl_ctx); hapd->ssl_ctx = NULL; } #endif /* EAP_TLS_FUNCS */ #ifdef EAP_SIM_DB if (hapd->eap_sim_db_priv) { eap_sim_db_deinit(hapd->eap_sim_db_priv); hapd->eap_sim_db_priv = NULL; } #endif /* EAP_SIM_DB */ } wpa-2.1/src/ap/authsrv.h000066400000000000000000000005401227414666700152150ustar00rootroot00000000000000/* * Authentication server setup * Copyright (c) 2002-2009, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef AUTHSRV_H #define AUTHSRV_H int authsrv_init(struct hostapd_data *hapd); void authsrv_deinit(struct hostapd_data *hapd); #endif /* AUTHSRV_H */ wpa-2.1/src/ap/beacon.c000066400000000000000000000614431227414666700147540ustar00rootroot00000000000000/* * hostapd / IEEE 802.11 Management: Beacon and Probe Request/Response * Copyright (c) 2002-2004, Instant802 Networks, Inc. * Copyright (c) 2005-2006, Devicescape Software, Inc. * Copyright (c) 2008-2012, 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 "utils/common.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" #include "wps/wps_defs.h" #include "p2p/p2p.h" #include "hostapd.h" #include "ieee802_11.h" #include "wpa_auth.h" #include "wmm.h" #include "ap_config.h" #include "sta_info.h" #include "p2p_hostapd.h" #include "ap_drv_ops.h" #include "beacon.h" #include "hs20.h" #ifdef NEED_AP_MLME static u8 * hostapd_eid_bss_load(struct hostapd_data *hapd, u8 *eid, size_t len) { #ifdef CONFIG_TESTING_OPTIONS if (hapd->conf->bss_load_test_set) { if (2 + 5 > len) return eid; *eid++ = WLAN_EID_BSS_LOAD; *eid++ = 5; os_memcpy(eid, hapd->conf->bss_load_test, 5); eid += 5; } #endif /* CONFIG_TESTING_OPTIONS */ return eid; } static u8 ieee802_11_erp_info(struct hostapd_data *hapd) { u8 erp = 0; if (hapd->iface->current_mode == NULL || hapd->iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G) return 0; if (hapd->iface->olbc) erp |= ERP_INFO_USE_PROTECTION; if (hapd->iface->num_sta_non_erp > 0) { erp |= ERP_INFO_NON_ERP_PRESENT | ERP_INFO_USE_PROTECTION; } if (hapd->iface->num_sta_no_short_preamble > 0 || hapd->iconf->preamble == LONG_PREAMBLE) erp |= ERP_INFO_BARKER_PREAMBLE_MODE; return erp; } static u8 * hostapd_eid_ds_params(struct hostapd_data *hapd, u8 *eid) { *eid++ = WLAN_EID_DS_PARAMS; *eid++ = 1; *eid++ = hapd->iconf->channel; return eid; } static u8 * hostapd_eid_erp_info(struct hostapd_data *hapd, u8 *eid) { if (hapd->iface->current_mode == NULL || hapd->iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G) return eid; /* Set NonERP_present and use_protection bits if there * are any associated NonERP stations. */ /* TODO: use_protection bit can be set to zero even if * there are NonERP stations present. This optimization * might be useful if NonERP stations are "quiet". * See 802.11g/D6 E-1 for recommended practice. * In addition, Non ERP present might be set, if AP detects Non ERP * operation on other APs. */ /* Add ERP Information element */ *eid++ = WLAN_EID_ERP_INFO; *eid++ = 1; *eid++ = ieee802_11_erp_info(hapd); return eid; } static u8 * hostapd_eid_country_add(u8 *pos, u8 *end, int chan_spacing, struct hostapd_channel_data *start, struct hostapd_channel_data *prev) { if (end - pos < 3) return pos; /* first channel number */ *pos++ = start->chan; /* number of channels */ *pos++ = (prev->chan - start->chan) / chan_spacing + 1; /* maximum transmit power level */ *pos++ = start->max_tx_power; return pos; } static u8 * hostapd_eid_country(struct hostapd_data *hapd, u8 *eid, int max_len) { u8 *pos = eid; u8 *end = eid + max_len; int i; struct hostapd_hw_modes *mode; struct hostapd_channel_data *start, *prev; int chan_spacing = 1; if (!hapd->iconf->ieee80211d || max_len < 6 || hapd->iface->current_mode == NULL) return eid; *pos++ = WLAN_EID_COUNTRY; pos++; /* length will be set later */ os_memcpy(pos, hapd->iconf->country, 3); /* e.g., 'US ' */ pos += 3; mode = hapd->iface->current_mode; if (mode->mode == HOSTAPD_MODE_IEEE80211A) chan_spacing = 4; start = prev = NULL; for (i = 0; i < mode->num_channels; i++) { struct hostapd_channel_data *chan = &mode->channels[i]; if (chan->flag & HOSTAPD_CHAN_DISABLED) continue; if (start && prev && prev->chan + chan_spacing == chan->chan && start->max_tx_power == chan->max_tx_power) { prev = chan; continue; /* can use same entry */ } if (start) { pos = hostapd_eid_country_add(pos, end, chan_spacing, start, prev); start = NULL; } /* Start new group */ start = prev = chan; } if (start) { pos = hostapd_eid_country_add(pos, end, chan_spacing, start, prev); } if ((pos - eid) & 1) { if (end - pos < 1) return eid; *pos++ = 0; /* pad for 16-bit alignment */ } eid[1] = (pos - eid) - 2; return pos; } static u8 * hostapd_eid_wpa(struct hostapd_data *hapd, u8 *eid, size_t len) { const u8 *ie; size_t ielen; ie = wpa_auth_get_wpa_ie(hapd->wpa_auth, &ielen); if (ie == NULL || ielen > len) return eid; os_memcpy(eid, ie, ielen); return eid + ielen; } static u8 * hostapd_eid_csa(struct hostapd_data *hapd, u8 *eid) { u8 chan; if (!hapd->iface->cs_freq_params.freq) return eid; if (ieee80211_freq_to_chan(hapd->iface->cs_freq_params.freq, &chan) == NUM_HOSTAPD_MODES) return eid; *eid++ = WLAN_EID_CHANNEL_SWITCH; *eid++ = 3; *eid++ = hapd->iface->cs_block_tx; *eid++ = chan; *eid++ = hapd->iface->cs_count; return eid; } static u8 * hostapd_eid_secondary_channel(struct hostapd_data *hapd, u8 *eid) { u8 sec_ch; if (!hapd->iface->cs_freq_params.sec_channel_offset) return eid; if (hapd->iface->cs_freq_params.sec_channel_offset == -1) sec_ch = HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW; else if (hapd->iface->cs_freq_params.sec_channel_offset == 1) sec_ch = HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE; else return eid; *eid++ = WLAN_EID_SECONDARY_CHANNEL_OFFSET; *eid++ = 1; *eid++ = sec_ch; return eid; } static u8 * hostapd_add_csa_elems(struct hostapd_data *hapd, u8 *pos, u8 *start, unsigned int *csa_counter_off) { u8 *old_pos = pos; if (!csa_counter_off) return pos; *csa_counter_off = 0; pos = hostapd_eid_csa(hapd, pos); if (pos != old_pos) { /* save an offset to the counter - should be last byte */ *csa_counter_off = pos - start - 1; pos = hostapd_eid_secondary_channel(hapd, pos); } return pos; } static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd, struct sta_info *sta, const struct ieee80211_mgmt *req, int is_p2p, size_t *resp_len) { struct ieee80211_mgmt *resp; u8 *pos, *epos; size_t buflen; #define MAX_PROBERESP_LEN 768 buflen = MAX_PROBERESP_LEN; #ifdef CONFIG_WPS if (hapd->wps_probe_resp_ie) buflen += wpabuf_len(hapd->wps_probe_resp_ie); #endif /* CONFIG_WPS */ #ifdef CONFIG_P2P if (hapd->p2p_probe_resp_ie) buflen += wpabuf_len(hapd->p2p_probe_resp_ie); #endif /* CONFIG_P2P */ if (hapd->conf->vendor_elements) buflen += wpabuf_len(hapd->conf->vendor_elements); resp = os_zalloc(buflen); if (resp == NULL) return NULL; epos = ((u8 *) resp) + MAX_PROBERESP_LEN; resp->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_PROBE_RESP); if (req) os_memcpy(resp->da, req->sa, ETH_ALEN); os_memcpy(resp->sa, hapd->own_addr, ETH_ALEN); os_memcpy(resp->bssid, hapd->own_addr, ETH_ALEN); resp->u.probe_resp.beacon_int = host_to_le16(hapd->iconf->beacon_int); /* hardware or low-level driver will setup seq_ctrl and timestamp */ resp->u.probe_resp.capab_info = host_to_le16(hostapd_own_capab_info(hapd, sta, 1)); pos = resp->u.probe_resp.variable; *pos++ = WLAN_EID_SSID; *pos++ = hapd->conf->ssid.ssid_len; os_memcpy(pos, hapd->conf->ssid.ssid, hapd->conf->ssid.ssid_len); pos += hapd->conf->ssid.ssid_len; /* Supported rates */ pos = hostapd_eid_supp_rates(hapd, pos); /* DS Params */ pos = hostapd_eid_ds_params(hapd, pos); pos = hostapd_eid_country(hapd, pos, epos - pos); /* ERP Information element */ pos = hostapd_eid_erp_info(hapd, pos); /* Extended supported rates */ pos = hostapd_eid_ext_supp_rates(hapd, pos); /* RSN, MDIE, WPA */ pos = hostapd_eid_wpa(hapd, pos, epos - pos); pos = hostapd_eid_bss_load(hapd, pos, epos - pos); #ifdef CONFIG_IEEE80211N pos = hostapd_eid_ht_capabilities(hapd, pos); pos = hostapd_eid_ht_operation(hapd, pos); #endif /* CONFIG_IEEE80211N */ pos = hostapd_eid_ext_capab(hapd, pos); pos = hostapd_eid_time_adv(hapd, pos); pos = hostapd_eid_time_zone(hapd, pos); pos = hostapd_eid_interworking(hapd, pos); pos = hostapd_eid_adv_proto(hapd, pos); pos = hostapd_eid_roaming_consortium(hapd, pos); pos = hostapd_add_csa_elems(hapd, pos, (u8 *)resp, &hapd->iface->cs_c_off_proberesp); #ifdef CONFIG_IEEE80211AC pos = hostapd_eid_vht_capabilities(hapd, pos); pos = hostapd_eid_vht_operation(hapd, pos); #endif /* CONFIG_IEEE80211AC */ /* Wi-Fi Alliance WMM */ pos = hostapd_eid_wmm(hapd, pos); #ifdef CONFIG_WPS if (hapd->conf->wps_state && hapd->wps_probe_resp_ie) { os_memcpy(pos, wpabuf_head(hapd->wps_probe_resp_ie), wpabuf_len(hapd->wps_probe_resp_ie)); pos += wpabuf_len(hapd->wps_probe_resp_ie); } #endif /* CONFIG_WPS */ #ifdef CONFIG_P2P if ((hapd->conf->p2p & P2P_ENABLED) && is_p2p && hapd->p2p_probe_resp_ie) { os_memcpy(pos, wpabuf_head(hapd->p2p_probe_resp_ie), wpabuf_len(hapd->p2p_probe_resp_ie)); pos += wpabuf_len(hapd->p2p_probe_resp_ie); } #endif /* CONFIG_P2P */ #ifdef CONFIG_P2P_MANAGER if ((hapd->conf->p2p & (P2P_MANAGE | P2P_ENABLED | P2P_GROUP_OWNER)) == P2P_MANAGE) pos = hostapd_eid_p2p_manage(hapd, pos); #endif /* CONFIG_P2P_MANAGER */ #ifdef CONFIG_HS20 pos = hostapd_eid_hs20_indication(hapd, pos); #endif /* CONFIG_HS20 */ if (hapd->conf->vendor_elements) { os_memcpy(pos, wpabuf_head(hapd->conf->vendor_elements), wpabuf_len(hapd->conf->vendor_elements)); pos += wpabuf_len(hapd->conf->vendor_elements); } *resp_len = pos - (u8 *) resp; return (u8 *) resp; } enum ssid_match_result { NO_SSID_MATCH, EXACT_SSID_MATCH, WILDCARD_SSID_MATCH }; static enum ssid_match_result ssid_match(struct hostapd_data *hapd, const u8 *ssid, size_t ssid_len, const u8 *ssid_list, size_t ssid_list_len) { const u8 *pos, *end; int wildcard = 0; if (ssid_len == 0) wildcard = 1; if (ssid_len == hapd->conf->ssid.ssid_len && os_memcmp(ssid, hapd->conf->ssid.ssid, ssid_len) == 0) return EXACT_SSID_MATCH; if (ssid_list == NULL) return wildcard ? WILDCARD_SSID_MATCH : NO_SSID_MATCH; pos = ssid_list; end = ssid_list + ssid_list_len; while (pos + 1 <= end) { if (pos + 2 + pos[1] > end) break; if (pos[1] == 0) wildcard = 1; if (pos[1] == hapd->conf->ssid.ssid_len && os_memcmp(pos + 2, hapd->conf->ssid.ssid, pos[1]) == 0) return EXACT_SSID_MATCH; pos += 2 + pos[1]; } return wildcard ? WILDCARD_SSID_MATCH : NO_SSID_MATCH; } void handle_probe_req(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, size_t len, int ssi_signal) { u8 *resp; struct ieee802_11_elems elems; const u8 *ie; size_t ie_len; struct sta_info *sta = NULL; size_t i, resp_len; int noack; enum ssid_match_result res; ie = mgmt->u.probe_req.variable; if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req)) return; ie_len = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req)); for (i = 0; hapd->probereq_cb && i < hapd->num_probereq_cb; i++) if (hapd->probereq_cb[i].cb(hapd->probereq_cb[i].ctx, mgmt->sa, mgmt->da, mgmt->bssid, ie, ie_len, ssi_signal) > 0) return; if (!hapd->iconf->send_probe_response) return; if (ieee802_11_parse_elems(ie, ie_len, &elems, 0) == ParseFailed) { wpa_printf(MSG_DEBUG, "Could not parse ProbeReq from " MACSTR, MAC2STR(mgmt->sa)); return; } if ((!elems.ssid || !elems.supp_rates)) { wpa_printf(MSG_DEBUG, "STA " MACSTR " sent probe request " "without SSID or supported rates element", MAC2STR(mgmt->sa)); return; } #ifdef CONFIG_P2P if (hapd->p2p && elems.wps_ie) { struct wpabuf *wps; wps = ieee802_11_vendor_ie_concat(ie, ie_len, WPS_DEV_OUI_WFA); if (wps && !p2p_group_match_dev_type(hapd->p2p_group, wps)) { wpa_printf(MSG_MSGDUMP, "P2P: Ignore Probe Request " "due to mismatch with Requested Device " "Type"); wpabuf_free(wps); return; } wpabuf_free(wps); } if (hapd->p2p && elems.p2p) { struct wpabuf *p2p; p2p = ieee802_11_vendor_ie_concat(ie, ie_len, P2P_IE_VENDOR_TYPE); if (p2p && !p2p_group_match_dev_id(hapd->p2p_group, p2p)) { wpa_printf(MSG_MSGDUMP, "P2P: Ignore Probe Request " "due to mismatch with Device ID"); wpabuf_free(p2p); return; } wpabuf_free(p2p); } #endif /* CONFIG_P2P */ if (hapd->conf->ignore_broadcast_ssid && elems.ssid_len == 0 && elems.ssid_list_len == 0) { wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR " for " "broadcast SSID ignored", MAC2STR(mgmt->sa)); return; } sta = ap_get_sta(hapd, mgmt->sa); #ifdef CONFIG_P2P if ((hapd->conf->p2p & P2P_GROUP_OWNER) && elems.ssid_len == P2P_WILDCARD_SSID_LEN && os_memcmp(elems.ssid, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN) == 0) { /* Process P2P Wildcard SSID like Wildcard SSID */ elems.ssid_len = 0; } #endif /* CONFIG_P2P */ res = ssid_match(hapd, elems.ssid, elems.ssid_len, elems.ssid_list, elems.ssid_list_len); if (res != NO_SSID_MATCH) { if (sta) sta->ssid_probe = &hapd->conf->ssid; } else { if (!(mgmt->da[0] & 0x01)) { char ssid_txt[33]; ieee802_11_print_ssid(ssid_txt, elems.ssid, elems.ssid_len); wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR " for foreign SSID '%s' (DA " MACSTR ")%s", MAC2STR(mgmt->sa), ssid_txt, MAC2STR(mgmt->da), elems.ssid_list ? " (SSID list)" : ""); } return; } #ifdef CONFIG_INTERWORKING if (elems.interworking && elems.interworking_len >= 1) { u8 ant = elems.interworking[0] & 0x0f; if (ant != INTERWORKING_ANT_WILDCARD && ant != hapd->conf->access_network_type) { wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR " for mismatching ANT %u ignored", MAC2STR(mgmt->sa), ant); return; } } if (elems.interworking && (elems.interworking_len == 7 || elems.interworking_len == 9)) { const u8 *hessid; if (elems.interworking_len == 7) hessid = elems.interworking + 1; else hessid = elems.interworking + 1 + 2; if (!is_broadcast_ether_addr(hessid) && os_memcmp(hessid, hapd->conf->hessid, ETH_ALEN) != 0) { wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR " for mismatching HESSID " MACSTR " ignored", MAC2STR(mgmt->sa), MAC2STR(hessid)); return; } } #endif /* CONFIG_INTERWORKING */ #ifdef CONFIG_P2P if ((hapd->conf->p2p & P2P_GROUP_OWNER) && supp_rates_11b_only(&elems)) { /* Indicates support for 11b rates only */ wpa_printf(MSG_EXCESSIVE, "P2P: Ignore Probe Request from " MACSTR " with only 802.11b rates", MAC2STR(mgmt->sa)); return; } #endif /* CONFIG_P2P */ /* TODO: verify that supp_rates contains at least one matching rate * with AP configuration */ #ifdef CONFIG_TESTING_OPTIONS if (hapd->iconf->ignore_probe_probability > 0.0d && drand48() < hapd->iconf->ignore_probe_probability) { wpa_printf(MSG_INFO, "TESTING: ignoring probe request from " MACSTR, MAC2STR(mgmt->sa)); return; } #endif /* CONFIG_TESTING_OPTIONS */ resp = hostapd_gen_probe_resp(hapd, sta, mgmt, elems.p2p != NULL, &resp_len); if (resp == NULL) return; /* * If this is a broadcast probe request, apply no ack policy to avoid * excessive retries. */ noack = !!(res == WILDCARD_SSID_MATCH && is_broadcast_ether_addr(mgmt->da)); if (hostapd_drv_send_mlme(hapd, resp, resp_len, noack) < 0) wpa_printf(MSG_INFO, "handle_probe_req: send failed"); os_free(resp); wpa_printf(MSG_EXCESSIVE, "STA " MACSTR " sent probe request for %s " "SSID", MAC2STR(mgmt->sa), elems.ssid_len == 0 ? "broadcast" : "our"); } static u8 * hostapd_probe_resp_offloads(struct hostapd_data *hapd, size_t *resp_len) { /* check probe response offloading caps and print warnings */ if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_PROBE_RESP_OFFLOAD)) return NULL; #ifdef CONFIG_WPS if (hapd->conf->wps_state && hapd->wps_probe_resp_ie && (!(hapd->iface->probe_resp_offloads & (WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS | WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS2)))) wpa_printf(MSG_WARNING, "Device is trying to offload WPS " "Probe Response while not supporting this"); #endif /* CONFIG_WPS */ #ifdef CONFIG_P2P if ((hapd->conf->p2p & P2P_ENABLED) && hapd->p2p_probe_resp_ie && !(hapd->iface->probe_resp_offloads & WPA_DRIVER_PROBE_RESP_OFFLOAD_P2P)) wpa_printf(MSG_WARNING, "Device is trying to offload P2P " "Probe Response while not supporting this"); #endif /* CONFIG_P2P */ if (hapd->conf->interworking && !(hapd->iface->probe_resp_offloads & WPA_DRIVER_PROBE_RESP_OFFLOAD_INTERWORKING)) wpa_printf(MSG_WARNING, "Device is trying to offload " "Interworking Probe Response while not supporting " "this"); /* Generate a Probe Response template for the non-P2P case */ return hostapd_gen_probe_resp(hapd, NULL, NULL, 0, resp_len); } #endif /* NEED_AP_MLME */ int ieee802_11_build_ap_params(struct hostapd_data *hapd, struct wpa_driver_ap_params *params) { struct ieee80211_mgmt *head = NULL; u8 *tail = NULL; size_t head_len = 0, tail_len = 0; u8 *resp = NULL; size_t resp_len = 0; #ifdef NEED_AP_MLME u16 capab_info; u8 *pos, *tailpos; #define BEACON_HEAD_BUF_SIZE 256 #define BEACON_TAIL_BUF_SIZE 512 head = os_zalloc(BEACON_HEAD_BUF_SIZE); tail_len = BEACON_TAIL_BUF_SIZE; #ifdef CONFIG_WPS if (hapd->conf->wps_state && hapd->wps_beacon_ie) tail_len += wpabuf_len(hapd->wps_beacon_ie); #endif /* CONFIG_WPS */ #ifdef CONFIG_P2P if (hapd->p2p_beacon_ie) tail_len += wpabuf_len(hapd->p2p_beacon_ie); #endif /* CONFIG_P2P */ if (hapd->conf->vendor_elements) tail_len += wpabuf_len(hapd->conf->vendor_elements); tailpos = tail = os_malloc(tail_len); if (head == NULL || tail == NULL) { wpa_printf(MSG_ERROR, "Failed to set beacon data"); os_free(head); os_free(tail); return -1; } head->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_BEACON); head->duration = host_to_le16(0); os_memset(head->da, 0xff, ETH_ALEN); os_memcpy(head->sa, hapd->own_addr, ETH_ALEN); os_memcpy(head->bssid, hapd->own_addr, ETH_ALEN); head->u.beacon.beacon_int = host_to_le16(hapd->iconf->beacon_int); /* hardware or low-level driver will setup seq_ctrl and timestamp */ capab_info = hostapd_own_capab_info(hapd, NULL, 0); head->u.beacon.capab_info = host_to_le16(capab_info); pos = &head->u.beacon.variable[0]; /* SSID */ *pos++ = WLAN_EID_SSID; if (hapd->conf->ignore_broadcast_ssid == 2) { /* clear the data, but keep the correct length of the SSID */ *pos++ = hapd->conf->ssid.ssid_len; os_memset(pos, 0, hapd->conf->ssid.ssid_len); pos += hapd->conf->ssid.ssid_len; } else if (hapd->conf->ignore_broadcast_ssid) { *pos++ = 0; /* empty SSID */ } else { *pos++ = hapd->conf->ssid.ssid_len; os_memcpy(pos, hapd->conf->ssid.ssid, hapd->conf->ssid.ssid_len); pos += hapd->conf->ssid.ssid_len; } /* Supported rates */ pos = hostapd_eid_supp_rates(hapd, pos); /* DS Params */ pos = hostapd_eid_ds_params(hapd, pos); head_len = pos - (u8 *) head; tailpos = hostapd_eid_country(hapd, tailpos, tail + BEACON_TAIL_BUF_SIZE - tailpos); /* ERP Information element */ tailpos = hostapd_eid_erp_info(hapd, tailpos); /* Extended supported rates */ tailpos = hostapd_eid_ext_supp_rates(hapd, tailpos); /* RSN, MDIE, WPA */ tailpos = hostapd_eid_wpa(hapd, tailpos, tail + BEACON_TAIL_BUF_SIZE - tailpos); tailpos = hostapd_eid_bss_load(hapd, tailpos, tail + BEACON_TAIL_BUF_SIZE - tailpos); #ifdef CONFIG_IEEE80211N tailpos = hostapd_eid_ht_capabilities(hapd, tailpos); tailpos = hostapd_eid_ht_operation(hapd, tailpos); #endif /* CONFIG_IEEE80211N */ tailpos = hostapd_eid_ext_capab(hapd, tailpos); /* * TODO: Time Advertisement element should only be included in some * DTIM Beacon frames. */ tailpos = hostapd_eid_time_adv(hapd, tailpos); tailpos = hostapd_eid_interworking(hapd, tailpos); tailpos = hostapd_eid_adv_proto(hapd, tailpos); tailpos = hostapd_eid_roaming_consortium(hapd, tailpos); tailpos = hostapd_add_csa_elems(hapd, tailpos, tail, &hapd->iface->cs_c_off_beacon); #ifdef CONFIG_IEEE80211AC tailpos = hostapd_eid_vht_capabilities(hapd, tailpos); tailpos = hostapd_eid_vht_operation(hapd, tailpos); #endif /* CONFIG_IEEE80211AC */ /* Wi-Fi Alliance WMM */ tailpos = hostapd_eid_wmm(hapd, tailpos); #ifdef CONFIG_WPS if (hapd->conf->wps_state && hapd->wps_beacon_ie) { os_memcpy(tailpos, wpabuf_head(hapd->wps_beacon_ie), wpabuf_len(hapd->wps_beacon_ie)); tailpos += wpabuf_len(hapd->wps_beacon_ie); } #endif /* CONFIG_WPS */ #ifdef CONFIG_P2P if ((hapd->conf->p2p & P2P_ENABLED) && hapd->p2p_beacon_ie) { os_memcpy(tailpos, wpabuf_head(hapd->p2p_beacon_ie), wpabuf_len(hapd->p2p_beacon_ie)); tailpos += wpabuf_len(hapd->p2p_beacon_ie); } #endif /* CONFIG_P2P */ #ifdef CONFIG_P2P_MANAGER if ((hapd->conf->p2p & (P2P_MANAGE | P2P_ENABLED | P2P_GROUP_OWNER)) == P2P_MANAGE) tailpos = hostapd_eid_p2p_manage(hapd, tailpos); #endif /* CONFIG_P2P_MANAGER */ #ifdef CONFIG_HS20 tailpos = hostapd_eid_hs20_indication(hapd, tailpos); #endif /* CONFIG_HS20 */ if (hapd->conf->vendor_elements) { os_memcpy(tailpos, wpabuf_head(hapd->conf->vendor_elements), wpabuf_len(hapd->conf->vendor_elements)); tailpos += wpabuf_len(hapd->conf->vendor_elements); } tail_len = tailpos > tail ? tailpos - tail : 0; resp = hostapd_probe_resp_offloads(hapd, &resp_len); #endif /* NEED_AP_MLME */ os_memset(params, 0, sizeof(*params)); params->head = (u8 *) head; params->head_len = head_len; params->tail = tail; params->tail_len = tail_len; params->proberesp = resp; params->proberesp_len = resp_len; params->dtim_period = hapd->conf->dtim_period; params->beacon_int = hapd->iconf->beacon_int; params->basic_rates = hapd->iface->basic_rates; params->ssid = hapd->conf->ssid.ssid; params->ssid_len = hapd->conf->ssid.ssid_len; params->pairwise_ciphers = hapd->conf->rsn_pairwise ? hapd->conf->rsn_pairwise : hapd->conf->wpa_pairwise; params->group_cipher = hapd->conf->wpa_group; params->key_mgmt_suites = hapd->conf->wpa_key_mgmt; params->auth_algs = hapd->conf->auth_algs; params->wpa_version = hapd->conf->wpa; params->privacy = hapd->conf->ssid.wep.keys_set || hapd->conf->wpa || (hapd->conf->ieee802_1x && (hapd->conf->default_wep_key_len || hapd->conf->individual_wep_key_len)); switch (hapd->conf->ignore_broadcast_ssid) { case 0: params->hide_ssid = NO_SSID_HIDING; break; case 1: params->hide_ssid = HIDDEN_SSID_ZERO_LEN; break; case 2: params->hide_ssid = HIDDEN_SSID_ZERO_CONTENTS; break; } params->isolate = hapd->conf->isolate; #ifdef NEED_AP_MLME params->cts_protect = !!(ieee802_11_erp_info(hapd) & ERP_INFO_USE_PROTECTION); params->preamble = hapd->iface->num_sta_no_short_preamble == 0 && hapd->iconf->preamble == SHORT_PREAMBLE; if (hapd->iface->current_mode && hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G) params->short_slot_time = hapd->iface->num_sta_no_short_slot_time > 0 ? 0 : 1; else params->short_slot_time = -1; if (!hapd->iconf->ieee80211n || hapd->conf->disable_11n) params->ht_opmode = -1; else params->ht_opmode = hapd->iface->ht_op_mode; #endif /* NEED_AP_MLME */ params->interworking = hapd->conf->interworking; if (hapd->conf->interworking && !is_zero_ether_addr(hapd->conf->hessid)) params->hessid = hapd->conf->hessid; params->access_network_type = hapd->conf->access_network_type; params->ap_max_inactivity = hapd->conf->ap_max_inactivity; #ifdef CONFIG_HS20 params->disable_dgaf = hapd->conf->disable_dgaf; #endif /* CONFIG_HS20 */ return 0; } void ieee802_11_free_ap_params(struct wpa_driver_ap_params *params) { os_free(params->tail); params->tail = NULL; os_free(params->head); params->head = NULL; os_free(params->proberesp); params->proberesp = NULL; } int ieee802_11_set_beacon(struct hostapd_data *hapd) { struct wpa_driver_ap_params params; struct wpabuf *beacon, *proberesp, *assocresp; int res, ret = -1; if (hapd->iface->csa_in_progress) { wpa_printf(MSG_ERROR, "Cannot set beacons during CSA period"); return -1; } hapd->beacon_set_done = 1; if (ieee802_11_build_ap_params(hapd, ¶ms) < 0) return -1; if (hostapd_build_ap_extra_ies(hapd, &beacon, &proberesp, &assocresp) < 0) goto fail; params.beacon_ies = beacon; params.proberesp_ies = proberesp; params.assocresp_ies = assocresp; res = hostapd_drv_set_ap(hapd, ¶ms); hostapd_free_ap_extra_ies(hapd, beacon, proberesp, assocresp); if (res) wpa_printf(MSG_ERROR, "Failed to set beacon parameters"); else ret = 0; fail: ieee802_11_free_ap_params(¶ms); return ret; } int ieee802_11_set_beacons(struct hostapd_iface *iface) { size_t i; int ret = 0; for (i = 0; i < iface->num_bss; i++) { if (iface->bss[i]->started && ieee802_11_set_beacon(iface->bss[i]) < 0) ret = -1; } return ret; } /* only update beacons if started */ int ieee802_11_update_beacons(struct hostapd_iface *iface) { size_t i; int ret = 0; for (i = 0; i < iface->num_bss; i++) { if (iface->bss[i]->beacon_set_done && iface->bss[i]->started && ieee802_11_set_beacon(iface->bss[i]) < 0) ret = -1; } return ret; } #endif /* CONFIG_NATIVE_WINDOWS */ wpa-2.1/src/ap/beacon.h000066400000000000000000000015241227414666700147530ustar00rootroot00000000000000/* * hostapd / IEEE 802.11 Management: Beacon and Probe Request/Response * Copyright (c) 2002-2004, Instant802 Networks, Inc. * Copyright (c) 2005-2006, Devicescape Software, Inc. * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef BEACON_H #define BEACON_H struct ieee80211_mgmt; void handle_probe_req(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, size_t len, int ssi_signal); int ieee802_11_set_beacon(struct hostapd_data *hapd); int ieee802_11_set_beacons(struct hostapd_iface *iface); int ieee802_11_update_beacons(struct hostapd_iface *iface); int ieee802_11_build_ap_params(struct hostapd_data *hapd, struct wpa_driver_ap_params *params); void ieee802_11_free_ap_params(struct wpa_driver_ap_params *params); #endif /* BEACON_H */ wpa-2.1/src/ap/ctrl_iface_ap.c000066400000000000000000000301561227414666700162750ustar00rootroot00000000000000/* * Control interface for shared AP commands * Copyright (c) 2004-2013, 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 "common/ieee802_11_defs.h" #include "eapol_auth/eapol_auth_sm.h" #include "hostapd.h" #include "ieee802_1x.h" #include "wpa_auth.h" #include "ieee802_11.h" #include "sta_info.h" #include "wps_hostapd.h" #include "p2p_hostapd.h" #include "ctrl_iface_ap.h" #include "ap_drv_ops.h" static int hostapd_get_sta_tx_rx(struct hostapd_data *hapd, struct sta_info *sta, char *buf, size_t buflen) { struct hostap_sta_driver_data data; int ret; if (hostapd_drv_read_sta_data(hapd, &data, sta->addr) < 0) return 0; ret = os_snprintf(buf, buflen, "rx_packets=%lu\ntx_packets=%lu\n" "rx_bytes=%lu\ntx_bytes=%lu\n", data.rx_packets, data.tx_packets, data.rx_bytes, data.tx_bytes); if (ret < 0 || (size_t) ret >= buflen) return 0; return ret; } static int hostapd_get_sta_conn_time(struct sta_info *sta, char *buf, size_t buflen) { struct os_reltime age; int ret; if (!sta->connected_time.sec) return 0; os_reltime_age(&sta->connected_time, &age); ret = os_snprintf(buf, buflen, "connected_time=%u\n", (unsigned int) age.sec); if (ret < 0 || (size_t) ret >= buflen) return 0; return ret; } static const char * timeout_next_str(int val) { switch (val) { case STA_NULLFUNC: return "NULLFUNC POLL"; case STA_DISASSOC: return "DISASSOC"; case STA_DEAUTH: return "DEAUTH"; case STA_REMOVE: return "REMOVE"; case STA_DISASSOC_FROM_CLI: return "DISASSOC_FROM_CLI"; } return "?"; } static int hostapd_ctrl_iface_sta_mib(struct hostapd_data *hapd, struct sta_info *sta, char *buf, size_t buflen) { int len, res, ret, i; if (!sta) return 0; len = 0; ret = os_snprintf(buf + len, buflen - len, MACSTR "\nflags=", MAC2STR(sta->addr)); if (ret < 0 || (size_t) ret >= buflen - len) return len; len += ret; ret = ap_sta_flags_txt(sta->flags, buf + len, buflen - len); if (ret < 0) return len; len += ret; ret = os_snprintf(buf + len, buflen - len, "\naid=%d\ncapability=0x%x\n" "listen_interval=%d\nsupported_rates=", sta->aid, sta->capability, sta->listen_interval); if (ret < 0 || (size_t) ret >= buflen - len) return len; len += ret; for (i = 0; i < sta->supported_rates_len; i++) { ret = os_snprintf(buf + len, buflen - len, "%02x%s", sta->supported_rates[i], i + 1 < sta->supported_rates_len ? " " : ""); if (ret < 0 || (size_t) ret >= buflen - len) return len; len += ret; } ret = os_snprintf(buf + len, buflen - len, "\ntimeout_next=%s\n", timeout_next_str(sta->timeout_next)); if (ret < 0 || (size_t) ret >= buflen - len) return len; len += ret; res = ieee802_11_get_mib_sta(hapd, sta, buf + len, buflen - len); if (res >= 0) len += res; res = wpa_get_mib_sta(sta->wpa_sm, buf + len, buflen - len); if (res >= 0) len += res; res = ieee802_1x_get_mib_sta(hapd, sta, buf + len, buflen - len); if (res >= 0) len += res; res = hostapd_wps_get_mib_sta(hapd, sta->addr, buf + len, buflen - len); if (res >= 0) len += res; res = hostapd_p2p_get_mib_sta(hapd, sta, buf + len, buflen - len); if (res >= 0) len += res; len += hostapd_get_sta_tx_rx(hapd, sta, buf + len, buflen - len); len += hostapd_get_sta_conn_time(sta, buf + len, buflen - len); return len; } int hostapd_ctrl_iface_sta_first(struct hostapd_data *hapd, char *buf, size_t buflen) { return hostapd_ctrl_iface_sta_mib(hapd, hapd->sta_list, buf, buflen); } int hostapd_ctrl_iface_sta(struct hostapd_data *hapd, const char *txtaddr, char *buf, size_t buflen) { u8 addr[ETH_ALEN]; int ret; const char *pos; struct sta_info *sta; if (hwaddr_aton(txtaddr, addr)) { ret = os_snprintf(buf, buflen, "FAIL\n"); if (ret < 0 || (size_t) ret >= buflen) return 0; return ret; } sta = ap_get_sta(hapd, addr); if (sta == NULL) return -1; pos = os_strchr(txtaddr, ' '); if (pos) { pos++; #ifdef HOSTAPD_DUMP_STATE if (os_strcmp(pos, "eapol") == 0) { if (sta->eapol_sm == NULL) return -1; return eapol_auth_dump_state(sta->eapol_sm, buf, buflen); } #endif /* HOSTAPD_DUMP_STATE */ return -1; } return hostapd_ctrl_iface_sta_mib(hapd, sta, buf, buflen); } int hostapd_ctrl_iface_sta_next(struct hostapd_data *hapd, const char *txtaddr, char *buf, size_t buflen) { u8 addr[ETH_ALEN]; struct sta_info *sta; int ret; if (hwaddr_aton(txtaddr, addr) || (sta = ap_get_sta(hapd, addr)) == NULL) { ret = os_snprintf(buf, buflen, "FAIL\n"); if (ret < 0 || (size_t) ret >= buflen) return 0; return ret; } if (!sta->next) return 0; return hostapd_ctrl_iface_sta_mib(hapd, sta->next, buf, buflen); } #ifdef CONFIG_P2P_MANAGER static int p2p_manager_disconnect(struct hostapd_data *hapd, u16 stype, u8 minor_reason_code, const u8 *addr) { struct ieee80211_mgmt *mgmt; int ret; u8 *pos; if (hapd->driver->send_frame == NULL) return -1; mgmt = os_zalloc(sizeof(*mgmt) + 100); if (mgmt == NULL) return -1; wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "P2P: Disconnect STA " MACSTR " with minor reason code %u (stype=%u)", MAC2STR(addr), minor_reason_code, stype); mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, stype); os_memcpy(mgmt->da, addr, ETH_ALEN); os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN); os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN); if (stype == WLAN_FC_STYPE_DEAUTH) { mgmt->u.deauth.reason_code = host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID); pos = (u8 *) (&mgmt->u.deauth.reason_code + 1); } else { mgmt->u.disassoc.reason_code = host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID); pos = (u8 *) (&mgmt->u.disassoc.reason_code + 1); } *pos++ = WLAN_EID_VENDOR_SPECIFIC; *pos++ = 4 + 3 + 1; WPA_PUT_BE24(pos, OUI_WFA); pos += 3; *pos++ = P2P_OUI_TYPE; *pos++ = P2P_ATTR_MINOR_REASON_CODE; WPA_PUT_LE16(pos, 1); pos += 2; *pos++ = minor_reason_code; ret = hapd->driver->send_frame(hapd->drv_priv, (u8 *) mgmt, pos - (u8 *) mgmt, 1); os_free(mgmt); return ret < 0 ? -1 : 0; } #endif /* CONFIG_P2P_MANAGER */ int hostapd_ctrl_iface_deauthenticate(struct hostapd_data *hapd, const char *txtaddr) { u8 addr[ETH_ALEN]; struct sta_info *sta; const char *pos; u16 reason = WLAN_REASON_PREV_AUTH_NOT_VALID; wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE DEAUTHENTICATE %s", txtaddr); if (hwaddr_aton(txtaddr, addr)) return -1; pos = os_strstr(txtaddr, " test="); if (pos) { struct ieee80211_mgmt mgmt; int encrypt; if (hapd->driver->send_frame == NULL) return -1; pos += 6; encrypt = atoi(pos); os_memset(&mgmt, 0, sizeof(mgmt)); mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_DEAUTH); os_memcpy(mgmt.da, addr, ETH_ALEN); os_memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN); os_memcpy(mgmt.bssid, hapd->own_addr, ETH_ALEN); mgmt.u.deauth.reason_code = host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID); if (hapd->driver->send_frame(hapd->drv_priv, (u8 *) &mgmt, IEEE80211_HDRLEN + sizeof(mgmt.u.deauth), encrypt) < 0) return -1; return 0; } #ifdef CONFIG_P2P_MANAGER pos = os_strstr(txtaddr, " p2p="); if (pos) { return p2p_manager_disconnect(hapd, WLAN_FC_STYPE_DEAUTH, atoi(pos + 5), addr); } #endif /* CONFIG_P2P_MANAGER */ pos = os_strstr(txtaddr, " reason="); if (pos) reason = atoi(pos + 8); hostapd_drv_sta_deauth(hapd, addr, reason); sta = ap_get_sta(hapd, addr); if (sta) ap_sta_deauthenticate(hapd, sta, reason); else if (addr[0] == 0xff) hostapd_free_stas(hapd); return 0; } int hostapd_ctrl_iface_disassociate(struct hostapd_data *hapd, const char *txtaddr) { u8 addr[ETH_ALEN]; struct sta_info *sta; const char *pos; u16 reason = WLAN_REASON_PREV_AUTH_NOT_VALID; wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE DISASSOCIATE %s", txtaddr); if (hwaddr_aton(txtaddr, addr)) return -1; pos = os_strstr(txtaddr, " test="); if (pos) { struct ieee80211_mgmt mgmt; int encrypt; if (hapd->driver->send_frame == NULL) return -1; pos += 6; encrypt = atoi(pos); os_memset(&mgmt, 0, sizeof(mgmt)); mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_DISASSOC); os_memcpy(mgmt.da, addr, ETH_ALEN); os_memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN); os_memcpy(mgmt.bssid, hapd->own_addr, ETH_ALEN); mgmt.u.disassoc.reason_code = host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID); if (hapd->driver->send_frame(hapd->drv_priv, (u8 *) &mgmt, IEEE80211_HDRLEN + sizeof(mgmt.u.deauth), encrypt) < 0) return -1; return 0; } #ifdef CONFIG_P2P_MANAGER pos = os_strstr(txtaddr, " p2p="); if (pos) { return p2p_manager_disconnect(hapd, WLAN_FC_STYPE_DISASSOC, atoi(pos + 5), addr); } #endif /* CONFIG_P2P_MANAGER */ pos = os_strstr(txtaddr, " reason="); if (pos) reason = atoi(pos + 8); hostapd_drv_sta_disassoc(hapd, addr, reason); sta = ap_get_sta(hapd, addr); if (sta) ap_sta_disassociate(hapd, sta, reason); else if (addr[0] == 0xff) hostapd_free_stas(hapd); return 0; } int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf, size_t buflen) { struct hostapd_iface *iface = hapd->iface; int len = 0, ret; size_t i; ret = os_snprintf(buf + len, buflen - len, "state=%s\n" "phy=%s\n" "freq=%d\n" "num_sta_non_erp=%d\n" "num_sta_no_short_slot_time=%d\n" "num_sta_no_short_preamble=%d\n" "olbc=%d\n" "num_sta_ht_no_gf=%d\n" "num_sta_no_ht=%d\n" "num_sta_ht_20_mhz=%d\n" "olbc_ht=%d\n" "ht_op_mode=0x%x\n", hostapd_state_text(iface->state), iface->phy, iface->freq, iface->num_sta_non_erp, iface->num_sta_no_short_slot_time, iface->num_sta_no_short_preamble, iface->olbc, iface->num_sta_ht_no_gf, iface->num_sta_no_ht, iface->num_sta_ht_20mhz, iface->olbc_ht, iface->ht_op_mode); if (ret < 0 || (size_t) ret >= buflen - len) return len; len += ret; ret = os_snprintf(buf + len, buflen - len, "channel=%u\n" "secondary_channel=%d\n" "ieee80211n=%d\n" "ieee80211ac=%d\n" "vht_oper_chwidth=%d\n" "vht_oper_centr_freq_seg0_idx=%d\n" "vht_oper_centr_freq_seg1_idx=%d\n", iface->conf->channel, iface->conf->secondary_channel, iface->conf->ieee80211n, iface->conf->ieee80211ac, iface->conf->vht_oper_chwidth, iface->conf->vht_oper_centr_freq_seg0_idx, iface->conf->vht_oper_centr_freq_seg1_idx); if (ret < 0 || (size_t) ret >= buflen - len) return len; len += ret; for (i = 0; i < iface->num_bss; i++) { struct hostapd_data *bss = iface->bss[i]; ret = os_snprintf(buf + len, buflen - len, "bss[%d]=%s\n" "bssid[%d]=" MACSTR "\n" "ssid[%d]=%s\n" "num_sta[%d]=%d\n", (int) i, bss->conf->iface, (int) i, MAC2STR(bss->own_addr), (int) i, wpa_ssid_txt(bss->conf->ssid.ssid, bss->conf->ssid.ssid_len), (int) i, bss->num_sta); if (ret < 0 || (size_t) ret >= buflen - len) return len; len += ret; } return len; } int hostapd_parse_csa_settings(const char *pos, struct csa_settings *settings) { char *end; if (!settings) return -1; os_memset(settings, 0, sizeof(*settings)); settings->cs_count = strtol(pos, &end, 10); if (pos == end) { wpa_printf(MSG_ERROR, "chanswitch: invalid cs_count provided"); return -1; } settings->freq_params.freq = atoi(end); if (settings->freq_params.freq == 0) { wpa_printf(MSG_ERROR, "chanswitch: invalid freq provided"); return -1; } #define SET_CSA_SETTING(str) \ do { \ const char *pos2 = os_strstr(pos, " " #str "="); \ if (pos2) { \ pos2 += sizeof(" " #str "=") - 1; \ settings->freq_params.str = atoi(pos2); \ } \ } while (0) SET_CSA_SETTING(center_freq1); SET_CSA_SETTING(center_freq2); SET_CSA_SETTING(bandwidth); SET_CSA_SETTING(sec_channel_offset); settings->freq_params.ht_enabled = !!os_strstr(pos, " ht"); settings->freq_params.vht_enabled = !!os_strstr(pos, " vht"); settings->block_tx = !!os_strstr(pos, " blocktx"); #undef SET_CSA_SETTING return 0; } wpa-2.1/src/ap/ctrl_iface_ap.h000066400000000000000000000017171227414666700163030ustar00rootroot00000000000000/* * Control interface for shared AP commands * Copyright (c) 2004-2013, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef CTRL_IFACE_AP_H #define CTRL_IFACE_AP_H int hostapd_ctrl_iface_sta_first(struct hostapd_data *hapd, char *buf, size_t buflen); int hostapd_ctrl_iface_sta(struct hostapd_data *hapd, const char *txtaddr, char *buf, size_t buflen); int hostapd_ctrl_iface_sta_next(struct hostapd_data *hapd, const char *txtaddr, char *buf, size_t buflen); int hostapd_ctrl_iface_deauthenticate(struct hostapd_data *hapd, const char *txtaddr); int hostapd_ctrl_iface_disassociate(struct hostapd_data *hapd, const char *txtaddr); int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf, size_t buflen); int hostapd_parse_csa_settings(const char *pos, struct csa_settings *settings); #endif /* CTRL_IFACE_AP_H */ wpa-2.1/src/ap/dfs.c000066400000000000000000000476131227414666700143040ustar00rootroot00000000000000/* * DFS - Dynamic Frequency Selection * Copyright (c) 2002-2013, Jouni Malinen * Copyright (c) 2013, Qualcomm Atheros, Inc. * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "utils/includes.h" #include "utils/common.h" #include "common/ieee802_11_defs.h" #include "common/wpa_ctrl.h" #include "hostapd.h" #include "ap_drv_ops.h" #include "drivers/driver.h" #include "dfs.h" static int dfs_get_used_n_chans(struct hostapd_iface *iface) { int n_chans = 1; if (iface->conf->ieee80211n && iface->conf->secondary_channel) n_chans = 2; if (iface->conf->ieee80211ac) { switch (iface->conf->vht_oper_chwidth) { case VHT_CHANWIDTH_USE_HT: break; case VHT_CHANWIDTH_80MHZ: n_chans = 4; break; case VHT_CHANWIDTH_160MHZ: n_chans = 8; break; default: break; } } return n_chans; } static int dfs_channel_available(struct hostapd_channel_data *chan, int skip_radar) { /* * When radar detection happens, CSA is performed. However, there's no * time for CAC, so radar channels must be skipped when finding a new * channel for CSA. */ if (skip_radar && chan->flag & HOSTAPD_CHAN_RADAR) return 0; if (chan->flag & HOSTAPD_CHAN_DISABLED) return 0; if ((chan->flag & HOSTAPD_CHAN_RADAR) && ((chan->flag & HOSTAPD_CHAN_DFS_MASK) == HOSTAPD_CHAN_DFS_UNAVAILABLE)) return 0; return 1; } static int dfs_is_chan_allowed(struct hostapd_channel_data *chan, int n_chans) { /* * The tables contain first valid channel number based on channel width. * We will also choose this first channel as the control one. */ int allowed_40[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157, 184, 192 }; /* * VHT80, valid channels based on center frequency: * 42, 58, 106, 122, 138, 155 */ int allowed_80[] = { 36, 52, 100, 116, 132, 149 }; int *allowed = allowed_40; unsigned int i, allowed_no = 0; switch (n_chans) { case 2: allowed = allowed_40; allowed_no = ARRAY_SIZE(allowed_40); break; case 4: allowed = allowed_80; allowed_no = ARRAY_SIZE(allowed_80); break; default: wpa_printf(MSG_DEBUG, "Unknown width for %d channels", n_chans); break; } for (i = 0; i < allowed_no; i++) { if (chan->chan == allowed[i]) return 1; } return 0; } static int dfs_chan_range_available(struct hostapd_hw_modes *mode, int first_chan_idx, int num_chans, int skip_radar) { struct hostapd_channel_data *first_chan, *chan; int i; if (first_chan_idx + num_chans >= mode->num_channels) return 0; first_chan = &mode->channels[first_chan_idx]; for (i = 0; i < num_chans; i++) { chan = &mode->channels[first_chan_idx + i]; if (first_chan->freq + i * 20 != chan->freq) return 0; if (!dfs_channel_available(chan, skip_radar)) return 0; } return 1; } /* * The function assumes HT40+ operation. * Make sure to adjust the following variables after calling this: * - hapd->secondary_channel * - hapd->vht_oper_centr_freq_seg0_idx * - hapd->vht_oper_centr_freq_seg1_idx */ static int dfs_find_channel(struct hostapd_iface *iface, struct hostapd_channel_data **ret_chan, int idx, int skip_radar) { struct hostapd_hw_modes *mode; struct hostapd_channel_data *chan; int i, channel_idx = 0, n_chans; mode = iface->current_mode; n_chans = dfs_get_used_n_chans(iface); wpa_printf(MSG_DEBUG, "DFS new chan checking %d channels", n_chans); for (i = 0; i < mode->num_channels; i++) { chan = &mode->channels[i]; /* Skip HT40/VHT incompatible channels */ if (iface->conf->ieee80211n && iface->conf->secondary_channel && !dfs_is_chan_allowed(chan, n_chans)) continue; /* Skip incompatible chandefs */ if (!dfs_chan_range_available(mode, i, n_chans, skip_radar)) continue; if (ret_chan && idx == channel_idx) { wpa_printf(MSG_DEBUG, "Selected ch. #%d", chan->chan); *ret_chan = chan; return idx; } wpa_printf(MSG_DEBUG, "Adding channel: %d", chan->chan); channel_idx++; } return channel_idx; } static void dfs_adjust_vht_center_freq(struct hostapd_iface *iface, struct hostapd_channel_data *chan, int secondary_channel, u8 *vht_oper_centr_freq_seg0_idx, u8 *vht_oper_centr_freq_seg1_idx) { if (!iface->conf->ieee80211ac) return; if (!chan) return; *vht_oper_centr_freq_seg1_idx = 0; switch (iface->conf->vht_oper_chwidth) { case VHT_CHANWIDTH_USE_HT: if (secondary_channel == 1) *vht_oper_centr_freq_seg0_idx = chan->chan + 2; else if (secondary_channel == -1) *vht_oper_centr_freq_seg0_idx = chan->chan - 2; else *vht_oper_centr_freq_seg0_idx = chan->chan; break; case VHT_CHANWIDTH_80MHZ: *vht_oper_centr_freq_seg0_idx = chan->chan + 6; break; case VHT_CHANWIDTH_160MHZ: *vht_oper_centr_freq_seg0_idx = chan->chan + 14; break; default: wpa_printf(MSG_INFO, "DFS only VHT20/40/80/160 is supported now"); break; } wpa_printf(MSG_DEBUG, "DFS adjusting VHT center frequency: %d, %d", *vht_oper_centr_freq_seg0_idx, *vht_oper_centr_freq_seg1_idx); } /* Return start channel idx we will use for mode->channels[idx] */ static int dfs_get_start_chan_idx(struct hostapd_iface *iface) { struct hostapd_hw_modes *mode; struct hostapd_channel_data *chan; int channel_no = iface->conf->channel; int res = -1, i; /* HT40- */ if (iface->conf->ieee80211n && iface->conf->secondary_channel == -1) channel_no -= 4; /* VHT */ if (iface->conf->ieee80211ac) { switch (iface->conf->vht_oper_chwidth) { case VHT_CHANWIDTH_USE_HT: break; case VHT_CHANWIDTH_80MHZ: channel_no = iface->conf->vht_oper_centr_freq_seg0_idx - 6; break; case VHT_CHANWIDTH_160MHZ: channel_no = iface->conf->vht_oper_centr_freq_seg0_idx - 14; break; default: wpa_printf(MSG_INFO, "DFS only VHT20/40/80/160 is supported now"); channel_no = -1; break; } } /* Get idx */ mode = iface->current_mode; for (i = 0; i < mode->num_channels; i++) { chan = &mode->channels[i]; if (chan->chan == channel_no) { res = i; break; } } if (res == -1) wpa_printf(MSG_DEBUG, "DFS chan_idx seems wrong: -1"); return res; } /* At least one channel have radar flag */ static int dfs_check_chans_radar(struct hostapd_iface *iface, int start_chan_idx, int n_chans) { struct hostapd_channel_data *channel; struct hostapd_hw_modes *mode; int i, res = 0; mode = iface->current_mode; for (i = 0; i < n_chans; i++) { channel = &mode->channels[start_chan_idx + i]; if (channel->flag & HOSTAPD_CHAN_RADAR) res++; } return res; } /* All channels available */ static int dfs_check_chans_available(struct hostapd_iface *iface, int start_chan_idx, int n_chans) { struct hostapd_channel_data *channel; struct hostapd_hw_modes *mode; int i; mode = iface->current_mode; for(i = 0; i < n_chans; i++) { channel = &mode->channels[start_chan_idx + i]; if ((channel->flag & HOSTAPD_CHAN_DFS_MASK) != HOSTAPD_CHAN_DFS_AVAILABLE) break; } return i == n_chans; } /* At least one channel unavailable */ static int dfs_check_chans_unavailable(struct hostapd_iface *iface, int start_chan_idx, int n_chans) { struct hostapd_channel_data *channel; struct hostapd_hw_modes *mode; int i, res = 0; mode = iface->current_mode; for(i = 0; i < n_chans; i++) { channel = &mode->channels[start_chan_idx + i]; if (channel->flag & HOSTAPD_CHAN_DISABLED) res++; if ((channel->flag & HOSTAPD_CHAN_DFS_MASK) == HOSTAPD_CHAN_DFS_UNAVAILABLE) res++; } return res; } static struct hostapd_channel_data * dfs_get_valid_channel(struct hostapd_iface *iface, int *secondary_channel, u8 *vht_oper_centr_freq_seg0_idx, u8 *vht_oper_centr_freq_seg1_idx, int skip_radar) { struct hostapd_hw_modes *mode; struct hostapd_channel_data *chan = NULL; int num_available_chandefs; int chan_idx; u32 _rand; wpa_printf(MSG_DEBUG, "DFS: Selecting random channel"); if (iface->current_mode == NULL) return NULL; mode = iface->current_mode; if (mode->mode != HOSTAPD_MODE_IEEE80211A) return NULL; /* Get the count first */ num_available_chandefs = dfs_find_channel(iface, NULL, 0, skip_radar); if (num_available_chandefs == 0) return NULL; os_get_random((u8 *) &_rand, sizeof(_rand)); chan_idx = _rand % num_available_chandefs; dfs_find_channel(iface, &chan, chan_idx, skip_radar); /* dfs_find_channel() calculations assume HT40+ */ if (iface->conf->secondary_channel) *secondary_channel = 1; else *secondary_channel = 0; dfs_adjust_vht_center_freq(iface, chan, *secondary_channel, vht_oper_centr_freq_seg0_idx, vht_oper_centr_freq_seg1_idx); return chan; } static int set_dfs_state_freq(struct hostapd_iface *iface, int freq, u32 state) { struct hostapd_hw_modes *mode; struct hostapd_channel_data *chan = NULL; int i; mode = iface->current_mode; if (mode == NULL) return 0; wpa_printf(MSG_DEBUG, "set_dfs_state 0x%X for %d MHz", state, freq); for (i = 0; i < iface->current_mode->num_channels; i++) { chan = &iface->current_mode->channels[i]; if (chan->freq == freq) { if (chan->flag & HOSTAPD_CHAN_RADAR) { chan->flag &= ~HOSTAPD_CHAN_DFS_MASK; chan->flag |= state; return 1; /* Channel found */ } } } wpa_printf(MSG_WARNING, "Can't set DFS state for freq %d MHz", freq); return 0; } static int set_dfs_state(struct hostapd_iface *iface, int freq, int ht_enabled, int chan_offset, int chan_width, int cf1, int cf2, u32 state) { int n_chans = 1, i; struct hostapd_hw_modes *mode; int frequency = freq; int ret = 0; mode = iface->current_mode; if (mode == NULL) return 0; if (mode->mode != HOSTAPD_MODE_IEEE80211A) { wpa_printf(MSG_WARNING, "current_mode != IEEE80211A"); return 0; } /* Seems cf1 and chan_width is enough here */ switch (chan_width) { case CHAN_WIDTH_20_NOHT: case CHAN_WIDTH_20: n_chans = 1; if (frequency == 0) frequency = cf1; break; case CHAN_WIDTH_40: n_chans = 2; frequency = cf1 - 10; break; case CHAN_WIDTH_80: n_chans = 4; frequency = cf1 - 30; break; case CHAN_WIDTH_160: n_chans = 8; frequency = cf1 - 70; break; default: wpa_printf(MSG_INFO, "DFS chan_width %d not supported", chan_width); break; } wpa_printf(MSG_DEBUG, "DFS freq: %dMHz, n_chans: %d", frequency, n_chans); for (i = 0; i < n_chans; i++) { ret += set_dfs_state_freq(iface, frequency, state); frequency = frequency + 20; } return ret; } static int dfs_are_channels_overlapped(struct hostapd_iface *iface, int freq, int chan_width, int cf1, int cf2) { int start_chan_idx; struct hostapd_hw_modes *mode; struct hostapd_channel_data *chan; int n_chans, i, j, frequency = freq, radar_n_chans = 1; u8 radar_chan; int res = 0; /* Our configuration */ mode = iface->current_mode; start_chan_idx = dfs_get_start_chan_idx(iface); n_chans = dfs_get_used_n_chans(iface); /* Check we are on DFS channel(s) */ if (!dfs_check_chans_radar(iface, start_chan_idx, n_chans)) return 0; /* Reported via radar event */ switch (chan_width) { case CHAN_WIDTH_20_NOHT: case CHAN_WIDTH_20: radar_n_chans = 1; if (frequency == 0) frequency = cf1; break; case CHAN_WIDTH_40: radar_n_chans = 2; frequency = cf1 - 10; break; case CHAN_WIDTH_80: radar_n_chans = 4; frequency = cf1 - 30; break; case CHAN_WIDTH_160: radar_n_chans = 8; frequency = cf1 - 70; break; default: wpa_printf(MSG_INFO, "DFS chan_width %d not supported", chan_width); break; } ieee80211_freq_to_chan(frequency, &radar_chan); for (i = 0; i < n_chans; i++) { chan = &mode->channels[start_chan_idx + i]; if (!(chan->flag & HOSTAPD_CHAN_RADAR)) continue; for (j = 0; j < radar_n_chans; j++) { wpa_printf(MSG_DEBUG, "checking our: %d, radar: %d", chan->chan, radar_chan + j * 4); if (chan->chan == radar_chan + j * 4) res++; } } wpa_printf(MSG_DEBUG, "overlapped: %d", res); return res; } /* * Main DFS handler * 1 - continue channel/ap setup * 0 - channel/ap setup will be continued after CAC * -1 - hit critical error */ int hostapd_handle_dfs(struct hostapd_iface *iface) { struct hostapd_channel_data *channel; int res, n_chans, start_chan_idx; int skip_radar = 0; iface->cac_started = 0; do { /* Get start (first) channel for current configuration */ start_chan_idx = dfs_get_start_chan_idx(iface); if (start_chan_idx == -1) return -1; /* Get number of used channels, depend on width */ n_chans = dfs_get_used_n_chans(iface); /* Check if any of configured channels require DFS */ res = dfs_check_chans_radar(iface, start_chan_idx, n_chans); wpa_printf(MSG_DEBUG, "DFS %d channels required radar detection", res); if (!res) return 1; /* Check if all channels are DFS available */ res = dfs_check_chans_available(iface, start_chan_idx, n_chans); wpa_printf(MSG_DEBUG, "DFS all channels available, (SKIP CAC): %s", res ? "yes" : "no"); if (res) return 1; /* Check if any of configured channels is unavailable */ res = dfs_check_chans_unavailable(iface, start_chan_idx, n_chans); wpa_printf(MSG_DEBUG, "DFS %d chans unavailable - choose other channel: %s", res, res ? "yes": "no"); if (res) { int sec; u8 cf1, cf2; channel = dfs_get_valid_channel(iface, &sec, &cf1, &cf2, skip_radar); if (!channel) { wpa_printf(MSG_ERROR, "could not get valid channel"); return -1; } iface->freq = channel->freq; iface->conf->channel = channel->chan; iface->conf->secondary_channel = sec; iface->conf->vht_oper_centr_freq_seg0_idx = cf1; iface->conf->vht_oper_centr_freq_seg1_idx = cf2; } } while (res); /* Finally start CAC */ hostapd_set_state(iface, HAPD_IFACE_DFS); wpa_printf(MSG_DEBUG, "DFS start CAC on %d MHz", iface->freq); wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_START "freq=%d chan=%d sec_chan=%d", iface->freq, iface->conf->channel, iface->conf->secondary_channel); if (hostapd_start_dfs_cac(iface, iface->conf->hw_mode, iface->freq, iface->conf->channel, iface->conf->ieee80211n, iface->conf->ieee80211ac, iface->conf->secondary_channel, iface->conf->vht_oper_chwidth, iface->conf->vht_oper_centr_freq_seg0_idx, iface->conf->vht_oper_centr_freq_seg1_idx)) { wpa_printf(MSG_DEBUG, "DFS start_dfs_cac() failed"); return -1; } return 0; } int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq, int ht_enabled, int chan_offset, int chan_width, int cf1, int cf2) { wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_COMPLETED "success=%d freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d", success, freq, ht_enabled, chan_offset, chan_width, cf1, cf2); if (success) { /* Complete iface/ap configuration */ set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width, cf1, cf2, HOSTAPD_CHAN_DFS_AVAILABLE); iface->cac_started = 0; hostapd_setup_interface_complete(iface, 0); } return 0; } static int hostapd_dfs_start_channel_switch_cac(struct hostapd_iface *iface) { struct hostapd_channel_data *channel; int secondary_channel; u8 vht_oper_centr_freq_seg0_idx; u8 vht_oper_centr_freq_seg1_idx; int skip_radar = 0; int err = 1; /* Radar detected during active CAC */ iface->cac_started = 0; channel = dfs_get_valid_channel(iface, &secondary_channel, &vht_oper_centr_freq_seg0_idx, &vht_oper_centr_freq_seg1_idx, skip_radar); if (!channel) { wpa_printf(MSG_ERROR, "No valid channel available"); hostapd_setup_interface_complete(iface, err); return err; } wpa_printf(MSG_DEBUG, "DFS will switch to a new channel %d", channel->chan); wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NEW_CHANNEL "freq=%d chan=%d sec_chan=%d", channel->freq, channel->chan, secondary_channel); iface->freq = channel->freq; iface->conf->channel = channel->chan; iface->conf->secondary_channel = secondary_channel; iface->conf->vht_oper_centr_freq_seg0_idx = vht_oper_centr_freq_seg0_idx; iface->conf->vht_oper_centr_freq_seg1_idx = vht_oper_centr_freq_seg1_idx; err = 0; hostapd_setup_interface_complete(iface, err); return err; } static int hostapd_dfs_start_channel_switch(struct hostapd_iface *iface) { struct hostapd_channel_data *channel; int secondary_channel; u8 vht_oper_centr_freq_seg0_idx; u8 vht_oper_centr_freq_seg1_idx; int skip_radar = 1; struct csa_settings csa_settings; struct hostapd_data *hapd = iface->bss[0]; int err = 1; wpa_printf(MSG_DEBUG, "%s called (CAC active: %s, CSA active: %s)", __func__, iface->cac_started ? "yes" : "no", iface->csa_in_progress ? "yes" : "no"); /* Check if CSA in progress */ if (iface->csa_in_progress) return 0; /* Check if active CAC */ if (iface->cac_started) return hostapd_dfs_start_channel_switch_cac(iface); /* Perform channel switch/CSA */ channel = dfs_get_valid_channel(iface, &secondary_channel, &vht_oper_centr_freq_seg0_idx, &vht_oper_centr_freq_seg1_idx, skip_radar); if (!channel) { /* FIXME: Wait for channel(s) to become available */ hostapd_disable_iface(iface); return err; } wpa_printf(MSG_DEBUG, "DFS will switch to a new channel %d", channel->chan); wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NEW_CHANNEL "freq=%d chan=%d sec_chan=%d", channel->freq, channel->chan, secondary_channel); /* Setup CSA request */ os_memset(&csa_settings, 0, sizeof(csa_settings)); csa_settings.cs_count = 5; csa_settings.block_tx = 1; err = hostapd_set_freq_params(&csa_settings.freq_params, iface->conf->hw_mode, channel->freq, channel->chan, iface->conf->ieee80211n, iface->conf->ieee80211ac, secondary_channel, iface->conf->vht_oper_chwidth, vht_oper_centr_freq_seg0_idx, vht_oper_centr_freq_seg1_idx, iface->current_mode->vht_capab); if (err) { wpa_printf(MSG_ERROR, "DFS failed to calculate CSA freq params"); hostapd_disable_iface(iface); return err; } err = hostapd_switch_channel(hapd, &csa_settings); if (err) { wpa_printf(MSG_WARNING, "DFS failed to schedule CSA (%d) - trying fallback", err); iface->freq = channel->freq; iface->conf->channel = channel->chan; iface->conf->secondary_channel = secondary_channel; iface->conf->vht_oper_centr_freq_seg0_idx = vht_oper_centr_freq_seg0_idx; iface->conf->vht_oper_centr_freq_seg1_idx = vht_oper_centr_freq_seg1_idx; hostapd_disable_iface(iface); hostapd_enable_iface(iface); return 0; } /* Channel configuration will be updated once CSA completes and * ch_switch_notify event is received */ wpa_printf(MSG_DEBUG, "DFS waiting channel switch event"); return 0; } int hostapd_dfs_radar_detected(struct hostapd_iface *iface, int freq, int ht_enabled, int chan_offset, int chan_width, int cf1, int cf2) { int res; if (!iface->conf->ieee80211h) return 0; wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_RADAR_DETECTED "freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d", freq, ht_enabled, chan_offset, chan_width, cf1, cf2); /* mark radar frequency as invalid */ res = set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width, cf1, cf2, HOSTAPD_CHAN_DFS_UNAVAILABLE); /* Skip if reported radar event not overlapped our channels */ res = dfs_are_channels_overlapped(iface, freq, chan_width, cf1, cf2); if (!res) return 0; /* radar detected while operating, switch the channel. */ res = hostapd_dfs_start_channel_switch(iface); return res; } int hostapd_dfs_nop_finished(struct hostapd_iface *iface, int freq, int ht_enabled, int chan_offset, int chan_width, int cf1, int cf2) { wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NOP_FINISHED "freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d", freq, ht_enabled, chan_offset, chan_width, cf1, cf2); /* TODO add correct implementation here */ set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width, cf1, cf2, HOSTAPD_CHAN_DFS_USABLE); return 0; } wpa-2.1/src/ap/dfs.h000066400000000000000000000015051227414666700142770ustar00rootroot00000000000000/* * DFS - Dynamic Frequency Selection * Copyright (c) 2002-2013, Jouni Malinen * Copyright (c) 2013, Qualcomm Atheros, Inc. * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef DFS_H #define DFS_H int hostapd_handle_dfs(struct hostapd_iface *iface); int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq, int ht_enabled, int chan_offset, int chan_width, int cf1, int cf2); int hostapd_dfs_radar_detected(struct hostapd_iface *iface, int freq, int ht_enabled, int chan_offset, int chan_width, int cf1, int cf2); int hostapd_dfs_nop_finished(struct hostapd_iface *iface, int freq, int ht_enabled, int chan_offset, int chan_width, int cf1, int cf2); #endif /* DFS_H */ wpa-2.1/src/ap/drv_callbacks.c000066400000000000000000000661401227414666700163160ustar00rootroot00000000000000/* * hostapd / Callback functions for driver wrappers * Copyright (c) 2002-2013, 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 "radius/radius.h" #include "drivers/driver.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" #include "common/wpa_ctrl.h" #include "crypto/random.h" #include "p2p/p2p.h" #include "wps/wps.h" #include "wnm_ap.h" #include "hostapd.h" #include "ieee802_11.h" #include "sta_info.h" #include "accounting.h" #include "tkip_countermeasures.h" #include "ieee802_1x.h" #include "wpa_auth.h" #include "wps_hostapd.h" #include "ap_drv_ops.h" #include "ap_config.h" #include "hw_features.h" #include "dfs.h" int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, const u8 *req_ies, size_t req_ies_len, int reassoc) { struct sta_info *sta; int new_assoc, res; struct ieee802_11_elems elems; const u8 *ie; size_t ielen; #ifdef CONFIG_IEEE80211R u8 buf[sizeof(struct ieee80211_mgmt) + 1024]; u8 *p = buf; #endif /* CONFIG_IEEE80211R */ u16 reason = WLAN_REASON_UNSPECIFIED; u16 status = WLAN_STATUS_SUCCESS; const u8 *p2p_dev_addr = NULL; if (addr == NULL) { /* * This could potentially happen with unexpected event from the * driver wrapper. This was seen at least in one case where the * driver ended up being set to station mode while hostapd was * running, so better make sure we stop processing such an * event here. */ wpa_printf(MSG_DEBUG, "hostapd_notif_assoc: Skip event with " "no address"); return -1; } random_add_randomness(addr, ETH_ALEN); hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "associated"); ieee802_11_parse_elems(req_ies, req_ies_len, &elems, 0); if (elems.wps_ie) { ie = elems.wps_ie - 2; ielen = elems.wps_ie_len + 2; wpa_printf(MSG_DEBUG, "STA included WPS IE in (Re)AssocReq"); } else if (elems.rsn_ie) { ie = elems.rsn_ie - 2; ielen = elems.rsn_ie_len + 2; wpa_printf(MSG_DEBUG, "STA included RSN IE in (Re)AssocReq"); } else if (elems.wpa_ie) { ie = elems.wpa_ie - 2; ielen = elems.wpa_ie_len + 2; wpa_printf(MSG_DEBUG, "STA included WPA IE in (Re)AssocReq"); } else { ie = NULL; ielen = 0; wpa_printf(MSG_DEBUG, "STA did not include WPS/RSN/WPA IE in " "(Re)AssocReq"); } sta = ap_get_sta(hapd, addr); if (sta) { ap_sta_no_session_timeout(hapd, sta); accounting_sta_stop(hapd, sta); /* * Make sure that the previously registered inactivity timer * will not remove the STA immediately. */ sta->timeout_next = STA_NULLFUNC; } else { sta = ap_sta_add(hapd, addr); if (sta == NULL) { hostapd_drv_sta_disassoc(hapd, addr, WLAN_REASON_DISASSOC_AP_BUSY); return -1; } } sta->flags &= ~(WLAN_STA_WPS | WLAN_STA_MAYBE_WPS | WLAN_STA_WPS2); #ifdef CONFIG_P2P if (elems.p2p) { wpabuf_free(sta->p2p_ie); sta->p2p_ie = ieee802_11_vendor_ie_concat(req_ies, req_ies_len, P2P_IE_VENDOR_TYPE); if (sta->p2p_ie) p2p_dev_addr = p2p_get_go_dev_addr(sta->p2p_ie); } #endif /* CONFIG_P2P */ #ifdef CONFIG_INTERWORKING if (elems.ext_capab && elems.ext_capab_len > 4) { if (elems.ext_capab[4] & 0x01) sta->qos_map_enabled = 1; } #endif /* CONFIG_INTERWORKING */ #ifdef CONFIG_HS20 wpabuf_free(sta->hs20_ie); if (elems.hs20 && elems.hs20_len > 4) { sta->hs20_ie = wpabuf_alloc_copy(elems.hs20 + 4, elems.hs20_len - 4); } else sta->hs20_ie = NULL; #endif /* CONFIG_HS20 */ if (hapd->conf->wpa) { if (ie == NULL || ielen == 0) { #ifdef CONFIG_WPS if (hapd->conf->wps_state) { wpa_printf(MSG_DEBUG, "STA did not include " "WPA/RSN IE in (Re)Association " "Request - possible WPS use"); sta->flags |= WLAN_STA_MAYBE_WPS; goto skip_wpa_check; } #endif /* CONFIG_WPS */ wpa_printf(MSG_DEBUG, "No WPA/RSN IE from STA"); return -1; } #ifdef CONFIG_WPS if (hapd->conf->wps_state && ie[0] == 0xdd && ie[1] >= 4 && os_memcmp(ie + 2, "\x00\x50\xf2\x04", 4) == 0) { struct wpabuf *wps; sta->flags |= WLAN_STA_WPS; wps = ieee802_11_vendor_ie_concat(ie, ielen, WPS_IE_VENDOR_TYPE); if (wps) { if (wps_is_20(wps)) { wpa_printf(MSG_DEBUG, "WPS: STA " "supports WPS 2.0"); sta->flags |= WLAN_STA_WPS2; } wpabuf_free(wps); } goto skip_wpa_check; } #endif /* CONFIG_WPS */ if (sta->wpa_sm == NULL) sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, sta->addr, p2p_dev_addr); if (sta->wpa_sm == NULL) { wpa_printf(MSG_ERROR, "Failed to initialize WPA state " "machine"); return -1; } res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm, ie, ielen, elems.mdie, elems.mdie_len); if (res != WPA_IE_OK) { wpa_printf(MSG_DEBUG, "WPA/RSN information element " "rejected? (res %u)", res); wpa_hexdump(MSG_DEBUG, "IE", ie, ielen); if (res == WPA_INVALID_GROUP) { reason = WLAN_REASON_GROUP_CIPHER_NOT_VALID; status = WLAN_STATUS_GROUP_CIPHER_NOT_VALID; } else if (res == WPA_INVALID_PAIRWISE) { reason = WLAN_REASON_PAIRWISE_CIPHER_NOT_VALID; status = WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID; } else if (res == WPA_INVALID_AKMP) { reason = WLAN_REASON_AKMP_NOT_VALID; status = WLAN_STATUS_AKMP_NOT_VALID; } #ifdef CONFIG_IEEE80211W else if (res == WPA_MGMT_FRAME_PROTECTION_VIOLATION) { reason = WLAN_REASON_INVALID_IE; status = WLAN_STATUS_INVALID_IE; } else if (res == WPA_INVALID_MGMT_GROUP_CIPHER) { reason = WLAN_REASON_GROUP_CIPHER_NOT_VALID; status = WLAN_STATUS_GROUP_CIPHER_NOT_VALID; } #endif /* CONFIG_IEEE80211W */ else { reason = WLAN_REASON_INVALID_IE; status = WLAN_STATUS_INVALID_IE; } goto fail; } #ifdef CONFIG_IEEE80211W if ((sta->flags & WLAN_STA_MFP) && !sta->sa_query_timed_out && sta->sa_query_count > 0) ap_check_sa_query_timeout(hapd, sta); if ((sta->flags & WLAN_STA_MFP) && !sta->sa_query_timed_out && (sta->auth_alg != WLAN_AUTH_FT)) { /* * STA has already been associated with MFP and SA * Query timeout has not been reached. Reject the * association attempt temporarily and start SA Query, * if one is not pending. */ if (sta->sa_query_count == 0) ap_sta_start_sa_query(hapd, sta); #ifdef CONFIG_IEEE80211R status = WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY; p = hostapd_eid_assoc_comeback_time(hapd, sta, p); hostapd_sta_assoc(hapd, addr, reassoc, status, buf, p - buf); #endif /* CONFIG_IEEE80211R */ return 0; } if (wpa_auth_uses_mfp(sta->wpa_sm)) sta->flags |= WLAN_STA_MFP; else sta->flags &= ~WLAN_STA_MFP; #endif /* CONFIG_IEEE80211W */ #ifdef CONFIG_IEEE80211R if (sta->auth_alg == WLAN_AUTH_FT) { status = wpa_ft_validate_reassoc(sta->wpa_sm, req_ies, req_ies_len); if (status != WLAN_STATUS_SUCCESS) { if (status == WLAN_STATUS_INVALID_PMKID) reason = WLAN_REASON_INVALID_IE; if (status == WLAN_STATUS_INVALID_MDIE) reason = WLAN_REASON_INVALID_IE; if (status == WLAN_STATUS_INVALID_FTIE) reason = WLAN_REASON_INVALID_IE; goto fail; } } #endif /* CONFIG_IEEE80211R */ } else if (hapd->conf->wps_state) { #ifdef CONFIG_WPS struct wpabuf *wps; if (req_ies) wps = ieee802_11_vendor_ie_concat(req_ies, req_ies_len, WPS_IE_VENDOR_TYPE); else wps = NULL; #ifdef CONFIG_WPS_STRICT if (wps && wps_validate_assoc_req(wps) < 0) { reason = WLAN_REASON_INVALID_IE; status = WLAN_STATUS_INVALID_IE; wpabuf_free(wps); goto fail; } #endif /* CONFIG_WPS_STRICT */ if (wps) { sta->flags |= WLAN_STA_WPS; if (wps_is_20(wps)) { wpa_printf(MSG_DEBUG, "WPS: STA supports " "WPS 2.0"); sta->flags |= WLAN_STA_WPS2; } } else sta->flags |= WLAN_STA_MAYBE_WPS; wpabuf_free(wps); #endif /* CONFIG_WPS */ } #ifdef CONFIG_WPS skip_wpa_check: #endif /* CONFIG_WPS */ #ifdef CONFIG_IEEE80211R p = wpa_sm_write_assoc_resp_ies(sta->wpa_sm, buf, sizeof(buf), sta->auth_alg, req_ies, req_ies_len); hostapd_sta_assoc(hapd, addr, reassoc, status, buf, p - buf); #else /* CONFIG_IEEE80211R */ /* Keep compiler silent about unused variables */ if (status) { } #endif /* CONFIG_IEEE80211R */ new_assoc = (sta->flags & WLAN_STA_ASSOC) == 0; sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC; sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE; if (reassoc && (sta->auth_alg == WLAN_AUTH_FT)) wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC_FT); else wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC); hostapd_new_assoc_sta(hapd, sta, !new_assoc); ieee802_1x_notify_port_enabled(sta->eapol_sm, 1); #ifdef CONFIG_P2P if (req_ies) { p2p_group_notif_assoc(hapd->p2p_group, sta->addr, req_ies, req_ies_len); } #endif /* CONFIG_P2P */ return 0; fail: #ifdef CONFIG_IEEE80211R hostapd_sta_assoc(hapd, addr, reassoc, status, buf, p - buf); #endif /* CONFIG_IEEE80211R */ hostapd_drv_sta_disassoc(hapd, sta->addr, reason); ap_free_sta(hapd, sta); return -1; } void hostapd_notif_disassoc(struct hostapd_data *hapd, const u8 *addr) { struct sta_info *sta; if (addr == NULL) { /* * This could potentially happen with unexpected event from the * driver wrapper. This was seen at least in one case where the * driver ended up reporting a station mode event while hostapd * was running, so better make sure we stop processing such an * event here. */ wpa_printf(MSG_DEBUG, "hostapd_notif_disassoc: Skip event " "with no address"); return; } hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "disassociated"); sta = ap_get_sta(hapd, addr); if (sta == NULL) { wpa_printf(MSG_DEBUG, "Disassociation notification for " "unknown STA " MACSTR, MAC2STR(addr)); return; } ap_sta_set_authorized(hapd, sta, 0); sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC); wpa_auth_sm_event(sta->wpa_sm, WPA_DISASSOC); sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST; ieee802_1x_notify_port_enabled(sta->eapol_sm, 0); ap_free_sta(hapd, sta); } void hostapd_event_sta_low_ack(struct hostapd_data *hapd, const u8 *addr) { struct sta_info *sta = ap_get_sta(hapd, addr); if (!sta || !hapd->conf->disassoc_low_ack) return; hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "disconnected due to excessive " "missing ACKs"); hostapd_drv_sta_disassoc(hapd, addr, WLAN_REASON_DISASSOC_LOW_ACK); if (sta) ap_sta_disassociate(hapd, sta, WLAN_REASON_DISASSOC_LOW_ACK); } void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht, int offset, int width, int cf1, int cf2) { #ifdef NEED_AP_MLME int channel, chwidth, seg0_idx = 0, seg1_idx = 0; hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "driver had channel switch: " "freq=%d, ht=%d, offset=%d, width=%d, cf1=%d, cf2=%d", freq, ht, offset, width, cf1, cf2); hapd->iface->freq = freq; channel = hostapd_hw_get_channel(hapd, freq); if (!channel) { hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_WARNING, "driver switched to " "bad channel!"); return; } switch (width) { case CHAN_WIDTH_80: chwidth = VHT_CHANWIDTH_80MHZ; break; case CHAN_WIDTH_80P80: chwidth = VHT_CHANWIDTH_80P80MHZ; break; case CHAN_WIDTH_160: chwidth = VHT_CHANWIDTH_160MHZ; break; case CHAN_WIDTH_20_NOHT: case CHAN_WIDTH_20: case CHAN_WIDTH_40: default: chwidth = VHT_CHANWIDTH_USE_HT; break; } switch (hapd->iface->current_mode->mode) { case HOSTAPD_MODE_IEEE80211A: if (cf1 > 5000) seg0_idx = (cf1 - 5000) / 5; if (cf2 > 5000) seg1_idx = (cf2 - 5000) / 5; break; default: seg0_idx = hostapd_hw_get_channel(hapd, cf1); seg1_idx = hostapd_hw_get_channel(hapd, cf2); break; } hapd->iconf->channel = channel; hapd->iconf->ieee80211n = ht; hapd->iconf->secondary_channel = offset; hapd->iconf->vht_oper_chwidth = chwidth; hapd->iconf->vht_oper_centr_freq_seg0_idx = seg0_idx; hapd->iconf->vht_oper_centr_freq_seg1_idx = seg1_idx; if (hapd->iface->csa_in_progress && freq == hapd->iface->cs_freq_params.freq) { hostapd_cleanup_cs_params(hapd); wpa_msg(hapd->msg_ctx, MSG_INFO, AP_CSA_FINISHED "freq=%d", freq); } #endif /* NEED_AP_MLME */ } void hostapd_event_connect_failed_reason(struct hostapd_data *hapd, const u8 *addr, int reason_code) { switch (reason_code) { case MAX_CLIENT_REACHED: wpa_msg(hapd->msg_ctx, MSG_INFO, AP_REJECTED_MAX_STA MACSTR, MAC2STR(addr)); break; case BLOCKED_CLIENT: wpa_msg(hapd->msg_ctx, MSG_INFO, AP_REJECTED_BLOCKED_STA MACSTR, MAC2STR(addr)); break; } } int hostapd_probe_req_rx(struct hostapd_data *hapd, const u8 *sa, const u8 *da, const u8 *bssid, const u8 *ie, size_t ie_len, int ssi_signal) { size_t i; int ret = 0; if (sa == NULL || ie == NULL) return -1; random_add_randomness(sa, ETH_ALEN); for (i = 0; hapd->probereq_cb && i < hapd->num_probereq_cb; i++) { if (hapd->probereq_cb[i].cb(hapd->probereq_cb[i].ctx, sa, da, bssid, ie, ie_len, ssi_signal) > 0) { ret = 1; break; } } return ret; } #ifdef HOSTAPD #ifdef CONFIG_IEEE80211R static void hostapd_notify_auth_ft_finish(void *ctx, const u8 *dst, const u8 *bssid, u16 auth_transaction, u16 status, const u8 *ies, size_t ies_len) { struct hostapd_data *hapd = ctx; struct sta_info *sta; sta = ap_get_sta(hapd, dst); if (sta == NULL) return; hostapd_logger(hapd, dst, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "authentication OK (FT)"); sta->flags |= WLAN_STA_AUTH; hostapd_sta_auth(hapd, dst, auth_transaction, status, ies, ies_len); } #endif /* CONFIG_IEEE80211R */ static void hostapd_notif_auth(struct hostapd_data *hapd, struct auth_info *rx_auth) { struct sta_info *sta; u16 status = WLAN_STATUS_SUCCESS; u8 resp_ies[2 + WLAN_AUTH_CHALLENGE_LEN]; size_t resp_ies_len = 0; sta = ap_get_sta(hapd, rx_auth->peer); if (!sta) { sta = ap_sta_add(hapd, rx_auth->peer); if (sta == NULL) { status = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA; goto fail; } } sta->flags &= ~WLAN_STA_PREAUTH; ieee802_1x_notify_pre_auth(sta->eapol_sm, 0); #ifdef CONFIG_IEEE80211R if (rx_auth->auth_type == WLAN_AUTH_FT && hapd->wpa_auth) { sta->auth_alg = WLAN_AUTH_FT; if (sta->wpa_sm == NULL) sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, sta->addr, NULL); if (sta->wpa_sm == NULL) { wpa_printf(MSG_DEBUG, "FT: Failed to initialize WPA " "state machine"); status = WLAN_STATUS_UNSPECIFIED_FAILURE; goto fail; } wpa_ft_process_auth(sta->wpa_sm, rx_auth->bssid, rx_auth->auth_transaction, rx_auth->ies, rx_auth->ies_len, hostapd_notify_auth_ft_finish, hapd); return; } #endif /* CONFIG_IEEE80211R */ fail: hostapd_sta_auth(hapd, rx_auth->peer, rx_auth->auth_transaction + 1, status, resp_ies, resp_ies_len); } static void hostapd_action_rx(struct hostapd_data *hapd, struct rx_mgmt *drv_mgmt) { struct ieee80211_mgmt *mgmt; struct sta_info *sta; size_t plen __maybe_unused; u16 fc; if (drv_mgmt->frame_len < 24 + 1) return; plen = drv_mgmt->frame_len - 24 - 1; mgmt = (struct ieee80211_mgmt *) drv_mgmt->frame; fc = le_to_host16(mgmt->frame_control); if (WLAN_FC_GET_STYPE(fc) != WLAN_FC_STYPE_ACTION) return; /* handled by the driver */ wpa_printf(MSG_DEBUG, "RX_ACTION cat %d action plen %d", mgmt->u.action.category, (int) plen); sta = ap_get_sta(hapd, mgmt->sa); if (sta == NULL) { wpa_printf(MSG_DEBUG, "%s: station not found", __func__); return; } #ifdef CONFIG_IEEE80211R if (mgmt->u.action.category == WLAN_ACTION_FT) { const u8 *payload = drv_mgmt->frame + 24 + 1; wpa_ft_action_rx(sta->wpa_sm, payload, plen); } #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_IEEE80211W if (mgmt->u.action.category == WLAN_ACTION_SA_QUERY && plen >= 4) { ieee802_11_sa_query_action( hapd, mgmt->sa, mgmt->u.action.u.sa_query_resp.action, mgmt->u.action.u.sa_query_resp.trans_id); } #endif /* CONFIG_IEEE80211W */ #ifdef CONFIG_WNM if (mgmt->u.action.category == WLAN_ACTION_WNM) { ieee802_11_rx_wnm_action_ap(hapd, mgmt, drv_mgmt->frame_len); } #endif /* CONFIG_WNM */ } #ifdef NEED_AP_MLME #define HAPD_BROADCAST ((struct hostapd_data *) -1) static struct hostapd_data * get_hapd_bssid(struct hostapd_iface *iface, const u8 *bssid) { size_t i; if (bssid == NULL) return NULL; if (bssid[0] == 0xff && bssid[1] == 0xff && bssid[2] == 0xff && bssid[3] == 0xff && bssid[4] == 0xff && bssid[5] == 0xff) return HAPD_BROADCAST; for (i = 0; i < iface->num_bss; i++) { if (os_memcmp(bssid, iface->bss[i]->own_addr, ETH_ALEN) == 0) return iface->bss[i]; } return NULL; } static void hostapd_rx_from_unknown_sta(struct hostapd_data *hapd, const u8 *bssid, const u8 *addr, int wds) { hapd = get_hapd_bssid(hapd->iface, bssid); if (hapd == NULL || hapd == HAPD_BROADCAST) return; ieee802_11_rx_from_unknown(hapd, addr, wds); } static int hostapd_mgmt_rx(struct hostapd_data *hapd, struct rx_mgmt *rx_mgmt) { struct hostapd_iface *iface = hapd->iface; const struct ieee80211_hdr *hdr; const u8 *bssid; struct hostapd_frame_info fi; int ret; hdr = (const struct ieee80211_hdr *) rx_mgmt->frame; bssid = get_hdr_bssid(hdr, rx_mgmt->frame_len); if (bssid == NULL) return 0; hapd = get_hapd_bssid(iface, bssid); if (hapd == NULL) { u16 fc; fc = le_to_host16(hdr->frame_control); /* * Drop frames to unknown BSSIDs except for Beacon frames which * could be used to update neighbor information. */ if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT && WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_BEACON) hapd = iface->bss[0]; else return 0; } os_memset(&fi, 0, sizeof(fi)); fi.datarate = rx_mgmt->datarate; fi.ssi_signal = rx_mgmt->ssi_signal; if (hapd == HAPD_BROADCAST) { size_t i; ret = 0; for (i = 0; i < iface->num_bss; i++) { if (ieee802_11_mgmt(iface->bss[i], rx_mgmt->frame, rx_mgmt->frame_len, &fi) > 0) ret = 1; } } else ret = ieee802_11_mgmt(hapd, rx_mgmt->frame, rx_mgmt->frame_len, &fi); random_add_randomness(&fi, sizeof(fi)); return ret; } static void hostapd_mgmt_tx_cb(struct hostapd_data *hapd, const u8 *buf, size_t len, u16 stype, int ok) { struct ieee80211_hdr *hdr; hdr = (struct ieee80211_hdr *) buf; hapd = get_hapd_bssid(hapd->iface, get_hdr_bssid(hdr, len)); if (hapd == NULL || hapd == HAPD_BROADCAST) return; ieee802_11_mgmt_cb(hapd, buf, len, stype, ok); } #endif /* NEED_AP_MLME */ static int hostapd_event_new_sta(struct hostapd_data *hapd, const u8 *addr) { struct sta_info *sta = ap_get_sta(hapd, addr); if (sta) return 0; wpa_printf(MSG_DEBUG, "Data frame from unknown STA " MACSTR " - adding a new STA", MAC2STR(addr)); sta = ap_sta_add(hapd, addr); if (sta) { hostapd_new_assoc_sta(hapd, sta, 0); } else { wpa_printf(MSG_DEBUG, "Failed to add STA entry for " MACSTR, MAC2STR(addr)); return -1; } return 0; } static void hostapd_event_eapol_rx(struct hostapd_data *hapd, const u8 *src, const u8 *data, size_t data_len) { struct hostapd_iface *iface = hapd->iface; struct sta_info *sta; size_t j; for (j = 0; j < iface->num_bss; j++) { if ((sta = ap_get_sta(iface->bss[j], src))) { if (sta->flags & WLAN_STA_ASSOC) { hapd = iface->bss[j]; break; } } } ieee802_1x_receive(hapd, src, data, data_len); } static struct hostapd_channel_data * hostapd_get_mode_channel( struct hostapd_iface *iface, unsigned int freq) { int i; struct hostapd_channel_data *chan; for (i = 0; i < iface->current_mode->num_channels; i++) { chan = &iface->current_mode->channels[i]; if (!chan) return NULL; if ((unsigned int) chan->freq == freq) return chan; } return NULL; } static void hostapd_update_nf(struct hostapd_iface *iface, struct hostapd_channel_data *chan, struct freq_survey *survey) { if (!iface->chans_surveyed) { chan->min_nf = survey->nf; iface->lowest_nf = survey->nf; } else { if (dl_list_empty(&chan->survey_list)) chan->min_nf = survey->nf; else if (survey->nf < chan->min_nf) chan->min_nf = survey->nf; if (survey->nf < iface->lowest_nf) iface->lowest_nf = survey->nf; } } static void hostapd_event_get_survey(struct hostapd_data *hapd, struct survey_results *survey_results) { struct hostapd_iface *iface = hapd->iface; struct freq_survey *survey, *tmp; struct hostapd_channel_data *chan; if (dl_list_empty(&survey_results->survey_list)) { wpa_printf(MSG_DEBUG, "No survey data received"); return; } dl_list_for_each_safe(survey, tmp, &survey_results->survey_list, struct freq_survey, list) { chan = hostapd_get_mode_channel(iface, survey->freq); if (!chan) continue; if (chan->flag & HOSTAPD_CHAN_DISABLED) continue; dl_list_del(&survey->list); dl_list_add_tail(&chan->survey_list, &survey->list); hostapd_update_nf(iface, chan, survey); iface->chans_surveyed++; } } #ifdef NEED_AP_MLME static void hostapd_event_dfs_radar_detected(struct hostapd_data *hapd, struct dfs_event *radar) { wpa_printf(MSG_DEBUG, "DFS radar detected on %d MHz", radar->freq); hostapd_dfs_radar_detected(hapd->iface, radar->freq, radar->ht_enabled, radar->chan_offset, radar->chan_width, radar->cf1, radar->cf2); } static void hostapd_event_dfs_cac_finished(struct hostapd_data *hapd, struct dfs_event *radar) { wpa_printf(MSG_DEBUG, "DFS CAC finished on %d MHz", radar->freq); hostapd_dfs_complete_cac(hapd->iface, 1, radar->freq, radar->ht_enabled, radar->chan_offset, radar->chan_width, radar->cf1, radar->cf2); } static void hostapd_event_dfs_cac_aborted(struct hostapd_data *hapd, struct dfs_event *radar) { wpa_printf(MSG_DEBUG, "DFS CAC aborted on %d MHz", radar->freq); hostapd_dfs_complete_cac(hapd->iface, 0, radar->freq, radar->ht_enabled, radar->chan_offset, radar->chan_width, radar->cf1, radar->cf2); } static void hostapd_event_dfs_nop_finished(struct hostapd_data *hapd, struct dfs_event *radar) { wpa_printf(MSG_DEBUG, "DFS NOP finished on %d MHz", radar->freq); hostapd_dfs_nop_finished(hapd->iface, radar->freq, radar->ht_enabled, radar->chan_offset, radar->chan_width, radar->cf1, radar->cf2); } #endif /* NEED_AP_MLME */ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, union wpa_event_data *data) { struct hostapd_data *hapd = ctx; #ifndef CONFIG_NO_STDOUT_DEBUG int level = MSG_DEBUG; if (event == EVENT_RX_MGMT && data->rx_mgmt.frame && data->rx_mgmt.frame_len >= 24) { const struct ieee80211_hdr *hdr; u16 fc; hdr = (const struct ieee80211_hdr *) data->rx_mgmt.frame; fc = le_to_host16(hdr->frame_control); if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT && WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_BEACON) level = MSG_EXCESSIVE; if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT && WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_PROBE_REQ) level = MSG_EXCESSIVE; } wpa_dbg(hapd->msg_ctx, level, "Event %s (%d) received", event_to_string(event), event); #endif /* CONFIG_NO_STDOUT_DEBUG */ switch (event) { case EVENT_MICHAEL_MIC_FAILURE: michael_mic_failure(hapd, data->michael_mic_failure.src, 1); break; case EVENT_SCAN_RESULTS: if (hapd->iface->scan_cb) hapd->iface->scan_cb(hapd->iface); break; #ifdef CONFIG_IEEE80211R case EVENT_FT_RRB_RX: wpa_ft_rrb_rx(hapd->wpa_auth, data->ft_rrb_rx.src, data->ft_rrb_rx.data, data->ft_rrb_rx.data_len); break; #endif /* CONFIG_IEEE80211R */ case EVENT_WPS_BUTTON_PUSHED: hostapd_wps_button_pushed(hapd, NULL); break; #ifdef NEED_AP_MLME case EVENT_TX_STATUS: switch (data->tx_status.type) { case WLAN_FC_TYPE_MGMT: hostapd_mgmt_tx_cb(hapd, data->tx_status.data, data->tx_status.data_len, data->tx_status.stype, data->tx_status.ack); break; case WLAN_FC_TYPE_DATA: hostapd_tx_status(hapd, data->tx_status.dst, data->tx_status.data, data->tx_status.data_len, data->tx_status.ack); break; } break; case EVENT_EAPOL_TX_STATUS: hostapd_eapol_tx_status(hapd, data->eapol_tx_status.dst, data->eapol_tx_status.data, data->eapol_tx_status.data_len, data->eapol_tx_status.ack); break; case EVENT_DRIVER_CLIENT_POLL_OK: hostapd_client_poll_ok(hapd, data->client_poll.addr); break; case EVENT_RX_FROM_UNKNOWN: hostapd_rx_from_unknown_sta(hapd, data->rx_from_unknown.bssid, data->rx_from_unknown.addr, data->rx_from_unknown.wds); break; #endif /* NEED_AP_MLME */ case EVENT_RX_MGMT: #ifdef NEED_AP_MLME if (hostapd_mgmt_rx(hapd, &data->rx_mgmt) > 0) break; #endif /* NEED_AP_MLME */ hostapd_action_rx(hapd, &data->rx_mgmt); break; case EVENT_RX_PROBE_REQ: if (data->rx_probe_req.sa == NULL || data->rx_probe_req.ie == NULL) break; hostapd_probe_req_rx(hapd, data->rx_probe_req.sa, data->rx_probe_req.da, data->rx_probe_req.bssid, data->rx_probe_req.ie, data->rx_probe_req.ie_len, data->rx_probe_req.ssi_signal); break; case EVENT_NEW_STA: hostapd_event_new_sta(hapd, data->new_sta.addr); break; case EVENT_EAPOL_RX: hostapd_event_eapol_rx(hapd, data->eapol_rx.src, data->eapol_rx.data, data->eapol_rx.data_len); break; case EVENT_ASSOC: hostapd_notif_assoc(hapd, data->assoc_info.addr, data->assoc_info.req_ies, data->assoc_info.req_ies_len, data->assoc_info.reassoc); break; case EVENT_DISASSOC: if (data) hostapd_notif_disassoc(hapd, data->disassoc_info.addr); break; case EVENT_DEAUTH: if (data) hostapd_notif_disassoc(hapd, data->deauth_info.addr); break; case EVENT_STATION_LOW_ACK: if (!data) break; hostapd_event_sta_low_ack(hapd, data->low_ack.addr); break; case EVENT_AUTH: hostapd_notif_auth(hapd, &data->auth); break; case EVENT_CH_SWITCH: if (!data) break; hostapd_event_ch_switch(hapd, data->ch_switch.freq, data->ch_switch.ht_enabled, data->ch_switch.ch_offset, data->ch_switch.ch_width, data->ch_switch.cf1, data->ch_switch.cf2); break; case EVENT_CONNECT_FAILED_REASON: if (!data) break; hostapd_event_connect_failed_reason( hapd, data->connect_failed_reason.addr, data->connect_failed_reason.code); break; case EVENT_SURVEY: hostapd_event_get_survey(hapd, &data->survey_results); break; #ifdef NEED_AP_MLME case EVENT_DFS_RADAR_DETECTED: if (!data) break; hostapd_event_dfs_radar_detected(hapd, &data->dfs_event); break; case EVENT_DFS_CAC_FINISHED: if (!data) break; hostapd_event_dfs_cac_finished(hapd, &data->dfs_event); break; case EVENT_DFS_CAC_ABORTED: if (!data) break; hostapd_event_dfs_cac_aborted(hapd, &data->dfs_event); break; case EVENT_DFS_NOP_FINISHED: if (!data) break; hostapd_event_dfs_nop_finished(hapd, &data->dfs_event); break; case EVENT_CHANNEL_LIST_CHANGED: /* channel list changed (regulatory?), update channel list */ /* TODO: check this. hostapd_get_hw_features() initializes * too much stuff. */ /* hostapd_get_hw_features(hapd->iface); */ hostapd_channel_list_updated( hapd->iface, data->channel_list_changed.initiator); break; #endif /* NEED_AP_MLME */ default: wpa_printf(MSG_DEBUG, "Unknown event %d", event); break; } } #endif /* HOSTAPD */ wpa-2.1/src/ap/eap_user_db.c000066400000000000000000000162631227414666700157750ustar00rootroot00000000000000/* * hostapd / EAP user database * Copyright (c) 2012, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "includes.h" #ifdef CONFIG_SQLITE #include #endif /* CONFIG_SQLITE */ #include "common.h" #include "eap_common/eap_wsc_common.h" #include "eap_server/eap_methods.h" #include "eap_server/eap.h" #include "ap_config.h" #include "hostapd.h" #ifdef CONFIG_SQLITE static void set_user_methods(struct hostapd_eap_user *user, const char *methods) { char *buf, *start; int num_methods; buf = os_strdup(methods); if (buf == NULL) return; os_memset(&user->methods, 0, sizeof(user->methods)); num_methods = 0; start = buf; 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; } wpa_printf(MSG_INFO, "DB: Unsupported EAP type '%s'", start); os_free(buf); return; } num_methods++; if (num_methods >= EAP_MAX_METHODS) break; skip_eap: if (pos3 == NULL) break; start = pos3; } os_free(buf); } static int get_user_cb(void *ctx, int argc, char *argv[], char *col[]) { struct hostapd_eap_user *user = ctx; int i; for (i = 0; i < argc; i++) { if (os_strcmp(col[i], "password") == 0 && argv[i]) { os_free(user->password); user->password_len = os_strlen(argv[i]); user->password = (u8 *) os_strdup(argv[i]); user->next = (void *) 1; } else if (os_strcmp(col[i], "methods") == 0 && argv[i]) { set_user_methods(user, argv[i]); } } return 0; } static int get_wildcard_cb(void *ctx, int argc, char *argv[], char *col[]) { struct hostapd_eap_user *user = ctx; int i, id = -1, methods = -1; size_t len; for (i = 0; i < argc; i++) { if (os_strcmp(col[i], "identity") == 0 && argv[i]) id = i; else if (os_strcmp(col[i], "methods") == 0 && argv[i]) methods = i; } if (id < 0 || methods < 0) return 0; len = os_strlen(argv[id]); if (len <= user->identity_len && os_memcmp(argv[id], user->identity, len) == 0 && (user->password == NULL || len > user->password_len)) { os_free(user->password); user->password_len = os_strlen(argv[id]); user->password = (u8 *) os_strdup(argv[id]); user->next = (void *) 1; set_user_methods(user, argv[methods]); } return 0; } static const struct hostapd_eap_user * eap_user_sqlite_get(struct hostapd_data *hapd, const u8 *identity, size_t identity_len, int phase2) { sqlite3 *db; struct hostapd_eap_user *user = NULL; char id_str[256], cmd[300]; size_t i; if (identity_len >= sizeof(id_str)) return NULL; os_memcpy(id_str, identity, identity_len); id_str[identity_len] = '\0'; for (i = 0; i < identity_len; i++) { if (id_str[i] >= 'a' && id_str[i] <= 'z') continue; if (id_str[i] >= 'A' && id_str[i] <= 'Z') continue; if (id_str[i] >= '0' && id_str[i] <= '9') continue; if (id_str[i] == '-' || id_str[i] == '_' || id_str[i] == '.' || id_str[i] == ',' || id_str[i] == '@' || id_str[i] == '\\' || id_str[i] == '!' || id_str[i] == '#' || id_str[i] == '%' || id_str[i] == '=' || id_str[i] == ' ') continue; wpa_printf(MSG_INFO, "DB: Unsupported character in identity"); return NULL; } os_free(hapd->tmp_eap_user.identity); os_free(hapd->tmp_eap_user.password); os_memset(&hapd->tmp_eap_user, 0, sizeof(hapd->tmp_eap_user)); hapd->tmp_eap_user.phase2 = phase2; hapd->tmp_eap_user.identity = os_zalloc(identity_len + 1); if (hapd->tmp_eap_user.identity == NULL) return NULL; os_memcpy(hapd->tmp_eap_user.identity, identity, identity_len); if (sqlite3_open(hapd->conf->eap_user_sqlite, &db)) { wpa_printf(MSG_INFO, "DB: Failed to open database %s: %s", hapd->conf->eap_user_sqlite, sqlite3_errmsg(db)); sqlite3_close(db); return NULL; } os_snprintf(cmd, sizeof(cmd), "SELECT password,methods FROM users WHERE " "identity='%s' AND phase2=%d;", id_str, phase2); wpa_printf(MSG_DEBUG, "DB: %s", cmd); if (sqlite3_exec(db, cmd, get_user_cb, &hapd->tmp_eap_user, NULL) != SQLITE_OK) { wpa_printf(MSG_DEBUG, "DB: Failed to complete SQL operation"); } else if (hapd->tmp_eap_user.next) user = &hapd->tmp_eap_user; if (user == NULL && !phase2) { os_snprintf(cmd, sizeof(cmd), "SELECT identity,methods FROM wildcards;"); wpa_printf(MSG_DEBUG, "DB: %s", cmd); if (sqlite3_exec(db, cmd, get_wildcard_cb, &hapd->tmp_eap_user, NULL) != SQLITE_OK) { wpa_printf(MSG_DEBUG, "DB: Failed to complete SQL " "operation"); } else if (hapd->tmp_eap_user.next) { user = &hapd->tmp_eap_user; os_free(user->identity); user->identity = user->password; user->identity_len = user->password_len; user->password = NULL; user->password_len = 0; } } sqlite3_close(db); return user; } #endif /* CONFIG_SQLITE */ const struct hostapd_eap_user * hostapd_get_eap_user(struct hostapd_data *hapd, const u8 *identity, size_t identity_len, int phase2) { const struct hostapd_bss_config *conf = hapd->conf; struct hostapd_eap_user *user = conf->eap_user; #ifdef CONFIG_WPS if (conf->wps_state && identity_len == WSC_ID_ENROLLEE_LEN && os_memcmp(identity, WSC_ID_ENROLLEE, WSC_ID_ENROLLEE_LEN) == 0) { static struct hostapd_eap_user wsc_enrollee; os_memset(&wsc_enrollee, 0, sizeof(wsc_enrollee)); wsc_enrollee.methods[0].method = eap_server_get_type( "WSC", &wsc_enrollee.methods[0].vendor); return &wsc_enrollee; } if (conf->wps_state && identity_len == WSC_ID_REGISTRAR_LEN && os_memcmp(identity, WSC_ID_REGISTRAR, WSC_ID_REGISTRAR_LEN) == 0) { static struct hostapd_eap_user wsc_registrar; os_memset(&wsc_registrar, 0, sizeof(wsc_registrar)); wsc_registrar.methods[0].method = eap_server_get_type( "WSC", &wsc_registrar.methods[0].vendor); wsc_registrar.password = (u8 *) conf->ap_pin; wsc_registrar.password_len = conf->ap_pin ? os_strlen(conf->ap_pin) : 0; return &wsc_registrar; } #endif /* CONFIG_WPS */ while (user) { if (!phase2 && user->identity == NULL) { /* Wildcard match */ break; } if (user->phase2 == !!phase2 && user->wildcard_prefix && identity_len >= user->identity_len && os_memcmp(user->identity, identity, user->identity_len) == 0) { /* Wildcard prefix match */ break; } if (user->phase2 == !!phase2 && user->identity_len == identity_len && os_memcmp(user->identity, identity, identity_len) == 0) break; user = user->next; } #ifdef CONFIG_SQLITE if (user == NULL && conf->eap_user_sqlite) { return eap_user_sqlite_get(hapd, identity, identity_len, phase2); } #endif /* CONFIG_SQLITE */ return user; } wpa-2.1/src/ap/gas_serv.c000066400000000000000000001002661227414666700153330ustar00rootroot00000000000000/* * Generic advertisement service (GAS) server * Copyright (c) 2011-2014, Qualcomm Atheros, Inc. * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "includes.h" #include "common.h" #include "common/ieee802_11_defs.h" #include "common/gas.h" #include "utils/eloop.h" #include "hostapd.h" #include "ap_config.h" #include "ap_drv_ops.h" #include "sta_info.h" #include "gas_serv.h" static void convert_to_protected_dual(struct wpabuf *msg) { u8 *categ = wpabuf_mhead_u8(msg); *categ = WLAN_ACTION_PROTECTED_DUAL; } static struct gas_dialog_info * gas_dialog_create(struct hostapd_data *hapd, const u8 *addr, u8 dialog_token) { struct sta_info *sta; struct gas_dialog_info *dia = NULL; int i, j; sta = ap_get_sta(hapd, addr); if (!sta) { /* * We need a STA entry to be able to maintain state for * the GAS query. */ wpa_printf(MSG_DEBUG, "ANQP: Add a temporary STA entry for " "GAS query"); sta = ap_sta_add(hapd, addr); if (!sta) { wpa_printf(MSG_DEBUG, "Failed to add STA " MACSTR " for GAS query", MAC2STR(addr)); return NULL; } sta->flags |= WLAN_STA_GAS; /* * The default inactivity is 300 seconds. We don't need * it to be that long. */ ap_sta_session_timeout(hapd, sta, 5); } else { ap_sta_replenish_timeout(hapd, sta, 5); } if (sta->gas_dialog == NULL) { sta->gas_dialog = os_zalloc(GAS_DIALOG_MAX * sizeof(struct gas_dialog_info)); if (sta->gas_dialog == NULL) return NULL; } for (i = sta->gas_dialog_next, j = 0; j < GAS_DIALOG_MAX; i++, j++) { if (i == GAS_DIALOG_MAX) i = 0; if (sta->gas_dialog[i].valid) continue; dia = &sta->gas_dialog[i]; dia->valid = 1; dia->index = i; dia->dialog_token = dialog_token; sta->gas_dialog_next = (++i == GAS_DIALOG_MAX) ? 0 : i; return dia; } wpa_msg(hapd->msg_ctx, MSG_ERROR, "ANQP: Could not create dialog for " MACSTR " dialog_token %u. Consider increasing " "GAS_DIALOG_MAX.", MAC2STR(addr), dialog_token); return NULL; } struct gas_dialog_info * gas_serv_dialog_find(struct hostapd_data *hapd, const u8 *addr, u8 dialog_token) { struct sta_info *sta; int i; sta = ap_get_sta(hapd, addr); if (!sta) { wpa_printf(MSG_DEBUG, "ANQP: could not find STA " MACSTR, MAC2STR(addr)); return NULL; } for (i = 0; sta->gas_dialog && i < GAS_DIALOG_MAX; i++) { if (sta->gas_dialog[i].dialog_token != dialog_token || !sta->gas_dialog[i].valid) continue; return &sta->gas_dialog[i]; } wpa_printf(MSG_DEBUG, "ANQP: Could not find dialog for " MACSTR " dialog_token %u", MAC2STR(addr), dialog_token); return NULL; } void gas_serv_dialog_clear(struct gas_dialog_info *dia) { wpabuf_free(dia->sd_resp); os_memset(dia, 0, sizeof(*dia)); } static void gas_serv_free_dialogs(struct hostapd_data *hapd, const u8 *sta_addr) { struct sta_info *sta; int i; sta = ap_get_sta(hapd, sta_addr); if (sta == NULL || sta->gas_dialog == NULL) return; for (i = 0; i < GAS_DIALOG_MAX; i++) { if (sta->gas_dialog[i].valid) return; } os_free(sta->gas_dialog); sta->gas_dialog = NULL; } #ifdef CONFIG_HS20 static void anqp_add_hs_capab_list(struct hostapd_data *hapd, struct wpabuf *buf) { u8 *len; len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); wpabuf_put_be24(buf, OUI_WFA); wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE); wpabuf_put_u8(buf, HS20_STYPE_CAPABILITY_LIST); wpabuf_put_u8(buf, 0); /* Reserved */ wpabuf_put_u8(buf, HS20_STYPE_CAPABILITY_LIST); if (hapd->conf->hs20_oper_friendly_name) wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_FRIENDLY_NAME); if (hapd->conf->hs20_wan_metrics) wpabuf_put_u8(buf, HS20_STYPE_WAN_METRICS); if (hapd->conf->hs20_connection_capability) wpabuf_put_u8(buf, HS20_STYPE_CONNECTION_CAPABILITY); if (hapd->conf->nai_realm_data) wpabuf_put_u8(buf, HS20_STYPE_NAI_HOME_REALM_QUERY); if (hapd->conf->hs20_operating_class) wpabuf_put_u8(buf, HS20_STYPE_OPERATING_CLASS); gas_anqp_set_element_len(buf, len); } #endif /* CONFIG_HS20 */ static void anqp_add_capab_list(struct hostapd_data *hapd, struct wpabuf *buf) { u8 *len; len = gas_anqp_add_element(buf, ANQP_CAPABILITY_LIST); wpabuf_put_le16(buf, ANQP_CAPABILITY_LIST); if (hapd->conf->venue_name) wpabuf_put_le16(buf, ANQP_VENUE_NAME); if (hapd->conf->network_auth_type) wpabuf_put_le16(buf, ANQP_NETWORK_AUTH_TYPE); if (hapd->conf->roaming_consortium) wpabuf_put_le16(buf, ANQP_ROAMING_CONSORTIUM); if (hapd->conf->ipaddr_type_configured) wpabuf_put_le16(buf, ANQP_IP_ADDR_TYPE_AVAILABILITY); if (hapd->conf->nai_realm_data) wpabuf_put_le16(buf, ANQP_NAI_REALM); if (hapd->conf->anqp_3gpp_cell_net) wpabuf_put_le16(buf, ANQP_3GPP_CELLULAR_NETWORK); if (hapd->conf->domain_name) wpabuf_put_le16(buf, ANQP_DOMAIN_NAME); #ifdef CONFIG_HS20 anqp_add_hs_capab_list(hapd, buf); #endif /* CONFIG_HS20 */ gas_anqp_set_element_len(buf, len); } static void anqp_add_venue_name(struct hostapd_data *hapd, struct wpabuf *buf) { if (hapd->conf->venue_name) { u8 *len; unsigned int i; len = gas_anqp_add_element(buf, ANQP_VENUE_NAME); wpabuf_put_u8(buf, hapd->conf->venue_group); wpabuf_put_u8(buf, hapd->conf->venue_type); for (i = 0; i < hapd->conf->venue_name_count; i++) { struct hostapd_lang_string *vn; vn = &hapd->conf->venue_name[i]; wpabuf_put_u8(buf, 3 + vn->name_len); wpabuf_put_data(buf, vn->lang, 3); wpabuf_put_data(buf, vn->name, vn->name_len); } gas_anqp_set_element_len(buf, len); } } static void anqp_add_network_auth_type(struct hostapd_data *hapd, struct wpabuf *buf) { if (hapd->conf->network_auth_type) { wpabuf_put_le16(buf, ANQP_NETWORK_AUTH_TYPE); wpabuf_put_le16(buf, hapd->conf->network_auth_type_len); wpabuf_put_data(buf, hapd->conf->network_auth_type, hapd->conf->network_auth_type_len); } } static void anqp_add_roaming_consortium(struct hostapd_data *hapd, struct wpabuf *buf) { unsigned int i; u8 *len; len = gas_anqp_add_element(buf, ANQP_ROAMING_CONSORTIUM); for (i = 0; i < hapd->conf->roaming_consortium_count; i++) { struct hostapd_roaming_consortium *rc; rc = &hapd->conf->roaming_consortium[i]; wpabuf_put_u8(buf, rc->len); wpabuf_put_data(buf, rc->oi, rc->len); } gas_anqp_set_element_len(buf, len); } static void anqp_add_ip_addr_type_availability(struct hostapd_data *hapd, struct wpabuf *buf) { if (hapd->conf->ipaddr_type_configured) { wpabuf_put_le16(buf, ANQP_IP_ADDR_TYPE_AVAILABILITY); wpabuf_put_le16(buf, 1); wpabuf_put_u8(buf, hapd->conf->ipaddr_type_availability); } } static void anqp_add_nai_realm_eap(struct wpabuf *buf, struct hostapd_nai_realm_data *realm) { unsigned int i, j; wpabuf_put_u8(buf, realm->eap_method_count); for (i = 0; i < realm->eap_method_count; i++) { struct hostapd_nai_realm_eap *eap = &realm->eap_method[i]; wpabuf_put_u8(buf, 2 + (3 * eap->num_auths)); wpabuf_put_u8(buf, eap->eap_method); wpabuf_put_u8(buf, eap->num_auths); for (j = 0; j < eap->num_auths; j++) { wpabuf_put_u8(buf, eap->auth_id[j]); wpabuf_put_u8(buf, 1); wpabuf_put_u8(buf, eap->auth_val[j]); } } } static void anqp_add_nai_realm_data(struct wpabuf *buf, struct hostapd_nai_realm_data *realm, unsigned int realm_idx) { u8 *realm_data_len; wpa_printf(MSG_DEBUG, "realm=%s, len=%d", realm->realm[realm_idx], (int) os_strlen(realm->realm[realm_idx])); realm_data_len = wpabuf_put(buf, 2); wpabuf_put_u8(buf, realm->encoding); wpabuf_put_u8(buf, os_strlen(realm->realm[realm_idx])); wpabuf_put_str(buf, realm->realm[realm_idx]); anqp_add_nai_realm_eap(buf, realm); gas_anqp_set_element_len(buf, realm_data_len); } static int hs20_add_nai_home_realm_matches(struct hostapd_data *hapd, struct wpabuf *buf, const u8 *home_realm, size_t home_realm_len) { unsigned int i, j, k; u8 num_realms, num_matching = 0, encoding, realm_len, *realm_list_len; struct hostapd_nai_realm_data *realm; const u8 *pos, *realm_name, *end; struct { unsigned int realm_data_idx; unsigned int realm_idx; } matches[10]; pos = home_realm; end = pos + home_realm_len; if (pos + 1 > end) { wpa_hexdump(MSG_DEBUG, "Too short NAI Home Realm Query", home_realm, home_realm_len); return -1; } num_realms = *pos++; for (i = 0; i < num_realms && num_matching < 10; i++) { if (pos + 2 > end) { wpa_hexdump(MSG_DEBUG, "Truncated NAI Home Realm Query", home_realm, home_realm_len); return -1; } encoding = *pos++; realm_len = *pos++; if (pos + realm_len > end) { wpa_hexdump(MSG_DEBUG, "Truncated NAI Home Realm Query", home_realm, home_realm_len); return -1; } realm_name = pos; for (j = 0; j < hapd->conf->nai_realm_count && num_matching < 10; j++) { const u8 *rpos, *rend; realm = &hapd->conf->nai_realm_data[j]; if (encoding != realm->encoding) continue; rpos = realm_name; while (rpos < realm_name + realm_len && num_matching < 10) { for (rend = rpos; rend < realm_name + realm_len; rend++) { if (*rend == ';') break; } for (k = 0; k < MAX_NAI_REALMS && realm->realm[k] && num_matching < 10; k++) { if ((int) os_strlen(realm->realm[k]) != rend - rpos || os_strncmp((char *) rpos, realm->realm[k], rend - rpos) != 0) continue; matches[num_matching].realm_data_idx = j; matches[num_matching].realm_idx = k; num_matching++; } rpos = rend + 1; } } pos += realm_len; } realm_list_len = gas_anqp_add_element(buf, ANQP_NAI_REALM); wpabuf_put_le16(buf, num_matching); /* * There are two ways to format. 1. each realm in a NAI Realm Data unit * 2. all realms that share the same EAP methods in a NAI Realm Data * unit. The first format is likely to be bigger in size than the * second, but may be easier to parse and process by the receiver. */ for (i = 0; i < num_matching; i++) { wpa_printf(MSG_DEBUG, "realm_idx %d, realm_data_idx %d", matches[i].realm_data_idx, matches[i].realm_idx); realm = &hapd->conf->nai_realm_data[matches[i].realm_data_idx]; anqp_add_nai_realm_data(buf, realm, matches[i].realm_idx); } gas_anqp_set_element_len(buf, realm_list_len); return 0; } static void anqp_add_nai_realm(struct hostapd_data *hapd, struct wpabuf *buf, const u8 *home_realm, size_t home_realm_len, int nai_realm, int nai_home_realm) { if (nai_realm && hapd->conf->nai_realm_data) { u8 *len; unsigned int i, j; len = gas_anqp_add_element(buf, ANQP_NAI_REALM); wpabuf_put_le16(buf, hapd->conf->nai_realm_count); for (i = 0; i < hapd->conf->nai_realm_count; i++) { u8 *realm_data_len, *realm_len; struct hostapd_nai_realm_data *realm; realm = &hapd->conf->nai_realm_data[i]; realm_data_len = wpabuf_put(buf, 2); wpabuf_put_u8(buf, realm->encoding); realm_len = wpabuf_put(buf, 1); for (j = 0; realm->realm[j]; j++) { if (j > 0) wpabuf_put_u8(buf, ';'); wpabuf_put_str(buf, realm->realm[j]); } *realm_len = (u8 *) wpabuf_put(buf, 0) - realm_len - 1; anqp_add_nai_realm_eap(buf, realm); gas_anqp_set_element_len(buf, realm_data_len); } gas_anqp_set_element_len(buf, len); } else if (nai_home_realm && hapd->conf->nai_realm_data) { hs20_add_nai_home_realm_matches(hapd, buf, home_realm, home_realm_len); } } static void anqp_add_3gpp_cellular_network(struct hostapd_data *hapd, struct wpabuf *buf) { if (hapd->conf->anqp_3gpp_cell_net) { wpabuf_put_le16(buf, ANQP_3GPP_CELLULAR_NETWORK); wpabuf_put_le16(buf, hapd->conf->anqp_3gpp_cell_net_len); wpabuf_put_data(buf, hapd->conf->anqp_3gpp_cell_net, hapd->conf->anqp_3gpp_cell_net_len); } } static void anqp_add_domain_name(struct hostapd_data *hapd, struct wpabuf *buf) { if (hapd->conf->domain_name) { wpabuf_put_le16(buf, ANQP_DOMAIN_NAME); wpabuf_put_le16(buf, hapd->conf->domain_name_len); wpabuf_put_data(buf, hapd->conf->domain_name, hapd->conf->domain_name_len); } } #ifdef CONFIG_HS20 static void anqp_add_operator_friendly_name(struct hostapd_data *hapd, struct wpabuf *buf) { if (hapd->conf->hs20_oper_friendly_name) { u8 *len; unsigned int i; len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); wpabuf_put_be24(buf, OUI_WFA); wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE); wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_FRIENDLY_NAME); wpabuf_put_u8(buf, 0); /* Reserved */ for (i = 0; i < hapd->conf->hs20_oper_friendly_name_count; i++) { struct hostapd_lang_string *vn; vn = &hapd->conf->hs20_oper_friendly_name[i]; wpabuf_put_u8(buf, 3 + vn->name_len); wpabuf_put_data(buf, vn->lang, 3); wpabuf_put_data(buf, vn->name, vn->name_len); } gas_anqp_set_element_len(buf, len); } } static void anqp_add_wan_metrics(struct hostapd_data *hapd, struct wpabuf *buf) { if (hapd->conf->hs20_wan_metrics) { u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); wpabuf_put_be24(buf, OUI_WFA); wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE); wpabuf_put_u8(buf, HS20_STYPE_WAN_METRICS); wpabuf_put_u8(buf, 0); /* Reserved */ wpabuf_put_data(buf, hapd->conf->hs20_wan_metrics, 13); gas_anqp_set_element_len(buf, len); } } static void anqp_add_connection_capability(struct hostapd_data *hapd, struct wpabuf *buf) { if (hapd->conf->hs20_connection_capability) { u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); wpabuf_put_be24(buf, OUI_WFA); wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE); wpabuf_put_u8(buf, HS20_STYPE_CONNECTION_CAPABILITY); wpabuf_put_u8(buf, 0); /* Reserved */ wpabuf_put_data(buf, hapd->conf->hs20_connection_capability, hapd->conf->hs20_connection_capability_len); gas_anqp_set_element_len(buf, len); } } static void anqp_add_operating_class(struct hostapd_data *hapd, struct wpabuf *buf) { if (hapd->conf->hs20_operating_class) { u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); wpabuf_put_be24(buf, OUI_WFA); wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE); wpabuf_put_u8(buf, HS20_STYPE_OPERATING_CLASS); wpabuf_put_u8(buf, 0); /* Reserved */ wpabuf_put_data(buf, hapd->conf->hs20_operating_class, hapd->conf->hs20_operating_class_len); gas_anqp_set_element_len(buf, len); } } #endif /* CONFIG_HS20 */ static struct wpabuf * gas_serv_build_gas_resp_payload(struct hostapd_data *hapd, unsigned int request, struct gas_dialog_info *di, const u8 *home_realm, size_t home_realm_len) { struct wpabuf *buf; buf = wpabuf_alloc(1400); if (buf == NULL) return NULL; if (request & ANQP_REQ_CAPABILITY_LIST) anqp_add_capab_list(hapd, buf); if (request & ANQP_REQ_VENUE_NAME) anqp_add_venue_name(hapd, buf); if (request & ANQP_REQ_NETWORK_AUTH_TYPE) anqp_add_network_auth_type(hapd, buf); if (request & ANQP_REQ_ROAMING_CONSORTIUM) anqp_add_roaming_consortium(hapd, buf); if (request & ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY) anqp_add_ip_addr_type_availability(hapd, buf); if (request & (ANQP_REQ_NAI_REALM | ANQP_REQ_NAI_HOME_REALM)) anqp_add_nai_realm(hapd, buf, home_realm, home_realm_len, request & ANQP_REQ_NAI_REALM, request & ANQP_REQ_NAI_HOME_REALM); if (request & ANQP_REQ_3GPP_CELLULAR_NETWORK) anqp_add_3gpp_cellular_network(hapd, buf); if (request & ANQP_REQ_DOMAIN_NAME) anqp_add_domain_name(hapd, buf); #ifdef CONFIG_HS20 if (request & ANQP_REQ_HS_CAPABILITY_LIST) anqp_add_hs_capab_list(hapd, buf); if (request & ANQP_REQ_OPERATOR_FRIENDLY_NAME) anqp_add_operator_friendly_name(hapd, buf); if (request & ANQP_REQ_WAN_METRICS) anqp_add_wan_metrics(hapd, buf); if (request & ANQP_REQ_CONNECTION_CAPABILITY) anqp_add_connection_capability(hapd, buf); if (request & ANQP_REQ_OPERATING_CLASS) anqp_add_operating_class(hapd, buf); #endif /* CONFIG_HS20 */ return buf; } static void gas_serv_clear_cached_ies(void *eloop_data, void *user_ctx) { struct gas_dialog_info *dia = eloop_data; wpa_printf(MSG_DEBUG, "GAS: Timeout triggered, clearing dialog for " "dialog token %d", dia->dialog_token); gas_serv_dialog_clear(dia); } struct anqp_query_info { unsigned int request; unsigned int remote_request; const u8 *home_realm_query; size_t home_realm_query_len; u16 remote_delay; }; static void set_anqp_req(unsigned int bit, const char *name, int local, unsigned int remote, u16 remote_delay, struct anqp_query_info *qi) { qi->request |= bit; if (local) { wpa_printf(MSG_DEBUG, "ANQP: %s (local)", name); } else if (bit & remote) { wpa_printf(MSG_DEBUG, "ANQP: %s (remote)", name); qi->remote_request |= bit; if (remote_delay > qi->remote_delay) qi->remote_delay = remote_delay; } else { wpa_printf(MSG_DEBUG, "ANQP: %s not available", name); } } static void rx_anqp_query_list_id(struct hostapd_data *hapd, u16 info_id, struct anqp_query_info *qi) { switch (info_id) { case ANQP_CAPABILITY_LIST: set_anqp_req(ANQP_REQ_CAPABILITY_LIST, "Capability List", 1, 0, 0, qi); break; case ANQP_VENUE_NAME: set_anqp_req(ANQP_REQ_VENUE_NAME, "Venue Name", hapd->conf->venue_name != NULL, 0, 0, qi); break; case ANQP_NETWORK_AUTH_TYPE: set_anqp_req(ANQP_REQ_NETWORK_AUTH_TYPE, "Network Auth Type", hapd->conf->network_auth_type != NULL, 0, 0, qi); break; case ANQP_ROAMING_CONSORTIUM: set_anqp_req(ANQP_REQ_ROAMING_CONSORTIUM, "Roaming Consortium", hapd->conf->roaming_consortium != NULL, 0, 0, qi); break; case ANQP_IP_ADDR_TYPE_AVAILABILITY: set_anqp_req(ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY, "IP Addr Type Availability", hapd->conf->ipaddr_type_configured, 0, 0, qi); break; case ANQP_NAI_REALM: set_anqp_req(ANQP_REQ_NAI_REALM, "NAI Realm", hapd->conf->nai_realm_data != NULL, 0, 0, qi); break; case ANQP_3GPP_CELLULAR_NETWORK: set_anqp_req(ANQP_REQ_3GPP_CELLULAR_NETWORK, "3GPP Cellular Network", hapd->conf->anqp_3gpp_cell_net != NULL, 0, 0, qi); break; case ANQP_DOMAIN_NAME: set_anqp_req(ANQP_REQ_DOMAIN_NAME, "Domain Name", hapd->conf->domain_name != NULL, 0, 0, qi); break; default: wpa_printf(MSG_DEBUG, "ANQP: Unsupported Info Id %u", info_id); break; } } static void rx_anqp_query_list(struct hostapd_data *hapd, const u8 *pos, const u8 *end, struct anqp_query_info *qi) { wpa_printf(MSG_DEBUG, "ANQP: %u Info IDs requested in Query list", (unsigned int) (end - pos) / 2); while (pos + 2 <= end) { rx_anqp_query_list_id(hapd, WPA_GET_LE16(pos), qi); pos += 2; } } #ifdef CONFIG_HS20 static void rx_anqp_hs_query_list(struct hostapd_data *hapd, u8 subtype, struct anqp_query_info *qi) { switch (subtype) { case HS20_STYPE_CAPABILITY_LIST: set_anqp_req(ANQP_REQ_HS_CAPABILITY_LIST, "HS Capability List", 1, 0, 0, qi); break; case HS20_STYPE_OPERATOR_FRIENDLY_NAME: set_anqp_req(ANQP_REQ_OPERATOR_FRIENDLY_NAME, "Operator Friendly Name", hapd->conf->hs20_oper_friendly_name != NULL, 0, 0, qi); break; case HS20_STYPE_WAN_METRICS: set_anqp_req(ANQP_REQ_WAN_METRICS, "WAN Metrics", hapd->conf->hs20_wan_metrics != NULL, 0, 0, qi); break; case HS20_STYPE_CONNECTION_CAPABILITY: set_anqp_req(ANQP_REQ_CONNECTION_CAPABILITY, "Connection Capability", hapd->conf->hs20_connection_capability != NULL, 0, 0, qi); break; case HS20_STYPE_OPERATING_CLASS: set_anqp_req(ANQP_REQ_OPERATING_CLASS, "Operating Class", hapd->conf->hs20_operating_class != NULL, 0, 0, qi); break; default: wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 subtype %u", subtype); break; } } static void rx_anqp_hs_nai_home_realm(struct hostapd_data *hapd, const u8 *pos, const u8 *end, struct anqp_query_info *qi) { qi->request |= ANQP_REQ_NAI_HOME_REALM; qi->home_realm_query = pos; qi->home_realm_query_len = end - pos; if (hapd->conf->nai_realm_data != NULL) { wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 NAI Home Realm Query " "(local)"); } else { wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 NAI Home Realm Query not " "available"); } } static void rx_anqp_vendor_specific(struct hostapd_data *hapd, const u8 *pos, const u8 *end, struct anqp_query_info *qi) { u32 oui; u8 subtype; if (pos + 4 > end) { wpa_printf(MSG_DEBUG, "ANQP: Too short vendor specific ANQP " "Query element"); return; } oui = WPA_GET_BE24(pos); pos += 3; if (oui != OUI_WFA) { wpa_printf(MSG_DEBUG, "ANQP: Unsupported vendor OUI %06x", oui); return; } if (*pos != HS20_ANQP_OUI_TYPE) { wpa_printf(MSG_DEBUG, "ANQP: Unsupported WFA vendor type %u", *pos); return; } pos++; if (pos + 1 >= end) return; subtype = *pos++; pos++; /* Reserved */ switch (subtype) { case HS20_STYPE_QUERY_LIST: wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 Query List"); while (pos < end) { rx_anqp_hs_query_list(hapd, *pos, qi); pos++; } break; case HS20_STYPE_NAI_HOME_REALM_QUERY: rx_anqp_hs_nai_home_realm(hapd, pos, end, qi); break; default: wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 query subtype " "%u", subtype); break; } } #endif /* CONFIG_HS20 */ static void gas_serv_req_local_processing(struct hostapd_data *hapd, const u8 *sa, u8 dialog_token, struct anqp_query_info *qi, int prot) { struct wpabuf *buf, *tx_buf; buf = gas_serv_build_gas_resp_payload(hapd, qi->request, NULL, qi->home_realm_query, qi->home_realm_query_len); wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Locally generated ANQP responses", buf); if (!buf) return; if (wpabuf_len(buf) > hapd->gas_frag_limit || hapd->conf->gas_comeback_delay) { struct gas_dialog_info *di; u16 comeback_delay = 1; if (hapd->conf->gas_comeback_delay) { /* Testing - allow overriding of the delay value */ comeback_delay = hapd->conf->gas_comeback_delay; } wpa_printf(MSG_DEBUG, "ANQP: Too long response to fit in " "initial response - use GAS comeback"); di = gas_dialog_create(hapd, sa, dialog_token); if (!di) { wpa_printf(MSG_INFO, "ANQP: Could not create dialog " "for " MACSTR " (dialog token %u)", MAC2STR(sa), dialog_token); wpabuf_free(buf); return; } di->prot = prot; di->sd_resp = buf; di->sd_resp_pos = 0; tx_buf = gas_anqp_build_initial_resp_buf( dialog_token, WLAN_STATUS_SUCCESS, comeback_delay, NULL); } else { wpa_printf(MSG_DEBUG, "ANQP: Initial response (no comeback)"); tx_buf = gas_anqp_build_initial_resp_buf( dialog_token, WLAN_STATUS_SUCCESS, 0, buf); wpabuf_free(buf); } if (!tx_buf) return; if (prot) convert_to_protected_dual(tx_buf); hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa, wpabuf_head(tx_buf), wpabuf_len(tx_buf)); wpabuf_free(tx_buf); } static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd, const u8 *sa, const u8 *data, size_t len, int prot) { const u8 *pos = data; const u8 *end = data + len; const u8 *next; u8 dialog_token; u16 slen; struct anqp_query_info qi; const u8 *adv_proto; if (len < 1 + 2) return; os_memset(&qi, 0, sizeof(qi)); dialog_token = *pos++; wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: GAS Initial Request from " MACSTR " (dialog token %u) ", MAC2STR(sa), dialog_token); if (*pos != WLAN_EID_ADV_PROTO) { wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Unexpected IE in GAS Initial Request: %u", *pos); return; } adv_proto = pos++; slen = *pos++; next = pos + slen; if (next > end || slen < 2) { wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Invalid IE in GAS Initial Request"); return; } pos++; /* skip QueryRespLenLimit and PAME-BI */ if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) { struct wpabuf *buf; wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Unsupported GAS advertisement protocol id %u", *pos); if (sa[0] & 0x01) return; /* Invalid source address - drop silently */ buf = gas_build_initial_resp( dialog_token, WLAN_STATUS_GAS_ADV_PROTO_NOT_SUPPORTED, 0, 2 + slen + 2); if (buf == NULL) return; wpabuf_put_data(buf, adv_proto, 2 + slen); wpabuf_put_le16(buf, 0); /* Query Response Length */ if (prot) convert_to_protected_dual(buf); hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa, wpabuf_head(buf), wpabuf_len(buf)); wpabuf_free(buf); return; } pos = next; /* Query Request */ if (pos + 2 > end) return; slen = WPA_GET_LE16(pos); pos += 2; if (pos + slen > end) return; end = pos + slen; /* ANQP Query Request */ while (pos < end) { u16 info_id, elen; if (pos + 4 > end) return; info_id = WPA_GET_LE16(pos); pos += 2; elen = WPA_GET_LE16(pos); pos += 2; if (pos + elen > end) { wpa_printf(MSG_DEBUG, "ANQP: Invalid Query Request"); return; } switch (info_id) { case ANQP_QUERY_LIST: rx_anqp_query_list(hapd, pos, pos + elen, &qi); break; #ifdef CONFIG_HS20 case ANQP_VENDOR_SPECIFIC: rx_anqp_vendor_specific(hapd, pos, pos + elen, &qi); break; #endif /* CONFIG_HS20 */ default: wpa_printf(MSG_DEBUG, "ANQP: Unsupported Query " "Request element %u", info_id); break; } pos += elen; } gas_serv_req_local_processing(hapd, sa, dialog_token, &qi, prot); } void gas_serv_tx_gas_response(struct hostapd_data *hapd, const u8 *dst, struct gas_dialog_info *dialog) { struct wpabuf *buf, *tx_buf; u8 dialog_token = dialog->dialog_token; size_t frag_len; if (dialog->sd_resp == NULL) { buf = gas_serv_build_gas_resp_payload(hapd, dialog->all_requested, dialog, NULL, 0); wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Generated ANQP responses", buf); if (!buf) goto tx_gas_response_done; dialog->sd_resp = buf; dialog->sd_resp_pos = 0; } frag_len = wpabuf_len(dialog->sd_resp) - dialog->sd_resp_pos; if (frag_len > hapd->gas_frag_limit || dialog->comeback_delay || hapd->conf->gas_comeback_delay) { u16 comeback_delay_tus = dialog->comeback_delay + GAS_SERV_COMEBACK_DELAY_FUDGE; u32 comeback_delay_secs, comeback_delay_usecs; if (hapd->conf->gas_comeback_delay) { /* Testing - allow overriding of the delay value */ comeback_delay_tus = hapd->conf->gas_comeback_delay; } wpa_printf(MSG_DEBUG, "GAS: Response frag_len %u (frag limit " "%u) and comeback delay %u, " "requesting comebacks", (unsigned int) frag_len, (unsigned int) hapd->gas_frag_limit, dialog->comeback_delay); tx_buf = gas_anqp_build_initial_resp_buf(dialog_token, WLAN_STATUS_SUCCESS, comeback_delay_tus, NULL); if (tx_buf) { wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Tx GAS Initial Resp (comeback = 10TU)"); if (dialog->prot) convert_to_protected_dual(tx_buf); hostapd_drv_send_action(hapd, hapd->iface->freq, 0, dst, wpabuf_head(tx_buf), wpabuf_len(tx_buf)); } wpabuf_free(tx_buf); /* start a timer of 1.5 * comeback-delay */ comeback_delay_tus = comeback_delay_tus + (comeback_delay_tus / 2); comeback_delay_secs = (comeback_delay_tus * 1024) / 1000000; comeback_delay_usecs = (comeback_delay_tus * 1024) - (comeback_delay_secs * 1000000); eloop_register_timeout(comeback_delay_secs, comeback_delay_usecs, gas_serv_clear_cached_ies, dialog, NULL); goto tx_gas_response_done; } buf = wpabuf_alloc_copy(wpabuf_head_u8(dialog->sd_resp) + dialog->sd_resp_pos, frag_len); if (buf == NULL) { wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Buffer allocation " "failed"); goto tx_gas_response_done; } tx_buf = gas_anqp_build_initial_resp_buf(dialog_token, WLAN_STATUS_SUCCESS, 0, buf); wpabuf_free(buf); if (tx_buf == NULL) goto tx_gas_response_done; wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Tx GAS Initial " "Response (frag_id %d frag_len %d)", dialog->sd_frag_id, (int) frag_len); dialog->sd_frag_id++; if (dialog->prot) convert_to_protected_dual(tx_buf); hostapd_drv_send_action(hapd, hapd->iface->freq, 0, dst, wpabuf_head(tx_buf), wpabuf_len(tx_buf)); wpabuf_free(tx_buf); tx_gas_response_done: gas_serv_clear_cached_ies(dialog, NULL); } static void gas_serv_rx_gas_comeback_req(struct hostapd_data *hapd, const u8 *sa, const u8 *data, size_t len, int prot) { struct gas_dialog_info *dialog; struct wpabuf *buf, *tx_buf; u8 dialog_token; size_t frag_len; int more = 0; wpa_hexdump(MSG_DEBUG, "GAS: RX GAS Comeback Request", data, len); if (len < 1) return; dialog_token = *data; wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Dialog Token: %u", dialog_token); dialog = gas_serv_dialog_find(hapd, sa, dialog_token); if (!dialog) { wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: No pending SD " "response fragment for " MACSTR " dialog token %u", MAC2STR(sa), dialog_token); if (sa[0] & 0x01) return; /* Invalid source address - drop silently */ tx_buf = gas_anqp_build_comeback_resp_buf( dialog_token, WLAN_STATUS_NO_OUTSTANDING_GAS_REQ, 0, 0, 0, NULL); if (tx_buf == NULL) return; goto send_resp; } if (dialog->sd_resp == NULL) { wpa_printf(MSG_DEBUG, "GAS: Remote request 0x%x received 0x%x", dialog->requested, dialog->received); if ((dialog->requested & dialog->received) != dialog->requested) { wpa_printf(MSG_DEBUG, "GAS: Did not receive response " "from remote processing"); gas_serv_dialog_clear(dialog); tx_buf = gas_anqp_build_comeback_resp_buf( dialog_token, WLAN_STATUS_GAS_RESP_NOT_RECEIVED, 0, 0, 0, NULL); if (tx_buf == NULL) return; goto send_resp; } buf = gas_serv_build_gas_resp_payload(hapd, dialog->all_requested, dialog, NULL, 0); wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Generated ANQP responses", buf); if (!buf) goto rx_gas_comeback_req_done; dialog->sd_resp = buf; dialog->sd_resp_pos = 0; } frag_len = wpabuf_len(dialog->sd_resp) - dialog->sd_resp_pos; if (frag_len > hapd->gas_frag_limit) { frag_len = hapd->gas_frag_limit; more = 1; } wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: resp frag_len %u", (unsigned int) frag_len); buf = wpabuf_alloc_copy(wpabuf_head_u8(dialog->sd_resp) + dialog->sd_resp_pos, frag_len); if (buf == NULL) { wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Failed to allocate " "buffer"); goto rx_gas_comeback_req_done; } tx_buf = gas_anqp_build_comeback_resp_buf(dialog_token, WLAN_STATUS_SUCCESS, dialog->sd_frag_id, more, 0, buf); wpabuf_free(buf); if (tx_buf == NULL) goto rx_gas_comeback_req_done; wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Tx GAS Comeback Response " "(frag_id %d more=%d frag_len=%d)", dialog->sd_frag_id, more, (int) frag_len); dialog->sd_frag_id++; dialog->sd_resp_pos += frag_len; if (more) { wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: %d more bytes remain " "to be sent", (int) (wpabuf_len(dialog->sd_resp) - dialog->sd_resp_pos)); } else { wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: All fragments of " "SD response sent"); gas_serv_dialog_clear(dialog); gas_serv_free_dialogs(hapd, sa); } send_resp: if (prot) convert_to_protected_dual(tx_buf); hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa, wpabuf_head(tx_buf), wpabuf_len(tx_buf)); wpabuf_free(tx_buf); return; rx_gas_comeback_req_done: gas_serv_clear_cached_ies(dialog, NULL); } static void gas_serv_rx_public_action(void *ctx, const u8 *buf, size_t len, int freq) { struct hostapd_data *hapd = ctx; const struct ieee80211_mgmt *mgmt; size_t hdr_len; const u8 *sa, *data; int prot; mgmt = (const struct ieee80211_mgmt *) buf; hdr_len = (const u8 *) &mgmt->u.action.u.vs_public_action.action - buf; if (hdr_len > len) return; if (mgmt->u.action.category != WLAN_ACTION_PUBLIC && mgmt->u.action.category != WLAN_ACTION_PROTECTED_DUAL) return; /* * Note: Public Action and Protected Dual of Public Action frames share * the same payload structure, so it is fine to use definitions of * Public Action frames to process both. */ prot = mgmt->u.action.category == WLAN_ACTION_PROTECTED_DUAL; sa = mgmt->sa; len -= hdr_len; data = &mgmt->u.action.u.public_action.action; switch (data[0]) { case WLAN_PA_GAS_INITIAL_REQ: gas_serv_rx_gas_initial_req(hapd, sa, data + 1, len - 1, prot); break; case WLAN_PA_GAS_COMEBACK_REQ: gas_serv_rx_gas_comeback_req(hapd, sa, data + 1, len - 1, prot); break; } } int gas_serv_init(struct hostapd_data *hapd) { hapd->public_action_cb2 = gas_serv_rx_public_action; hapd->public_action_cb2_ctx = hapd; hapd->gas_frag_limit = 1400; if (hapd->conf->gas_frag_limit > 0) hapd->gas_frag_limit = hapd->conf->gas_frag_limit; return 0; } void gas_serv_deinit(struct hostapd_data *hapd) { } wpa-2.1/src/ap/gas_serv.h000066400000000000000000000045011227414666700153330ustar00rootroot00000000000000/* * Generic advertisement service (GAS) server * Copyright (c) 2011-2012, Qualcomm Atheros, Inc. * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef GAS_SERV_H #define GAS_SERV_H #define ANQP_REQ_CAPABILITY_LIST \ (1 << (ANQP_CAPABILITY_LIST - ANQP_QUERY_LIST)) #define ANQP_REQ_VENUE_NAME \ (1 << (ANQP_VENUE_NAME - ANQP_QUERY_LIST)) #define ANQP_REQ_NETWORK_AUTH_TYPE \ (1 << (ANQP_NETWORK_AUTH_TYPE - ANQP_QUERY_LIST)) #define ANQP_REQ_ROAMING_CONSORTIUM \ (1 << (ANQP_ROAMING_CONSORTIUM - ANQP_QUERY_LIST)) #define ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY \ (1 << (ANQP_IP_ADDR_TYPE_AVAILABILITY - ANQP_QUERY_LIST)) #define ANQP_REQ_NAI_REALM \ (1 << (ANQP_NAI_REALM - ANQP_QUERY_LIST)) #define ANQP_REQ_3GPP_CELLULAR_NETWORK \ (1 << (ANQP_3GPP_CELLULAR_NETWORK - ANQP_QUERY_LIST)) #define ANQP_REQ_DOMAIN_NAME \ (1 << (ANQP_DOMAIN_NAME - ANQP_QUERY_LIST)) #define ANQP_REQ_HS_CAPABILITY_LIST \ (0x10000 << HS20_STYPE_CAPABILITY_LIST) #define ANQP_REQ_OPERATOR_FRIENDLY_NAME \ (0x10000 << HS20_STYPE_OPERATOR_FRIENDLY_NAME) #define ANQP_REQ_WAN_METRICS \ (0x10000 << HS20_STYPE_WAN_METRICS) #define ANQP_REQ_CONNECTION_CAPABILITY \ (0x10000 << HS20_STYPE_CONNECTION_CAPABILITY) #define ANQP_REQ_NAI_HOME_REALM \ (0x10000 << HS20_STYPE_NAI_HOME_REALM_QUERY) #define ANQP_REQ_OPERATING_CLASS \ (0x10000 << HS20_STYPE_OPERATING_CLASS) /* To account for latencies between hostapd and external ANQP processor */ #define GAS_SERV_COMEBACK_DELAY_FUDGE 10 #define GAS_SERV_MIN_COMEBACK_DELAY 100 /* in TU */ struct gas_dialog_info { u8 valid; u8 index; struct wpabuf *sd_resp; /* Fragmented response */ u8 dialog_token; size_t sd_resp_pos; /* Offset in sd_resp */ u8 sd_frag_id; u16 comeback_delay; int prot; /* whether Protected Dual of Public Action frame is used */ unsigned int requested; unsigned int received; unsigned int all_requested; }; struct hostapd_data; void gas_serv_tx_gas_response(struct hostapd_data *hapd, const u8 *dst, struct gas_dialog_info *dialog); struct gas_dialog_info * gas_serv_dialog_find(struct hostapd_data *hapd, const u8 *addr, u8 dialog_token); void gas_serv_dialog_clear(struct gas_dialog_info *dialog); int gas_serv_init(struct hostapd_data *hapd); void gas_serv_deinit(struct hostapd_data *hapd); #endif /* GAS_SERV_H */ wpa-2.1/src/ap/hostapd.c000066400000000000000000001626201227414666700151660ustar00rootroot00000000000000/* * hostapd / Initialization and configuration * Copyright (c) 2002-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/eloop.h" #include "common/ieee802_11_defs.h" #include "common/wpa_ctrl.h" #include "radius/radius_client.h" #include "radius/radius_das.h" #include "hostapd.h" #include "authsrv.h" #include "sta_info.h" #include "accounting.h" #include "ap_list.h" #include "beacon.h" #include "iapp.h" #include "ieee802_1x.h" #include "ieee802_11_auth.h" #include "vlan_init.h" #include "wpa_auth.h" #include "wps_hostapd.h" #include "hw_features.h" #include "wpa_auth_glue.h" #include "ap_drv_ops.h" #include "ap_config.h" #include "p2p_hostapd.h" #include "gas_serv.h" #include "dfs.h" static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason); static int hostapd_setup_encryption(char *iface, struct hostapd_data *hapd); static int hostapd_broadcast_wep_clear(struct hostapd_data *hapd); static int setup_interface2(struct hostapd_iface *iface); static void channel_list_update_timeout(void *eloop_ctx, void *timeout_ctx); int hostapd_for_each_interface(struct hapd_interfaces *interfaces, int (*cb)(struct hostapd_iface *iface, void *ctx), void *ctx) { size_t i; int ret; for (i = 0; i < interfaces->count; i++) { ret = cb(interfaces->iface[i], ctx); if (ret) return ret; } return 0; } static void hostapd_reload_bss(struct hostapd_data *hapd) { struct hostapd_ssid *ssid; #ifndef CONFIG_NO_RADIUS radius_client_reconfig(hapd->radius, hapd->conf->radius); #endif /* CONFIG_NO_RADIUS */ ssid = &hapd->conf->ssid; if (!ssid->wpa_psk_set && ssid->wpa_psk && !ssid->wpa_psk->next && ssid->wpa_passphrase_set && ssid->wpa_passphrase) { /* * Force PSK to be derived again since SSID or passphrase may * have changed. */ os_free(ssid->wpa_psk); ssid->wpa_psk = NULL; } if (hostapd_setup_wpa_psk(hapd->conf)) { wpa_printf(MSG_ERROR, "Failed to re-configure WPA PSK " "after reloading configuration"); } if (hapd->conf->ieee802_1x || hapd->conf->wpa) hostapd_set_drv_ieee8021x(hapd, hapd->conf->iface, 1); else hostapd_set_drv_ieee8021x(hapd, hapd->conf->iface, 0); if (hapd->conf->wpa && hapd->wpa_auth == NULL) { hostapd_setup_wpa(hapd); if (hapd->wpa_auth) wpa_init_keys(hapd->wpa_auth); } else if (hapd->conf->wpa) { const u8 *wpa_ie; size_t wpa_ie_len; hostapd_reconfig_wpa(hapd); wpa_ie = wpa_auth_get_wpa_ie(hapd->wpa_auth, &wpa_ie_len); if (hostapd_set_generic_elem(hapd, wpa_ie, wpa_ie_len)) wpa_printf(MSG_ERROR, "Failed to configure WPA IE for " "the kernel driver."); } else if (hapd->wpa_auth) { wpa_deinit(hapd->wpa_auth); hapd->wpa_auth = NULL; hostapd_set_privacy(hapd, 0); hostapd_setup_encryption(hapd->conf->iface, hapd); hostapd_set_generic_elem(hapd, (u8 *) "", 0); } ieee802_11_set_beacon(hapd); hostapd_update_wps(hapd); if (hapd->conf->ssid.ssid_set && hostapd_set_ssid(hapd, hapd->conf->ssid.ssid, hapd->conf->ssid.ssid_len)) { wpa_printf(MSG_ERROR, "Could not set SSID for kernel driver"); /* try to continue */ } wpa_printf(MSG_DEBUG, "Reconfigured interface %s", hapd->conf->iface); } static void hostapd_clear_old(struct hostapd_iface *iface) { size_t j; /* * Deauthenticate all stations since the new configuration may not * allow them to use the BSS anymore. */ for (j = 0; j < iface->num_bss; j++) { hostapd_flush_old_stations(iface->bss[j], WLAN_REASON_PREV_AUTH_NOT_VALID); hostapd_broadcast_wep_clear(iface->bss[j]); #ifndef CONFIG_NO_RADIUS /* TODO: update dynamic data based on changed configuration * items (e.g., open/close sockets, etc.) */ radius_client_flush(iface->bss[j]->radius, 0); #endif /* CONFIG_NO_RADIUS */ } } int hostapd_reload_config(struct hostapd_iface *iface) { struct hostapd_data *hapd = iface->bss[0]; struct hostapd_config *newconf, *oldconf; size_t j; if (iface->config_fname == NULL) { /* Only in-memory config in use - assume it has been updated */ hostapd_clear_old(iface); for (j = 0; j < iface->num_bss; j++) hostapd_reload_bss(iface->bss[j]); return 0; } if (iface->interfaces == NULL || iface->interfaces->config_read_cb == NULL) return -1; newconf = iface->interfaces->config_read_cb(iface->config_fname); if (newconf == NULL) return -1; hostapd_clear_old(iface); oldconf = hapd->iconf; iface->conf = newconf; for (j = 0; j < iface->num_bss; j++) { hapd = iface->bss[j]; hapd->iconf = newconf; hapd->conf = newconf->bss[j]; hostapd_reload_bss(hapd); } hostapd_config_free(oldconf); return 0; } static void hostapd_broadcast_key_clear_iface(struct hostapd_data *hapd, char *ifname) { int i; for (i = 0; i < NUM_WEP_KEYS; i++) { if (hostapd_drv_set_key(ifname, hapd, WPA_ALG_NONE, NULL, i, 0, NULL, 0, NULL, 0)) { wpa_printf(MSG_DEBUG, "Failed to clear default " "encryption keys (ifname=%s keyidx=%d)", ifname, i); } } #ifdef CONFIG_IEEE80211W if (hapd->conf->ieee80211w) { for (i = NUM_WEP_KEYS; i < NUM_WEP_KEYS + 2; i++) { if (hostapd_drv_set_key(ifname, hapd, WPA_ALG_NONE, NULL, i, 0, NULL, 0, NULL, 0)) { wpa_printf(MSG_DEBUG, "Failed to clear " "default mgmt encryption keys " "(ifname=%s keyidx=%d)", ifname, i); } } } #endif /* CONFIG_IEEE80211W */ } static int hostapd_broadcast_wep_clear(struct hostapd_data *hapd) { hostapd_broadcast_key_clear_iface(hapd, hapd->conf->iface); return 0; } static int hostapd_broadcast_wep_set(struct hostapd_data *hapd) { int errors = 0, idx; struct hostapd_ssid *ssid = &hapd->conf->ssid; idx = ssid->wep.idx; if (ssid->wep.default_len && hostapd_drv_set_key(hapd->conf->iface, hapd, WPA_ALG_WEP, broadcast_ether_addr, idx, 1, NULL, 0, ssid->wep.key[idx], ssid->wep.len[idx])) { wpa_printf(MSG_WARNING, "Could not set WEP encryption."); errors++; } return errors; } static void hostapd_free_hapd_data(struct hostapd_data *hapd) { if (!hapd->started) { wpa_printf(MSG_ERROR, "%s: Interface %s wasn't started", __func__, hapd->conf->iface); return; } hapd->started = 0; wpa_printf(MSG_DEBUG, "%s(%s)", __func__, hapd->conf->iface); iapp_deinit(hapd->iapp); hapd->iapp = NULL; accounting_deinit(hapd); hostapd_deinit_wpa(hapd); vlan_deinit(hapd); hostapd_acl_deinit(hapd); #ifndef CONFIG_NO_RADIUS radius_client_deinit(hapd->radius); hapd->radius = NULL; radius_das_deinit(hapd->radius_das); hapd->radius_das = NULL; #endif /* CONFIG_NO_RADIUS */ hostapd_deinit_wps(hapd); authsrv_deinit(hapd); if (hapd->interface_added && hostapd_if_remove(hapd, WPA_IF_AP_BSS, hapd->conf->iface)) { wpa_printf(MSG_WARNING, "Failed to remove BSS interface %s", hapd->conf->iface); } os_free(hapd->probereq_cb); hapd->probereq_cb = NULL; #ifdef CONFIG_P2P wpabuf_free(hapd->p2p_beacon_ie); hapd->p2p_beacon_ie = NULL; wpabuf_free(hapd->p2p_probe_resp_ie); hapd->p2p_probe_resp_ie = NULL; #endif /* CONFIG_P2P */ wpabuf_free(hapd->time_adv); #ifdef CONFIG_INTERWORKING gas_serv_deinit(hapd); #endif /* CONFIG_INTERWORKING */ #ifdef CONFIG_SQLITE os_free(hapd->tmp_eap_user.identity); os_free(hapd->tmp_eap_user.password); #endif /* CONFIG_SQLITE */ } /** * hostapd_cleanup - Per-BSS cleanup (deinitialization) * @hapd: Pointer to BSS data * * This function is used to free all per-BSS data structures and resources. * Most of the modules that are initialized in hostapd_setup_bss() are * deinitialized here. */ static void hostapd_cleanup(struct hostapd_data *hapd) { wpa_printf(MSG_DEBUG, "%s(hapd=%p (%s))", __func__, hapd, hapd->conf->iface); if (hapd->iface->interfaces && hapd->iface->interfaces->ctrl_iface_deinit) hapd->iface->interfaces->ctrl_iface_deinit(hapd); hostapd_free_hapd_data(hapd); } static void hostapd_cleanup_iface_partial(struct hostapd_iface *iface) { wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface); hostapd_free_hw_features(iface->hw_features, iface->num_hw_features); iface->hw_features = NULL; os_free(iface->current_rates); iface->current_rates = NULL; os_free(iface->basic_rates); iface->basic_rates = NULL; ap_list_deinit(iface); } /** * hostapd_cleanup_iface - Complete per-interface cleanup * @iface: Pointer to interface data * * This function is called after per-BSS data structures are deinitialized * with hostapd_cleanup(). */ static void hostapd_cleanup_iface(struct hostapd_iface *iface) { wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface); eloop_cancel_timeout(channel_list_update_timeout, iface, NULL); hostapd_cleanup_iface_partial(iface); hostapd_config_free(iface->conf); iface->conf = NULL; os_free(iface->config_fname); os_free(iface->bss); wpa_printf(MSG_DEBUG, "%s: free iface=%p", __func__, iface); os_free(iface); } static void hostapd_clear_wep(struct hostapd_data *hapd) { if (hapd->drv_priv) { hostapd_set_privacy(hapd, 0); hostapd_broadcast_wep_clear(hapd); } } static int hostapd_setup_encryption(char *iface, struct hostapd_data *hapd) { int i; hostapd_broadcast_wep_set(hapd); if (hapd->conf->ssid.wep.default_len) { hostapd_set_privacy(hapd, 1); return 0; } /* * When IEEE 802.1X is not enabled, the driver may need to know how to * set authentication algorithms for static WEP. */ hostapd_drv_set_authmode(hapd, hapd->conf->auth_algs); for (i = 0; i < 4; i++) { if (hapd->conf->ssid.wep.key[i] && hostapd_drv_set_key(iface, hapd, WPA_ALG_WEP, NULL, i, i == hapd->conf->ssid.wep.idx, NULL, 0, hapd->conf->ssid.wep.key[i], hapd->conf->ssid.wep.len[i])) { wpa_printf(MSG_WARNING, "Could not set WEP " "encryption."); return -1; } if (hapd->conf->ssid.wep.key[i] && i == hapd->conf->ssid.wep.idx) hostapd_set_privacy(hapd, 1); } return 0; } static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason) { int ret = 0; u8 addr[ETH_ALEN]; if (hostapd_drv_none(hapd) || hapd->drv_priv == NULL) return 0; wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "Flushing old station entries"); if (hostapd_flush(hapd)) { wpa_msg(hapd->msg_ctx, MSG_WARNING, "Could not connect to " "kernel driver"); ret = -1; } wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "Deauthenticate all stations"); os_memset(addr, 0xff, ETH_ALEN); hostapd_drv_sta_deauth(hapd, addr, reason); hostapd_free_stas(hapd); return ret; } /** * hostapd_validate_bssid_configuration - Validate BSSID configuration * @iface: Pointer to interface data * Returns: 0 on success, -1 on failure * * This function is used to validate that the configured BSSIDs are valid. */ static int hostapd_validate_bssid_configuration(struct hostapd_iface *iface) { u8 mask[ETH_ALEN] = { 0 }; struct hostapd_data *hapd = iface->bss[0]; unsigned int i = iface->conf->num_bss, bits = 0, j; int auto_addr = 0; if (hostapd_drv_none(hapd)) return 0; /* Generate BSSID mask that is large enough to cover the BSSIDs. */ /* Determine the bits necessary to cover the number of BSSIDs. */ for (i--; i; i >>= 1) bits++; /* Determine the bits necessary to any configured BSSIDs, if they are higher than the number of BSSIDs. */ for (j = 0; j < iface->conf->num_bss; j++) { if (hostapd_mac_comp_empty(iface->conf->bss[j]->bssid) == 0) { if (j) auto_addr++; continue; } for (i = 0; i < ETH_ALEN; i++) { mask[i] |= iface->conf->bss[j]->bssid[i] ^ hapd->own_addr[i]; } } if (!auto_addr) goto skip_mask_ext; for (i = 0; i < ETH_ALEN && mask[i] == 0; i++) ; j = 0; if (i < ETH_ALEN) { j = (5 - i) * 8; while (mask[i] != 0) { mask[i] >>= 1; j++; } } if (bits < j) bits = j; if (bits > 40) { wpa_printf(MSG_ERROR, "Too many bits in the BSSID mask (%u)", bits); return -1; } os_memset(mask, 0xff, ETH_ALEN); j = bits / 8; for (i = 5; i > 5 - j; i--) mask[i] = 0; j = bits % 8; while (j--) mask[i] <<= 1; skip_mask_ext: wpa_printf(MSG_DEBUG, "BSS count %lu, BSSID mask " MACSTR " (%d bits)", (unsigned long) iface->conf->num_bss, MAC2STR(mask), bits); if (!auto_addr) return 0; for (i = 0; i < ETH_ALEN; i++) { if ((hapd->own_addr[i] & mask[i]) != hapd->own_addr[i]) { wpa_printf(MSG_ERROR, "Invalid BSSID mask " MACSTR " for start address " MACSTR ".", MAC2STR(mask), MAC2STR(hapd->own_addr)); wpa_printf(MSG_ERROR, "Start address must be the " "first address in the block (i.e., addr " "AND mask == addr)."); return -1; } } return 0; } static int mac_in_conf(struct hostapd_config *conf, const void *a) { size_t i; for (i = 0; i < conf->num_bss; i++) { if (hostapd_mac_comp(conf->bss[i]->bssid, a) == 0) { return 1; } } return 0; } #ifndef CONFIG_NO_RADIUS static int hostapd_das_nas_mismatch(struct hostapd_data *hapd, struct radius_das_attrs *attr) { /* TODO */ return 0; } static struct sta_info * hostapd_das_find_sta(struct hostapd_data *hapd, struct radius_das_attrs *attr) { struct sta_info *sta = NULL; char buf[128]; if (attr->sta_addr) sta = ap_get_sta(hapd, attr->sta_addr); if (sta == NULL && attr->acct_session_id && attr->acct_session_id_len == 17) { for (sta = hapd->sta_list; sta; sta = sta->next) { os_snprintf(buf, sizeof(buf), "%08X-%08X", sta->acct_session_id_hi, sta->acct_session_id_lo); if (os_memcmp(attr->acct_session_id, buf, 17) == 0) break; } } if (sta == NULL && attr->cui) { for (sta = hapd->sta_list; sta; sta = sta->next) { struct wpabuf *cui; cui = ieee802_1x_get_radius_cui(sta->eapol_sm); if (cui && wpabuf_len(cui) == attr->cui_len && os_memcmp(wpabuf_head(cui), attr->cui, attr->cui_len) == 0) break; } } if (sta == NULL && attr->user_name) { for (sta = hapd->sta_list; sta; sta = sta->next) { u8 *identity; size_t identity_len; identity = ieee802_1x_get_identity(sta->eapol_sm, &identity_len); if (identity && identity_len == attr->user_name_len && os_memcmp(identity, attr->user_name, identity_len) == 0) break; } } return sta; } static enum radius_das_res hostapd_das_disconnect(void *ctx, struct radius_das_attrs *attr) { struct hostapd_data *hapd = ctx; struct sta_info *sta; if (hostapd_das_nas_mismatch(hapd, attr)) return RADIUS_DAS_NAS_MISMATCH; sta = hostapd_das_find_sta(hapd, attr); if (sta == NULL) return RADIUS_DAS_SESSION_NOT_FOUND; hostapd_drv_sta_deauth(hapd, sta->addr, WLAN_REASON_PREV_AUTH_NOT_VALID); ap_sta_deauthenticate(hapd, sta, WLAN_REASON_PREV_AUTH_NOT_VALID); return RADIUS_DAS_SUCCESS; } #endif /* CONFIG_NO_RADIUS */ /** * hostapd_setup_bss - Per-BSS setup (initialization) * @hapd: Pointer to BSS data * @first: Whether this BSS is the first BSS of an interface; -1 = not first, * but interface may exist * * This function is used to initialize all per-BSS data structures and * resources. This gets called in a loop for each BSS when an interface is * initialized. Most of the modules that are initialized here will be * deinitialized in hostapd_cleanup(). */ static int hostapd_setup_bss(struct hostapd_data *hapd, int first) { struct hostapd_bss_config *conf = hapd->conf; u8 ssid[HOSTAPD_MAX_SSID_LEN + 1]; int ssid_len, set_ssid; char force_ifname[IFNAMSIZ]; u8 if_addr[ETH_ALEN]; wpa_printf(MSG_DEBUG, "%s(hapd=%p (%s), first=%d)", __func__, hapd, hapd->conf->iface, first); if (hapd->started) { wpa_printf(MSG_ERROR, "%s: Interface %s was already started", __func__, hapd->conf->iface); return -1; } hapd->started = 1; if (!first || first == -1) { if (hostapd_mac_comp_empty(hapd->conf->bssid) == 0) { /* Allocate the next available BSSID. */ do { inc_byte_array(hapd->own_addr, ETH_ALEN); } while (mac_in_conf(hapd->iconf, hapd->own_addr)); } else { /* Allocate the configured BSSID. */ os_memcpy(hapd->own_addr, hapd->conf->bssid, ETH_ALEN); if (hostapd_mac_comp(hapd->own_addr, hapd->iface->bss[0]->own_addr) == 0) { wpa_printf(MSG_ERROR, "BSS '%s' may not have " "BSSID set to the MAC address of " "the radio", hapd->conf->iface); return -1; } } hapd->interface_added = 1; if (hostapd_if_add(hapd->iface->bss[0], WPA_IF_AP_BSS, hapd->conf->iface, hapd->own_addr, hapd, &hapd->drv_priv, force_ifname, if_addr, hapd->conf->bridge[0] ? hapd->conf->bridge : NULL, first == -1)) { wpa_printf(MSG_ERROR, "Failed to add BSS (BSSID=" MACSTR ")", MAC2STR(hapd->own_addr)); hapd->interface_added = 0; return -1; } } if (conf->wmm_enabled < 0) conf->wmm_enabled = hapd->iconf->ieee80211n; hostapd_flush_old_stations(hapd, WLAN_REASON_PREV_AUTH_NOT_VALID); hostapd_set_privacy(hapd, 0); hostapd_broadcast_wep_clear(hapd); if (hostapd_setup_encryption(hapd->conf->iface, hapd)) return -1; /* * Fetch the SSID from the system and use it or, * if one was specified in the config file, verify they * match. */ ssid_len = hostapd_get_ssid(hapd, ssid, sizeof(ssid)); if (ssid_len < 0) { wpa_printf(MSG_ERROR, "Could not read SSID from system"); return -1; } if (conf->ssid.ssid_set) { /* * If SSID is specified in the config file and it differs * from what is being used then force installation of the * new SSID. */ set_ssid = (conf->ssid.ssid_len != (size_t) ssid_len || os_memcmp(conf->ssid.ssid, ssid, ssid_len) != 0); } else { /* * No SSID in the config file; just use the one we got * from the system. */ set_ssid = 0; conf->ssid.ssid_len = ssid_len; os_memcpy(conf->ssid.ssid, ssid, conf->ssid.ssid_len); } if (!hostapd_drv_none(hapd)) { wpa_printf(MSG_ERROR, "Using interface %s with hwaddr " MACSTR " and ssid \"%s\"", hapd->conf->iface, MAC2STR(hapd->own_addr), wpa_ssid_txt(hapd->conf->ssid.ssid, hapd->conf->ssid.ssid_len)); } if (hostapd_setup_wpa_psk(conf)) { wpa_printf(MSG_ERROR, "WPA-PSK setup failed."); return -1; } /* Set SSID for the kernel driver (to be used in beacon and probe * response frames) */ if (set_ssid && hostapd_set_ssid(hapd, conf->ssid.ssid, conf->ssid.ssid_len)) { wpa_printf(MSG_ERROR, "Could not set SSID for kernel driver"); return -1; } if (wpa_debug_level == MSG_MSGDUMP) conf->radius->msg_dumps = 1; #ifndef CONFIG_NO_RADIUS hapd->radius = radius_client_init(hapd, conf->radius); if (hapd->radius == NULL) { wpa_printf(MSG_ERROR, "RADIUS client initialization failed."); return -1; } if (hapd->conf->radius_das_port) { struct radius_das_conf das_conf; os_memset(&das_conf, 0, sizeof(das_conf)); das_conf.port = hapd->conf->radius_das_port; das_conf.shared_secret = hapd->conf->radius_das_shared_secret; das_conf.shared_secret_len = hapd->conf->radius_das_shared_secret_len; das_conf.client_addr = &hapd->conf->radius_das_client_addr; das_conf.time_window = hapd->conf->radius_das_time_window; das_conf.require_event_timestamp = hapd->conf->radius_das_require_event_timestamp; das_conf.ctx = hapd; das_conf.disconnect = hostapd_das_disconnect; hapd->radius_das = radius_das_init(&das_conf); if (hapd->radius_das == NULL) { wpa_printf(MSG_ERROR, "RADIUS DAS initialization " "failed."); return -1; } } #endif /* CONFIG_NO_RADIUS */ if (hostapd_acl_init(hapd)) { wpa_printf(MSG_ERROR, "ACL initialization failed."); return -1; } if (hostapd_init_wps(hapd, conf)) return -1; if (authsrv_init(hapd) < 0) return -1; if (ieee802_1x_init(hapd)) { wpa_printf(MSG_ERROR, "IEEE 802.1X initialization failed."); return -1; } if (hapd->conf->wpa && hostapd_setup_wpa(hapd)) return -1; if (accounting_init(hapd)) { wpa_printf(MSG_ERROR, "Accounting initialization failed."); return -1; } if (hapd->conf->ieee802_11f && (hapd->iapp = iapp_init(hapd, hapd->conf->iapp_iface)) == NULL) { wpa_printf(MSG_ERROR, "IEEE 802.11F (IAPP) initialization " "failed."); return -1; } #ifdef CONFIG_INTERWORKING if (gas_serv_init(hapd)) { wpa_printf(MSG_ERROR, "GAS server initialization failed"); return -1; } if (conf->qos_map_set_len && hostapd_drv_set_qos_map(hapd, conf->qos_map_set, conf->qos_map_set_len)) { wpa_printf(MSG_ERROR, "Failed to initialize QoS Map"); return -1; } #endif /* CONFIG_INTERWORKING */ if (!hostapd_drv_none(hapd) && vlan_init(hapd)) { wpa_printf(MSG_ERROR, "VLAN initialization failed."); return -1; } if (!hapd->conf->start_disabled && ieee802_11_set_beacon(hapd) < 0) return -1; if (hapd->wpa_auth && wpa_init_keys(hapd->wpa_auth) < 0) return -1; if (hapd->driver && hapd->driver->set_operstate) hapd->driver->set_operstate(hapd->drv_priv, 1); return 0; } static void hostapd_tx_queue_params(struct hostapd_iface *iface) { struct hostapd_data *hapd = iface->bss[0]; int i; struct hostapd_tx_queue_params *p; for (i = 0; i < NUM_TX_QUEUES; i++) { p = &iface->conf->tx_queue[i]; if (hostapd_set_tx_queue_params(hapd, i, p->aifs, p->cwmin, p->cwmax, p->burst)) { wpa_printf(MSG_DEBUG, "Failed to set TX queue " "parameters for queue %d.", i); /* Continue anyway */ } } } static int hostapd_set_acl_list(struct hostapd_data *hapd, struct mac_acl_entry *mac_acl, int n_entries, u8 accept_acl) { struct hostapd_acl_params *acl_params; int i, err; acl_params = os_zalloc(sizeof(*acl_params) + (n_entries * sizeof(acl_params->mac_acl[0]))); if (!acl_params) return -ENOMEM; for (i = 0; i < n_entries; i++) os_memcpy(acl_params->mac_acl[i].addr, mac_acl[i].addr, ETH_ALEN); acl_params->acl_policy = accept_acl; acl_params->num_mac_acl = n_entries; err = hostapd_drv_set_acl(hapd, acl_params); os_free(acl_params); return err; } static void hostapd_set_acl(struct hostapd_data *hapd) { struct hostapd_config *conf = hapd->iconf; int err; u8 accept_acl; if (hapd->iface->drv_max_acl_mac_addrs == 0) return; if (!(conf->bss[0]->num_accept_mac || conf->bss[0]->num_deny_mac)) return; if (conf->bss[0]->macaddr_acl == DENY_UNLESS_ACCEPTED) { if (conf->bss[0]->num_accept_mac) { accept_acl = 1; err = hostapd_set_acl_list(hapd, conf->bss[0]->accept_mac, conf->bss[0]->num_accept_mac, accept_acl); if (err) { wpa_printf(MSG_DEBUG, "Failed to set accept acl"); return; } } else { wpa_printf(MSG_DEBUG, "Mismatch between ACL Policy & Accept/deny lists file"); } } else if (conf->bss[0]->macaddr_acl == ACCEPT_UNLESS_DENIED) { if (conf->bss[0]->num_deny_mac) { accept_acl = 0; err = hostapd_set_acl_list(hapd, conf->bss[0]->deny_mac, conf->bss[0]->num_deny_mac, accept_acl); if (err) { wpa_printf(MSG_DEBUG, "Failed to set deny acl"); return; } } else { wpa_printf(MSG_DEBUG, "Mismatch between ACL Policy & Accept/deny lists file"); } } } static int start_ctrl_iface_bss(struct hostapd_data *hapd) { if (!hapd->iface->interfaces || !hapd->iface->interfaces->ctrl_iface_init) return 0; if (hapd->iface->interfaces->ctrl_iface_init(hapd)) { wpa_printf(MSG_ERROR, "Failed to setup control interface for %s", hapd->conf->iface); return -1; } return 0; } static int start_ctrl_iface(struct hostapd_iface *iface) { size_t i; if (!iface->interfaces || !iface->interfaces->ctrl_iface_init) return 0; for (i = 0; i < iface->num_bss; i++) { struct hostapd_data *hapd = iface->bss[i]; if (iface->interfaces->ctrl_iface_init(hapd)) { wpa_printf(MSG_ERROR, "Failed to setup control interface for %s", hapd->conf->iface); return -1; } } return 0; } static void channel_list_update_timeout(void *eloop_ctx, void *timeout_ctx) { struct hostapd_iface *iface = eloop_ctx; if (!iface->wait_channel_update) { wpa_printf(MSG_INFO, "Channel list update timeout, but interface was not waiting for it"); return; } /* * It is possible that the existing channel list is acceptable, so try * to proceed. */ wpa_printf(MSG_DEBUG, "Channel list update timeout - try to continue anyway"); setup_interface2(iface); } void hostapd_channel_list_updated(struct hostapd_iface *iface, int initiator) { if (!iface->wait_channel_update || initiator != REGDOM_SET_BY_USER) return; wpa_printf(MSG_DEBUG, "Channel list updated - continue setup"); eloop_cancel_timeout(channel_list_update_timeout, iface, NULL); setup_interface2(iface); } static int setup_interface(struct hostapd_iface *iface) { struct hostapd_data *hapd = iface->bss[0]; size_t i; if (!iface->phy[0]) { const char *phy = hostapd_drv_get_radio_name(hapd); if (phy) { wpa_printf(MSG_DEBUG, "phy: %s", phy); os_strlcpy(iface->phy, phy, sizeof(iface->phy)); } } /* * Make sure that all BSSes get configured with a pointer to the same * driver interface. */ for (i = 1; i < iface->num_bss; i++) { iface->bss[i]->driver = hapd->driver; iface->bss[i]->drv_priv = hapd->drv_priv; } if (hostapd_validate_bssid_configuration(iface)) return -1; /* * Initialize control interfaces early to allow external monitoring of * channel setup operations that may take considerable amount of time * especially for DFS cases. */ if (start_ctrl_iface(iface)) return -1; if (hapd->iconf->country[0] && hapd->iconf->country[1]) { char country[4], previous_country[4]; hostapd_set_state(iface, HAPD_IFACE_COUNTRY_UPDATE); if (hostapd_get_country(hapd, previous_country) < 0) previous_country[0] = '\0'; os_memcpy(country, hapd->iconf->country, 3); country[3] = '\0'; if (hostapd_set_country(hapd, country) < 0) { wpa_printf(MSG_ERROR, "Failed to set country code"); return -1; } wpa_printf(MSG_DEBUG, "Previous country code %s, new country code %s", previous_country, country); if (os_strncmp(previous_country, country, 2) != 0) { wpa_printf(MSG_DEBUG, "Continue interface setup after channel list update"); iface->wait_channel_update = 1; eloop_register_timeout(5, 0, channel_list_update_timeout, iface, NULL); return 0; } } return setup_interface2(iface); } static int setup_interface2(struct hostapd_iface *iface) { iface->wait_channel_update = 0; if (hostapd_get_hw_features(iface)) { /* Not all drivers support this yet, so continue without hw * feature data. */ } else { int ret = hostapd_select_hw_mode(iface); if (ret < 0) { wpa_printf(MSG_ERROR, "Could not select hw_mode and " "channel. (%d)", ret); return -1; } if (ret == 1) { wpa_printf(MSG_DEBUG, "Interface initialization will be completed in a callback (ACS)"); return 0; } ret = hostapd_check_ht_capab(iface); if (ret < 0) return -1; if (ret == 1) { wpa_printf(MSG_DEBUG, "Interface initialization will " "be completed in a callback"); return 0; } if (iface->conf->ieee80211h) wpa_printf(MSG_DEBUG, "DFS support is enabled"); } return hostapd_setup_interface_complete(iface, 0); } /** * hostapd_setup_interface_complete - Complete interface setup * * This function is called when previous steps in the interface setup has been * completed. This can also start operations, e.g., DFS, that will require * additional processing before interface is ready to be enabled. Such * operations will call this function from eloop callbacks when finished. */ int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err) { struct hostapd_data *hapd = iface->bss[0]; size_t j; u8 *prev_addr; if (err) { wpa_printf(MSG_ERROR, "Interface initialization failed"); hostapd_set_state(iface, HAPD_IFACE_DISABLED); if (iface->interfaces && iface->interfaces->terminate_on_error) eloop_terminate(); return -1; } wpa_printf(MSG_DEBUG, "Completing interface initialization"); if (iface->conf->channel) { #ifdef NEED_AP_MLME int res; #endif /* NEED_AP_MLME */ iface->freq = hostapd_hw_get_freq(hapd, iface->conf->channel); wpa_printf(MSG_DEBUG, "Mode: %s Channel: %d " "Frequency: %d MHz", hostapd_hw_mode_txt(iface->conf->hw_mode), iface->conf->channel, iface->freq); #ifdef NEED_AP_MLME /* Check DFS */ res = hostapd_handle_dfs(iface); if (res <= 0) return res; #endif /* NEED_AP_MLME */ if (hostapd_set_freq(hapd, hapd->iconf->hw_mode, iface->freq, hapd->iconf->channel, hapd->iconf->ieee80211n, hapd->iconf->ieee80211ac, hapd->iconf->secondary_channel, hapd->iconf->vht_oper_chwidth, hapd->iconf->vht_oper_centr_freq_seg0_idx, hapd->iconf->vht_oper_centr_freq_seg1_idx)) { wpa_printf(MSG_ERROR, "Could not set channel for " "kernel driver"); return -1; } } if (iface->current_mode) { if (hostapd_prepare_rates(iface, iface->current_mode)) { wpa_printf(MSG_ERROR, "Failed to prepare rates " "table."); hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_WARNING, "Failed to prepare rates table."); return -1; } } if (hapd->iconf->rts_threshold > -1 && hostapd_set_rts(hapd, hapd->iconf->rts_threshold)) { wpa_printf(MSG_ERROR, "Could not set RTS threshold for " "kernel driver"); return -1; } if (hapd->iconf->fragm_threshold > -1 && hostapd_set_frag(hapd, hapd->iconf->fragm_threshold)) { wpa_printf(MSG_ERROR, "Could not set fragmentation threshold " "for kernel driver"); return -1; } prev_addr = hapd->own_addr; for (j = 0; j < iface->num_bss; j++) { hapd = iface->bss[j]; if (j) os_memcpy(hapd->own_addr, prev_addr, ETH_ALEN); if (hostapd_setup_bss(hapd, j == 0)) return -1; if (hostapd_mac_comp_empty(hapd->conf->bssid) == 0) prev_addr = hapd->own_addr; } hapd = iface->bss[0]; hostapd_tx_queue_params(iface); ap_list_init(iface); hostapd_set_acl(hapd); if (hostapd_driver_commit(hapd) < 0) { wpa_printf(MSG_ERROR, "%s: Failed to commit driver " "configuration", __func__); return -1; } /* * WPS UPnP module can be initialized only when the "upnp_iface" is up. * If "interface" and "upnp_iface" are the same (e.g., non-bridge * mode), the interface is up only after driver_commit, so initialize * WPS after driver_commit. */ for (j = 0; j < iface->num_bss; j++) { if (hostapd_init_wps_complete(iface->bss[j])) return -1; } hostapd_set_state(iface, HAPD_IFACE_ENABLED); wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, AP_EVENT_ENABLED); if (hapd->setup_complete_cb) hapd->setup_complete_cb(hapd->setup_complete_cb_ctx); wpa_printf(MSG_DEBUG, "%s: Setup of interface done.", iface->bss[0]->conf->iface); if (iface->interfaces && iface->interfaces->terminate_on_error > 0) iface->interfaces->terminate_on_error--; return 0; } /** * hostapd_setup_interface - Setup of an interface * @iface: Pointer to interface data. * Returns: 0 on success, -1 on failure * * Initializes the driver interface, validates the configuration, * and sets driver parameters based on the configuration. * Flushes old stations, sets the channel, encryption, * beacons, and WDS links based on the configuration. * * If interface setup requires more time, e.g., to perform HT co-ex scans, ACS, * or DFS operations, this function returns 0 before such operations have been * completed. The pending operations are registered into eloop and will be * completed from eloop callbacks. Those callbacks end up calling * hostapd_setup_interface_complete() once setup has been completed. */ int hostapd_setup_interface(struct hostapd_iface *iface) { int ret; ret = setup_interface(iface); if (ret) { wpa_printf(MSG_ERROR, "%s: Unable to setup interface.", iface->bss[0]->conf->iface); return -1; } return 0; } /** * hostapd_alloc_bss_data - Allocate and initialize per-BSS data * @hapd_iface: Pointer to interface data * @conf: Pointer to per-interface configuration * @bss: Pointer to per-BSS configuration for this BSS * Returns: Pointer to allocated BSS data * * This function is used to allocate per-BSS data structure. This data will be * freed after hostapd_cleanup() is called for it during interface * deinitialization. */ struct hostapd_data * hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface, struct hostapd_config *conf, struct hostapd_bss_config *bss) { struct hostapd_data *hapd; hapd = os_zalloc(sizeof(*hapd)); if (hapd == NULL) return NULL; hapd->new_assoc_sta_cb = hostapd_new_assoc_sta; hapd->iconf = conf; hapd->conf = bss; hapd->iface = hapd_iface; hapd->driver = hapd->iconf->driver; hapd->ctrl_sock = -1; return hapd; } static void hostapd_bss_deinit(struct hostapd_data *hapd) { wpa_printf(MSG_DEBUG, "%s: deinit bss %s", __func__, hapd->conf->iface); hostapd_free_stas(hapd); hostapd_flush_old_stations(hapd, WLAN_REASON_DEAUTH_LEAVING); hostapd_clear_wep(hapd); hostapd_cleanup(hapd); } void hostapd_interface_deinit(struct hostapd_iface *iface) { int j; wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface); if (iface == NULL) return; eloop_cancel_timeout(channel_list_update_timeout, iface, NULL); iface->wait_channel_update = 0; for (j = iface->num_bss - 1; j >= 0; j--) hostapd_bss_deinit(iface->bss[j]); } void hostapd_interface_free(struct hostapd_iface *iface) { size_t j; wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface); for (j = 0; j < iface->num_bss; j++) { wpa_printf(MSG_DEBUG, "%s: free hapd %p", __func__, iface->bss[j]); os_free(iface->bss[j]); } hostapd_cleanup_iface(iface); } /** * hostapd_init - Allocate and initialize per-interface data * @config_file: Path to the configuration file * Returns: Pointer to the allocated interface data or %NULL on failure * * This function is used to allocate main data structures for per-interface * data. The allocated data buffer will be freed by calling * hostapd_cleanup_iface(). */ struct hostapd_iface * hostapd_init(struct hapd_interfaces *interfaces, const char *config_file) { struct hostapd_iface *hapd_iface = NULL; struct hostapd_config *conf = NULL; struct hostapd_data *hapd; size_t i; hapd_iface = os_zalloc(sizeof(*hapd_iface)); if (hapd_iface == NULL) goto fail; hapd_iface->config_fname = os_strdup(config_file); if (hapd_iface->config_fname == NULL) goto fail; conf = interfaces->config_read_cb(hapd_iface->config_fname); if (conf == NULL) goto fail; hapd_iface->conf = conf; hapd_iface->num_bss = conf->num_bss; hapd_iface->bss = os_calloc(conf->num_bss, sizeof(struct hostapd_data *)); if (hapd_iface->bss == NULL) goto fail; for (i = 0; i < conf->num_bss; i++) { hapd = hapd_iface->bss[i] = hostapd_alloc_bss_data(hapd_iface, conf, conf->bss[i]); if (hapd == NULL) goto fail; hapd->msg_ctx = hapd; } return hapd_iface; fail: wpa_printf(MSG_ERROR, "Failed to set up interface with %s", config_file); if (conf) hostapd_config_free(conf); if (hapd_iface) { os_free(hapd_iface->config_fname); os_free(hapd_iface->bss); wpa_printf(MSG_DEBUG, "%s: free iface %p", __func__, hapd_iface); os_free(hapd_iface); } return NULL; } static int ifname_in_use(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 = iface->bss[j]; if (os_strcmp(ifname, hapd->conf->iface) == 0) return 1; } } return 0; } /** * hostapd_interface_init_bss - Read configuration file and init BSS data * * This function is used to parse configuration file for a BSS. This BSS is * added to an existing interface sharing the same radio (if any) or a new * interface is created if this is the first interface on a radio. This * allocate memory for the BSS. No actual driver operations are started. * * This is similar to hostapd_interface_init(), but for a case where the * configuration is used to add a single BSS instead of all BSSes for a radio. */ struct hostapd_iface * hostapd_interface_init_bss(struct hapd_interfaces *interfaces, const char *phy, const char *config_fname, int debug) { struct hostapd_iface *new_iface = NULL, *iface = NULL; struct hostapd_data *hapd; int k; size_t i, bss_idx; if (!phy || !*phy) return NULL; for (i = 0; i < interfaces->count; i++) { if (os_strcmp(interfaces->iface[i]->phy, phy) == 0) { iface = interfaces->iface[i]; break; } } wpa_printf(MSG_INFO, "Configuration file: %s (phy %s)%s", config_fname, phy, iface ? "" : " --> new PHY"); if (iface) { struct hostapd_config *conf; struct hostapd_bss_config **tmp_conf; struct hostapd_data **tmp_bss; struct hostapd_bss_config *bss; const char *ifname; /* Add new BSS to existing iface */ conf = interfaces->config_read_cb(config_fname); if (conf == NULL) return NULL; if (conf->num_bss > 1) { wpa_printf(MSG_ERROR, "Multiple BSSes specified in BSS-config"); hostapd_config_free(conf); return NULL; } ifname = conf->bss[0]->iface; if (ifname[0] != '\0' && ifname_in_use(interfaces, ifname)) { wpa_printf(MSG_ERROR, "Interface name %s already in use", ifname); hostapd_config_free(conf); return NULL; } tmp_conf = os_realloc_array( iface->conf->bss, iface->conf->num_bss + 1, sizeof(struct hostapd_bss_config *)); tmp_bss = os_realloc_array(iface->bss, iface->num_bss + 1, sizeof(struct hostapd_data *)); if (tmp_bss) iface->bss = tmp_bss; if (tmp_conf) { iface->conf->bss = tmp_conf; iface->conf->last_bss = tmp_conf[0]; } if (tmp_bss == NULL || tmp_conf == NULL) { hostapd_config_free(conf); return NULL; } bss = iface->conf->bss[iface->conf->num_bss] = conf->bss[0]; iface->conf->num_bss++; hapd = hostapd_alloc_bss_data(iface, iface->conf, bss); if (hapd == NULL) { iface->conf->num_bss--; hostapd_config_free(conf); return NULL; } iface->conf->last_bss = bss; iface->bss[iface->num_bss] = hapd; hapd->msg_ctx = hapd; bss_idx = iface->num_bss++; conf->num_bss--; conf->bss[0] = NULL; hostapd_config_free(conf); } else { /* Add a new iface with the first BSS */ new_iface = iface = hostapd_init(interfaces, config_fname); if (!iface) return NULL; os_strlcpy(iface->phy, phy, sizeof(iface->phy)); iface->interfaces = interfaces; bss_idx = 0; } for (k = 0; k < debug; k++) { if (iface->bss[bss_idx]->conf->logger_stdout_level > 0) iface->bss[bss_idx]->conf->logger_stdout_level--; } if (iface->conf->bss[bss_idx]->iface[0] == '\0' && !hostapd_drv_none(iface->bss[bss_idx])) { wpa_printf(MSG_ERROR, "Interface name not specified in %s", config_fname); if (new_iface) hostapd_interface_deinit_free(new_iface); return NULL; } return iface; } void hostapd_interface_deinit_free(struct hostapd_iface *iface) { const struct wpa_driver_ops *driver; void *drv_priv; wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface); if (iface == NULL) return; wpa_printf(MSG_DEBUG, "%s: num_bss=%u conf->num_bss=%u", __func__, (unsigned int) iface->num_bss, (unsigned int) iface->conf->num_bss); driver = iface->bss[0]->driver; drv_priv = iface->bss[0]->drv_priv; hostapd_interface_deinit(iface); wpa_printf(MSG_DEBUG, "%s: driver=%p drv_priv=%p -> hapd_deinit", __func__, driver, drv_priv); if (driver && driver->hapd_deinit && drv_priv) driver->hapd_deinit(drv_priv); hostapd_interface_free(iface); } int hostapd_enable_iface(struct hostapd_iface *hapd_iface) { if (hapd_iface->bss[0]->drv_priv != NULL) { wpa_printf(MSG_ERROR, "Interface %s already enabled", hapd_iface->conf->bss[0]->iface); return -1; } wpa_printf(MSG_DEBUG, "Enable interface %s", hapd_iface->conf->bss[0]->iface); if (hostapd_config_check(hapd_iface->conf, 1) < 0) { wpa_printf(MSG_INFO, "Invalid configuration - cannot enable"); return -1; } if (hapd_iface->interfaces == NULL || hapd_iface->interfaces->driver_init == NULL || hapd_iface->interfaces->driver_init(hapd_iface)) return -1; if (hostapd_setup_interface(hapd_iface)) { const struct wpa_driver_ops *driver; void *drv_priv; driver = hapd_iface->bss[0]->driver; drv_priv = hapd_iface->bss[0]->drv_priv; wpa_printf(MSG_DEBUG, "%s: driver=%p drv_priv=%p -> hapd_deinit", __func__, driver, drv_priv); if (driver && driver->hapd_deinit && drv_priv) { driver->hapd_deinit(drv_priv); hapd_iface->bss[0]->drv_priv = NULL; } return -1; } return 0; } int hostapd_reload_iface(struct hostapd_iface *hapd_iface) { size_t j; wpa_printf(MSG_DEBUG, "Reload interface %s", hapd_iface->conf->bss[0]->iface); for (j = 0; j < hapd_iface->num_bss; j++) hostapd_set_security_params(hapd_iface->conf->bss[j]); if (hostapd_config_check(hapd_iface->conf, 1) < 0) { wpa_printf(MSG_ERROR, "Updated configuration is invalid"); return -1; } hostapd_clear_old(hapd_iface); for (j = 0; j < hapd_iface->num_bss; j++) hostapd_reload_bss(hapd_iface->bss[j]); return 0; } int hostapd_disable_iface(struct hostapd_iface *hapd_iface) { size_t j; const struct wpa_driver_ops *driver; void *drv_priv; if (hapd_iface == NULL) return -1; wpa_msg(hapd_iface->bss[0]->msg_ctx, MSG_INFO, AP_EVENT_DISABLED); driver = hapd_iface->bss[0]->driver; drv_priv = hapd_iface->bss[0]->drv_priv; /* whatever hostapd_interface_deinit does */ for (j = 0; j < hapd_iface->num_bss; j++) { struct hostapd_data *hapd = hapd_iface->bss[j]; hostapd_free_stas(hapd); hostapd_flush_old_stations(hapd, WLAN_REASON_DEAUTH_LEAVING); hostapd_clear_wep(hapd); hostapd_free_hapd_data(hapd); } wpa_printf(MSG_DEBUG, "%s: driver=%p drv_priv=%p -> hapd_deinit", __func__, driver, drv_priv); if (driver && driver->hapd_deinit && drv_priv) { driver->hapd_deinit(drv_priv); hapd_iface->bss[0]->drv_priv = NULL; } /* From hostapd_cleanup_iface: These were initialized in * hostapd_setup_interface and hostapd_setup_interface_complete */ hostapd_cleanup_iface_partial(hapd_iface); wpa_printf(MSG_DEBUG, "Interface %s disabled", hapd_iface->bss[0]->conf->iface); hostapd_set_state(hapd_iface, HAPD_IFACE_DISABLED); return 0; } static struct hostapd_iface * hostapd_iface_alloc(struct hapd_interfaces *interfaces) { struct hostapd_iface **iface, *hapd_iface; iface = os_realloc_array(interfaces->iface, interfaces->count + 1, sizeof(struct hostapd_iface *)); if (iface == NULL) return NULL; interfaces->iface = iface; hapd_iface = interfaces->iface[interfaces->count] = os_zalloc(sizeof(*hapd_iface)); if (hapd_iface == NULL) { wpa_printf(MSG_ERROR, "%s: Failed to allocate memory for " "the interface", __func__); return NULL; } interfaces->count++; hapd_iface->interfaces = interfaces; return hapd_iface; } static struct hostapd_config * hostapd_config_alloc(struct hapd_interfaces *interfaces, const char *ifname, const char *ctrl_iface) { struct hostapd_bss_config *bss; struct hostapd_config *conf; /* Allocates memory for bss and conf */ conf = hostapd_config_defaults(); if (conf == NULL) { wpa_printf(MSG_ERROR, "%s: Failed to allocate memory for " "configuration", __func__); return NULL; } conf->driver = wpa_drivers[0]; if (conf->driver == NULL) { wpa_printf(MSG_ERROR, "No driver wrappers registered!"); hostapd_config_free(conf); return NULL; } bss = conf->last_bss = conf->bss[0]; os_strlcpy(bss->iface, ifname, sizeof(bss->iface)); bss->ctrl_interface = os_strdup(ctrl_iface); if (bss->ctrl_interface == NULL) { hostapd_config_free(conf); return NULL; } /* Reading configuration file skipped, will be done in SET! * From reading the configuration till the end has to be done in * SET */ return conf; } static struct hostapd_iface * hostapd_data_alloc( struct hapd_interfaces *interfaces, struct hostapd_config *conf) { size_t i; struct hostapd_iface *hapd_iface = interfaces->iface[interfaces->count - 1]; struct hostapd_data *hapd; hapd_iface->conf = conf; hapd_iface->num_bss = conf->num_bss; hapd_iface->bss = os_zalloc(conf->num_bss * sizeof(struct hostapd_data *)); if (hapd_iface->bss == NULL) return NULL; for (i = 0; i < conf->num_bss; i++) { hapd = hapd_iface->bss[i] = hostapd_alloc_bss_data(hapd_iface, conf, conf->bss[i]); if (hapd == NULL) return NULL; hapd->msg_ctx = hapd; } hapd_iface->interfaces = interfaces; return hapd_iface; } int hostapd_add_iface(struct hapd_interfaces *interfaces, char *buf) { struct hostapd_config *conf = NULL; struct hostapd_iface *hapd_iface = NULL, *new_iface = NULL; struct hostapd_data *hapd; char *ptr; size_t i, j; const char *conf_file = NULL, *phy_name = NULL; if (os_strncmp(buf, "bss_config=", 11) == 0) { char *pos; phy_name = buf + 11; pos = os_strchr(phy_name, ':'); if (!pos) return -1; *pos++ = '\0'; conf_file = pos; if (!os_strlen(conf_file)) return -1; hapd_iface = hostapd_interface_init_bss(interfaces, phy_name, conf_file, 0); if (!hapd_iface) return -1; for (j = 0; j < interfaces->count; j++) { if (interfaces->iface[j] == hapd_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) { hostapd_interface_deinit_free(hapd_iface); return -1; } interfaces->iface = tmp; interfaces->iface[interfaces->count++] = hapd_iface; new_iface = hapd_iface; } if (new_iface) { if (interfaces->driver_init(hapd_iface) || hostapd_setup_interface(hapd_iface)) { interfaces->count--; goto fail; } } else { /* Assign new BSS with bss[0]'s driver info */ hapd = hapd_iface->bss[hapd_iface->num_bss - 1]; hapd->driver = hapd_iface->bss[0]->driver; hapd->drv_priv = hapd_iface->bss[0]->drv_priv; os_memcpy(hapd->own_addr, hapd_iface->bss[0]->own_addr, ETH_ALEN); if (start_ctrl_iface_bss(hapd) < 0 || (hapd_iface->state == HAPD_IFACE_ENABLED && hostapd_setup_bss(hapd, -1))) { hapd_iface->conf->num_bss--; hapd_iface->num_bss--; wpa_printf(MSG_DEBUG, "%s: free hapd %p %s", __func__, hapd, hapd->conf->iface); os_free(hapd); return -1; } } return 0; } ptr = os_strchr(buf, ' '); if (ptr == NULL) return -1; *ptr++ = '\0'; if (os_strncmp(ptr, "config=", 7) == 0) conf_file = ptr + 7; for (i = 0; i < interfaces->count; i++) { if (!os_strcmp(interfaces->iface[i]->conf->bss[0]->iface, buf)) { wpa_printf(MSG_INFO, "Cannot add interface - it " "already exists"); return -1; } } hapd_iface = hostapd_iface_alloc(interfaces); if (hapd_iface == NULL) { wpa_printf(MSG_ERROR, "%s: Failed to allocate memory " "for interface", __func__); goto fail; } if (conf_file && interfaces->config_read_cb) { conf = interfaces->config_read_cb(conf_file); if (conf && conf->bss) os_strlcpy(conf->bss[0]->iface, buf, sizeof(conf->bss[0]->iface)); } else conf = hostapd_config_alloc(interfaces, buf, ptr); if (conf == NULL || conf->bss == NULL) { wpa_printf(MSG_ERROR, "%s: Failed to allocate memory " "for configuration", __func__); goto fail; } hapd_iface = hostapd_data_alloc(interfaces, conf); if (hapd_iface == NULL) { wpa_printf(MSG_ERROR, "%s: Failed to allocate memory " "for hostapd", __func__); goto fail; } if (start_ctrl_iface(hapd_iface) < 0) goto fail; wpa_printf(MSG_INFO, "Add interface '%s'", conf->bss[0]->iface); return 0; fail: if (conf) hostapd_config_free(conf); if (hapd_iface) { if (hapd_iface->bss) { for (i = 0; i < hapd_iface->num_bss; i++) { hapd = hapd_iface->bss[i]; if (hapd && hapd_iface->interfaces && hapd_iface->interfaces->ctrl_iface_deinit) hapd_iface->interfaces-> ctrl_iface_deinit(hapd); wpa_printf(MSG_DEBUG, "%s: free hapd %p (%s)", __func__, hapd_iface->bss[i], hapd_iface->bss[i]->conf->iface); os_free(hapd_iface->bss[i]); } os_free(hapd_iface->bss); } wpa_printf(MSG_DEBUG, "%s: free iface %p", __func__, hapd_iface); os_free(hapd_iface); } return -1; } static int hostapd_remove_bss(struct hostapd_iface *iface, unsigned int idx) { size_t i; wpa_printf(MSG_INFO, "Remove BSS '%s'", iface->conf->bss[idx]->iface); /* Remove hostapd_data only if it has already been initialized */ if (idx < iface->num_bss) { struct hostapd_data *hapd = iface->bss[idx]; hostapd_bss_deinit(hapd); wpa_printf(MSG_DEBUG, "%s: free hapd %p (%s)", __func__, hapd, hapd->conf->iface); hostapd_config_free_bss(hapd->conf); os_free(hapd); iface->num_bss--; for (i = idx; i < iface->num_bss; i++) iface->bss[i] = iface->bss[i + 1]; } else { hostapd_config_free_bss(iface->conf->bss[idx]); iface->conf->bss[idx] = NULL; } iface->conf->num_bss--; for (i = idx; i < iface->conf->num_bss; i++) iface->conf->bss[i] = iface->conf->bss[i + 1]; return 0; } int hostapd_remove_iface(struct hapd_interfaces *interfaces, char *buf) { struct hostapd_iface *hapd_iface; size_t i, j, k = 0; for (i = 0; i < interfaces->count; i++) { hapd_iface = interfaces->iface[i]; if (hapd_iface == NULL) return -1; if (!os_strcmp(hapd_iface->conf->bss[0]->iface, buf)) { wpa_printf(MSG_INFO, "Remove interface '%s'", buf); hostapd_interface_deinit_free(hapd_iface); k = i; while (k < (interfaces->count - 1)) { interfaces->iface[k] = interfaces->iface[k + 1]; k++; } interfaces->count--; return 0; } for (j = 0; j < hapd_iface->conf->num_bss; j++) { if (!os_strcmp(hapd_iface->conf->bss[j]->iface, buf)) return hostapd_remove_bss(hapd_iface, j); } } return -1; } /** * hostapd_new_assoc_sta - Notify that a new station associated with the AP * @hapd: Pointer to BSS data * @sta: Pointer to the associated STA data * @reassoc: 1 to indicate this was a re-association; 0 = first association * * This function will be called whenever a station associates with the AP. It * can be called from ieee802_11.c for drivers that export MLME to hostapd and * from drv_callbacks.c based on driver events for drivers that take care of * management frames (IEEE 802.11 authentication and association) internally. */ void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta, int reassoc) { if (hapd->tkip_countermeasures) { hostapd_drv_sta_deauth(hapd, sta->addr, WLAN_REASON_MICHAEL_MIC_FAILURE); return; } hostapd_prune_associations(hapd, sta->addr); /* IEEE 802.11F (IAPP) */ if (hapd->conf->ieee802_11f) iapp_new_station(hapd->iapp, sta); #ifdef CONFIG_P2P if (sta->p2p_ie == NULL && !sta->no_p2p_set) { sta->no_p2p_set = 1; hapd->num_sta_no_p2p++; if (hapd->num_sta_no_p2p == 1) hostapd_p2p_non_p2p_sta_connected(hapd); } #endif /* CONFIG_P2P */ /* Start accounting here, if IEEE 802.1X and WPA are not used. * IEEE 802.1X/WPA code will start accounting after the station has * been authorized. */ if (!hapd->conf->ieee802_1x && !hapd->conf->wpa) { os_get_reltime(&sta->connected_time); accounting_sta_start(hapd, sta); } /* Start IEEE 802.1X authentication process for new stations */ ieee802_1x_new_station(hapd, sta); if (reassoc) { if (sta->auth_alg != WLAN_AUTH_FT && !(sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS))) wpa_auth_sm_event(sta->wpa_sm, WPA_REAUTH); } else wpa_auth_sta_associated(hapd->wpa_auth, sta->wpa_sm); if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_INACTIVITY_TIMER)) { wpa_printf(MSG_DEBUG, "%s: reschedule ap_handle_timer timeout " "for " MACSTR " (%d seconds - ap_max_inactivity)", __func__, MAC2STR(sta->addr), hapd->conf->ap_max_inactivity); eloop_cancel_timeout(ap_handle_timer, hapd, sta); eloop_register_timeout(hapd->conf->ap_max_inactivity, 0, ap_handle_timer, hapd, sta); } } const char * hostapd_state_text(enum hostapd_iface_state s) { switch (s) { case HAPD_IFACE_UNINITIALIZED: return "UNINITIALIZED"; case HAPD_IFACE_DISABLED: return "DISABLED"; case HAPD_IFACE_COUNTRY_UPDATE: return "COUNTRY_UPDATE"; case HAPD_IFACE_ACS: return "ACS"; case HAPD_IFACE_HT_SCAN: return "HT_SCAN"; case HAPD_IFACE_DFS: return "DFS"; case HAPD_IFACE_ENABLED: return "ENABLED"; } return "UNKNOWN"; } void hostapd_set_state(struct hostapd_iface *iface, enum hostapd_iface_state s) { wpa_printf(MSG_INFO, "%s: interface state %s->%s", iface->conf->bss[0]->iface, hostapd_state_text(iface->state), hostapd_state_text(s)); iface->state = s; } #ifdef NEED_AP_MLME static void free_beacon_data(struct beacon_data *beacon) { os_free(beacon->head); beacon->head = NULL; os_free(beacon->tail); beacon->tail = NULL; os_free(beacon->probe_resp); beacon->probe_resp = NULL; os_free(beacon->beacon_ies); beacon->beacon_ies = NULL; os_free(beacon->proberesp_ies); beacon->proberesp_ies = NULL; os_free(beacon->assocresp_ies); beacon->assocresp_ies = NULL; } static int hostapd_build_beacon_data(struct hostapd_iface *iface, struct beacon_data *beacon) { struct wpabuf *beacon_extra, *proberesp_extra, *assocresp_extra; struct wpa_driver_ap_params params; int ret; struct hostapd_data *hapd = iface->bss[0]; os_memset(beacon, 0, sizeof(*beacon)); ret = ieee802_11_build_ap_params(hapd, ¶ms); if (ret < 0) return ret; ret = hostapd_build_ap_extra_ies(hapd, &beacon_extra, &proberesp_extra, &assocresp_extra); if (ret) goto free_ap_params; ret = -1; beacon->head = os_malloc(params.head_len); if (!beacon->head) goto free_ap_extra_ies; os_memcpy(beacon->head, params.head, params.head_len); beacon->head_len = params.head_len; beacon->tail = os_malloc(params.tail_len); if (!beacon->tail) goto free_beacon; os_memcpy(beacon->tail, params.tail, params.tail_len); beacon->tail_len = params.tail_len; if (params.proberesp != NULL) { beacon->probe_resp = os_malloc(params.proberesp_len); if (!beacon->probe_resp) goto free_beacon; os_memcpy(beacon->probe_resp, params.proberesp, params.proberesp_len); beacon->probe_resp_len = params.proberesp_len; } /* copy the extra ies */ if (beacon_extra) { beacon->beacon_ies = os_malloc(wpabuf_len(beacon_extra)); if (!beacon->beacon_ies) goto free_beacon; os_memcpy(beacon->beacon_ies, beacon_extra->buf, wpabuf_len(beacon_extra)); beacon->beacon_ies_len = wpabuf_len(beacon_extra); } if (proberesp_extra) { beacon->proberesp_ies = os_malloc(wpabuf_len(proberesp_extra)); if (!beacon->proberesp_ies) goto free_beacon; os_memcpy(beacon->proberesp_ies, proberesp_extra->buf, wpabuf_len(proberesp_extra)); beacon->proberesp_ies_len = wpabuf_len(proberesp_extra); } if (assocresp_extra) { beacon->assocresp_ies = os_malloc(wpabuf_len(assocresp_extra)); if (!beacon->assocresp_ies) goto free_beacon; os_memcpy(beacon->assocresp_ies, assocresp_extra->buf, wpabuf_len(assocresp_extra)); beacon->assocresp_ies_len = wpabuf_len(assocresp_extra); } ret = 0; free_beacon: /* if the function fails, the caller should not free beacon data */ if (ret) free_beacon_data(beacon); free_ap_extra_ies: hostapd_free_ap_extra_ies(hapd, beacon_extra, proberesp_extra, assocresp_extra); free_ap_params: ieee802_11_free_ap_params(¶ms); return ret; } /* * TODO: This flow currently supports only changing frequency within the * same hw_mode. Any other changes to MAC parameters or provided settings (even * width) are not supported. */ static int hostapd_change_config_freq(struct hostapd_data *hapd, struct hostapd_config *conf, struct hostapd_freq_params *params, struct hostapd_freq_params *old_params) { int channel; if (!params->channel) { /* check if the new channel is supported by hw */ channel = hostapd_hw_get_channel(hapd, params->freq); if (!channel) return -1; } else { channel = params->channel; } /* if a pointer to old_params is provided we save previous state */ if (old_params) { old_params->channel = conf->channel; old_params->ht_enabled = conf->ieee80211n; old_params->sec_channel_offset = conf->secondary_channel; } conf->channel = channel; conf->ieee80211n = params->ht_enabled; conf->secondary_channel = params->sec_channel_offset; /* TODO: maybe call here hostapd_config_check here? */ return 0; } static int hostapd_fill_csa_settings(struct hostapd_iface *iface, struct csa_settings *settings) { struct hostapd_freq_params old_freq; int ret; os_memset(&old_freq, 0, sizeof(old_freq)); if (!iface || !iface->freq || iface->csa_in_progress) return -1; ret = hostapd_change_config_freq(iface->bss[0], iface->conf, &settings->freq_params, &old_freq); if (ret) return ret; ret = hostapd_build_beacon_data(iface, &settings->beacon_after); /* change back the configuration */ hostapd_change_config_freq(iface->bss[0], iface->conf, &old_freq, NULL); if (ret) return ret; /* set channel switch parameters for csa ie */ iface->cs_freq_params = settings->freq_params; iface->cs_count = settings->cs_count; iface->cs_block_tx = settings->block_tx; ret = hostapd_build_beacon_data(iface, &settings->beacon_csa); if (ret) { free_beacon_data(&settings->beacon_after); return ret; } settings->counter_offset_beacon = iface->cs_c_off_beacon; settings->counter_offset_presp = iface->cs_c_off_proberesp; return 0; } void hostapd_cleanup_cs_params(struct hostapd_data *hapd) { os_memset(&hapd->iface->cs_freq_params, 0, sizeof(hapd->iface->cs_freq_params)); hapd->iface->cs_count = 0; hapd->iface->cs_block_tx = 0; hapd->iface->cs_c_off_beacon = 0; hapd->iface->cs_c_off_proberesp = 0; hapd->iface->csa_in_progress = 0; } int hostapd_switch_channel(struct hostapd_data *hapd, struct csa_settings *settings) { int ret; ret = hostapd_fill_csa_settings(hapd->iface, settings); if (ret) return ret; ret = hostapd_drv_switch_channel(hapd, settings); free_beacon_data(&settings->beacon_csa); free_beacon_data(&settings->beacon_after); if (ret) { /* if we failed, clean cs parameters */ hostapd_cleanup_cs_params(hapd); return ret; } hapd->iface->csa_in_progress = 1; return 0; } #endif /* NEED_AP_MLME */ wpa-2.1/src/ap/hostapd.h000066400000000000000000000270621227414666700151730ustar00rootroot00000000000000/* * hostapd / Initialization and configuration * Copyright (c) 2002-2013, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef HOSTAPD_H #define HOSTAPD_H #include "common/defs.h" #include "ap_config.h" #include "drivers/driver.h" struct wpa_ctrl_dst; struct radius_server_data; struct upnp_wps_device_sm; struct hostapd_data; struct sta_info; struct ieee80211_ht_capabilities; struct full_dynamic_vlan; enum wps_event; union wps_event_data; struct hostapd_iface; struct hostapd_dynamic_iface; struct hapd_interfaces { int (*reload_config)(struct hostapd_iface *iface); struct hostapd_config * (*config_read_cb)(const char *config_fname); int (*ctrl_iface_init)(struct hostapd_data *hapd); void (*ctrl_iface_deinit)(struct hostapd_data *hapd); int (*for_each_interface)(struct hapd_interfaces *interfaces, int (*cb)(struct hostapd_iface *iface, void *ctx), void *ctx); int (*driver_init)(struct hostapd_iface *iface); size_t count; size_t count_dynamic; int global_ctrl_sock; char *global_iface_path; char *global_iface_name; #ifndef CONFIG_NATIVE_WINDOWS gid_t ctrl_iface_group; #endif /* CONFIG_NATIVE_WINDOWS */ struct hostapd_iface **iface; struct hostapd_dynamic_iface **dynamic_iface; size_t terminate_on_error; }; enum hostapd_chan_status { HOSTAPD_CHAN_VALID = 0, /* channel is ready */ HOSTAPD_CHAN_INVALID = 1, /* no usable channel found */ HOSTAPD_CHAN_ACS = 2, /* ACS work being performed */ }; struct hostapd_probereq_cb { int (*cb)(void *ctx, const u8 *sa, const u8 *da, const u8 *bssid, const u8 *ie, size_t ie_len, int ssi_signal); void *ctx; }; #define HOSTAPD_RATE_BASIC 0x00000001 struct hostapd_rate_data { int rate; /* rate in 100 kbps */ int flags; /* HOSTAPD_RATE_ flags */ }; struct hostapd_frame_info { u32 channel; u32 datarate; int ssi_signal; /* dBm */ }; enum wps_status { WPS_STATUS_SUCCESS = 1, WPS_STATUS_FAILURE }; enum pbc_status { WPS_PBC_STATUS_DISABLE, WPS_PBC_STATUS_ACTIVE, WPS_PBC_STATUS_TIMEOUT, WPS_PBC_STATUS_OVERLAP }; struct wps_stat { enum wps_status status; enum wps_error_indication failure_reason; enum pbc_status pbc_status; u8 peer_addr[ETH_ALEN]; }; /** * struct hostapd_data - hostapd per-BSS data structure */ struct hostapd_data { struct hostapd_iface *iface; struct hostapd_config *iconf; struct hostapd_bss_config *conf; int interface_added; /* virtual interface added for this BSS */ unsigned int started:1; u8 own_addr[ETH_ALEN]; int num_sta; /* number of entries in sta_list */ struct sta_info *sta_list; /* STA info list head */ #define STA_HASH_SIZE 256 #define STA_HASH(sta) (sta[5]) struct sta_info *sta_hash[STA_HASH_SIZE]; /* * Bitfield for indicating which AIDs are allocated. Only AID values * 1-2007 are used and as such, the bit at index 0 corresponds to AID * 1. */ #define AID_WORDS ((2008 + 31) / 32) u32 sta_aid[AID_WORDS]; const struct wpa_driver_ops *driver; void *drv_priv; void (*new_assoc_sta_cb)(struct hostapd_data *hapd, struct sta_info *sta, int reassoc); void *msg_ctx; /* ctx for wpa_msg() calls */ void *msg_ctx_parent; /* parent interface ctx for wpa_msg() calls */ struct radius_client_data *radius; u32 acct_session_id_hi, acct_session_id_lo; struct radius_das_data *radius_das; struct iapp_data *iapp; struct hostapd_cached_radius_acl *acl_cache; struct hostapd_acl_query_data *acl_queries; struct wpa_authenticator *wpa_auth; struct eapol_authenticator *eapol_auth; struct rsn_preauth_interface *preauth_iface; struct os_reltime michael_mic_failure; int michael_mic_failures; int tkip_countermeasures; int ctrl_sock; struct wpa_ctrl_dst *ctrl_dst; void *ssl_ctx; void *eap_sim_db_priv; struct radius_server_data *radius_srv; int parameter_set_count; /* Time Advertisement */ u8 time_update_counter; struct wpabuf *time_adv; #ifdef CONFIG_FULL_DYNAMIC_VLAN struct full_dynamic_vlan *full_dynamic_vlan; #endif /* CONFIG_FULL_DYNAMIC_VLAN */ struct l2_packet_data *l2; struct wps_context *wps; int beacon_set_done; struct wpabuf *wps_beacon_ie; struct wpabuf *wps_probe_resp_ie; #ifdef CONFIG_WPS unsigned int ap_pin_failures; unsigned int ap_pin_failures_consecutive; struct upnp_wps_device_sm *wps_upnp; unsigned int ap_pin_lockout_time; struct wps_stat wps_stats; #endif /* CONFIG_WPS */ struct hostapd_probereq_cb *probereq_cb; size_t num_probereq_cb; void (*public_action_cb)(void *ctx, const u8 *buf, size_t len, int freq); void *public_action_cb_ctx; void (*public_action_cb2)(void *ctx, const u8 *buf, size_t len, int freq); void *public_action_cb2_ctx; int (*vendor_action_cb)(void *ctx, const u8 *buf, size_t len, int freq); void *vendor_action_cb_ctx; void (*wps_reg_success_cb)(void *ctx, const u8 *mac_addr, const u8 *uuid_e); void *wps_reg_success_cb_ctx; void (*wps_event_cb)(void *ctx, enum wps_event event, union wps_event_data *data); void *wps_event_cb_ctx; void (*sta_authorized_cb)(void *ctx, const u8 *mac_addr, int authorized, const u8 *p2p_dev_addr); void *sta_authorized_cb_ctx; void (*setup_complete_cb)(void *ctx); void *setup_complete_cb_ctx; void (*new_psk_cb)(void *ctx, const u8 *mac_addr, const u8 *p2p_dev_addr, const u8 *psk, size_t psk_len); void *new_psk_cb_ctx; #ifdef CONFIG_P2P struct p2p_data *p2p; struct p2p_group *p2p_group; struct wpabuf *p2p_beacon_ie; struct wpabuf *p2p_probe_resp_ie; /* Number of non-P2P association stations */ int num_sta_no_p2p; /* Periodic NoA (used only when no non-P2P clients in the group) */ int noa_enabled; int noa_start; int noa_duration; #endif /* CONFIG_P2P */ #ifdef CONFIG_INTERWORKING size_t gas_frag_limit; #endif /* CONFIG_INTERWORKING */ #ifdef CONFIG_SQLITE struct hostapd_eap_user tmp_eap_user; #endif /* CONFIG_SQLITE */ #ifdef CONFIG_SAE /** Key used for generating SAE anti-clogging tokens */ u8 sae_token_key[8]; struct os_reltime last_sae_token_key_update; #endif /* CONFIG_SAE */ #ifdef CONFIG_TESTING_OPTIONS int ext_mgmt_frame_handling; #endif /* CONFIG_TESTING_OPTIONS */ }; /** * struct hostapd_iface - hostapd per-interface data structure */ struct hostapd_iface { struct hapd_interfaces *interfaces; void *owner; char *config_fname; struct hostapd_config *conf; char phy[16]; /* Name of the PHY (radio) */ enum hostapd_iface_state { HAPD_IFACE_UNINITIALIZED, HAPD_IFACE_DISABLED, HAPD_IFACE_COUNTRY_UPDATE, HAPD_IFACE_ACS, HAPD_IFACE_HT_SCAN, HAPD_IFACE_DFS, HAPD_IFACE_ENABLED } state; size_t num_bss; struct hostapd_data **bss; unsigned int wait_channel_update:1; unsigned int cac_started:1; int num_ap; /* number of entries in ap_list */ struct ap_info *ap_list; /* AP info list head */ struct ap_info *ap_hash[STA_HASH_SIZE]; unsigned int drv_flags; /* * A bitmap of supported protocols for probe response offload. See * struct wpa_driver_capa in driver.h */ unsigned int probe_resp_offloads; /* extended capabilities supported by the driver */ const u8 *extended_capa, *extended_capa_mask; unsigned int extended_capa_len; unsigned int drv_max_acl_mac_addrs; struct hostapd_hw_modes *hw_features; int num_hw_features; struct hostapd_hw_modes *current_mode; /* Rates that are currently used (i.e., filtered copy of * current_mode->channels */ int num_rates; struct hostapd_rate_data *current_rates; int *basic_rates; int freq; u16 hw_flags; /* Number of associated Non-ERP stations (i.e., stations using 802.11b * in 802.11g BSS) */ int num_sta_non_erp; /* Number of associated stations that do not support Short Slot Time */ int num_sta_no_short_slot_time; /* Number of associated stations that do not support Short Preamble */ int num_sta_no_short_preamble; int olbc; /* Overlapping Legacy BSS Condition */ /* Number of HT associated stations that do not support greenfield */ int num_sta_ht_no_gf; /* Number of associated non-HT stations */ int num_sta_no_ht; /* Number of HT associated stations 20 MHz */ int num_sta_ht_20mhz; /* Overlapping BSS information */ int olbc_ht; u16 ht_op_mode; /* surveying helpers */ /* number of channels surveyed */ unsigned int chans_surveyed; /* lowest observed noise floor in dBm */ s8 lowest_nf; /* channel switch parameters */ struct hostapd_freq_params cs_freq_params; u8 cs_count; int cs_block_tx; unsigned int cs_c_off_beacon; unsigned int cs_c_off_proberesp; int csa_in_progress; #ifdef CONFIG_ACS unsigned int acs_num_completed_scans; #endif /* CONFIG_ACS */ void (*scan_cb)(struct hostapd_iface *iface); }; /** * struct hostapd_dynamic_iface - hostapd per dynamically allocated * or added interface data structure */ struct hostapd_dynamic_iface { char parent[IFNAMSIZ + 1]; char iface[IFNAMSIZ + 1]; unsigned int usage; }; /* hostapd.c */ int hostapd_for_each_interface(struct hapd_interfaces *interfaces, int (*cb)(struct hostapd_iface *iface, void *ctx), void *ctx); int hostapd_reload_config(struct hostapd_iface *iface); struct hostapd_data * hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface, struct hostapd_config *conf, struct hostapd_bss_config *bss); int hostapd_setup_interface(struct hostapd_iface *iface); int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err); void hostapd_interface_deinit(struct hostapd_iface *iface); void hostapd_interface_free(struct hostapd_iface *iface); struct hostapd_iface * hostapd_init(struct hapd_interfaces *interfaces, const char *config_file); struct hostapd_iface * hostapd_interface_init_bss(struct hapd_interfaces *interfaces, const char *phy, const char *config_fname, int debug); void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta, int reassoc); void hostapd_interface_deinit_free(struct hostapd_iface *iface); int hostapd_enable_iface(struct hostapd_iface *hapd_iface); int hostapd_reload_iface(struct hostapd_iface *hapd_iface); int hostapd_disable_iface(struct hostapd_iface *hapd_iface); int hostapd_add_iface(struct hapd_interfaces *ifaces, char *buf); int hostapd_remove_iface(struct hapd_interfaces *ifaces, char *buf); void hostapd_channel_list_updated(struct hostapd_iface *iface, int initiator); void hostapd_set_state(struct hostapd_iface *iface, enum hostapd_iface_state s); const char * hostapd_state_text(enum hostapd_iface_state s); int hostapd_switch_channel(struct hostapd_data *hapd, struct csa_settings *settings); void hostapd_cleanup_cs_params(struct hostapd_data *hapd); /* utils.c */ int hostapd_register_probereq_cb(struct hostapd_data *hapd, int (*cb)(void *ctx, const u8 *sa, const u8 *da, const u8 *bssid, const u8 *ie, size_t ie_len, int ssi_signal), void *ctx); void hostapd_prune_associations(struct hostapd_data *hapd, const u8 *addr); /* drv_callbacks.c (TODO: move to somewhere else?) */ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, const u8 *ie, size_t ielen, int reassoc); void hostapd_notif_disassoc(struct hostapd_data *hapd, const u8 *addr); void hostapd_event_sta_low_ack(struct hostapd_data *hapd, const u8 *addr); void hostapd_event_connect_failed_reason(struct hostapd_data *hapd, const u8 *addr, int reason_code); int hostapd_probe_req_rx(struct hostapd_data *hapd, const u8 *sa, const u8 *da, const u8 *bssid, const u8 *ie, size_t ie_len, int ssi_signal); void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht, int offset, int width, int cf1, int cf2); const struct hostapd_eap_user * hostapd_get_eap_user(struct hostapd_data *hapd, const u8 *identity, size_t identity_len, int phase2); #endif /* HOSTAPD_H */ wpa-2.1/src/ap/hs20.c000066400000000000000000000013411227414666700142700ustar00rootroot00000000000000/* * Hotspot 2.0 AP ANQP processing * Copyright (c) 2009, Atheros Communications, Inc. * Copyright (c) 2011-2012, Qualcomm Atheros, Inc. * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "includes.h" #include "common.h" #include "common/ieee802_11_defs.h" #include "hostapd.h" #include "ap_config.h" #include "hs20.h" u8 * hostapd_eid_hs20_indication(struct hostapd_data *hapd, u8 *eid) { if (!hapd->conf->hs20) return eid; *eid++ = WLAN_EID_VENDOR_SPECIFIC; *eid++ = 5; WPA_PUT_BE24(eid, OUI_WFA); eid += 3; *eid++ = HS20_INDICATION_OUI_TYPE; /* Hotspot Configuration: DGAF Enabled */ *eid++ = hapd->conf->disable_dgaf ? 0x01 : 0x00; return eid; } wpa-2.1/src/ap/hs20.h000066400000000000000000000005301227414666700142740ustar00rootroot00000000000000/* * Hotspot 2.0 AP ANQP processing * Copyright (c) 2011-2012, Qualcomm Atheros, Inc. * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef HS20_H #define HS20_H struct hostapd_data; u8 * hostapd_eid_hs20_indication(struct hostapd_data *hapd, u8 *eid); #endif /* HS20_H */ wpa-2.1/src/ap/hw_features.c000066400000000000000000000637471227414666700160520ustar00rootroot00000000000000/* * hostapd / Hardware feature query and different modes * Copyright 2002-2003, Instant802 Networks, Inc. * Copyright 2005-2006, Devicescape Software, Inc. * Copyright (c) 2008-2012, 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/eloop.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" #include "common/wpa_ctrl.h" #include "hostapd.h" #include "ap_config.h" #include "ap_drv_ops.h" #include "acs.h" #include "hw_features.h" void hostapd_free_hw_features(struct hostapd_hw_modes *hw_features, size_t num_hw_features) { size_t i; if (hw_features == NULL) return; for (i = 0; i < num_hw_features; i++) { os_free(hw_features[i].channels); os_free(hw_features[i].rates); } os_free(hw_features); } #ifndef CONFIG_NO_STDOUT_DEBUG static char * dfs_info(struct hostapd_channel_data *chan) { static char info[256]; char *state; switch (chan->flag & HOSTAPD_CHAN_DFS_MASK) { case HOSTAPD_CHAN_DFS_UNKNOWN: state = "unknown"; break; case HOSTAPD_CHAN_DFS_USABLE: state = "usable"; break; case HOSTAPD_CHAN_DFS_UNAVAILABLE: state = "unavailable"; break; case HOSTAPD_CHAN_DFS_AVAILABLE: state = "available"; break; default: return ""; } os_snprintf(info, sizeof(info), " (DFS state = %s)", state); info[sizeof(info) - 1] = '\0'; return info; } #endif /* CONFIG_NO_STDOUT_DEBUG */ int hostapd_get_hw_features(struct hostapd_iface *iface) { struct hostapd_data *hapd = iface->bss[0]; int ret = 0, i, j; u16 num_modes, flags; struct hostapd_hw_modes *modes; if (hostapd_drv_none(hapd)) return -1; modes = hostapd_get_hw_feature_data(hapd, &num_modes, &flags); if (modes == NULL) { hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "Fetching hardware channel/rate support not " "supported."); return -1; } iface->hw_flags = flags; hostapd_free_hw_features(iface->hw_features, iface->num_hw_features); iface->hw_features = modes; iface->num_hw_features = num_modes; for (i = 0; i < num_modes; i++) { struct hostapd_hw_modes *feature = &modes[i]; int dfs_enabled = hapd->iconf->ieee80211h && (iface->drv_flags & WPA_DRIVER_FLAGS_RADAR); /* set flag for channels we can use in current regulatory * domain */ for (j = 0; j < feature->num_channels; j++) { int dfs = 0; /* * Disable all channels that are marked not to allow * IBSS operation or active scanning. * Use radar channels only if the driver supports DFS. */ if ((feature->channels[j].flag & HOSTAPD_CHAN_RADAR) && dfs_enabled) { dfs = 1; } else if (feature->channels[j].flag & (HOSTAPD_CHAN_NO_IBSS | HOSTAPD_CHAN_PASSIVE_SCAN | HOSTAPD_CHAN_RADAR)) { feature->channels[j].flag |= HOSTAPD_CHAN_DISABLED; } if (feature->channels[j].flag & HOSTAPD_CHAN_DISABLED) continue; wpa_printf(MSG_MSGDUMP, "Allowed channel: mode=%d " "chan=%d freq=%d MHz max_tx_power=%d dBm%s", feature->mode, feature->channels[j].chan, feature->channels[j].freq, feature->channels[j].max_tx_power, dfs ? dfs_info(&feature->channels[j]) : ""); } } return ret; } int hostapd_prepare_rates(struct hostapd_iface *iface, struct hostapd_hw_modes *mode) { int i, num_basic_rates = 0; int basic_rates_a[] = { 60, 120, 240, -1 }; int basic_rates_b[] = { 10, 20, -1 }; int basic_rates_g[] = { 10, 20, 55, 110, -1 }; int *basic_rates; if (iface->conf->basic_rates) basic_rates = iface->conf->basic_rates; else switch (mode->mode) { case HOSTAPD_MODE_IEEE80211A: basic_rates = basic_rates_a; break; case HOSTAPD_MODE_IEEE80211B: basic_rates = basic_rates_b; break; case HOSTAPD_MODE_IEEE80211G: basic_rates = basic_rates_g; break; case HOSTAPD_MODE_IEEE80211AD: return 0; /* No basic rates for 11ad */ default: return -1; } i = 0; while (basic_rates[i] >= 0) i++; if (i) i++; /* -1 termination */ os_free(iface->basic_rates); iface->basic_rates = os_malloc(i * sizeof(int)); if (iface->basic_rates) os_memcpy(iface->basic_rates, basic_rates, i * sizeof(int)); os_free(iface->current_rates); iface->num_rates = 0; iface->current_rates = os_calloc(mode->num_rates, sizeof(struct hostapd_rate_data)); if (!iface->current_rates) { wpa_printf(MSG_ERROR, "Failed to allocate memory for rate " "table."); return -1; } for (i = 0; i < mode->num_rates; i++) { struct hostapd_rate_data *rate; if (iface->conf->supported_rates && !hostapd_rate_found(iface->conf->supported_rates, mode->rates[i])) continue; rate = &iface->current_rates[iface->num_rates]; rate->rate = mode->rates[i]; if (hostapd_rate_found(basic_rates, rate->rate)) { rate->flags |= HOSTAPD_RATE_BASIC; num_basic_rates++; } wpa_printf(MSG_DEBUG, "RATE[%d] rate=%d flags=0x%x", iface->num_rates, rate->rate, rate->flags); iface->num_rates++; } if ((iface->num_rates == 0 || num_basic_rates == 0) && (!iface->conf->ieee80211n || !iface->conf->require_ht)) { wpa_printf(MSG_ERROR, "No rates remaining in supported/basic " "rate sets (%d,%d).", iface->num_rates, num_basic_rates); return -1; } return 0; } #ifdef CONFIG_IEEE80211N static int ieee80211n_allowed_ht40_channel_pair(struct hostapd_iface *iface) { int sec_chan, ok, j, first; int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157, 184, 192 }; size_t k; if (!iface->conf->secondary_channel) return 1; /* HT40 not used */ sec_chan = iface->conf->channel + iface->conf->secondary_channel * 4; wpa_printf(MSG_DEBUG, "HT40: control channel: %d " "secondary channel: %d", iface->conf->channel, sec_chan); /* Verify that HT40 secondary channel is an allowed 20 MHz * channel */ ok = 0; for (j = 0; j < iface->current_mode->num_channels; j++) { struct hostapd_channel_data *chan = &iface->current_mode->channels[j]; if (!(chan->flag & HOSTAPD_CHAN_DISABLED) && chan->chan == sec_chan) { ok = 1; break; } } if (!ok) { wpa_printf(MSG_ERROR, "HT40 secondary channel %d not allowed", sec_chan); return 0; } /* * Verify that HT40 primary,secondary channel pair is allowed per * IEEE 802.11n Annex J. This is only needed for 5 GHz band since * 2.4 GHz rules allow all cases where the secondary channel fits into * the list of allowed channels (already checked above). */ if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211A) return 1; if (iface->conf->secondary_channel > 0) first = iface->conf->channel; else first = sec_chan; ok = 0; for (k = 0; k < ARRAY_SIZE(allowed); k++) { if (first == allowed[k]) { ok = 1; break; } } if (!ok) { wpa_printf(MSG_ERROR, "HT40 channel pair (%d, %d) not allowed", iface->conf->channel, iface->conf->secondary_channel); return 0; } return 1; } static void ieee80211n_switch_pri_sec(struct hostapd_iface *iface) { if (iface->conf->secondary_channel > 0) { iface->conf->channel += 4; iface->conf->secondary_channel = -1; } else { iface->conf->channel -= 4; iface->conf->secondary_channel = 1; } } static void ieee80211n_get_pri_sec_chan(struct wpa_scan_res *bss, int *pri_chan, int *sec_chan) { struct ieee80211_ht_operation *oper; struct ieee802_11_elems elems; *pri_chan = *sec_chan = 0; ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems, 0); if (elems.ht_operation && elems.ht_operation_len >= sizeof(*oper)) { oper = (struct ieee80211_ht_operation *) elems.ht_operation; *pri_chan = oper->control_chan; if (oper->ht_param & HT_INFO_HT_PARAM_REC_TRANS_CHNL_WIDTH) { int sec = oper->ht_param & HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK; if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE) *sec_chan = *pri_chan + 4; else if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW) *sec_chan = *pri_chan - 4; } } } static int ieee80211n_check_40mhz_5g(struct hostapd_iface *iface, struct wpa_scan_results *scan_res) { int pri_chan, sec_chan, pri_freq, sec_freq, pri_bss, sec_bss; int bss_pri_chan, bss_sec_chan; size_t i; int match; pri_chan = iface->conf->channel; sec_chan = iface->conf->secondary_channel * 4; pri_freq = hostapd_hw_get_freq(iface->bss[0], pri_chan); if (iface->conf->secondary_channel > 0) sec_freq = pri_freq + 20; else sec_freq = pri_freq - 20; /* * Switch PRI/SEC channels if Beacons were detected on selected SEC * channel, but not on selected PRI channel. */ pri_bss = sec_bss = 0; for (i = 0; i < scan_res->num; i++) { struct wpa_scan_res *bss = scan_res->res[i]; if (bss->freq == pri_freq) pri_bss++; else if (bss->freq == sec_freq) sec_bss++; } if (sec_bss && !pri_bss) { wpa_printf(MSG_INFO, "Switch own primary and secondary " "channel to get secondary channel with no Beacons " "from other BSSes"); ieee80211n_switch_pri_sec(iface); } /* * Match PRI/SEC channel with any existing HT40 BSS on the same * channels that we are about to use (if already mixed order in * existing BSSes, use own preference). */ match = 0; for (i = 0; i < scan_res->num; i++) { struct wpa_scan_res *bss = scan_res->res[i]; ieee80211n_get_pri_sec_chan(bss, &bss_pri_chan, &bss_sec_chan); if (pri_chan == bss_pri_chan && sec_chan == bss_sec_chan) { match = 1; break; } } if (!match) { for (i = 0; i < scan_res->num; i++) { struct wpa_scan_res *bss = scan_res->res[i]; ieee80211n_get_pri_sec_chan(bss, &bss_pri_chan, &bss_sec_chan); if (pri_chan == bss_sec_chan && sec_chan == bss_pri_chan) { wpa_printf(MSG_INFO, "Switch own primary and " "secondary channel due to BSS " "overlap with " MACSTR, MAC2STR(bss->bssid)); ieee80211n_switch_pri_sec(iface); break; } } } return 1; } static int ieee80211n_check_40mhz_2g4(struct hostapd_iface *iface, struct wpa_scan_results *scan_res) { int pri_freq, sec_freq; int affected_start, affected_end; size_t i; pri_freq = hostapd_hw_get_freq(iface->bss[0], iface->conf->channel); if (iface->conf->secondary_channel > 0) sec_freq = pri_freq + 20; else sec_freq = pri_freq - 20; affected_start = (pri_freq + sec_freq) / 2 - 25; affected_end = (pri_freq + sec_freq) / 2 + 25; wpa_printf(MSG_DEBUG, "40 MHz affected channel range: [%d,%d] MHz", affected_start, affected_end); for (i = 0; i < scan_res->num; i++) { struct wpa_scan_res *bss = scan_res->res[i]; int pri = bss->freq; int sec = pri; int sec_chan, pri_chan; ieee80211n_get_pri_sec_chan(bss, &pri_chan, &sec_chan); if (sec_chan) { if (sec_chan < pri_chan) sec = pri - 20; else sec = pri + 20; } if ((pri < affected_start || pri > affected_end) && (sec < affected_start || sec > affected_end)) continue; /* not within affected channel range */ wpa_printf(MSG_DEBUG, "Neighboring BSS: " MACSTR " freq=%d pri=%d sec=%d", MAC2STR(bss->bssid), bss->freq, pri_chan, sec_chan); if (sec_chan) { if (pri_freq != pri || sec_freq != sec) { wpa_printf(MSG_DEBUG, "40 MHz pri/sec " "mismatch with BSS " MACSTR " <%d,%d> (chan=%d%c) vs. <%d,%d>", MAC2STR(bss->bssid), pri, sec, pri_chan, sec > pri ? '+' : '-', pri_freq, sec_freq); return 0; } } /* TODO: 40 MHz intolerant */ } return 1; } static void ieee80211n_check_scan(struct hostapd_iface *iface) { struct wpa_scan_results *scan_res; int oper40; int res; /* Check list of neighboring BSSes (from scan) to see whether 40 MHz is * allowed per IEEE Std 802.11-2012, 10.15.3.2 */ iface->scan_cb = NULL; scan_res = hostapd_driver_get_scan_results(iface->bss[0]); if (scan_res == NULL) { hostapd_setup_interface_complete(iface, 1); return; } if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A) oper40 = ieee80211n_check_40mhz_5g(iface, scan_res); else oper40 = ieee80211n_check_40mhz_2g4(iface, scan_res); wpa_scan_results_free(scan_res); if (!oper40) { wpa_printf(MSG_INFO, "20/40 MHz operation not permitted on " "channel pri=%d sec=%d based on overlapping BSSes", iface->conf->channel, iface->conf->channel + iface->conf->secondary_channel * 4); iface->conf->secondary_channel = 0; } res = ieee80211n_allowed_ht40_channel_pair(iface); hostapd_setup_interface_complete(iface, !res); } static void ieee80211n_scan_channels_2g4(struct hostapd_iface *iface, struct wpa_driver_scan_params *params) { /* Scan only the affected frequency range */ int pri_freq, sec_freq; int affected_start, affected_end; int i, pos; struct hostapd_hw_modes *mode; if (iface->current_mode == NULL) return; pri_freq = hostapd_hw_get_freq(iface->bss[0], iface->conf->channel); if (iface->conf->secondary_channel > 0) sec_freq = pri_freq + 20; else sec_freq = pri_freq - 20; affected_start = (pri_freq + sec_freq) / 2 - 25; affected_end = (pri_freq + sec_freq) / 2 + 25; wpa_printf(MSG_DEBUG, "40 MHz affected channel range: [%d,%d] MHz", affected_start, affected_end); mode = iface->current_mode; params->freqs = os_calloc(mode->num_channels + 1, sizeof(int)); if (params->freqs == NULL) return; pos = 0; for (i = 0; i < mode->num_channels; i++) { struct hostapd_channel_data *chan = &mode->channels[i]; if (chan->flag & HOSTAPD_CHAN_DISABLED) continue; if (chan->freq < affected_start || chan->freq > affected_end) continue; params->freqs[pos++] = chan->freq; } } static void ieee80211n_scan_channels_5g(struct hostapd_iface *iface, struct wpa_driver_scan_params *params) { /* Scan only the affected frequency range */ int pri_freq; int affected_start, affected_end; int i, pos; struct hostapd_hw_modes *mode; if (iface->current_mode == NULL) return; pri_freq = hostapd_hw_get_freq(iface->bss[0], iface->conf->channel); if (iface->conf->secondary_channel > 0) { affected_start = pri_freq - 10; affected_end = pri_freq + 30; } else { affected_start = pri_freq - 30; affected_end = pri_freq + 10; } wpa_printf(MSG_DEBUG, "40 MHz affected channel range: [%d,%d] MHz", affected_start, affected_end); mode = iface->current_mode; params->freqs = os_calloc(mode->num_channels + 1, sizeof(int)); if (params->freqs == NULL) return; pos = 0; for (i = 0; i < mode->num_channels; i++) { struct hostapd_channel_data *chan = &mode->channels[i]; if (chan->flag & HOSTAPD_CHAN_DISABLED) continue; if (chan->freq < affected_start || chan->freq > affected_end) continue; params->freqs[pos++] = chan->freq; } } static int ieee80211n_check_40mhz(struct hostapd_iface *iface) { struct wpa_driver_scan_params params; if (!iface->conf->secondary_channel) return 0; /* HT40 not used */ hostapd_set_state(iface, HAPD_IFACE_HT_SCAN); wpa_printf(MSG_DEBUG, "Scan for neighboring BSSes prior to enabling " "40 MHz channel"); os_memset(¶ms, 0, sizeof(params)); if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G) ieee80211n_scan_channels_2g4(iface, ¶ms); else ieee80211n_scan_channels_5g(iface, ¶ms); if (hostapd_driver_scan(iface->bss[0], ¶ms) < 0) { wpa_printf(MSG_ERROR, "Failed to request a scan of " "neighboring BSSes"); os_free(params.freqs); return -1; } os_free(params.freqs); iface->scan_cb = ieee80211n_check_scan; return 1; } static int ieee80211n_supported_ht_capab(struct hostapd_iface *iface) { u16 hw = iface->current_mode->ht_capab; u16 conf = iface->conf->ht_capab; if ((conf & HT_CAP_INFO_LDPC_CODING_CAP) && !(hw & HT_CAP_INFO_LDPC_CODING_CAP)) { wpa_printf(MSG_ERROR, "Driver does not support configured " "HT capability [LDPC]"); return 0; } if ((conf & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) && !(hw & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) { wpa_printf(MSG_ERROR, "Driver does not support configured " "HT capability [HT40*]"); return 0; } if ((conf & HT_CAP_INFO_SMPS_MASK) != (hw & HT_CAP_INFO_SMPS_MASK) && (conf & HT_CAP_INFO_SMPS_MASK) != HT_CAP_INFO_SMPS_DISABLED) { wpa_printf(MSG_ERROR, "Driver does not support configured " "HT capability [SMPS-*]"); return 0; } if ((conf & HT_CAP_INFO_GREEN_FIELD) && !(hw & HT_CAP_INFO_GREEN_FIELD)) { wpa_printf(MSG_ERROR, "Driver does not support configured " "HT capability [GF]"); return 0; } if ((conf & HT_CAP_INFO_SHORT_GI20MHZ) && !(hw & HT_CAP_INFO_SHORT_GI20MHZ)) { wpa_printf(MSG_ERROR, "Driver does not support configured " "HT capability [SHORT-GI-20]"); return 0; } if ((conf & HT_CAP_INFO_SHORT_GI40MHZ) && !(hw & HT_CAP_INFO_SHORT_GI40MHZ)) { wpa_printf(MSG_ERROR, "Driver does not support configured " "HT capability [SHORT-GI-40]"); return 0; } if ((conf & HT_CAP_INFO_TX_STBC) && !(hw & HT_CAP_INFO_TX_STBC)) { wpa_printf(MSG_ERROR, "Driver does not support configured " "HT capability [TX-STBC]"); return 0; } if ((conf & HT_CAP_INFO_RX_STBC_MASK) > (hw & HT_CAP_INFO_RX_STBC_MASK)) { wpa_printf(MSG_ERROR, "Driver does not support configured " "HT capability [RX-STBC*]"); return 0; } if ((conf & HT_CAP_INFO_DELAYED_BA) && !(hw & HT_CAP_INFO_DELAYED_BA)) { wpa_printf(MSG_ERROR, "Driver does not support configured " "HT capability [DELAYED-BA]"); return 0; } if ((conf & HT_CAP_INFO_MAX_AMSDU_SIZE) && !(hw & HT_CAP_INFO_MAX_AMSDU_SIZE)) { wpa_printf(MSG_ERROR, "Driver does not support configured " "HT capability [MAX-AMSDU-7935]"); return 0; } if ((conf & HT_CAP_INFO_DSSS_CCK40MHZ) && !(hw & HT_CAP_INFO_DSSS_CCK40MHZ)) { wpa_printf(MSG_ERROR, "Driver does not support configured " "HT capability [DSSS_CCK-40]"); return 0; } if ((conf & HT_CAP_INFO_PSMP_SUPP) && !(hw & HT_CAP_INFO_PSMP_SUPP)) { wpa_printf(MSG_ERROR, "Driver does not support configured " "HT capability [PSMP]"); return 0; } if ((conf & HT_CAP_INFO_LSIG_TXOP_PROTECT_SUPPORT) && !(hw & HT_CAP_INFO_LSIG_TXOP_PROTECT_SUPPORT)) { wpa_printf(MSG_ERROR, "Driver does not support configured " "HT capability [LSIG-TXOP-PROT]"); return 0; } return 1; } #ifdef CONFIG_IEEE80211AC static int ieee80211ac_cap_check(u32 hw, u32 conf, u32 cap, const char *name) { u32 req_cap = conf & cap; /* * Make sure we support all requested capabilities. * NOTE: We assume that 'cap' represents a capability mask, * not a discrete value. */ if ((hw & req_cap) != req_cap) { wpa_printf(MSG_ERROR, "Driver does not support configured VHT capability [%s]", name); return 0; } return 1; } static int ieee80211ac_cap_check_max(u32 hw, u32 conf, u32 cap, const char *name) { u32 hw_max = hw & cap; u32 conf_val = conf & cap; if (conf_val > hw_max) { int offset = find_first_bit(cap); wpa_printf(MSG_ERROR, "Configured VHT capability [%s] exceeds max value supported by the driver (%d > %d)", name, conf_val >> offset, hw_max >> offset); return 0; } return 1; } static int ieee80211ac_supported_vht_capab(struct hostapd_iface *iface) { u32 hw = iface->current_mode->vht_capab; u32 conf = iface->conf->vht_capab; wpa_printf(MSG_DEBUG, "hw vht capab: 0x%x, conf vht capab: 0x%x", hw, conf); #define VHT_CAP_CHECK(cap) \ do { \ if (!ieee80211ac_cap_check(hw, conf, cap, #cap)) \ return 0; \ } while (0) #define VHT_CAP_CHECK_MAX(cap) \ do { \ if (!ieee80211ac_cap_check_max(hw, conf, cap, #cap)) \ return 0; \ } while (0) VHT_CAP_CHECK_MAX(VHT_CAP_MAX_MPDU_LENGTH_MASK); VHT_CAP_CHECK(VHT_CAP_SUPP_CHAN_WIDTH_160MHZ); VHT_CAP_CHECK(VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ); VHT_CAP_CHECK(VHT_CAP_RXLDPC); VHT_CAP_CHECK(VHT_CAP_SHORT_GI_80); VHT_CAP_CHECK(VHT_CAP_SHORT_GI_160); VHT_CAP_CHECK(VHT_CAP_TXSTBC); VHT_CAP_CHECK_MAX(VHT_CAP_RXSTBC_MASK); VHT_CAP_CHECK(VHT_CAP_SU_BEAMFORMER_CAPABLE); VHT_CAP_CHECK(VHT_CAP_SU_BEAMFORMEE_CAPABLE); VHT_CAP_CHECK_MAX(VHT_CAP_BEAMFORMEE_STS_MAX); VHT_CAP_CHECK_MAX(VHT_CAP_SOUNDING_DIMENSION_MAX); VHT_CAP_CHECK(VHT_CAP_MU_BEAMFORMER_CAPABLE); VHT_CAP_CHECK(VHT_CAP_MU_BEAMFORMEE_CAPABLE); VHT_CAP_CHECK(VHT_CAP_VHT_TXOP_PS); VHT_CAP_CHECK(VHT_CAP_HTC_VHT); VHT_CAP_CHECK_MAX(VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT); VHT_CAP_CHECK(VHT_CAP_VHT_LINK_ADAPTATION_VHT_UNSOL_MFB); VHT_CAP_CHECK(VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB); VHT_CAP_CHECK(VHT_CAP_RX_ANTENNA_PATTERN); VHT_CAP_CHECK(VHT_CAP_TX_ANTENNA_PATTERN); #undef VHT_CAP_CHECK #undef VHT_CAP_CHECK_MAX return 1; } #endif /* CONFIG_IEEE80211AC */ #endif /* CONFIG_IEEE80211N */ int hostapd_check_ht_capab(struct hostapd_iface *iface) { #ifdef CONFIG_IEEE80211N int ret; if (!iface->conf->ieee80211n) return 0; if (!ieee80211n_supported_ht_capab(iface)) return -1; #ifdef CONFIG_IEEE80211AC if (!ieee80211ac_supported_vht_capab(iface)) return -1; #endif /* CONFIG_IEEE80211AC */ ret = ieee80211n_check_40mhz(iface); if (ret) return ret; if (!ieee80211n_allowed_ht40_channel_pair(iface)) return -1; #endif /* CONFIG_IEEE80211N */ return 0; } static int hostapd_is_usable_chan(struct hostapd_iface *iface, int channel, int primary) { int i; struct hostapd_channel_data *chan; for (i = 0; i < iface->current_mode->num_channels; i++) { chan = &iface->current_mode->channels[i]; if (chan->chan != channel) continue; if (!(chan->flag & HOSTAPD_CHAN_DISABLED)) return 1; wpa_printf(MSG_DEBUG, "%schannel [%i] (%i) is disabled for use in AP mode, flags: 0x%x%s%s%s", primary ? "" : "Configured HT40 secondary ", i, chan->chan, chan->flag, chan->flag & HOSTAPD_CHAN_NO_IBSS ? " NO-IBSS" : "", chan->flag & HOSTAPD_CHAN_PASSIVE_SCAN ? " PASSIVE-SCAN" : "", chan->flag & HOSTAPD_CHAN_RADAR ? " RADAR" : ""); } return 0; } static int hostapd_is_usable_chans(struct hostapd_iface *iface) { if (!hostapd_is_usable_chan(iface, iface->conf->channel, 1)) return 0; if (!iface->conf->secondary_channel) return 1; return hostapd_is_usable_chan(iface, iface->conf->channel + iface->conf->secondary_channel * 4, 0); } static enum hostapd_chan_status hostapd_check_chans(struct hostapd_iface *iface) { if (iface->conf->channel) { if (hostapd_is_usable_chans(iface)) return HOSTAPD_CHAN_VALID; else return HOSTAPD_CHAN_INVALID; } /* * The user set channel=0 or channel=acs_survey * which is used to trigger ACS. */ switch (acs_init(iface)) { case HOSTAPD_CHAN_ACS: return HOSTAPD_CHAN_ACS; case HOSTAPD_CHAN_VALID: case HOSTAPD_CHAN_INVALID: default: return HOSTAPD_CHAN_INVALID; } } static void hostapd_notify_bad_chans(struct hostapd_iface *iface) { hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_WARNING, "Configured channel (%d) not found from the " "channel list of current mode (%d) %s", iface->conf->channel, iface->current_mode->mode, hostapd_hw_mode_txt(iface->current_mode->mode)); hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_WARNING, "Hardware does not support configured channel"); } int hostapd_acs_completed(struct hostapd_iface *iface, int err) { int ret = -1; if (err) goto out; switch (hostapd_check_chans(iface)) { case HOSTAPD_CHAN_VALID: wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, ACS_EVENT_COMPLETED "freq=%d channel=%d", hostapd_hw_get_freq(iface->bss[0], iface->conf->channel), iface->conf->channel); break; case HOSTAPD_CHAN_ACS: wpa_printf(MSG_ERROR, "ACS error - reported complete, but no result available"); wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, ACS_EVENT_FAILED); hostapd_notify_bad_chans(iface); goto out; case HOSTAPD_CHAN_INVALID: default: wpa_printf(MSG_ERROR, "ACS picked unusable channels"); wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, ACS_EVENT_FAILED); hostapd_notify_bad_chans(iface); goto out; } ret = hostapd_check_ht_capab(iface); if (ret < 0) goto out; if (ret == 1) { wpa_printf(MSG_DEBUG, "Interface initialization will be completed in a callback"); return 0; } ret = 0; out: return hostapd_setup_interface_complete(iface, ret); } /** * hostapd_select_hw_mode - Select the hardware mode * @iface: Pointer to interface data. * Returns: 0 on success, < 0 on failure * * Sets up the hardware mode, channel, rates, and passive scanning * based on the configuration. */ int hostapd_select_hw_mode(struct hostapd_iface *iface) { int i; if (iface->num_hw_features < 1) return -1; iface->current_mode = NULL; for (i = 0; i < iface->num_hw_features; i++) { struct hostapd_hw_modes *mode = &iface->hw_features[i]; if (mode->mode == iface->conf->hw_mode) { iface->current_mode = mode; break; } } if (iface->current_mode == NULL) { wpa_printf(MSG_ERROR, "Hardware does not support configured " "mode"); hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_WARNING, "Hardware does not support configured mode " "(%d) (hw_mode in hostapd.conf)", (int) iface->conf->hw_mode); return -2; } switch (hostapd_check_chans(iface)) { case HOSTAPD_CHAN_VALID: return 0; case HOSTAPD_CHAN_ACS: /* ACS will run and later complete */ return 1; case HOSTAPD_CHAN_INVALID: default: hostapd_notify_bad_chans(iface); return -3; } return 0; } const char * hostapd_hw_mode_txt(int mode) { switch (mode) { case HOSTAPD_MODE_IEEE80211A: return "IEEE 802.11a"; case HOSTAPD_MODE_IEEE80211B: return "IEEE 802.11b"; case HOSTAPD_MODE_IEEE80211G: return "IEEE 802.11g"; case HOSTAPD_MODE_IEEE80211AD: return "IEEE 802.11ad"; default: return "UNKNOWN"; } } int hostapd_hw_get_freq(struct hostapd_data *hapd, int chan) { int i; if (!hapd->iface->current_mode) return 0; for (i = 0; i < hapd->iface->current_mode->num_channels; i++) { struct hostapd_channel_data *ch = &hapd->iface->current_mode->channels[i]; if (ch->chan == chan) return ch->freq; } return 0; } int hostapd_hw_get_channel(struct hostapd_data *hapd, int freq) { int i; if (!hapd->iface->current_mode) return 0; for (i = 0; i < hapd->iface->current_mode->num_channels; i++) { struct hostapd_channel_data *ch = &hapd->iface->current_mode->channels[i]; if (ch->freq == freq) return ch->chan; } return 0; } wpa-2.1/src/ap/hw_features.h000066400000000000000000000033111227414666700160340ustar00rootroot00000000000000/* * hostapd / Hardware feature query and different modes * Copyright 2002-2003, Instant802 Networks, Inc. * Copyright 2005-2006, Devicescape Software, Inc. * Copyright (c) 2008-2011, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef HW_FEATURES_H #define HW_FEATURES_H #ifdef NEED_AP_MLME void hostapd_free_hw_features(struct hostapd_hw_modes *hw_features, size_t num_hw_features); int hostapd_get_hw_features(struct hostapd_iface *iface); int hostapd_acs_completed(struct hostapd_iface *iface, int err); int hostapd_select_hw_mode(struct hostapd_iface *iface); const char * hostapd_hw_mode_txt(int mode); int hostapd_hw_get_freq(struct hostapd_data *hapd, int chan); int hostapd_hw_get_channel(struct hostapd_data *hapd, int freq); int hostapd_check_ht_capab(struct hostapd_iface *iface); int hostapd_prepare_rates(struct hostapd_iface *iface, struct hostapd_hw_modes *mode); #else /* NEED_AP_MLME */ static inline void hostapd_free_hw_features(struct hostapd_hw_modes *hw_features, size_t num_hw_features) { } static inline int hostapd_get_hw_features(struct hostapd_iface *iface) { return -1; } static inline int hostapd_select_hw_mode(struct hostapd_iface *iface) { return -100; } static inline const char * hostapd_hw_mode_txt(int mode) { return NULL; } static inline int hostapd_hw_get_freq(struct hostapd_data *hapd, int chan) { return -1; } static inline int hostapd_check_ht_capab(struct hostapd_iface *iface) { return 0; } static inline int hostapd_prepare_rates(struct hostapd_iface *iface, struct hostapd_hw_modes *mode) { return 0; } #endif /* NEED_AP_MLME */ #endif /* HW_FEATURES_H */ wpa-2.1/src/ap/iapp.c000066400000000000000000000350571227414666700144600ustar00rootroot00000000000000/* * hostapd / IEEE 802.11F-2003 Inter-Access Point Protocol (IAPP) * Copyright (c) 2002-2007, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. * * Note: IEEE 802.11F-2003 was a experimental use specification. It has expired * and IEEE has withdrawn it. In other words, it is likely better to look at * using some other mechanism for AP-to-AP communication than extending the * implementation here. */ /* TODO: * Level 1: no administrative or security support * (e.g., static BSSID to IP address mapping in each AP) * Level 2: support for dynamic mapping of BSSID to IP address * Level 3: support for encryption and authentication of IAPP messages * - add support for MOVE-notify and MOVE-response (this requires support for * finding out IP address for previous AP using RADIUS) * - add support for Send- and ACK-Security-Block to speedup IEEE 802.1X during * reassociation to another AP * - implement counters etc. for IAPP MIB * - verify endianness of fields in IAPP messages; are they big-endian as * used here? * - RADIUS connection for AP registration and BSSID to IP address mapping * - TCP connection for IAPP MOVE, CACHE * - broadcast ESP for IAPP ADD-notify * - ESP for IAPP MOVE messages * - security block sending/processing * - IEEE 802.11 context transfer */ #include "utils/includes.h" #include #include #ifdef USE_KERNEL_HEADERS #include #else /* USE_KERNEL_HEADERS */ #include #endif /* USE_KERNEL_HEADERS */ #include "utils/common.h" #include "utils/eloop.h" #include "common/ieee802_11_defs.h" #include "hostapd.h" #include "ap_config.h" #include "ieee802_11.h" #include "sta_info.h" #include "iapp.h" #define IAPP_MULTICAST "224.0.1.178" #define IAPP_UDP_PORT 3517 #define IAPP_TCP_PORT 3517 struct iapp_hdr { u8 version; u8 command; be16 identifier; be16 length; /* followed by length-6 octets of data */ } __attribute__ ((packed)); #define IAPP_VERSION 0 enum IAPP_COMMAND { IAPP_CMD_ADD_notify = 0, IAPP_CMD_MOVE_notify = 1, IAPP_CMD_MOVE_response = 2, IAPP_CMD_Send_Security_Block = 3, IAPP_CMD_ACK_Security_Block = 4, IAPP_CMD_CACHE_notify = 5, IAPP_CMD_CACHE_response = 6, }; /* ADD-notify - multicast UDP on the local LAN */ struct iapp_add_notify { u8 addr_len; /* ETH_ALEN */ u8 reserved; u8 mac_addr[ETH_ALEN]; be16 seq_num; } __attribute__ ((packed)); /* Layer 2 Update frame (802.2 Type 1 LLC XID Update response) */ struct iapp_layer2_update { u8 da[ETH_ALEN]; /* broadcast */ u8 sa[ETH_ALEN]; /* STA addr */ be16 len; /* 6 */ u8 dsap; /* null DSAP address */ u8 ssap; /* null SSAP address, CR=Response */ u8 control; u8 xid_info[3]; } __attribute__ ((packed)); /* MOVE-notify - unicast TCP */ struct iapp_move_notify { u8 addr_len; /* ETH_ALEN */ u8 reserved; u8 mac_addr[ETH_ALEN]; u16 seq_num; u16 ctx_block_len; /* followed by ctx_block_len bytes */ } __attribute__ ((packed)); /* MOVE-response - unicast TCP */ struct iapp_move_response { u8 addr_len; /* ETH_ALEN */ u8 status; u8 mac_addr[ETH_ALEN]; u16 seq_num; u16 ctx_block_len; /* followed by ctx_block_len bytes */ } __attribute__ ((packed)); enum { IAPP_MOVE_SUCCESSFUL = 0, IAPP_MOVE_DENIED = 1, IAPP_MOVE_STALE_MOVE = 2, }; /* CACHE-notify */ struct iapp_cache_notify { u8 addr_len; /* ETH_ALEN */ u8 reserved; u8 mac_addr[ETH_ALEN]; u16 seq_num; u8 current_ap[ETH_ALEN]; u16 ctx_block_len; /* ctx_block_len bytes of context block followed by 16-bit context * timeout */ } __attribute__ ((packed)); /* CACHE-response - unicast TCP */ struct iapp_cache_response { u8 addr_len; /* ETH_ALEN */ u8 status; u8 mac_addr[ETH_ALEN]; u16 seq_num; } __attribute__ ((packed)); enum { IAPP_CACHE_SUCCESSFUL = 0, IAPP_CACHE_STALE_CACHE = 1, }; /* Send-Security-Block - unicast TCP */ struct iapp_send_security_block { u8 iv[8]; u16 sec_block_len; /* followed by sec_block_len bytes of security block */ } __attribute__ ((packed)); /* ACK-Security-Block - unicast TCP */ struct iapp_ack_security_block { u8 iv[8]; u8 new_ap_ack_authenticator[48]; } __attribute__ ((packed)); struct iapp_data { struct hostapd_data *hapd; u16 identifier; /* next IAPP identifier */ struct in_addr own, multicast; int udp_sock; int packet_sock; }; static void iapp_send_add(struct iapp_data *iapp, u8 *mac_addr, u16 seq_num) { char buf[128]; struct iapp_hdr *hdr; struct iapp_add_notify *add; struct sockaddr_in addr; /* Send IAPP ADD-notify to remove possible association from other APs */ hdr = (struct iapp_hdr *) buf; hdr->version = IAPP_VERSION; hdr->command = IAPP_CMD_ADD_notify; hdr->identifier = host_to_be16(iapp->identifier++); hdr->length = host_to_be16(sizeof(*hdr) + sizeof(*add)); add = (struct iapp_add_notify *) (hdr + 1); add->addr_len = ETH_ALEN; add->reserved = 0; os_memcpy(add->mac_addr, mac_addr, ETH_ALEN); add->seq_num = host_to_be16(seq_num); os_memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = iapp->multicast.s_addr; addr.sin_port = htons(IAPP_UDP_PORT); if (sendto(iapp->udp_sock, buf, (char *) (add + 1) - buf, 0, (struct sockaddr *) &addr, sizeof(addr)) < 0) wpa_printf(MSG_INFO, "sendto[IAPP-ADD]: %s", strerror(errno)); } static void iapp_send_layer2_update(struct iapp_data *iapp, u8 *addr) { struct iapp_layer2_update msg; /* Send Level 2 Update Frame to update forwarding tables in layer 2 * bridge devices */ /* 802.2 Type 1 Logical Link Control (LLC) Exchange Identifier (XID) * Update response frame; IEEE Std 802.2-1998, 5.4.1.2.1 */ os_memset(msg.da, 0xff, ETH_ALEN); os_memcpy(msg.sa, addr, ETH_ALEN); msg.len = host_to_be16(6); msg.dsap = 0; /* NULL DSAP address */ msg.ssap = 0x01; /* NULL SSAP address, CR Bit: Response */ msg.control = 0xaf; /* XID response lsb.1111F101. * F=0 (no poll command; unsolicited frame) */ msg.xid_info[0] = 0x81; /* XID format identifier */ msg.xid_info[1] = 1; /* LLC types/classes: Type 1 LLC */ msg.xid_info[2] = 1 << 1; /* XID sender's receive window size (RW) * FIX: what is correct RW with 802.11? */ if (send(iapp->packet_sock, &msg, sizeof(msg), 0) < 0) wpa_printf(MSG_INFO, "send[L2 Update]: %s", strerror(errno)); } /** * iapp_new_station - IAPP processing for a new STA * @iapp: IAPP data * @sta: The associated station */ void iapp_new_station(struct iapp_data *iapp, struct sta_info *sta) { struct ieee80211_mgmt *assoc; u16 seq; if (iapp == NULL) return; assoc = sta->last_assoc_req; seq = assoc ? WLAN_GET_SEQ_SEQ(le_to_host16(assoc->seq_ctrl)) : 0; /* IAPP-ADD.request(MAC Address, Sequence Number, Timeout) */ hostapd_logger(iapp->hapd, sta->addr, HOSTAPD_MODULE_IAPP, HOSTAPD_LEVEL_DEBUG, "IAPP-ADD.request(seq=%d)", seq); iapp_send_layer2_update(iapp, sta->addr); iapp_send_add(iapp, sta->addr, seq); if (assoc && WLAN_FC_GET_STYPE(le_to_host16(assoc->frame_control)) == WLAN_FC_STYPE_REASSOC_REQ) { /* IAPP-MOVE.request(MAC Address, Sequence Number, Old AP, * Context Block, Timeout) */ /* TODO: Send IAPP-MOVE to the old AP; Map Old AP BSSID to * IP address */ } } static void iapp_process_add_notify(struct iapp_data *iapp, struct sockaddr_in *from, struct iapp_hdr *hdr, int len) { struct iapp_add_notify *add = (struct iapp_add_notify *) (hdr + 1); struct sta_info *sta; if (len != sizeof(*add)) { wpa_printf(MSG_INFO, "Invalid IAPP-ADD packet length %d (expected %lu)", len, (unsigned long) sizeof(*add)); return; } sta = ap_get_sta(iapp->hapd, add->mac_addr); /* IAPP-ADD.indication(MAC Address, Sequence Number) */ hostapd_logger(iapp->hapd, add->mac_addr, HOSTAPD_MODULE_IAPP, HOSTAPD_LEVEL_INFO, "Received IAPP ADD-notify (seq# %d) from %s:%d%s", be_to_host16(add->seq_num), inet_ntoa(from->sin_addr), ntohs(from->sin_port), sta ? "" : " (STA not found)"); if (!sta) return; /* TODO: could use seq_num to try to determine whether last association * to this AP is newer than the one advertised in IAPP-ADD. Although, * this is not really a reliable verification. */ hostapd_logger(iapp->hapd, add->mac_addr, HOSTAPD_MODULE_IAPP, HOSTAPD_LEVEL_DEBUG, "Removing STA due to IAPP ADD-notify"); ap_sta_disconnect(iapp->hapd, sta, NULL, 0); } /** * iapp_receive_udp - Process IAPP UDP frames * @sock: File descriptor for the socket * @eloop_ctx: IAPP data (struct iapp_data *) * @sock_ctx: Not used */ static void iapp_receive_udp(int sock, void *eloop_ctx, void *sock_ctx) { struct iapp_data *iapp = eloop_ctx; int len, hlen; unsigned char buf[128]; struct sockaddr_in from; socklen_t fromlen; struct iapp_hdr *hdr; /* Handle incoming IAPP frames (over UDP/IP) */ fromlen = sizeof(from); len = recvfrom(iapp->udp_sock, buf, sizeof(buf), 0, (struct sockaddr *) &from, &fromlen); if (len < 0) { wpa_printf(MSG_INFO, "iapp_receive_udp - recvfrom: %s", strerror(errno)); return; } if (from.sin_addr.s_addr == iapp->own.s_addr) return; /* ignore own IAPP messages */ hostapd_logger(iapp->hapd, NULL, HOSTAPD_MODULE_IAPP, HOSTAPD_LEVEL_DEBUG, "Received %d byte IAPP frame from %s%s\n", len, inet_ntoa(from.sin_addr), len < (int) sizeof(*hdr) ? " (too short)" : ""); if (len < (int) sizeof(*hdr)) return; hdr = (struct iapp_hdr *) buf; hlen = be_to_host16(hdr->length); hostapd_logger(iapp->hapd, NULL, HOSTAPD_MODULE_IAPP, HOSTAPD_LEVEL_DEBUG, "RX: version=%d command=%d id=%d len=%d\n", hdr->version, hdr->command, be_to_host16(hdr->identifier), hlen); if (hdr->version != IAPP_VERSION) { wpa_printf(MSG_INFO, "Dropping IAPP frame with unknown version %d", hdr->version); return; } if (hlen > len) { wpa_printf(MSG_INFO, "Underflow IAPP frame (hlen=%d len=%d)", hlen, len); return; } if (hlen < len) { wpa_printf(MSG_INFO, "Ignoring %d extra bytes from IAPP frame", len - hlen); len = hlen; } switch (hdr->command) { case IAPP_CMD_ADD_notify: iapp_process_add_notify(iapp, &from, hdr, hlen - sizeof(*hdr)); break; case IAPP_CMD_MOVE_notify: /* TODO: MOVE is using TCP; so move this to TCP handler once it * is implemented.. */ /* IAPP-MOVE.indication(MAC Address, New BSSID, * Sequence Number, AP Address, Context Block) */ /* TODO: process */ break; default: wpa_printf(MSG_INFO, "Unknown IAPP command %d", hdr->command); break; } } struct iapp_data * iapp_init(struct hostapd_data *hapd, const char *iface) { struct ifreq ifr; struct sockaddr_ll addr; int ifindex; struct sockaddr_in *paddr, uaddr; struct iapp_data *iapp; struct ip_mreqn mreq; iapp = os_zalloc(sizeof(*iapp)); if (iapp == NULL) return NULL; iapp->hapd = hapd; iapp->udp_sock = iapp->packet_sock = -1; /* TODO: * open socket for sending and receiving IAPP frames over TCP */ iapp->udp_sock = socket(PF_INET, SOCK_DGRAM, 0); if (iapp->udp_sock < 0) { wpa_printf(MSG_INFO, "iapp_init - socket[PF_INET,SOCK_DGRAM]: %s", strerror(errno)); iapp_deinit(iapp); return NULL; } os_memset(&ifr, 0, sizeof(ifr)); os_strlcpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name)); if (ioctl(iapp->udp_sock, SIOCGIFINDEX, &ifr) != 0) { wpa_printf(MSG_INFO, "iapp_init - ioctl(SIOCGIFINDEX): %s", strerror(errno)); iapp_deinit(iapp); return NULL; } ifindex = ifr.ifr_ifindex; if (ioctl(iapp->udp_sock, SIOCGIFADDR, &ifr) != 0) { wpa_printf(MSG_INFO, "iapp_init - ioctl(SIOCGIFADDR): %s", strerror(errno)); iapp_deinit(iapp); return NULL; } paddr = (struct sockaddr_in *) &ifr.ifr_addr; if (paddr->sin_family != AF_INET) { wpa_printf(MSG_INFO, "IAPP: Invalid address family %i (SIOCGIFADDR)", paddr->sin_family); iapp_deinit(iapp); return NULL; } iapp->own.s_addr = paddr->sin_addr.s_addr; if (ioctl(iapp->udp_sock, SIOCGIFBRDADDR, &ifr) != 0) { wpa_printf(MSG_INFO, "iapp_init - ioctl(SIOCGIFBRDADDR): %s", strerror(errno)); iapp_deinit(iapp); return NULL; } paddr = (struct sockaddr_in *) &ifr.ifr_addr; if (paddr->sin_family != AF_INET) { wpa_printf(MSG_INFO, "Invalid address family %i (SIOCGIFBRDADDR)", paddr->sin_family); iapp_deinit(iapp); return NULL; } inet_aton(IAPP_MULTICAST, &iapp->multicast); os_memset(&uaddr, 0, sizeof(uaddr)); uaddr.sin_family = AF_INET; uaddr.sin_port = htons(IAPP_UDP_PORT); if (bind(iapp->udp_sock, (struct sockaddr *) &uaddr, sizeof(uaddr)) < 0) { wpa_printf(MSG_INFO, "iapp_init - bind[UDP]: %s", strerror(errno)); iapp_deinit(iapp); return NULL; } os_memset(&mreq, 0, sizeof(mreq)); mreq.imr_multiaddr = iapp->multicast; mreq.imr_address.s_addr = INADDR_ANY; mreq.imr_ifindex = 0; if (setsockopt(iapp->udp_sock, SOL_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) { wpa_printf(MSG_INFO, "iapp_init - setsockopt[UDP,IP_ADD_MEMBERSHIP]: %s", strerror(errno)); iapp_deinit(iapp); return NULL; } iapp->packet_sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); if (iapp->packet_sock < 0) { wpa_printf(MSG_INFO, "iapp_init - socket[PF_PACKET,SOCK_RAW]: %s", strerror(errno)); iapp_deinit(iapp); return NULL; } os_memset(&addr, 0, sizeof(addr)); addr.sll_family = AF_PACKET; addr.sll_ifindex = ifindex; if (bind(iapp->packet_sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { wpa_printf(MSG_INFO, "iapp_init - bind[PACKET]: %s", strerror(errno)); iapp_deinit(iapp); return NULL; } if (eloop_register_read_sock(iapp->udp_sock, iapp_receive_udp, iapp, NULL)) { wpa_printf(MSG_INFO, "Could not register read socket for IAPP"); iapp_deinit(iapp); return NULL; } wpa_printf(MSG_INFO, "IEEE 802.11F (IAPP) using interface %s", iface); /* TODO: For levels 2 and 3: send RADIUS Initiate-Request, receive * RADIUS Initiate-Accept or Initiate-Reject. IAPP port should actually * be openned only after receiving Initiate-Accept. If Initiate-Reject * is received, IAPP is not started. */ return iapp; } void iapp_deinit(struct iapp_data *iapp) { struct ip_mreqn mreq; if (iapp == NULL) return; if (iapp->udp_sock >= 0) { os_memset(&mreq, 0, sizeof(mreq)); mreq.imr_multiaddr = iapp->multicast; mreq.imr_address.s_addr = INADDR_ANY; mreq.imr_ifindex = 0; if (setsockopt(iapp->udp_sock, SOL_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) { wpa_printf(MSG_INFO, "iapp_deinit - setsockopt[UDP,IP_DEL_MEMBERSHIP]: %s", strerror(errno)); } eloop_unregister_read_sock(iapp->udp_sock); close(iapp->udp_sock); } if (iapp->packet_sock >= 0) { eloop_unregister_read_sock(iapp->packet_sock); close(iapp->packet_sock); } os_free(iapp); } wpa-2.1/src/ap/iapp.h000066400000000000000000000015011227414666700144500ustar00rootroot00000000000000/* * hostapd / IEEE 802.11F-2003 Inter-Access Point Protocol (IAPP) * Copyright (c) 2002-2005, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef IAPP_H #define IAPP_H struct iapp_data; #ifdef CONFIG_IAPP void iapp_new_station(struct iapp_data *iapp, struct sta_info *sta); struct iapp_data * iapp_init(struct hostapd_data *hapd, const char *iface); void iapp_deinit(struct iapp_data *iapp); #else /* CONFIG_IAPP */ static inline void iapp_new_station(struct iapp_data *iapp, struct sta_info *sta) { } static inline struct iapp_data * iapp_init(struct hostapd_data *hapd, const char *iface) { return NULL; } static inline void iapp_deinit(struct iapp_data *iapp) { } #endif /* CONFIG_IAPP */ #endif /* IAPP_H */ wpa-2.1/src/ap/ieee802_11.c000066400000000000000000001741471227414666700151750ustar00rootroot00000000000000/* * hostapd / IEEE 802.11 Management * Copyright (c) 2002-2013, 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 "utils/common.h" #include "utils/eloop.h" #include "crypto/crypto.h" #include "crypto/sha256.h" #include "crypto/random.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" #include "common/wpa_ctrl.h" #include "common/sae.h" #include "radius/radius.h" #include "radius/radius_client.h" #include "p2p/p2p.h" #include "wps/wps.h" #include "hostapd.h" #include "beacon.h" #include "ieee802_11_auth.h" #include "sta_info.h" #include "ieee802_1x.h" #include "wpa_auth.h" #include "wmm.h" #include "ap_list.h" #include "accounting.h" #include "ap_config.h" #include "ap_mlme.h" #include "p2p_hostapd.h" #include "ap_drv_ops.h" #include "wnm_ap.h" #include "ieee802_11.h" u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid) { u8 *pos = eid; int i, num, count; if (hapd->iface->current_rates == NULL) return eid; *pos++ = WLAN_EID_SUPP_RATES; num = hapd->iface->num_rates; if (hapd->iconf->ieee80211n && hapd->iconf->require_ht) num++; if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht) num++; if (num > 8) { /* rest of the rates are encoded in Extended supported * rates element */ num = 8; } *pos++ = num; count = 0; for (i = 0, count = 0; i < hapd->iface->num_rates && count < num; i++) { count++; *pos = hapd->iface->current_rates[i].rate / 5; if (hapd->iface->current_rates[i].flags & HOSTAPD_RATE_BASIC) *pos |= 0x80; pos++; } if (hapd->iconf->ieee80211n && hapd->iconf->require_ht && count < 8) { count++; *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_HT_PHY; } if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht && count < 8) { count++; *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_VHT_PHY; } return pos; } u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid) { u8 *pos = eid; int i, num, count; if (hapd->iface->current_rates == NULL) return eid; num = hapd->iface->num_rates; if (hapd->iconf->ieee80211n && hapd->iconf->require_ht) num++; if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht) num++; if (num <= 8) return eid; num -= 8; *pos++ = WLAN_EID_EXT_SUPP_RATES; *pos++ = num; count = 0; for (i = 0, count = 0; i < hapd->iface->num_rates && count < num + 8; i++) { count++; if (count <= 8) continue; /* already in SuppRates IE */ *pos = hapd->iface->current_rates[i].rate / 5; if (hapd->iface->current_rates[i].flags & HOSTAPD_RATE_BASIC) *pos |= 0x80; pos++; } if (hapd->iconf->ieee80211n && hapd->iconf->require_ht) { count++; if (count > 8) *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_HT_PHY; } if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht) { count++; if (count > 8) *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_VHT_PHY; } return pos; } u16 hostapd_own_capab_info(struct hostapd_data *hapd, struct sta_info *sta, int probe) { int capab = WLAN_CAPABILITY_ESS; int privacy; if (hapd->iface->num_sta_no_short_preamble == 0 && hapd->iconf->preamble == SHORT_PREAMBLE) capab |= WLAN_CAPABILITY_SHORT_PREAMBLE; privacy = hapd->conf->ssid.wep.keys_set; if (hapd->conf->ieee802_1x && (hapd->conf->default_wep_key_len || hapd->conf->individual_wep_key_len)) privacy = 1; if (hapd->conf->wpa) privacy = 1; if (sta) { int policy, def_klen; if (probe && sta->ssid_probe) { policy = sta->ssid_probe->security_policy; def_klen = sta->ssid_probe->wep.default_len; } else { policy = sta->ssid->security_policy; def_klen = sta->ssid->wep.default_len; } privacy = policy != SECURITY_PLAINTEXT; if (policy == SECURITY_IEEE_802_1X && def_klen == 0) privacy = 0; } if (privacy) capab |= WLAN_CAPABILITY_PRIVACY; if (hapd->iface->current_mode && hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G && hapd->iface->num_sta_no_short_slot_time == 0) capab |= WLAN_CAPABILITY_SHORT_SLOT_TIME; return capab; } void ieee802_11_print_ssid(char *buf, const u8 *ssid, u8 len) { int i; if (len > HOSTAPD_MAX_SSID_LEN) len = HOSTAPD_MAX_SSID_LEN; for (i = 0; i < len; i++) { if (ssid[i] >= 32 && ssid[i] < 127) buf[i] = ssid[i]; else buf[i] = '.'; } buf[len] = '\0'; } static u16 auth_shared_key(struct hostapd_data *hapd, struct sta_info *sta, u16 auth_transaction, const u8 *challenge, int iswep) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "authentication (shared key, transaction %d)", auth_transaction); if (auth_transaction == 1) { if (!sta->challenge) { /* Generate a pseudo-random challenge */ u8 key[8]; struct os_time now; int r; sta->challenge = os_zalloc(WLAN_AUTH_CHALLENGE_LEN); if (sta->challenge == NULL) return WLAN_STATUS_UNSPECIFIED_FAILURE; os_get_time(&now); r = os_random(); os_memcpy(key, &now.sec, 4); os_memcpy(key + 4, &r, 4); rc4_skip(key, sizeof(key), 0, sta->challenge, WLAN_AUTH_CHALLENGE_LEN); } return 0; } if (auth_transaction != 3) return WLAN_STATUS_UNSPECIFIED_FAILURE; /* Transaction 3 */ if (!iswep || !sta->challenge || !challenge || os_memcmp(sta->challenge, challenge, WLAN_AUTH_CHALLENGE_LEN)) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "shared key authentication - invalid " "challenge-response"); return WLAN_STATUS_CHALLENGE_FAIL; } hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "authentication OK (shared key)"); sta->flags |= WLAN_STA_AUTH; wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH); os_free(sta->challenge); sta->challenge = NULL; return 0; } static void send_auth_reply(struct hostapd_data *hapd, const u8 *dst, const u8 *bssid, u16 auth_alg, u16 auth_transaction, u16 resp, const u8 *ies, size_t ies_len) { struct ieee80211_mgmt *reply; u8 *buf; size_t rlen; rlen = IEEE80211_HDRLEN + sizeof(reply->u.auth) + ies_len; buf = os_zalloc(rlen); if (buf == NULL) return; reply = (struct ieee80211_mgmt *) buf; reply->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_AUTH); os_memcpy(reply->da, dst, ETH_ALEN); os_memcpy(reply->sa, hapd->own_addr, ETH_ALEN); os_memcpy(reply->bssid, bssid, ETH_ALEN); reply->u.auth.auth_alg = host_to_le16(auth_alg); reply->u.auth.auth_transaction = host_to_le16(auth_transaction); reply->u.auth.status_code = host_to_le16(resp); if (ies && ies_len) os_memcpy(reply->u.auth.variable, ies, ies_len); wpa_printf(MSG_DEBUG, "authentication reply: STA=" MACSTR " auth_alg=%d auth_transaction=%d resp=%d (IE len=%lu)", MAC2STR(dst), auth_alg, auth_transaction, resp, (unsigned long) ies_len); if (hostapd_drv_send_mlme(hapd, reply, rlen, 0) < 0) wpa_printf(MSG_INFO, "send_auth_reply: send"); os_free(buf); } #ifdef CONFIG_IEEE80211R static void handle_auth_ft_finish(void *ctx, const u8 *dst, const u8 *bssid, u16 auth_transaction, u16 status, const u8 *ies, size_t ies_len) { struct hostapd_data *hapd = ctx; struct sta_info *sta; send_auth_reply(hapd, dst, bssid, WLAN_AUTH_FT, auth_transaction, status, ies, ies_len); if (status != WLAN_STATUS_SUCCESS) return; sta = ap_get_sta(hapd, dst); if (sta == NULL) return; hostapd_logger(hapd, dst, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "authentication OK (FT)"); sta->flags |= WLAN_STA_AUTH; mlme_authenticate_indication(hapd, sta); } #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_SAE static struct wpabuf * auth_process_sae_commit(struct hostapd_data *hapd, struct sta_info *sta) { struct wpabuf *buf; if (hapd->conf->ssid.wpa_passphrase == NULL) { wpa_printf(MSG_DEBUG, "SAE: No password available"); return NULL; } if (sae_prepare_commit(hapd->own_addr, sta->addr, (u8 *) hapd->conf->ssid.wpa_passphrase, os_strlen(hapd->conf->ssid.wpa_passphrase), sta->sae) < 0) { wpa_printf(MSG_DEBUG, "SAE: Could not pick PWE"); return NULL; } if (sae_process_commit(sta->sae) < 0) { wpa_printf(MSG_DEBUG, "SAE: Failed to process peer commit"); return NULL; } buf = wpabuf_alloc(SAE_COMMIT_MAX_LEN); if (buf == NULL) return NULL; sae_write_commit(sta->sae, buf, NULL); return buf; } static struct wpabuf * auth_build_sae_confirm(struct hostapd_data *hapd, struct sta_info *sta) { struct wpabuf *buf; buf = wpabuf_alloc(SAE_CONFIRM_MAX_LEN); if (buf == NULL) return NULL; sae_write_confirm(sta->sae, buf); return buf; } static int use_sae_anti_clogging(struct hostapd_data *hapd) { struct sta_info *sta; unsigned int open = 0; if (hapd->conf->sae_anti_clogging_threshold == 0) return 1; for (sta = hapd->sta_list; sta; sta = sta->next) { if (!sta->sae) continue; if (sta->sae->state != SAE_COMMITTED && sta->sae->state != SAE_CONFIRMED) continue; open++; if (open >= hapd->conf->sae_anti_clogging_threshold) return 1; } return 0; } static int check_sae_token(struct hostapd_data *hapd, const u8 *addr, const u8 *token, size_t token_len) { u8 mac[SHA256_MAC_LEN]; if (token_len != SHA256_MAC_LEN) return -1; if (hmac_sha256(hapd->sae_token_key, sizeof(hapd->sae_token_key), addr, ETH_ALEN, mac) < 0 || os_memcmp(token, mac, SHA256_MAC_LEN) != 0) return -1; return 0; } static struct wpabuf * auth_build_token_req(struct hostapd_data *hapd, const u8 *addr) { struct wpabuf *buf; u8 *token; struct os_reltime now; os_get_reltime(&now); if (!os_reltime_initialized(&hapd->last_sae_token_key_update) || os_reltime_expired(&now, &hapd->last_sae_token_key_update, 60)) { if (random_get_bytes(hapd->sae_token_key, sizeof(hapd->sae_token_key)) < 0) return NULL; wpa_hexdump(MSG_DEBUG, "SAE: Updated token key", hapd->sae_token_key, sizeof(hapd->sae_token_key)); hapd->last_sae_token_key_update = now; } buf = wpabuf_alloc(SHA256_MAC_LEN); if (buf == NULL) return NULL; token = wpabuf_put(buf, SHA256_MAC_LEN); hmac_sha256(hapd->sae_token_key, sizeof(hapd->sae_token_key), addr, ETH_ALEN, token); return buf; } static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, const struct ieee80211_mgmt *mgmt, size_t len, u8 auth_transaction) { u16 resp = WLAN_STATUS_SUCCESS; struct wpabuf *data = NULL; if (!sta->sae) { if (auth_transaction != 1) return; sta->sae = os_zalloc(sizeof(*sta->sae)); if (sta->sae == NULL) return; sta->sae->state = SAE_NOTHING; } if (auth_transaction == 1) { const u8 *token = NULL; size_t token_len = 0; hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "start SAE authentication (RX commit)"); resp = sae_parse_commit(sta->sae, mgmt->u.auth.variable, ((const u8 *) mgmt) + len - mgmt->u.auth.variable, &token, &token_len, hapd->conf->sae_groups); if (token && check_sae_token(hapd, sta->addr, token, token_len) < 0) { wpa_printf(MSG_DEBUG, "SAE: Drop commit message with " "incorrect token from " MACSTR, MAC2STR(sta->addr)); return; } if (resp == WLAN_STATUS_SUCCESS) { if (!token && use_sae_anti_clogging(hapd)) { wpa_printf(MSG_DEBUG, "SAE: Request anti-" "clogging token from " MACSTR, MAC2STR(sta->addr)); data = auth_build_token_req(hapd, sta->addr); resp = WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ; } else { data = auth_process_sae_commit(hapd, sta); if (data == NULL) resp = WLAN_STATUS_UNSPECIFIED_FAILURE; else sta->sae->state = SAE_COMMITTED; } } } else if (auth_transaction == 2) { if (sta->sae->state != SAE_COMMITTED) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "SAE confirm before commit"); resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION; } hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "SAE authentication (RX confirm)"); if (sae_check_confirm(sta->sae, mgmt->u.auth.variable, ((u8 *) mgmt) + len - mgmt->u.auth.variable) < 0) { resp = WLAN_STATUS_UNSPECIFIED_FAILURE; } else { resp = WLAN_STATUS_SUCCESS; sta->flags |= WLAN_STA_AUTH; wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH); sta->auth_alg = WLAN_AUTH_SAE; mlme_authenticate_indication(hapd, sta); data = auth_build_sae_confirm(hapd, sta); if (data == NULL) resp = WLAN_STATUS_UNSPECIFIED_FAILURE; else { sta->sae->state = SAE_ACCEPTED; sae_clear_temp_data(sta->sae); } } } else { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "unexpected SAE authentication transaction %u", auth_transaction); resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION; } sta->auth_alg = WLAN_AUTH_SAE; send_auth_reply(hapd, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE, auth_transaction, resp, data ? wpabuf_head(data) : (u8 *) "", data ? wpabuf_len(data) : 0); wpabuf_free(data); } #endif /* CONFIG_SAE */ static void handle_auth(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, size_t len) { u16 auth_alg, auth_transaction, status_code; u16 resp = WLAN_STATUS_SUCCESS; struct sta_info *sta = NULL; int res; u16 fc; const u8 *challenge = NULL; u32 session_timeout, acct_interim_interval; int vlan_id = 0; struct hostapd_sta_wpa_psk_short *psk = NULL; u8 resp_ies[2 + WLAN_AUTH_CHALLENGE_LEN]; size_t resp_ies_len = 0; char *identity = NULL; char *radius_cui = NULL; if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) { wpa_printf(MSG_INFO, "handle_auth - too short payload (len=%lu)", (unsigned long) len); return; } #ifdef CONFIG_TESTING_OPTIONS if (hapd->iconf->ignore_auth_probability > 0.0d && drand48() < hapd->iconf->ignore_auth_probability) { wpa_printf(MSG_INFO, "TESTING: ignoring auth frame from " MACSTR, MAC2STR(mgmt->sa)); return; } #endif /* CONFIG_TESTING_OPTIONS */ auth_alg = le_to_host16(mgmt->u.auth.auth_alg); auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction); status_code = le_to_host16(mgmt->u.auth.status_code); fc = le_to_host16(mgmt->frame_control); if (len >= IEEE80211_HDRLEN + sizeof(mgmt->u.auth) + 2 + WLAN_AUTH_CHALLENGE_LEN && mgmt->u.auth.variable[0] == WLAN_EID_CHALLENGE && mgmt->u.auth.variable[1] == WLAN_AUTH_CHALLENGE_LEN) challenge = &mgmt->u.auth.variable[2]; wpa_printf(MSG_DEBUG, "authentication: STA=" MACSTR " auth_alg=%d " "auth_transaction=%d status_code=%d wep=%d%s", MAC2STR(mgmt->sa), auth_alg, auth_transaction, status_code, !!(fc & WLAN_FC_ISWEP), challenge ? " challenge" : ""); if (hapd->tkip_countermeasures) { resp = WLAN_REASON_MICHAEL_MIC_FAILURE; goto fail; } if (!(((hapd->conf->auth_algs & WPA_AUTH_ALG_OPEN) && auth_alg == WLAN_AUTH_OPEN) || #ifdef CONFIG_IEEE80211R (hapd->conf->wpa && wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt) && auth_alg == WLAN_AUTH_FT) || #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_SAE (hapd->conf->wpa && wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt) && auth_alg == WLAN_AUTH_SAE) || #endif /* CONFIG_SAE */ ((hapd->conf->auth_algs & WPA_AUTH_ALG_SHARED) && auth_alg == WLAN_AUTH_SHARED_KEY))) { wpa_printf(MSG_INFO, "Unsupported authentication algorithm (%d)", auth_alg); resp = WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG; goto fail; } if (!(auth_transaction == 1 || auth_alg == WLAN_AUTH_SAE || (auth_alg == WLAN_AUTH_SHARED_KEY && auth_transaction == 3))) { wpa_printf(MSG_INFO, "Unknown authentication transaction number (%d)", auth_transaction); resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION; goto fail; } if (os_memcmp(mgmt->sa, hapd->own_addr, ETH_ALEN) == 0) { wpa_printf(MSG_INFO, "Station " MACSTR " not allowed to authenticate", MAC2STR(mgmt->sa)); resp = WLAN_STATUS_UNSPECIFIED_FAILURE; goto fail; } res = hostapd_allowed_address(hapd, mgmt->sa, (u8 *) mgmt, len, &session_timeout, &acct_interim_interval, &vlan_id, &psk, &identity, &radius_cui); if (res == HOSTAPD_ACL_REJECT) { wpa_printf(MSG_INFO, "Station " MACSTR " not allowed to authenticate", MAC2STR(mgmt->sa)); resp = WLAN_STATUS_UNSPECIFIED_FAILURE; goto fail; } if (res == HOSTAPD_ACL_PENDING) { wpa_printf(MSG_DEBUG, "Authentication frame from " MACSTR " waiting for an external authentication", MAC2STR(mgmt->sa)); /* Authentication code will re-send the authentication frame * after it has received (and cached) information from the * external source. */ return; } sta = ap_sta_add(hapd, mgmt->sa); if (!sta) { resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA; goto fail; } if (vlan_id > 0) { if (!hostapd_vlan_id_valid(hapd->conf->vlan, vlan_id)) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_INFO, "Invalid VLAN ID " "%d received from RADIUS server", vlan_id); resp = WLAN_STATUS_UNSPECIFIED_FAILURE; goto fail; } sta->vlan_id = vlan_id; hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_INFO, "VLAN ID %d", sta->vlan_id); } hostapd_free_psk_list(sta->psk); if (hapd->conf->wpa_psk_radius != PSK_RADIUS_IGNORED) { sta->psk = psk; psk = NULL; } else { sta->psk = NULL; } sta->identity = identity; identity = NULL; sta->radius_cui = radius_cui; radius_cui = NULL; sta->flags &= ~WLAN_STA_PREAUTH; ieee802_1x_notify_pre_auth(sta->eapol_sm, 0); if (hapd->conf->acct_interim_interval == 0 && acct_interim_interval) sta->acct_interim_interval = acct_interim_interval; if (res == HOSTAPD_ACL_ACCEPT_TIMEOUT) ap_sta_session_timeout(hapd, sta, session_timeout); else ap_sta_no_session_timeout(hapd, sta); switch (auth_alg) { case WLAN_AUTH_OPEN: hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "authentication OK (open system)"); sta->flags |= WLAN_STA_AUTH; wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH); sta->auth_alg = WLAN_AUTH_OPEN; mlme_authenticate_indication(hapd, sta); break; case WLAN_AUTH_SHARED_KEY: resp = auth_shared_key(hapd, sta, auth_transaction, challenge, fc & WLAN_FC_ISWEP); sta->auth_alg = WLAN_AUTH_SHARED_KEY; mlme_authenticate_indication(hapd, sta); if (sta->challenge && auth_transaction == 1) { resp_ies[0] = WLAN_EID_CHALLENGE; resp_ies[1] = WLAN_AUTH_CHALLENGE_LEN; os_memcpy(resp_ies + 2, sta->challenge, WLAN_AUTH_CHALLENGE_LEN); resp_ies_len = 2 + WLAN_AUTH_CHALLENGE_LEN; } break; #ifdef CONFIG_IEEE80211R case WLAN_AUTH_FT: sta->auth_alg = WLAN_AUTH_FT; if (sta->wpa_sm == NULL) sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, sta->addr, NULL); if (sta->wpa_sm == NULL) { wpa_printf(MSG_DEBUG, "FT: Failed to initialize WPA " "state machine"); resp = WLAN_STATUS_UNSPECIFIED_FAILURE; goto fail; } wpa_ft_process_auth(sta->wpa_sm, mgmt->bssid, auth_transaction, mgmt->u.auth.variable, len - IEEE80211_HDRLEN - sizeof(mgmt->u.auth), handle_auth_ft_finish, hapd); /* handle_auth_ft_finish() callback will complete auth. */ return; #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_SAE case WLAN_AUTH_SAE: handle_auth_sae(hapd, sta, mgmt, len, auth_transaction); return; #endif /* CONFIG_SAE */ } fail: os_free(identity); os_free(radius_cui); hostapd_free_psk_list(psk); send_auth_reply(hapd, mgmt->sa, mgmt->bssid, auth_alg, auth_transaction + 1, resp, resp_ies, resp_ies_len); } static int hostapd_get_aid(struct hostapd_data *hapd, struct sta_info *sta) { int i, j = 32, aid; /* get a unique AID */ if (sta->aid > 0) { wpa_printf(MSG_DEBUG, " old AID %d", sta->aid); return 0; } for (i = 0; i < AID_WORDS; i++) { if (hapd->sta_aid[i] == (u32) -1) continue; for (j = 0; j < 32; j++) { if (!(hapd->sta_aid[i] & BIT(j))) break; } if (j < 32) break; } if (j == 32) return -1; aid = i * 32 + j + 1; if (aid > 2007) return -1; sta->aid = aid; hapd->sta_aid[i] |= BIT(j); wpa_printf(MSG_DEBUG, " new AID %d", sta->aid); return 0; } static u16 check_ssid(struct hostapd_data *hapd, struct sta_info *sta, const u8 *ssid_ie, size_t ssid_ie_len) { if (ssid_ie == NULL) return WLAN_STATUS_UNSPECIFIED_FAILURE; if (ssid_ie_len != hapd->conf->ssid.ssid_len || os_memcmp(ssid_ie, hapd->conf->ssid.ssid, ssid_ie_len) != 0) { char ssid_txt[33]; ieee802_11_print_ssid(ssid_txt, ssid_ie, ssid_ie_len); hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "Station tried to associate with unknown SSID " "'%s'", ssid_txt); return WLAN_STATUS_UNSPECIFIED_FAILURE; } return WLAN_STATUS_SUCCESS; } static u16 check_wmm(struct hostapd_data *hapd, struct sta_info *sta, const u8 *wmm_ie, size_t wmm_ie_len) { sta->flags &= ~WLAN_STA_WMM; sta->qosinfo = 0; if (wmm_ie && hapd->conf->wmm_enabled) { struct wmm_information_element *wmm; if (!hostapd_eid_wmm_valid(hapd, wmm_ie, wmm_ie_len)) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, HOSTAPD_LEVEL_DEBUG, "invalid WMM element in association " "request"); return WLAN_STATUS_UNSPECIFIED_FAILURE; } sta->flags |= WLAN_STA_WMM; wmm = (struct wmm_information_element *) wmm_ie; sta->qosinfo = wmm->qos_info; } return WLAN_STATUS_SUCCESS; } static u16 copy_supp_rates(struct hostapd_data *hapd, struct sta_info *sta, struct ieee802_11_elems *elems) { if (!elems->supp_rates) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "No supported rates element in AssocReq"); return WLAN_STATUS_UNSPECIFIED_FAILURE; } if (elems->supp_rates_len + elems->ext_supp_rates_len > sizeof(sta->supported_rates)) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "Invalid supported rates element length %d+%d", elems->supp_rates_len, elems->ext_supp_rates_len); return WLAN_STATUS_UNSPECIFIED_FAILURE; } sta->supported_rates_len = merge_byte_arrays( sta->supported_rates, sizeof(sta->supported_rates), elems->supp_rates, elems->supp_rates_len, elems->ext_supp_rates, elems->ext_supp_rates_len); return WLAN_STATUS_SUCCESS; } static u16 check_ext_capab(struct hostapd_data *hapd, struct sta_info *sta, const u8 *ext_capab_ie, size_t ext_capab_ie_len) { #ifdef CONFIG_INTERWORKING /* check for QoS Map support */ if (ext_capab_ie_len >= 5) { if (ext_capab_ie[4] & 0x01) sta->qos_map_enabled = 1; } #endif /* CONFIG_INTERWORKING */ return WLAN_STATUS_SUCCESS; } static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, const u8 *ies, size_t ies_len, int reassoc) { struct ieee802_11_elems elems; u16 resp; const u8 *wpa_ie; size_t wpa_ie_len; const u8 *p2p_dev_addr = NULL; if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "Station sent an invalid " "association request"); return WLAN_STATUS_UNSPECIFIED_FAILURE; } resp = check_ssid(hapd, sta, elems.ssid, elems.ssid_len); if (resp != WLAN_STATUS_SUCCESS) return resp; resp = check_wmm(hapd, sta, elems.wmm, elems.wmm_len); if (resp != WLAN_STATUS_SUCCESS) return resp; resp = check_ext_capab(hapd, sta, elems.ext_capab, elems.ext_capab_len); if (resp != WLAN_STATUS_SUCCESS) return resp; resp = copy_supp_rates(hapd, sta, &elems); if (resp != WLAN_STATUS_SUCCESS) return resp; #ifdef CONFIG_IEEE80211N resp = copy_sta_ht_capab(hapd, sta, elems.ht_capabilities, elems.ht_capabilities_len); if (resp != WLAN_STATUS_SUCCESS) return resp; if (hapd->iconf->ieee80211n && hapd->iconf->require_ht && !(sta->flags & WLAN_STA_HT)) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "Station does not support " "mandatory HT PHY - reject association"); return WLAN_STATUS_ASSOC_DENIED_NO_HT; } #endif /* CONFIG_IEEE80211N */ #ifdef CONFIG_IEEE80211AC resp = copy_sta_vht_capab(hapd, sta, elems.vht_capabilities, elems.vht_capabilities_len); if (resp != WLAN_STATUS_SUCCESS) return resp; if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht && !(sta->flags & WLAN_STA_VHT)) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "Station does not support " "mandatory VHT PHY - reject association"); return WLAN_STATUS_ASSOC_DENIED_NO_VHT; } #endif /* CONFIG_IEEE80211AC */ #ifdef CONFIG_P2P if (elems.p2p) { wpabuf_free(sta->p2p_ie); sta->p2p_ie = ieee802_11_vendor_ie_concat(ies, ies_len, P2P_IE_VENDOR_TYPE); if (sta->p2p_ie) p2p_dev_addr = p2p_get_go_dev_addr(sta->p2p_ie); } else { wpabuf_free(sta->p2p_ie); sta->p2p_ie = NULL; } #endif /* CONFIG_P2P */ if ((hapd->conf->wpa & WPA_PROTO_RSN) && elems.rsn_ie) { wpa_ie = elems.rsn_ie; wpa_ie_len = elems.rsn_ie_len; } else if ((hapd->conf->wpa & WPA_PROTO_WPA) && elems.wpa_ie) { wpa_ie = elems.wpa_ie; wpa_ie_len = elems.wpa_ie_len; } else { wpa_ie = NULL; wpa_ie_len = 0; } #ifdef CONFIG_WPS sta->flags &= ~(WLAN_STA_WPS | WLAN_STA_MAYBE_WPS | WLAN_STA_WPS2); if (hapd->conf->wps_state && elems.wps_ie) { wpa_printf(MSG_DEBUG, "STA included WPS IE in (Re)Association " "Request - assume WPS is used"); sta->flags |= WLAN_STA_WPS; wpabuf_free(sta->wps_ie); sta->wps_ie = ieee802_11_vendor_ie_concat(ies, ies_len, WPS_IE_VENDOR_TYPE); if (sta->wps_ie && wps_is_20(sta->wps_ie)) { wpa_printf(MSG_DEBUG, "WPS: STA supports WPS 2.0"); sta->flags |= WLAN_STA_WPS2; } wpa_ie = NULL; wpa_ie_len = 0; if (sta->wps_ie && wps_validate_assoc_req(sta->wps_ie) < 0) { wpa_printf(MSG_DEBUG, "WPS: Invalid WPS IE in " "(Re)Association Request - reject"); return WLAN_STATUS_INVALID_IE; } } else if (hapd->conf->wps_state && wpa_ie == NULL) { wpa_printf(MSG_DEBUG, "STA did not include WPA/RSN IE in " "(Re)Association Request - possible WPS use"); sta->flags |= WLAN_STA_MAYBE_WPS; } else #endif /* CONFIG_WPS */ if (hapd->conf->wpa && wpa_ie == NULL) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "No WPA/RSN IE in association request"); return WLAN_STATUS_INVALID_IE; } if (hapd->conf->wpa && wpa_ie) { int res; wpa_ie -= 2; wpa_ie_len += 2; if (sta->wpa_sm == NULL) sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, sta->addr, p2p_dev_addr); if (sta->wpa_sm == NULL) { wpa_printf(MSG_WARNING, "Failed to initialize WPA " "state machine"); return WLAN_STATUS_UNSPECIFIED_FAILURE; } res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm, wpa_ie, wpa_ie_len, elems.mdie, elems.mdie_len); if (res == WPA_INVALID_GROUP) resp = WLAN_STATUS_GROUP_CIPHER_NOT_VALID; else if (res == WPA_INVALID_PAIRWISE) resp = WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID; else if (res == WPA_INVALID_AKMP) resp = WLAN_STATUS_AKMP_NOT_VALID; else if (res == WPA_ALLOC_FAIL) resp = WLAN_STATUS_UNSPECIFIED_FAILURE; #ifdef CONFIG_IEEE80211W else if (res == WPA_MGMT_FRAME_PROTECTION_VIOLATION) resp = WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION; else if (res == WPA_INVALID_MGMT_GROUP_CIPHER) resp = WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION; #endif /* CONFIG_IEEE80211W */ else if (res == WPA_INVALID_MDIE) resp = WLAN_STATUS_INVALID_MDIE; else if (res != WPA_IE_OK) resp = WLAN_STATUS_INVALID_IE; if (resp != WLAN_STATUS_SUCCESS) return resp; #ifdef CONFIG_IEEE80211W if ((sta->flags & WLAN_STA_MFP) && !sta->sa_query_timed_out && sta->sa_query_count > 0) ap_check_sa_query_timeout(hapd, sta); if ((sta->flags & WLAN_STA_MFP) && !sta->sa_query_timed_out && (!reassoc || sta->auth_alg != WLAN_AUTH_FT)) { /* * STA has already been associated with MFP and SA * Query timeout has not been reached. Reject the * association attempt temporarily and start SA Query, * if one is not pending. */ if (sta->sa_query_count == 0) ap_sta_start_sa_query(hapd, sta); return WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY; } if (wpa_auth_uses_mfp(sta->wpa_sm)) sta->flags |= WLAN_STA_MFP; else sta->flags &= ~WLAN_STA_MFP; #endif /* CONFIG_IEEE80211W */ #ifdef CONFIG_IEEE80211R if (sta->auth_alg == WLAN_AUTH_FT) { if (!reassoc) { wpa_printf(MSG_DEBUG, "FT: " MACSTR " tried " "to use association (not " "re-association) with FT auth_alg", MAC2STR(sta->addr)); return WLAN_STATUS_UNSPECIFIED_FAILURE; } resp = wpa_ft_validate_reassoc(sta->wpa_sm, ies, ies_len); if (resp != WLAN_STATUS_SUCCESS) return resp; } #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_SAE if (wpa_auth_uses_sae(sta->wpa_sm) && sta->auth_alg != WLAN_AUTH_SAE && !(sta->auth_alg == WLAN_AUTH_FT && wpa_auth_uses_ft_sae(sta->wpa_sm))) { wpa_printf(MSG_DEBUG, "SAE: " MACSTR " tried to use " "SAE AKM after non-SAE auth_alg %u", MAC2STR(sta->addr), sta->auth_alg); return WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG; } #endif /* CONFIG_SAE */ #ifdef CONFIG_IEEE80211N if ((sta->flags & (WLAN_STA_HT | WLAN_STA_VHT)) && wpa_auth_get_pairwise(sta->wpa_sm) == WPA_CIPHER_TKIP) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "Station tried to use TKIP with HT " "association"); return WLAN_STATUS_CIPHER_REJECTED_PER_POLICY; } #endif /* CONFIG_IEEE80211N */ } else wpa_auth_sta_no_wpa(sta->wpa_sm); #ifdef CONFIG_P2P p2p_group_notif_assoc(hapd->p2p_group, sta->addr, ies, ies_len); #endif /* CONFIG_P2P */ #ifdef CONFIG_HS20 wpabuf_free(sta->hs20_ie); if (elems.hs20 && elems.hs20_len > 4) { sta->hs20_ie = wpabuf_alloc_copy(elems.hs20 + 4, elems.hs20_len - 4); } else sta->hs20_ie = NULL; #endif /* CONFIG_HS20 */ return WLAN_STATUS_SUCCESS; } static void send_deauth(struct hostapd_data *hapd, const u8 *addr, u16 reason_code) { int send_len; struct ieee80211_mgmt reply; os_memset(&reply, 0, sizeof(reply)); reply.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_DEAUTH); os_memcpy(reply.da, addr, ETH_ALEN); os_memcpy(reply.sa, hapd->own_addr, ETH_ALEN); os_memcpy(reply.bssid, hapd->own_addr, ETH_ALEN); send_len = IEEE80211_HDRLEN + sizeof(reply.u.deauth); reply.u.deauth.reason_code = host_to_le16(reason_code); if (hostapd_drv_send_mlme(hapd, &reply, send_len, 0) < 0) wpa_printf(MSG_INFO, "Failed to send deauth: %s", strerror(errno)); } static void send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, u16 status_code, int reassoc, const u8 *ies, size_t ies_len) { int send_len; u8 buf[sizeof(struct ieee80211_mgmt) + 1024]; struct ieee80211_mgmt *reply; u8 *p; os_memset(buf, 0, sizeof(buf)); reply = (struct ieee80211_mgmt *) buf; reply->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, (reassoc ? WLAN_FC_STYPE_REASSOC_RESP : WLAN_FC_STYPE_ASSOC_RESP)); os_memcpy(reply->da, sta->addr, ETH_ALEN); os_memcpy(reply->sa, hapd->own_addr, ETH_ALEN); os_memcpy(reply->bssid, hapd->own_addr, ETH_ALEN); send_len = IEEE80211_HDRLEN; send_len += sizeof(reply->u.assoc_resp); reply->u.assoc_resp.capab_info = host_to_le16(hostapd_own_capab_info(hapd, sta, 0)); reply->u.assoc_resp.status_code = host_to_le16(status_code); reply->u.assoc_resp.aid = host_to_le16((sta ? sta->aid : 0) | BIT(14) | BIT(15)); /* Supported rates */ p = hostapd_eid_supp_rates(hapd, reply->u.assoc_resp.variable); /* Extended supported rates */ p = hostapd_eid_ext_supp_rates(hapd, p); #ifdef CONFIG_IEEE80211R if (status_code == WLAN_STATUS_SUCCESS) { /* IEEE 802.11r: Mobility Domain Information, Fast BSS * Transition Information, RSN, [RIC Response] */ p = wpa_sm_write_assoc_resp_ies(sta->wpa_sm, p, buf + sizeof(buf) - p, sta->auth_alg, ies, ies_len); } #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_IEEE80211W if (status_code == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY) p = hostapd_eid_assoc_comeback_time(hapd, sta, p); #endif /* CONFIG_IEEE80211W */ #ifdef CONFIG_IEEE80211N p = hostapd_eid_ht_capabilities(hapd, p); p = hostapd_eid_ht_operation(hapd, p); #endif /* CONFIG_IEEE80211N */ #ifdef CONFIG_IEEE80211AC p = hostapd_eid_vht_capabilities(hapd, p); p = hostapd_eid_vht_operation(hapd, p); #endif /* CONFIG_IEEE80211AC */ p = hostapd_eid_ext_capab(hapd, p); p = hostapd_eid_bss_max_idle_period(hapd, p); if (sta->qos_map_enabled) p = hostapd_eid_qos_map_set(hapd, p); if (sta->flags & WLAN_STA_WMM) p = hostapd_eid_wmm(hapd, p); #ifdef CONFIG_WPS if ((sta->flags & WLAN_STA_WPS) || ((sta->flags & WLAN_STA_MAYBE_WPS) && hapd->conf->wpa)) { struct wpabuf *wps = wps_build_assoc_resp_ie(); if (wps) { os_memcpy(p, wpabuf_head(wps), wpabuf_len(wps)); p += wpabuf_len(wps); wpabuf_free(wps); } } #endif /* CONFIG_WPS */ #ifdef CONFIG_P2P if (sta->p2p_ie) { struct wpabuf *p2p_resp_ie; enum p2p_status_code status; switch (status_code) { case WLAN_STATUS_SUCCESS: status = P2P_SC_SUCCESS; break; case WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA: status = P2P_SC_FAIL_LIMIT_REACHED; break; default: status = P2P_SC_FAIL_INVALID_PARAMS; break; } p2p_resp_ie = p2p_group_assoc_resp_ie(hapd->p2p_group, status); if (p2p_resp_ie) { os_memcpy(p, wpabuf_head(p2p_resp_ie), wpabuf_len(p2p_resp_ie)); p += wpabuf_len(p2p_resp_ie); wpabuf_free(p2p_resp_ie); } } #endif /* CONFIG_P2P */ #ifdef CONFIG_P2P_MANAGER if (hapd->conf->p2p & P2P_MANAGE) p = hostapd_eid_p2p_manage(hapd, p); #endif /* CONFIG_P2P_MANAGER */ send_len += p - reply->u.assoc_resp.variable; if (hostapd_drv_send_mlme(hapd, reply, send_len, 0) < 0) wpa_printf(MSG_INFO, "Failed to send assoc resp: %s", strerror(errno)); } static void handle_assoc(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, size_t len, int reassoc) { u16 capab_info, listen_interval; u16 resp = WLAN_STATUS_SUCCESS; const u8 *pos; int left, i; struct sta_info *sta; if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_req) : sizeof(mgmt->u.assoc_req))) { wpa_printf(MSG_INFO, "handle_assoc(reassoc=%d) - too short payload (len=%lu)", reassoc, (unsigned long) len); return; } #ifdef CONFIG_TESTING_OPTIONS if (reassoc) { if (hapd->iconf->ignore_reassoc_probability > 0.0d && drand48() < hapd->iconf->ignore_reassoc_probability) { wpa_printf(MSG_INFO, "TESTING: ignoring reassoc request from " MACSTR, MAC2STR(mgmt->sa)); return; } } else { if (hapd->iconf->ignore_assoc_probability > 0.0d && drand48() < hapd->iconf->ignore_assoc_probability) { wpa_printf(MSG_INFO, "TESTING: ignoring assoc request from " MACSTR, MAC2STR(mgmt->sa)); return; } } #endif /* CONFIG_TESTING_OPTIONS */ if (reassoc) { capab_info = le_to_host16(mgmt->u.reassoc_req.capab_info); listen_interval = le_to_host16( mgmt->u.reassoc_req.listen_interval); wpa_printf(MSG_DEBUG, "reassociation request: STA=" MACSTR " capab_info=0x%02x listen_interval=%d current_ap=" MACSTR, MAC2STR(mgmt->sa), capab_info, listen_interval, MAC2STR(mgmt->u.reassoc_req.current_ap)); left = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.reassoc_req)); pos = mgmt->u.reassoc_req.variable; } else { capab_info = le_to_host16(mgmt->u.assoc_req.capab_info); listen_interval = le_to_host16( mgmt->u.assoc_req.listen_interval); wpa_printf(MSG_DEBUG, "association request: STA=" MACSTR " capab_info=0x%02x listen_interval=%d", MAC2STR(mgmt->sa), capab_info, listen_interval); left = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.assoc_req)); pos = mgmt->u.assoc_req.variable; } sta = ap_get_sta(hapd, mgmt->sa); #ifdef CONFIG_IEEE80211R if (sta && sta->auth_alg == WLAN_AUTH_FT && (sta->flags & WLAN_STA_AUTH) == 0) { wpa_printf(MSG_DEBUG, "FT: Allow STA " MACSTR " to associate " "prior to authentication since it is using " "over-the-DS FT", MAC2STR(mgmt->sa)); } else #endif /* CONFIG_IEEE80211R */ if (sta == NULL || (sta->flags & WLAN_STA_AUTH) == 0) { hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "Station tried to " "associate before authentication " "(aid=%d flags=0x%x)", sta ? sta->aid : -1, sta ? sta->flags : 0); send_deauth(hapd, mgmt->sa, WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA); return; } if (hapd->tkip_countermeasures) { resp = WLAN_REASON_MICHAEL_MIC_FAILURE; goto fail; } if (listen_interval > hapd->conf->max_listen_interval) { hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "Too large Listen Interval (%d)", listen_interval); resp = WLAN_STATUS_ASSOC_DENIED_LISTEN_INT_TOO_LARGE; goto fail; } /* followed by SSID and Supported rates; and HT capabilities if 802.11n * is used */ resp = check_assoc_ies(hapd, sta, pos, left, reassoc); if (resp != WLAN_STATUS_SUCCESS) goto fail; if (hostapd_get_aid(hapd, sta) < 0) { hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "No room for more AIDs"); resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA; goto fail; } sta->capability = capab_info; sta->listen_interval = listen_interval; if (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G) sta->flags |= WLAN_STA_NONERP; for (i = 0; i < sta->supported_rates_len; i++) { if ((sta->supported_rates[i] & 0x7f) > 22) { sta->flags &= ~WLAN_STA_NONERP; break; } } if (sta->flags & WLAN_STA_NONERP && !sta->nonerp_set) { sta->nonerp_set = 1; hapd->iface->num_sta_non_erp++; if (hapd->iface->num_sta_non_erp == 1) ieee802_11_set_beacons(hapd->iface); } if (!(sta->capability & WLAN_CAPABILITY_SHORT_SLOT_TIME) && !sta->no_short_slot_time_set) { sta->no_short_slot_time_set = 1; hapd->iface->num_sta_no_short_slot_time++; if (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G && hapd->iface->num_sta_no_short_slot_time == 1) ieee802_11_set_beacons(hapd->iface); } if (sta->capability & WLAN_CAPABILITY_SHORT_PREAMBLE) sta->flags |= WLAN_STA_SHORT_PREAMBLE; else sta->flags &= ~WLAN_STA_SHORT_PREAMBLE; if (!(sta->capability & WLAN_CAPABILITY_SHORT_PREAMBLE) && !sta->no_short_preamble_set) { sta->no_short_preamble_set = 1; hapd->iface->num_sta_no_short_preamble++; if (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G && hapd->iface->num_sta_no_short_preamble == 1) ieee802_11_set_beacons(hapd->iface); } #ifdef CONFIG_IEEE80211N update_ht_state(hapd, sta); #endif /* CONFIG_IEEE80211N */ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "association OK (aid %d)", sta->aid); /* Station will be marked associated, after it acknowledges AssocResp */ sta->flags |= WLAN_STA_ASSOC_REQ_OK; #ifdef CONFIG_IEEE80211W if ((sta->flags & WLAN_STA_MFP) && sta->sa_query_timed_out) { wpa_printf(MSG_DEBUG, "Allowing %sassociation after timed out " "SA Query procedure", reassoc ? "re" : ""); /* TODO: Send a protected Disassociate frame to the STA using * the old key and Reason Code "Previous Authentication no * longer valid". Make sure this is only sent protected since * unprotected frame would be received by the STA that is now * trying to associate. */ } #endif /* CONFIG_IEEE80211W */ if (reassoc) { os_memcpy(sta->previous_ap, mgmt->u.reassoc_req.current_ap, ETH_ALEN); } if (sta->last_assoc_req) os_free(sta->last_assoc_req); sta->last_assoc_req = os_malloc(len); if (sta->last_assoc_req) os_memcpy(sta->last_assoc_req, mgmt, len); /* Make sure that the previously registered inactivity timer will not * remove the STA immediately. */ sta->timeout_next = STA_NULLFUNC; fail: send_assoc_resp(hapd, sta, resp, reassoc, pos, left); } static void handle_disassoc(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, size_t len) { struct sta_info *sta; if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.disassoc)) { wpa_printf(MSG_INFO, "handle_disassoc - too short payload (len=%lu)", (unsigned long) len); return; } wpa_printf(MSG_DEBUG, "disassocation: STA=" MACSTR " reason_code=%d", MAC2STR(mgmt->sa), le_to_host16(mgmt->u.disassoc.reason_code)); sta = ap_get_sta(hapd, mgmt->sa); if (sta == NULL) { wpa_printf(MSG_INFO, "Station " MACSTR " trying to disassociate, but it is not associated", MAC2STR(mgmt->sa)); return; } ap_sta_set_authorized(hapd, sta, 0); sta->flags &= ~(WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK); wpa_auth_sm_event(sta->wpa_sm, WPA_DISASSOC); hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "disassociated"); sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST; ieee802_1x_notify_port_enabled(sta->eapol_sm, 0); /* Stop Accounting and IEEE 802.1X sessions, but leave the STA * authenticated. */ accounting_sta_stop(hapd, sta); ieee802_1x_free_station(sta); hostapd_drv_sta_remove(hapd, sta->addr); if (sta->timeout_next == STA_NULLFUNC || sta->timeout_next == STA_DISASSOC) { sta->timeout_next = STA_DEAUTH; eloop_cancel_timeout(ap_handle_timer, hapd, sta); eloop_register_timeout(AP_DEAUTH_DELAY, 0, ap_handle_timer, hapd, sta); } mlme_disassociate_indication( hapd, sta, le_to_host16(mgmt->u.disassoc.reason_code)); } static void handle_deauth(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, size_t len) { struct sta_info *sta; if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.deauth)) { wpa_msg(hapd->msg_ctx, MSG_DEBUG, "handle_deauth - too short " "payload (len=%lu)", (unsigned long) len); return; } wpa_msg(hapd->msg_ctx, MSG_DEBUG, "deauthentication: STA=" MACSTR " reason_code=%d", MAC2STR(mgmt->sa), le_to_host16(mgmt->u.deauth.reason_code)); sta = ap_get_sta(hapd, mgmt->sa); if (sta == NULL) { wpa_msg(hapd->msg_ctx, MSG_DEBUG, "Station " MACSTR " trying " "to deauthenticate, but it is not authenticated", MAC2STR(mgmt->sa)); return; } ap_sta_set_authorized(hapd, sta, 0); sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK); wpa_auth_sm_event(sta->wpa_sm, WPA_DEAUTH); hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "deauthenticated"); mlme_deauthenticate_indication( hapd, sta, le_to_host16(mgmt->u.deauth.reason_code)); sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST; ieee802_1x_notify_port_enabled(sta->eapol_sm, 0); ap_free_sta(hapd, sta); } static void handle_beacon(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, size_t len, struct hostapd_frame_info *fi) { struct ieee802_11_elems elems; if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.beacon)) { wpa_printf(MSG_INFO, "handle_beacon - too short payload (len=%lu)", (unsigned long) len); return; } (void) ieee802_11_parse_elems(mgmt->u.beacon.variable, len - (IEEE80211_HDRLEN + sizeof(mgmt->u.beacon)), &elems, 0); ap_list_process_beacon(hapd->iface, mgmt, &elems, fi); } #ifdef CONFIG_IEEE80211W static int hostapd_sa_query_action(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, size_t len) { const u8 *end; end = mgmt->u.action.u.sa_query_resp.trans_id + WLAN_SA_QUERY_TR_ID_LEN; if (((u8 *) mgmt) + len < end) { wpa_printf(MSG_DEBUG, "IEEE 802.11: Too short SA Query Action " "frame (len=%lu)", (unsigned long) len); return 0; } ieee802_11_sa_query_action(hapd, mgmt->sa, mgmt->u.action.u.sa_query_resp.action, mgmt->u.action.u.sa_query_resp.trans_id); return 1; } static int robust_action_frame(u8 category) { return category != WLAN_ACTION_PUBLIC && category != WLAN_ACTION_HT; } #endif /* CONFIG_IEEE80211W */ static int handle_action(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, size_t len) { struct sta_info *sta; sta = ap_get_sta(hapd, mgmt->sa); if (len < IEEE80211_HDRLEN + 1) { hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "handle_action - too short payload (len=%lu)", (unsigned long) len); return 0; } if (mgmt->u.action.category != WLAN_ACTION_PUBLIC && (sta == NULL || !(sta->flags & WLAN_STA_ASSOC))) { wpa_printf(MSG_DEBUG, "IEEE 802.11: Ignored Action " "frame (category=%u) from unassociated STA " MACSTR, MAC2STR(mgmt->sa), mgmt->u.action.category); return 0; } #ifdef CONFIG_IEEE80211W if (sta && (sta->flags & WLAN_STA_MFP) && !(mgmt->frame_control & host_to_le16(WLAN_FC_ISWEP)) && robust_action_frame(mgmt->u.action.category)) { hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "Dropped unprotected Robust Action frame from " "an MFP STA"); return 0; } #endif /* CONFIG_IEEE80211W */ switch (mgmt->u.action.category) { #ifdef CONFIG_IEEE80211R case WLAN_ACTION_FT: if (wpa_ft_action_rx(sta->wpa_sm, (u8 *) &mgmt->u.action, len - IEEE80211_HDRLEN)) break; return 1; #endif /* CONFIG_IEEE80211R */ case WLAN_ACTION_WMM: hostapd_wmm_action(hapd, mgmt, len); return 1; #ifdef CONFIG_IEEE80211W case WLAN_ACTION_SA_QUERY: return hostapd_sa_query_action(hapd, mgmt, len); #endif /* CONFIG_IEEE80211W */ #ifdef CONFIG_WNM case WLAN_ACTION_WNM: ieee802_11_rx_wnm_action_ap(hapd, mgmt, len); return 1; #endif /* CONFIG_WNM */ case WLAN_ACTION_PUBLIC: case WLAN_ACTION_PROTECTED_DUAL: if (hapd->public_action_cb) { hapd->public_action_cb(hapd->public_action_cb_ctx, (u8 *) mgmt, len, hapd->iface->freq); } if (hapd->public_action_cb2) { hapd->public_action_cb2(hapd->public_action_cb2_ctx, (u8 *) mgmt, len, hapd->iface->freq); } if (hapd->public_action_cb || hapd->public_action_cb2) return 1; break; case WLAN_ACTION_VENDOR_SPECIFIC: if (hapd->vendor_action_cb) { if (hapd->vendor_action_cb(hapd->vendor_action_cb_ctx, (u8 *) mgmt, len, hapd->iface->freq) == 0) return 1; } break; } hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "handle_action - unknown action category %d or invalid " "frame", mgmt->u.action.category); if (!(mgmt->da[0] & 0x01) && !(mgmt->u.action.category & 0x80) && !(mgmt->sa[0] & 0x01)) { struct ieee80211_mgmt *resp; /* * IEEE 802.11-REVma/D9.0 - 7.3.1.11 * Return the Action frame to the source without change * except that MSB of the Category set to 1. */ wpa_printf(MSG_DEBUG, "IEEE 802.11: Return unknown Action " "frame back to sender"); resp = os_malloc(len); if (resp == NULL) return 0; os_memcpy(resp, mgmt, len); os_memcpy(resp->da, resp->sa, ETH_ALEN); os_memcpy(resp->sa, hapd->own_addr, ETH_ALEN); os_memcpy(resp->bssid, hapd->own_addr, ETH_ALEN); resp->u.action.category |= 0x80; if (hostapd_drv_send_mlme(hapd, resp, len, 0) < 0) { wpa_printf(MSG_ERROR, "IEEE 802.11: Failed to send " "Action frame"); } os_free(resp); } return 1; } /** * ieee802_11_mgmt - process incoming IEEE 802.11 management frames * @hapd: hostapd BSS data structure (the BSS to which the management frame was * sent to) * @buf: management frame data (starting from IEEE 802.11 header) * @len: length of frame data in octets * @fi: meta data about received frame (signal level, etc.) * * Process all incoming IEEE 802.11 management frames. This will be called for * each frame received from the kernel driver through wlan#ap interface. In * addition, it can be called to re-inserted pending frames (e.g., when using * external RADIUS server as an MAC ACL). */ int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len, struct hostapd_frame_info *fi) { struct ieee80211_mgmt *mgmt; int broadcast; u16 fc, stype; int ret = 0; #ifdef CONFIG_TESTING_OPTIONS if (hapd->ext_mgmt_frame_handling) { size_t hex_len = 2 * len + 1; char *hex = os_malloc(hex_len); if (hex) { wpa_snprintf_hex(hex, hex_len, buf, len); wpa_msg(hapd->msg_ctx, MSG_INFO, "MGMT-RX %s", hex); os_free(hex); } return 1; } #endif /* CONFIG_TESTING_OPTIONS */ if (len < 24) return 0; mgmt = (struct ieee80211_mgmt *) buf; fc = le_to_host16(mgmt->frame_control); stype = WLAN_FC_GET_STYPE(fc); if (stype == WLAN_FC_STYPE_BEACON) { handle_beacon(hapd, mgmt, len, fi); return 1; } broadcast = mgmt->bssid[0] == 0xff && mgmt->bssid[1] == 0xff && mgmt->bssid[2] == 0xff && mgmt->bssid[3] == 0xff && mgmt->bssid[4] == 0xff && mgmt->bssid[5] == 0xff; if (!broadcast && #ifdef CONFIG_P2P /* Invitation responses can be sent with the peer MAC as BSSID */ !((hapd->conf->p2p & P2P_GROUP_OWNER) && stype == WLAN_FC_STYPE_ACTION) && #endif /* CONFIG_P2P */ os_memcmp(mgmt->bssid, hapd->own_addr, ETH_ALEN) != 0) { wpa_printf(MSG_INFO, "MGMT: BSSID=" MACSTR " not our address", MAC2STR(mgmt->bssid)); return 0; } if (stype == WLAN_FC_STYPE_PROBE_REQ) { handle_probe_req(hapd, mgmt, len, fi->ssi_signal); return 1; } if (os_memcmp(mgmt->da, hapd->own_addr, ETH_ALEN) != 0) { hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "MGMT: DA=" MACSTR " not our address", MAC2STR(mgmt->da)); return 0; } switch (stype) { case WLAN_FC_STYPE_AUTH: wpa_printf(MSG_DEBUG, "mgmt::auth"); handle_auth(hapd, mgmt, len); ret = 1; break; case WLAN_FC_STYPE_ASSOC_REQ: wpa_printf(MSG_DEBUG, "mgmt::assoc_req"); handle_assoc(hapd, mgmt, len, 0); ret = 1; break; case WLAN_FC_STYPE_REASSOC_REQ: wpa_printf(MSG_DEBUG, "mgmt::reassoc_req"); handle_assoc(hapd, mgmt, len, 1); ret = 1; break; case WLAN_FC_STYPE_DISASSOC: wpa_printf(MSG_DEBUG, "mgmt::disassoc"); handle_disassoc(hapd, mgmt, len); ret = 1; break; case WLAN_FC_STYPE_DEAUTH: wpa_msg(hapd->msg_ctx, MSG_DEBUG, "mgmt::deauth"); handle_deauth(hapd, mgmt, len); ret = 1; break; case WLAN_FC_STYPE_ACTION: wpa_printf(MSG_DEBUG, "mgmt::action"); ret = handle_action(hapd, mgmt, len); break; default: hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "unknown mgmt frame subtype %d", stype); break; } return ret; } static void handle_auth_cb(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, size_t len, int ok) { u16 auth_alg, auth_transaction, status_code; struct sta_info *sta; if (!ok) { hostapd_logger(hapd, mgmt->da, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_NOTICE, "did not acknowledge authentication response"); return; } if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) { wpa_printf(MSG_INFO, "handle_auth_cb - too short payload (len=%lu)", (unsigned long) len); return; } auth_alg = le_to_host16(mgmt->u.auth.auth_alg); auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction); status_code = le_to_host16(mgmt->u.auth.status_code); sta = ap_get_sta(hapd, mgmt->da); if (!sta) { wpa_printf(MSG_INFO, "handle_auth_cb: STA " MACSTR " not found", MAC2STR(mgmt->da)); return; } if (status_code == WLAN_STATUS_SUCCESS && ((auth_alg == WLAN_AUTH_OPEN && auth_transaction == 2) || (auth_alg == WLAN_AUTH_SHARED_KEY && auth_transaction == 4))) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "authenticated"); sta->flags |= WLAN_STA_AUTH; } } static void hostapd_set_wds_encryption(struct hostapd_data *hapd, struct sta_info *sta, char *ifname_wds) { int i; struct hostapd_ssid *ssid = sta->ssid; if (hapd->conf->ieee802_1x || hapd->conf->wpa) return; for (i = 0; i < 4; i++) { if (ssid->wep.key[i] && hostapd_drv_set_key(ifname_wds, hapd, WPA_ALG_WEP, NULL, i, i == ssid->wep.idx, NULL, 0, ssid->wep.key[i], ssid->wep.len[i])) { wpa_printf(MSG_WARNING, "Could not set WEP keys for WDS interface; %s", ifname_wds); break; } } } static void handle_assoc_cb(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, size_t len, int reassoc, int ok) { u16 status; struct sta_info *sta; int new_assoc = 1; struct ieee80211_ht_capabilities ht_cap; struct ieee80211_vht_capabilities vht_cap; if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_resp) : sizeof(mgmt->u.assoc_resp))) { wpa_printf(MSG_INFO, "handle_assoc_cb(reassoc=%d) - too short payload (len=%lu)", reassoc, (unsigned long) len); return; } sta = ap_get_sta(hapd, mgmt->da); if (!sta) { wpa_printf(MSG_INFO, "handle_assoc_cb: STA " MACSTR " not found", MAC2STR(mgmt->da)); return; } if (!ok) { hostapd_logger(hapd, mgmt->da, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "did not acknowledge association response"); sta->flags &= ~WLAN_STA_ASSOC_REQ_OK; return; } if (reassoc) status = le_to_host16(mgmt->u.reassoc_resp.status_code); else status = le_to_host16(mgmt->u.assoc_resp.status_code); if (status != WLAN_STATUS_SUCCESS) goto fail; /* Stop previous accounting session, if one is started, and allocate * new session id for the new session. */ accounting_sta_stop(hapd, sta); hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "associated (aid %d)", sta->aid); if (sta->flags & WLAN_STA_ASSOC) new_assoc = 0; sta->flags |= WLAN_STA_ASSOC; sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE; if ((!hapd->conf->ieee802_1x && !hapd->conf->wpa) || sta->auth_alg == WLAN_AUTH_FT) { /* * Open, static WEP, or FT protocol; no separate authorization * step. */ ap_sta_set_authorized(hapd, sta, 1); } if (reassoc) mlme_reassociate_indication(hapd, sta); else mlme_associate_indication(hapd, sta); #ifdef CONFIG_IEEE80211W sta->sa_query_timed_out = 0; #endif /* CONFIG_IEEE80211W */ /* * Remove the STA entry in order to make sure the STA PS state gets * cleared and configuration gets updated in case of reassociation back * to the same AP. */ hostapd_drv_sta_remove(hapd, sta->addr); #ifdef CONFIG_IEEE80211N if (sta->flags & WLAN_STA_HT) hostapd_get_ht_capab(hapd, sta->ht_capabilities, &ht_cap); #endif /* CONFIG_IEEE80211N */ #ifdef CONFIG_IEEE80211AC if (sta->flags & WLAN_STA_VHT) hostapd_get_vht_capab(hapd, sta->vht_capabilities, &vht_cap); #endif /* CONFIG_IEEE80211AC */ if (hostapd_sta_add(hapd, sta->addr, sta->aid, sta->capability, sta->supported_rates, sta->supported_rates_len, sta->listen_interval, sta->flags & WLAN_STA_HT ? &ht_cap : NULL, sta->flags & WLAN_STA_VHT ? &vht_cap : NULL, sta->flags, sta->qosinfo)) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_NOTICE, "Could not add STA to kernel driver"); ap_sta_disconnect(hapd, sta, sta->addr, WLAN_REASON_DISASSOC_AP_BUSY); goto fail; } if (sta->flags & WLAN_STA_WDS) { int ret; char ifname_wds[IFNAMSIZ + 1]; ret = hostapd_set_wds_sta(hapd, ifname_wds, sta->addr, sta->aid, 1); if (!ret) hostapd_set_wds_encryption(hapd, sta, ifname_wds); } if (sta->eapol_sm == NULL) { /* * This STA does not use RADIUS server for EAP authentication, * so bind it to the selected VLAN interface now, since the * interface selection is not going to change anymore. */ if (ap_sta_bind_vlan(hapd, sta, 0) < 0) goto fail; } else if (sta->vlan_id) { /* VLAN ID already set (e.g., by PMKSA caching), so bind STA */ if (ap_sta_bind_vlan(hapd, sta, 0) < 0) goto fail; } hostapd_set_sta_flags(hapd, sta); if (sta->auth_alg == WLAN_AUTH_FT) wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC_FT); else wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC); hapd->new_assoc_sta_cb(hapd, sta, !new_assoc); ieee802_1x_notify_port_enabled(sta->eapol_sm, 1); fail: /* Copy of the association request is not needed anymore */ if (sta->last_assoc_req) { os_free(sta->last_assoc_req); sta->last_assoc_req = NULL; } } static void handle_deauth_cb(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, size_t len, int ok) { struct sta_info *sta; if (mgmt->da[0] & 0x01) return; sta = ap_get_sta(hapd, mgmt->da); if (!sta) { wpa_printf(MSG_DEBUG, "handle_deauth_cb: STA " MACSTR " not found", MAC2STR(mgmt->da)); return; } if (ok) wpa_printf(MSG_DEBUG, "STA " MACSTR " acknowledged deauth", MAC2STR(sta->addr)); else wpa_printf(MSG_DEBUG, "STA " MACSTR " did not acknowledge " "deauth", MAC2STR(sta->addr)); ap_sta_deauth_cb(hapd, sta); } static void handle_disassoc_cb(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, size_t len, int ok) { struct sta_info *sta; if (mgmt->da[0] & 0x01) return; sta = ap_get_sta(hapd, mgmt->da); if (!sta) { wpa_printf(MSG_DEBUG, "handle_disassoc_cb: STA " MACSTR " not found", MAC2STR(mgmt->da)); return; } if (ok) wpa_printf(MSG_DEBUG, "STA " MACSTR " acknowledged disassoc", MAC2STR(sta->addr)); else wpa_printf(MSG_DEBUG, "STA " MACSTR " did not acknowledge " "disassoc", MAC2STR(sta->addr)); ap_sta_disassoc_cb(hapd, sta); } /** * ieee802_11_mgmt_cb - Process management frame TX status callback * @hapd: hostapd BSS data structure (the BSS from which the management frame * was sent from) * @buf: management frame data (starting from IEEE 802.11 header) * @len: length of frame data in octets * @stype: management frame subtype from frame control field * @ok: Whether the frame was ACK'ed */ void ieee802_11_mgmt_cb(struct hostapd_data *hapd, const u8 *buf, size_t len, u16 stype, int ok) { const struct ieee80211_mgmt *mgmt; mgmt = (const struct ieee80211_mgmt *) buf; #ifdef CONFIG_TESTING_OPTIONS if (hapd->ext_mgmt_frame_handling) { wpa_msg(hapd->msg_ctx, MSG_INFO, "MGMT-TX-STATUS stype=%u ok=%d", stype, ok); return; } #endif /* CONFIG_TESTING_OPTIONS */ switch (stype) { case WLAN_FC_STYPE_AUTH: wpa_printf(MSG_DEBUG, "mgmt::auth cb"); handle_auth_cb(hapd, mgmt, len, ok); break; case WLAN_FC_STYPE_ASSOC_RESP: wpa_printf(MSG_DEBUG, "mgmt::assoc_resp cb"); handle_assoc_cb(hapd, mgmt, len, 0, ok); break; case WLAN_FC_STYPE_REASSOC_RESP: wpa_printf(MSG_DEBUG, "mgmt::reassoc_resp cb"); handle_assoc_cb(hapd, mgmt, len, 1, ok); break; case WLAN_FC_STYPE_PROBE_RESP: wpa_printf(MSG_EXCESSIVE, "mgmt::proberesp cb"); break; case WLAN_FC_STYPE_DEAUTH: wpa_printf(MSG_DEBUG, "mgmt::deauth cb"); handle_deauth_cb(hapd, mgmt, len, ok); break; case WLAN_FC_STYPE_DISASSOC: wpa_printf(MSG_DEBUG, "mgmt::disassoc cb"); handle_disassoc_cb(hapd, mgmt, len, ok); break; case WLAN_FC_STYPE_ACTION: wpa_printf(MSG_DEBUG, "mgmt::action cb"); break; default: wpa_printf(MSG_INFO, "unknown mgmt cb frame subtype %d", stype); break; } } int ieee802_11_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen) { /* TODO */ return 0; } int ieee802_11_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, char *buf, size_t buflen) { /* TODO */ return 0; } void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr, const u8 *buf, size_t len, int ack) { struct sta_info *sta; struct hostapd_iface *iface = hapd->iface; sta = ap_get_sta(hapd, addr); if (sta == NULL && iface->num_bss > 1) { size_t j; for (j = 0; j < iface->num_bss; j++) { hapd = iface->bss[j]; sta = ap_get_sta(hapd, addr); if (sta) break; } } if (sta == NULL || !(sta->flags & WLAN_STA_ASSOC)) return; if (sta->flags & WLAN_STA_PENDING_POLL) { wpa_printf(MSG_DEBUG, "STA " MACSTR " %s pending " "activity poll", MAC2STR(sta->addr), ack ? "ACKed" : "did not ACK"); if (ack) sta->flags &= ~WLAN_STA_PENDING_POLL; } ieee802_1x_tx_status(hapd, sta, buf, len, ack); } void hostapd_eapol_tx_status(struct hostapd_data *hapd, const u8 *dst, const u8 *data, size_t len, int ack) { struct sta_info *sta; struct hostapd_iface *iface = hapd->iface; sta = ap_get_sta(hapd, dst); if (sta == NULL && iface->num_bss > 1) { size_t j; for (j = 0; j < iface->num_bss; j++) { hapd = iface->bss[j]; sta = ap_get_sta(hapd, dst); if (sta) break; } } if (sta == NULL || !(sta->flags & WLAN_STA_ASSOC)) { wpa_printf(MSG_DEBUG, "Ignore TX status for Data frame to STA " MACSTR " that is not currently associated", MAC2STR(dst)); return; } ieee802_1x_eapol_tx_status(hapd, sta, data, len, ack); } void hostapd_client_poll_ok(struct hostapd_data *hapd, const u8 *addr) { struct sta_info *sta; struct hostapd_iface *iface = hapd->iface; sta = ap_get_sta(hapd, addr); if (sta == NULL && iface->num_bss > 1) { size_t j; for (j = 0; j < iface->num_bss; j++) { hapd = iface->bss[j]; sta = ap_get_sta(hapd, addr); if (sta) break; } } if (sta == NULL) return; if (!(sta->flags & WLAN_STA_PENDING_POLL)) return; wpa_printf(MSG_DEBUG, "STA " MACSTR " ACKed pending " "activity poll", MAC2STR(sta->addr)); sta->flags &= ~WLAN_STA_PENDING_POLL; } void ieee802_11_rx_from_unknown(struct hostapd_data *hapd, const u8 *src, int wds) { struct sta_info *sta; sta = ap_get_sta(hapd, src); if (sta && (sta->flags & WLAN_STA_ASSOC)) { if (!hapd->conf->wds_sta) return; if (wds && !(sta->flags & WLAN_STA_WDS)) { int ret; char ifname_wds[IFNAMSIZ + 1]; wpa_printf(MSG_DEBUG, "Enable 4-address WDS mode for " "STA " MACSTR " (aid %u)", MAC2STR(sta->addr), sta->aid); sta->flags |= WLAN_STA_WDS; ret = hostapd_set_wds_sta(hapd, ifname_wds, sta->addr, sta->aid, 1); if (!ret) hostapd_set_wds_encryption(hapd, sta, ifname_wds); } return; } wpa_printf(MSG_DEBUG, "Data/PS-poll frame from not associated STA " MACSTR, MAC2STR(src)); if (src[0] & 0x01) { /* Broadcast bit set in SA?! Ignore the frame silently. */ return; } if (sta && (sta->flags & WLAN_STA_ASSOC_REQ_OK)) { wpa_printf(MSG_DEBUG, "Association Response to the STA has " "already been sent, but no TX status yet known - " "ignore Class 3 frame issue with " MACSTR, MAC2STR(src)); return; } if (sta && (sta->flags & WLAN_STA_AUTH)) hostapd_drv_sta_disassoc( hapd, src, WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA); else hostapd_drv_sta_deauth( hapd, src, WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA); } #endif /* CONFIG_NATIVE_WINDOWS */ wpa-2.1/src/ap/ieee802_11.h000066400000000000000000000071551227414666700151740ustar00rootroot00000000000000/* * hostapd / IEEE 802.11 Management * Copyright (c) 2002-2009, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef IEEE802_11_H #define IEEE802_11_H struct hostapd_iface; struct hostapd_data; struct sta_info; struct hostapd_frame_info; struct ieee80211_ht_capabilities; int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len, struct hostapd_frame_info *fi); void ieee802_11_mgmt_cb(struct hostapd_data *hapd, const u8 *buf, size_t len, u16 stype, int ok); void ieee802_11_print_ssid(char *buf, const u8 *ssid, u8 len); #ifdef NEED_AP_MLME int ieee802_11_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen); int ieee802_11_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, char *buf, size_t buflen); #else /* NEED_AP_MLME */ static inline int ieee802_11_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen) { return 0; } static inline int ieee802_11_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, char *buf, size_t buflen) { return 0; } #endif /* NEED_AP_MLME */ u16 hostapd_own_capab_info(struct hostapd_data *hapd, struct sta_info *sta, int probe); u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_qos_map_set(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_ht_capabilities(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid); int hostapd_ht_operation_update(struct hostapd_iface *iface); void ieee802_11_send_sa_query_req(struct hostapd_data *hapd, const u8 *addr, const u8 *trans_id); void hostapd_get_ht_capab(struct hostapd_data *hapd, struct ieee80211_ht_capabilities *ht_cap, struct ieee80211_ht_capabilities *neg_ht_cap); void hostapd_get_vht_capab(struct hostapd_data *hapd, struct ieee80211_vht_capabilities *vht_cap, struct ieee80211_vht_capabilities *neg_vht_cap); u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta, const u8 *ht_capab, size_t ht_capab_len); void update_ht_state(struct hostapd_data *hapd, struct sta_info *sta); u16 copy_sta_vht_capab(struct hostapd_data *hapd, struct sta_info *sta, const u8 *vht_capab, size_t vht_capab_len); void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr, const u8 *buf, size_t len, int ack); void hostapd_eapol_tx_status(struct hostapd_data *hapd, const u8 *dst, const u8 *data, size_t len, int ack); void ieee802_11_rx_from_unknown(struct hostapd_data *hapd, const u8 *src, int wds); u8 * hostapd_eid_assoc_comeback_time(struct hostapd_data *hapd, struct sta_info *sta, u8 *eid); void ieee802_11_sa_query_action(struct hostapd_data *hapd, const u8 *sa, const u8 action_type, const u8 *trans_id); u8 * hostapd_eid_interworking(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_adv_proto(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_roaming_consortium(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_time_adv(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_time_zone(struct hostapd_data *hapd, u8 *eid); int hostapd_update_time_adv(struct hostapd_data *hapd); void hostapd_client_poll_ok(struct hostapd_data *hapd, const u8 *addr); u8 * hostapd_eid_bss_max_idle_period(struct hostapd_data *hapd, u8 *eid); #endif /* IEEE802_11_H */ wpa-2.1/src/ap/ieee802_11_auth.c000066400000000000000000000414071227414666700162060ustar00rootroot00000000000000/* * hostapd / IEEE 802.11 authentication (ACL) * Copyright (c) 2003-2012, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. * * Access control list for IEEE 802.11 authentication can uses statically * configured ACL from configuration files or an external RADIUS server. * Results from external RADIUS queries are cached to allow faster * authentication frame processing. */ #include "utils/includes.h" #include "utils/common.h" #include "utils/eloop.h" #include "crypto/sha1.h" #include "radius/radius.h" #include "radius/radius_client.h" #include "hostapd.h" #include "ap_config.h" #include "ap_drv_ops.h" #include "ieee802_11.h" #include "ieee802_1x.h" #include "ieee802_11_auth.h" #define RADIUS_ACL_TIMEOUT 30 struct hostapd_cached_radius_acl { struct os_reltime timestamp; macaddr addr; int accepted; /* HOSTAPD_ACL_* */ struct hostapd_cached_radius_acl *next; u32 session_timeout; u32 acct_interim_interval; int vlan_id; struct hostapd_sta_wpa_psk_short *psk; char *identity; char *radius_cui; }; struct hostapd_acl_query_data { struct os_reltime timestamp; u8 radius_id; macaddr addr; u8 *auth_msg; /* IEEE 802.11 authentication frame from station */ size_t auth_msg_len; struct hostapd_acl_query_data *next; }; #ifndef CONFIG_NO_RADIUS static void hostapd_acl_cache_free_entry(struct hostapd_cached_radius_acl *e) { os_free(e->identity); os_free(e->radius_cui); hostapd_free_psk_list(e->psk); os_free(e); } static void hostapd_acl_cache_free(struct hostapd_cached_radius_acl *acl_cache) { struct hostapd_cached_radius_acl *prev; while (acl_cache) { prev = acl_cache; acl_cache = acl_cache->next; hostapd_acl_cache_free_entry(prev); } } static void copy_psk_list(struct hostapd_sta_wpa_psk_short **psk, struct hostapd_sta_wpa_psk_short *src) { struct hostapd_sta_wpa_psk_short **copy_to; struct hostapd_sta_wpa_psk_short *copy_from; /* Copy PSK linked list */ copy_to = psk; copy_from = src; while (copy_from && copy_to) { *copy_to = os_zalloc(sizeof(struct hostapd_sta_wpa_psk_short)); if (*copy_to == NULL) break; os_memcpy(*copy_to, copy_from, sizeof(struct hostapd_sta_wpa_psk_short)); copy_from = copy_from->next; copy_to = &((*copy_to)->next); } if (copy_to) *copy_to = NULL; } static int hostapd_acl_cache_get(struct hostapd_data *hapd, const u8 *addr, u32 *session_timeout, u32 *acct_interim_interval, int *vlan_id, struct hostapd_sta_wpa_psk_short **psk, char **identity, char **radius_cui) { struct hostapd_cached_radius_acl *entry; struct os_reltime now; os_get_reltime(&now); for (entry = hapd->acl_cache; entry; entry = entry->next) { if (os_memcmp(entry->addr, addr, ETH_ALEN) != 0) continue; if (os_reltime_expired(&now, &entry->timestamp, RADIUS_ACL_TIMEOUT)) return -1; /* entry has expired */ if (entry->accepted == HOSTAPD_ACL_ACCEPT_TIMEOUT) if (session_timeout) *session_timeout = entry->session_timeout; if (acct_interim_interval) *acct_interim_interval = entry->acct_interim_interval; if (vlan_id) *vlan_id = entry->vlan_id; copy_psk_list(psk, entry->psk); if (identity) { if (entry->identity) *identity = os_strdup(entry->identity); else *identity = NULL; } if (radius_cui) { if (entry->radius_cui) *radius_cui = os_strdup(entry->radius_cui); else *radius_cui = NULL; } return entry->accepted; } return -1; } #endif /* CONFIG_NO_RADIUS */ static void hostapd_acl_query_free(struct hostapd_acl_query_data *query) { if (query == NULL) return; os_free(query->auth_msg); os_free(query); } #ifndef CONFIG_NO_RADIUS static int hostapd_radius_acl_query(struct hostapd_data *hapd, const u8 *addr, struct hostapd_acl_query_data *query) { struct radius_msg *msg; char buf[128]; query->radius_id = radius_client_get_id(hapd->radius); msg = radius_msg_new(RADIUS_CODE_ACCESS_REQUEST, query->radius_id); if (msg == NULL) return -1; radius_msg_make_authenticator(msg, addr, ETH_ALEN); os_snprintf(buf, sizeof(buf), RADIUS_ADDR_FORMAT, MAC2STR(addr)); if (!radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME, (u8 *) buf, os_strlen(buf))) { wpa_printf(MSG_DEBUG, "Could not add User-Name"); goto fail; } if (!radius_msg_add_attr_user_password( msg, (u8 *) buf, os_strlen(buf), hapd->conf->radius->auth_server->shared_secret, hapd->conf->radius->auth_server->shared_secret_len)) { wpa_printf(MSG_DEBUG, "Could not add User-Password"); goto fail; } if (add_common_radius_attr(hapd, hapd->conf->radius_auth_req_attr, NULL, msg) < 0) goto fail; os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT, MAC2STR(addr)); if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID, (u8 *) buf, os_strlen(buf))) { wpa_printf(MSG_DEBUG, "Could not add Calling-Station-Id"); goto fail; } os_snprintf(buf, sizeof(buf), "CONNECT 11Mbps 802.11b"); if (!radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO, (u8 *) buf, os_strlen(buf))) { wpa_printf(MSG_DEBUG, "Could not add Connect-Info"); goto fail; } if (radius_client_send(hapd->radius, msg, RADIUS_AUTH, addr) < 0) goto fail; return 0; fail: radius_msg_free(msg); return -1; } #endif /* CONFIG_NO_RADIUS */ /** * hostapd_allowed_address - Check whether a specified STA can be authenticated * @hapd: hostapd BSS data * @addr: MAC address of the STA * @msg: Authentication message * @len: Length of msg in octets * @session_timeout: Buffer for returning session timeout (from RADIUS) * @acct_interim_interval: Buffer for returning account interval (from RADIUS) * @vlan_id: Buffer for returning VLAN ID * @psk: Linked list buffer for returning WPA PSK * @identity: Buffer for returning identity (from RADIUS) * @radius_cui: Buffer for returning CUI (from RADIUS) * Returns: HOSTAPD_ACL_ACCEPT, HOSTAPD_ACL_REJECT, or HOSTAPD_ACL_PENDING * * The caller is responsible for freeing the returned *identity and *radius_cui * values with os_free(). */ int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr, const u8 *msg, size_t len, u32 *session_timeout, u32 *acct_interim_interval, int *vlan_id, struct hostapd_sta_wpa_psk_short **psk, char **identity, char **radius_cui) { if (session_timeout) *session_timeout = 0; if (acct_interim_interval) *acct_interim_interval = 0; if (vlan_id) *vlan_id = 0; if (psk) *psk = NULL; if (identity) *identity = NULL; if (radius_cui) *radius_cui = NULL; if (hostapd_maclist_found(hapd->conf->accept_mac, hapd->conf->num_accept_mac, addr, vlan_id)) return HOSTAPD_ACL_ACCEPT; if (hostapd_maclist_found(hapd->conf->deny_mac, hapd->conf->num_deny_mac, addr, vlan_id)) return HOSTAPD_ACL_REJECT; if (hapd->conf->macaddr_acl == ACCEPT_UNLESS_DENIED) return HOSTAPD_ACL_ACCEPT; if (hapd->conf->macaddr_acl == DENY_UNLESS_ACCEPTED) return HOSTAPD_ACL_REJECT; if (hapd->conf->macaddr_acl == USE_EXTERNAL_RADIUS_AUTH) { #ifdef CONFIG_NO_RADIUS return HOSTAPD_ACL_REJECT; #else /* CONFIG_NO_RADIUS */ struct hostapd_acl_query_data *query; /* Check whether ACL cache has an entry for this station */ int res = hostapd_acl_cache_get(hapd, addr, session_timeout, acct_interim_interval, vlan_id, psk, identity, radius_cui); if (res == HOSTAPD_ACL_ACCEPT || res == HOSTAPD_ACL_ACCEPT_TIMEOUT) return res; if (res == HOSTAPD_ACL_REJECT) return HOSTAPD_ACL_REJECT; query = hapd->acl_queries; while (query) { if (os_memcmp(query->addr, addr, ETH_ALEN) == 0) { /* pending query in RADIUS retransmit queue; * do not generate a new one */ if (identity) { os_free(*identity); *identity = NULL; } if (radius_cui) { os_free(*radius_cui); *radius_cui = NULL; } return HOSTAPD_ACL_PENDING; } query = query->next; } if (!hapd->conf->radius->auth_server) return HOSTAPD_ACL_REJECT; /* No entry in the cache - query external RADIUS server */ query = os_zalloc(sizeof(*query)); if (query == NULL) { wpa_printf(MSG_ERROR, "malloc for query data failed"); return HOSTAPD_ACL_REJECT; } os_get_reltime(&query->timestamp); os_memcpy(query->addr, addr, ETH_ALEN); if (hostapd_radius_acl_query(hapd, addr, query)) { wpa_printf(MSG_DEBUG, "Failed to send Access-Request " "for ACL query."); hostapd_acl_query_free(query); return HOSTAPD_ACL_REJECT; } query->auth_msg = os_malloc(len); if (query->auth_msg == NULL) { wpa_printf(MSG_ERROR, "Failed to allocate memory for " "auth frame."); hostapd_acl_query_free(query); return HOSTAPD_ACL_REJECT; } os_memcpy(query->auth_msg, msg, len); query->auth_msg_len = len; query->next = hapd->acl_queries; hapd->acl_queries = query; /* Queued data will be processed in hostapd_acl_recv_radius() * when RADIUS server replies to the sent Access-Request. */ return HOSTAPD_ACL_PENDING; #endif /* CONFIG_NO_RADIUS */ } return HOSTAPD_ACL_REJECT; } #ifndef CONFIG_NO_RADIUS static void hostapd_acl_expire_cache(struct hostapd_data *hapd, struct os_reltime *now) { struct hostapd_cached_radius_acl *prev, *entry, *tmp; prev = NULL; entry = hapd->acl_cache; while (entry) { if (os_reltime_expired(now, &entry->timestamp, RADIUS_ACL_TIMEOUT)) { wpa_printf(MSG_DEBUG, "Cached ACL entry for " MACSTR " has expired.", MAC2STR(entry->addr)); if (prev) prev->next = entry->next; else hapd->acl_cache = entry->next; hostapd_drv_set_radius_acl_expire(hapd, entry->addr); tmp = entry; entry = entry->next; hostapd_acl_cache_free_entry(tmp); continue; } prev = entry; entry = entry->next; } } static void hostapd_acl_expire_queries(struct hostapd_data *hapd, struct os_reltime *now) { struct hostapd_acl_query_data *prev, *entry, *tmp; prev = NULL; entry = hapd->acl_queries; while (entry) { if (os_reltime_expired(now, &entry->timestamp, RADIUS_ACL_TIMEOUT)) { wpa_printf(MSG_DEBUG, "ACL query for " MACSTR " has expired.", MAC2STR(entry->addr)); if (prev) prev->next = entry->next; else hapd->acl_queries = entry->next; tmp = entry; entry = entry->next; hostapd_acl_query_free(tmp); continue; } prev = entry; entry = entry->next; } } /** * hostapd_acl_expire - ACL cache expiration callback * @eloop_ctx: struct hostapd_data * * @timeout_ctx: Not used */ static void hostapd_acl_expire(void *eloop_ctx, void *timeout_ctx) { struct hostapd_data *hapd = eloop_ctx; struct os_reltime now; os_get_reltime(&now); hostapd_acl_expire_cache(hapd, &now); hostapd_acl_expire_queries(hapd, &now); eloop_register_timeout(10, 0, hostapd_acl_expire, hapd, NULL); } static void decode_tunnel_passwords(struct hostapd_data *hapd, const u8 *shared_secret, size_t shared_secret_len, struct radius_msg *msg, struct radius_msg *req, struct hostapd_cached_radius_acl *cache) { int passphraselen; char *passphrase, *strpassphrase; size_t i; struct hostapd_sta_wpa_psk_short *psk; /* * Decode all tunnel passwords as PSK and save them into a linked list. */ for (i = 0; ; i++) { passphrase = radius_msg_get_tunnel_password( msg, &passphraselen, shared_secret, shared_secret_len, req, i); /* * Passphrase is NULL iff there is no i-th Tunnel-Password * attribute in msg. */ if (passphrase == NULL) break; /* * passphrase does not contain the NULL termination. * Add it here as pbkdf2_sha1() requires it. */ strpassphrase = os_zalloc(passphraselen + 1); psk = os_zalloc(sizeof(struct hostapd_sta_wpa_psk_short)); if (strpassphrase && psk) { os_memcpy(strpassphrase, passphrase, passphraselen); pbkdf2_sha1(strpassphrase, hapd->conf->ssid.ssid, hapd->conf->ssid.ssid_len, 4096, psk->psk, PMK_LEN); psk->next = cache->psk; cache->psk = psk; psk = NULL; } os_free(strpassphrase); os_free(psk); os_free(passphrase); } } /** * hostapd_acl_recv_radius - Process incoming RADIUS Authentication messages * @msg: RADIUS response message * @req: RADIUS request message * @shared_secret: RADIUS shared secret * @shared_secret_len: Length of shared_secret in octets * @data: Context data (struct hostapd_data *) * Returns: RADIUS_RX_PROCESSED if RADIUS message was a reply to ACL query (and * was processed here) or RADIUS_RX_UNKNOWN if not. */ static RadiusRxResult hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req, const u8 *shared_secret, size_t shared_secret_len, void *data) { struct hostapd_data *hapd = data; struct hostapd_acl_query_data *query, *prev; struct hostapd_cached_radius_acl *cache; struct radius_hdr *hdr = radius_msg_get_hdr(msg); query = hapd->acl_queries; prev = NULL; while (query) { if (query->radius_id == hdr->identifier) break; prev = query; query = query->next; } if (query == NULL) return RADIUS_RX_UNKNOWN; wpa_printf(MSG_DEBUG, "Found matching Access-Request for RADIUS " "message (id=%d)", query->radius_id); if (radius_msg_verify(msg, shared_secret, shared_secret_len, req, 0)) { wpa_printf(MSG_INFO, "Incoming RADIUS packet did not have " "correct authenticator - dropped\n"); return RADIUS_RX_INVALID_AUTHENTICATOR; } if (hdr->code != RADIUS_CODE_ACCESS_ACCEPT && hdr->code != RADIUS_CODE_ACCESS_REJECT) { wpa_printf(MSG_DEBUG, "Unknown RADIUS message code %d to ACL " "query", hdr->code); return RADIUS_RX_UNKNOWN; } /* Insert Accept/Reject info into ACL cache */ cache = os_zalloc(sizeof(*cache)); if (cache == NULL) { wpa_printf(MSG_DEBUG, "Failed to add ACL cache entry"); goto done; } os_get_reltime(&cache->timestamp); os_memcpy(cache->addr, query->addr, sizeof(cache->addr)); if (hdr->code == RADIUS_CODE_ACCESS_ACCEPT) { u8 *buf; size_t len; if (radius_msg_get_attr_int32(msg, RADIUS_ATTR_SESSION_TIMEOUT, &cache->session_timeout) == 0) cache->accepted = HOSTAPD_ACL_ACCEPT_TIMEOUT; else cache->accepted = HOSTAPD_ACL_ACCEPT; if (radius_msg_get_attr_int32( msg, RADIUS_ATTR_ACCT_INTERIM_INTERVAL, &cache->acct_interim_interval) == 0 && cache->acct_interim_interval < 60) { wpa_printf(MSG_DEBUG, "Ignored too small " "Acct-Interim-Interval %d for STA " MACSTR, cache->acct_interim_interval, MAC2STR(query->addr)); cache->acct_interim_interval = 0; } cache->vlan_id = radius_msg_get_vlanid(msg); decode_tunnel_passwords(hapd, shared_secret, shared_secret_len, msg, req, cache); if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_USER_NAME, &buf, &len, NULL) == 0) { cache->identity = os_zalloc(len + 1); if (cache->identity) os_memcpy(cache->identity, buf, len); } if (radius_msg_get_attr_ptr( msg, RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, &buf, &len, NULL) == 0) { cache->radius_cui = os_zalloc(len + 1); if (cache->radius_cui) os_memcpy(cache->radius_cui, buf, len); } if (hapd->conf->wpa_psk_radius == PSK_RADIUS_REQUIRED && !cache->psk) cache->accepted = HOSTAPD_ACL_REJECT; } else cache->accepted = HOSTAPD_ACL_REJECT; cache->next = hapd->acl_cache; hapd->acl_cache = cache; #ifdef CONFIG_DRIVER_RADIUS_ACL hostapd_drv_set_radius_acl_auth(hapd, query->addr, cache->accepted, cache->session_timeout); #else /* CONFIG_DRIVER_RADIUS_ACL */ #ifdef NEED_AP_MLME /* Re-send original authentication frame for 802.11 processing */ wpa_printf(MSG_DEBUG, "Re-sending authentication frame after " "successful RADIUS ACL query"); ieee802_11_mgmt(hapd, query->auth_msg, query->auth_msg_len, NULL); #endif /* NEED_AP_MLME */ #endif /* CONFIG_DRIVER_RADIUS_ACL */ done: if (prev == NULL) hapd->acl_queries = query->next; else prev->next = query->next; hostapd_acl_query_free(query); return RADIUS_RX_PROCESSED; } #endif /* CONFIG_NO_RADIUS */ /** * hostapd_acl_init: Initialize IEEE 802.11 ACL * @hapd: hostapd BSS data * Returns: 0 on success, -1 on failure */ int hostapd_acl_init(struct hostapd_data *hapd) { #ifndef CONFIG_NO_RADIUS if (radius_client_register(hapd->radius, RADIUS_AUTH, hostapd_acl_recv_radius, hapd)) return -1; eloop_register_timeout(10, 0, hostapd_acl_expire, hapd, NULL); #endif /* CONFIG_NO_RADIUS */ return 0; } /** * hostapd_acl_deinit - Deinitialize IEEE 802.11 ACL * @hapd: hostapd BSS data */ void hostapd_acl_deinit(struct hostapd_data *hapd) { struct hostapd_acl_query_data *query, *prev; #ifndef CONFIG_NO_RADIUS eloop_cancel_timeout(hostapd_acl_expire, hapd, NULL); hostapd_acl_cache_free(hapd->acl_cache); #endif /* CONFIG_NO_RADIUS */ query = hapd->acl_queries; while (query) { prev = query; query = query->next; hostapd_acl_query_free(prev); } } void hostapd_free_psk_list(struct hostapd_sta_wpa_psk_short *psk) { while (psk) { struct hostapd_sta_wpa_psk_short *prev = psk; psk = psk->next; os_free(prev); } } wpa-2.1/src/ap/ieee802_11_auth.h000066400000000000000000000015241227414666700162070ustar00rootroot00000000000000/* * hostapd / IEEE 802.11 authentication (ACL) * Copyright (c) 2003-2005, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef IEEE802_11_AUTH_H #define IEEE802_11_AUTH_H enum { HOSTAPD_ACL_REJECT = 0, HOSTAPD_ACL_ACCEPT = 1, HOSTAPD_ACL_PENDING = 2, HOSTAPD_ACL_ACCEPT_TIMEOUT = 3 }; int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr, const u8 *msg, size_t len, u32 *session_timeout, u32 *acct_interim_interval, int *vlan_id, struct hostapd_sta_wpa_psk_short **psk, char **identity, char **radius_cui); int hostapd_acl_init(struct hostapd_data *hapd); void hostapd_acl_deinit(struct hostapd_data *hapd); void hostapd_free_psk_list(struct hostapd_sta_wpa_psk_short *psk); #endif /* IEEE802_11_AUTH_H */ wpa-2.1/src/ap/ieee802_11_ht.c000066400000000000000000000177241227414666700156650ustar00rootroot00000000000000/* * hostapd / IEEE 802.11n HT * Copyright (c) 2002-2009, Jouni Malinen * Copyright (c) 2007-2008, Intel Corporation * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "utils/includes.h" #include "utils/common.h" #include "common/ieee802_11_defs.h" #include "hostapd.h" #include "ap_config.h" #include "sta_info.h" #include "beacon.h" #include "ieee802_11.h" u8 * hostapd_eid_ht_capabilities(struct hostapd_data *hapd, u8 *eid) { struct ieee80211_ht_capabilities *cap; u8 *pos = eid; if (!hapd->iconf->ieee80211n || !hapd->iface->current_mode || hapd->conf->disable_11n) return eid; *pos++ = WLAN_EID_HT_CAP; *pos++ = sizeof(*cap); cap = (struct ieee80211_ht_capabilities *) pos; os_memset(cap, 0, sizeof(*cap)); cap->ht_capabilities_info = host_to_le16(hapd->iconf->ht_capab); cap->a_mpdu_params = hapd->iface->current_mode->a_mpdu_params; os_memcpy(cap->supported_mcs_set, hapd->iface->current_mode->mcs_set, 16); /* TODO: ht_extended_capabilities (now fully disabled) */ /* TODO: tx_bf_capability_info (now fully disabled) */ /* TODO: asel_capabilities (now fully disabled) */ pos += sizeof(*cap); if (hapd->iconf->obss_interval) { struct ieee80211_obss_scan_parameters *scan_params; *pos++ = WLAN_EID_OVERLAPPING_BSS_SCAN_PARAMS; *pos++ = sizeof(*scan_params); scan_params = (struct ieee80211_obss_scan_parameters *) pos; os_memset(scan_params, 0, sizeof(*scan_params)); scan_params->width_trigger_scan_interval = host_to_le16(hapd->iconf->obss_interval); /* TODO: Fill in more parameters (supplicant ignores them) */ pos += sizeof(*scan_params); } return pos; } u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid) { struct ieee80211_ht_operation *oper; u8 *pos = eid; if (!hapd->iconf->ieee80211n || hapd->conf->disable_11n) return eid; *pos++ = WLAN_EID_HT_OPERATION; *pos++ = sizeof(*oper); oper = (struct ieee80211_ht_operation *) pos; os_memset(oper, 0, sizeof(*oper)); oper->control_chan = hapd->iconf->channel; oper->operation_mode = host_to_le16(hapd->iface->ht_op_mode); if (hapd->iconf->secondary_channel == 1) oper->ht_param |= HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE | HT_INFO_HT_PARAM_REC_TRANS_CHNL_WIDTH; if (hapd->iconf->secondary_channel == -1) oper->ht_param |= HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW | HT_INFO_HT_PARAM_REC_TRANS_CHNL_WIDTH; pos += sizeof(*oper); return pos; } /* op_mode Set to 0 (HT pure) under the followign conditions - all STAs in the BSS are 20/40 MHz HT in 20/40 MHz BSS or - all STAs in the BSS are 20 MHz HT in 20 MHz BSS Set to 1 (HT non-member protection) if there may be non-HT STAs in both the primary and the secondary channel Set to 2 if only HT STAs are associated in BSS, however and at least one 20 MHz HT STA is associated Set to 3 (HT mixed mode) when one or more non-HT STAs are associated */ int hostapd_ht_operation_update(struct hostapd_iface *iface) { u16 cur_op_mode, new_op_mode; int op_mode_changes = 0; if (!iface->conf->ieee80211n || iface->conf->ht_op_mode_fixed) return 0; wpa_printf(MSG_DEBUG, "%s current operation mode=0x%X", __func__, iface->ht_op_mode); if (!(iface->ht_op_mode & HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT) && iface->num_sta_ht_no_gf) { iface->ht_op_mode |= HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT; op_mode_changes++; } else if ((iface->ht_op_mode & HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT) && iface->num_sta_ht_no_gf == 0) { iface->ht_op_mode &= ~HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT; op_mode_changes++; } if (!(iface->ht_op_mode & HT_INFO_OPERATION_MODE_NON_HT_STA_PRESENT) && (iface->num_sta_no_ht || iface->olbc_ht)) { iface->ht_op_mode |= HT_INFO_OPERATION_MODE_NON_HT_STA_PRESENT; op_mode_changes++; } else if ((iface->ht_op_mode & HT_INFO_OPERATION_MODE_NON_HT_STA_PRESENT) && (iface->num_sta_no_ht == 0 && !iface->olbc_ht)) { iface->ht_op_mode &= ~HT_INFO_OPERATION_MODE_NON_HT_STA_PRESENT; op_mode_changes++; } new_op_mode = 0; if (iface->num_sta_no_ht) new_op_mode = OP_MODE_MIXED; else if (iface->conf->secondary_channel && iface->num_sta_ht_20mhz) new_op_mode = OP_MODE_20MHZ_HT_STA_ASSOCED; else if (iface->olbc_ht) new_op_mode = OP_MODE_MAY_BE_LEGACY_STAS; else new_op_mode = OP_MODE_PURE; cur_op_mode = iface->ht_op_mode & HT_INFO_OPERATION_MODE_OP_MODE_MASK; if (cur_op_mode != new_op_mode) { iface->ht_op_mode &= ~HT_INFO_OPERATION_MODE_OP_MODE_MASK; iface->ht_op_mode |= new_op_mode; op_mode_changes++; } wpa_printf(MSG_DEBUG, "%s new operation mode=0x%X changes=%d", __func__, iface->ht_op_mode, op_mode_changes); return op_mode_changes; } u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta, const u8 *ht_capab, size_t ht_capab_len) { /* Disable HT caps for STAs associated to no-HT BSSes. */ if (!ht_capab || ht_capab_len < sizeof(struct ieee80211_ht_capabilities) || hapd->conf->disable_11n) { sta->flags &= ~WLAN_STA_HT; os_free(sta->ht_capabilities); sta->ht_capabilities = NULL; return WLAN_STATUS_SUCCESS; } if (sta->ht_capabilities == NULL) { sta->ht_capabilities = os_zalloc(sizeof(struct ieee80211_ht_capabilities)); if (sta->ht_capabilities == NULL) return WLAN_STATUS_UNSPECIFIED_FAILURE; } sta->flags |= WLAN_STA_HT; os_memcpy(sta->ht_capabilities, ht_capab, sizeof(struct ieee80211_ht_capabilities)); return WLAN_STATUS_SUCCESS; } static void update_sta_ht(struct hostapd_data *hapd, struct sta_info *sta) { u16 ht_capab; ht_capab = le_to_host16(sta->ht_capabilities->ht_capabilities_info); wpa_printf(MSG_DEBUG, "HT: STA " MACSTR " HT Capabilities Info: " "0x%04x", MAC2STR(sta->addr), ht_capab); if ((ht_capab & HT_CAP_INFO_GREEN_FIELD) == 0) { if (!sta->no_ht_gf_set) { sta->no_ht_gf_set = 1; hapd->iface->num_sta_ht_no_gf++; } wpa_printf(MSG_DEBUG, "%s STA " MACSTR " - no greenfield, num " "of non-gf stations %d", __func__, MAC2STR(sta->addr), hapd->iface->num_sta_ht_no_gf); } if ((ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) == 0) { if (!sta->ht_20mhz_set) { sta->ht_20mhz_set = 1; hapd->iface->num_sta_ht_20mhz++; } wpa_printf(MSG_DEBUG, "%s STA " MACSTR " - 20 MHz HT, num of " "20MHz HT STAs %d", __func__, MAC2STR(sta->addr), hapd->iface->num_sta_ht_20mhz); } } static void update_sta_no_ht(struct hostapd_data *hapd, struct sta_info *sta) { if (!sta->no_ht_set) { sta->no_ht_set = 1; hapd->iface->num_sta_no_ht++; } if (hapd->iconf->ieee80211n) { wpa_printf(MSG_DEBUG, "%s STA " MACSTR " - no HT, num of " "non-HT stations %d", __func__, MAC2STR(sta->addr), hapd->iface->num_sta_no_ht); } } void update_ht_state(struct hostapd_data *hapd, struct sta_info *sta) { if ((sta->flags & WLAN_STA_HT) && sta->ht_capabilities) update_sta_ht(hapd, sta); else update_sta_no_ht(hapd, sta); if (hostapd_ht_operation_update(hapd->iface) > 0) ieee802_11_set_beacons(hapd->iface); } void hostapd_get_ht_capab(struct hostapd_data *hapd, struct ieee80211_ht_capabilities *ht_cap, struct ieee80211_ht_capabilities *neg_ht_cap) { u16 cap; if (ht_cap == NULL) return; os_memcpy(neg_ht_cap, ht_cap, sizeof(*neg_ht_cap)); cap = le_to_host16(neg_ht_cap->ht_capabilities_info); /* * Mask out HT features we don't support, but don't overwrite * non-symmetric features like STBC and SMPS. Just because * we're not in dynamic SMPS mode the STA might still be. */ cap &= (hapd->iconf->ht_capab | HT_CAP_INFO_RX_STBC_MASK | HT_CAP_INFO_TX_STBC | HT_CAP_INFO_SMPS_MASK); /* * STBC needs to be handled specially * if we don't support RX STBC, mask out TX STBC in the STA's HT caps * if we don't support TX STBC, mask out RX STBC in the STA's HT caps */ if (!(hapd->iconf->ht_capab & HT_CAP_INFO_RX_STBC_MASK)) cap &= ~HT_CAP_INFO_TX_STBC; if (!(hapd->iconf->ht_capab & HT_CAP_INFO_TX_STBC)) cap &= ~HT_CAP_INFO_RX_STBC_MASK; neg_ht_cap->ht_capabilities_info = host_to_le16(cap); } wpa-2.1/src/ap/ieee802_11_shared.c000066400000000000000000000273341227414666700165160ustar00rootroot00000000000000/* * hostapd / IEEE 802.11 Management * Copyright (c) 2002-2012, 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 "common/ieee802_11_defs.h" #include "hostapd.h" #include "sta_info.h" #include "ap_config.h" #include "ap_drv_ops.h" #include "ieee802_11.h" #ifdef CONFIG_IEEE80211W u8 * hostapd_eid_assoc_comeback_time(struct hostapd_data *hapd, struct sta_info *sta, u8 *eid) { u8 *pos = eid; u32 timeout, tu; struct os_reltime now, passed; *pos++ = WLAN_EID_TIMEOUT_INTERVAL; *pos++ = 5; *pos++ = WLAN_TIMEOUT_ASSOC_COMEBACK; os_get_reltime(&now); os_reltime_sub(&now, &sta->sa_query_start, &passed); tu = (passed.sec * 1000000 + passed.usec) / 1024; if (hapd->conf->assoc_sa_query_max_timeout > tu) timeout = hapd->conf->assoc_sa_query_max_timeout - tu; else timeout = 0; if (timeout < hapd->conf->assoc_sa_query_max_timeout) timeout++; /* add some extra time for local timers */ WPA_PUT_LE32(pos, timeout); pos += 4; return pos; } /* MLME-SAQuery.request */ void ieee802_11_send_sa_query_req(struct hostapd_data *hapd, const u8 *addr, const u8 *trans_id) { struct ieee80211_mgmt mgmt; u8 *end; wpa_printf(MSG_DEBUG, "IEEE 802.11: Sending SA Query Request to " MACSTR, MAC2STR(addr)); wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID", trans_id, WLAN_SA_QUERY_TR_ID_LEN); os_memset(&mgmt, 0, sizeof(mgmt)); mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_ACTION); os_memcpy(mgmt.da, addr, ETH_ALEN); os_memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN); os_memcpy(mgmt.bssid, hapd->own_addr, ETH_ALEN); mgmt.u.action.category = WLAN_ACTION_SA_QUERY; mgmt.u.action.u.sa_query_req.action = WLAN_SA_QUERY_REQUEST; os_memcpy(mgmt.u.action.u.sa_query_req.trans_id, trans_id, WLAN_SA_QUERY_TR_ID_LEN); end = mgmt.u.action.u.sa_query_req.trans_id + WLAN_SA_QUERY_TR_ID_LEN; if (hostapd_drv_send_mlme(hapd, &mgmt, end - (u8 *) &mgmt, 0) < 0) wpa_printf(MSG_INFO, "ieee802_11_send_sa_query_req: send failed"); } static void ieee802_11_send_sa_query_resp(struct hostapd_data *hapd, const u8 *sa, const u8 *trans_id) { struct sta_info *sta; struct ieee80211_mgmt resp; u8 *end; wpa_printf(MSG_DEBUG, "IEEE 802.11: Received SA Query Request from " MACSTR, MAC2STR(sa)); wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID", trans_id, WLAN_SA_QUERY_TR_ID_LEN); sta = ap_get_sta(hapd, sa); if (sta == NULL || !(sta->flags & WLAN_STA_ASSOC)) { wpa_printf(MSG_DEBUG, "IEEE 802.11: Ignore SA Query Request " "from unassociated STA " MACSTR, MAC2STR(sa)); return; } wpa_printf(MSG_DEBUG, "IEEE 802.11: Sending SA Query Response to " MACSTR, MAC2STR(sa)); os_memset(&resp, 0, sizeof(resp)); resp.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_ACTION); os_memcpy(resp.da, sa, ETH_ALEN); os_memcpy(resp.sa, hapd->own_addr, ETH_ALEN); os_memcpy(resp.bssid, hapd->own_addr, ETH_ALEN); resp.u.action.category = WLAN_ACTION_SA_QUERY; resp.u.action.u.sa_query_req.action = WLAN_SA_QUERY_RESPONSE; os_memcpy(resp.u.action.u.sa_query_req.trans_id, trans_id, WLAN_SA_QUERY_TR_ID_LEN); end = resp.u.action.u.sa_query_req.trans_id + WLAN_SA_QUERY_TR_ID_LEN; if (hostapd_drv_send_mlme(hapd, &resp, end - (u8 *) &resp, 0) < 0) wpa_printf(MSG_INFO, "ieee80211_mgmt_sa_query_request: send failed"); } void ieee802_11_sa_query_action(struct hostapd_data *hapd, const u8 *sa, const u8 action_type, const u8 *trans_id) { struct sta_info *sta; int i; if (action_type == WLAN_SA_QUERY_REQUEST) { ieee802_11_send_sa_query_resp(hapd, sa, trans_id); return; } if (action_type != WLAN_SA_QUERY_RESPONSE) { wpa_printf(MSG_DEBUG, "IEEE 802.11: Unexpected SA Query " "Action %d", action_type); return; } wpa_printf(MSG_DEBUG, "IEEE 802.11: Received SA Query Response from " MACSTR, MAC2STR(sa)); wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID", trans_id, WLAN_SA_QUERY_TR_ID_LEN); /* MLME-SAQuery.confirm */ sta = ap_get_sta(hapd, sa); if (sta == NULL || sta->sa_query_trans_id == NULL) { wpa_printf(MSG_DEBUG, "IEEE 802.11: No matching STA with " "pending SA Query request found"); return; } for (i = 0; i < sta->sa_query_count; i++) { if (os_memcmp(sta->sa_query_trans_id + i * WLAN_SA_QUERY_TR_ID_LEN, trans_id, WLAN_SA_QUERY_TR_ID_LEN) == 0) break; } if (i >= sta->sa_query_count) { wpa_printf(MSG_DEBUG, "IEEE 802.11: No matching SA Query " "transaction identifier found"); return; } hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "Reply to pending SA Query received"); ap_sta_stop_sa_query(hapd, sta); } #endif /* CONFIG_IEEE80211W */ static void hostapd_ext_capab_byte(struct hostapd_data *hapd, u8 *pos, int idx) { *pos = 0x00; switch (idx) { case 0: /* Bits 0-7 */ break; case 1: /* Bits 8-15 */ break; case 2: /* Bits 16-23 */ if (hapd->conf->wnm_sleep_mode) *pos |= 0x02; /* Bit 17 - WNM-Sleep Mode */ if (hapd->conf->bss_transition) *pos |= 0x08; /* Bit 19 - BSS Transition */ break; case 3: /* Bits 24-31 */ #ifdef CONFIG_WNM *pos |= 0x02; /* Bit 25 - SSID List */ #endif /* CONFIG_WNM */ if (hapd->conf->time_advertisement == 2) *pos |= 0x08; /* Bit 27 - UTC TSF Offset */ if (hapd->conf->interworking) *pos |= 0x80; /* Bit 31 - Interworking */ break; case 4: /* Bits 32-39 */ if (hapd->conf->qos_map_set_len) *pos |= 0x01; /* Bit 32 - QoS Map */ if (hapd->conf->tdls & TDLS_PROHIBIT) *pos |= 0x40; /* Bit 38 - TDLS Prohibited */ if (hapd->conf->tdls & TDLS_PROHIBIT_CHAN_SWITCH) { /* Bit 39 - TDLS Channel Switching Prohibited */ *pos |= 0x80; } break; case 5: /* Bits 40-47 */ break; case 6: /* Bits 48-55 */ if (hapd->conf->ssid.utf8_ssid) *pos |= 0x01; /* Bit 48 - UTF-8 SSID */ break; } } u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid) { u8 *pos = eid; u8 len = 0, i; if (hapd->conf->tdls & (TDLS_PROHIBIT | TDLS_PROHIBIT_CHAN_SWITCH)) len = 5; if (len < 4 && hapd->conf->interworking) len = 4; if (len < 3 && hapd->conf->wnm_sleep_mode) len = 3; if (len < 7 && hapd->conf->ssid.utf8_ssid) len = 7; #ifdef CONFIG_WNM if (len < 4) len = 4; #endif /* CONFIG_WNM */ if (len < hapd->iface->extended_capa_len) len = hapd->iface->extended_capa_len; if (len == 0) return eid; *pos++ = WLAN_EID_EXT_CAPAB; *pos++ = len; for (i = 0; i < len; i++, pos++) { hostapd_ext_capab_byte(hapd, pos, i); if (i < hapd->iface->extended_capa_len) { *pos &= ~hapd->iface->extended_capa_mask[i]; *pos |= hapd->iface->extended_capa[i]; } } while (len > 0 && eid[1 + len] == 0) { len--; eid[1] = len; } if (len == 0) return eid; return eid + 2 + len; } u8 * hostapd_eid_qos_map_set(struct hostapd_data *hapd, u8 *eid) { u8 *pos = eid; u8 len = hapd->conf->qos_map_set_len; if (!len) return eid; *pos++ = WLAN_EID_QOS_MAP_SET; *pos++ = len; os_memcpy(pos, hapd->conf->qos_map_set, len); pos += len; return pos; } u8 * hostapd_eid_interworking(struct hostapd_data *hapd, u8 *eid) { u8 *pos = eid; #ifdef CONFIG_INTERWORKING u8 *len; if (!hapd->conf->interworking) return eid; *pos++ = WLAN_EID_INTERWORKING; len = pos++; *pos = hapd->conf->access_network_type; if (hapd->conf->internet) *pos |= INTERWORKING_ANO_INTERNET; if (hapd->conf->asra) *pos |= INTERWORKING_ANO_ASRA; if (hapd->conf->esr) *pos |= INTERWORKING_ANO_ESR; if (hapd->conf->uesa) *pos |= INTERWORKING_ANO_UESA; pos++; if (hapd->conf->venue_info_set) { *pos++ = hapd->conf->venue_group; *pos++ = hapd->conf->venue_type; } if (!is_zero_ether_addr(hapd->conf->hessid)) { os_memcpy(pos, hapd->conf->hessid, ETH_ALEN); pos += ETH_ALEN; } *len = pos - len - 1; #endif /* CONFIG_INTERWORKING */ return pos; } u8 * hostapd_eid_adv_proto(struct hostapd_data *hapd, u8 *eid) { u8 *pos = eid; #ifdef CONFIG_INTERWORKING /* TODO: Separate configuration for ANQP? */ if (!hapd->conf->interworking) return eid; *pos++ = WLAN_EID_ADV_PROTO; *pos++ = 2; *pos++ = 0x7F; /* Query Response Length Limit | PAME-BI */ *pos++ = ACCESS_NETWORK_QUERY_PROTOCOL; #endif /* CONFIG_INTERWORKING */ return pos; } u8 * hostapd_eid_roaming_consortium(struct hostapd_data *hapd, u8 *eid) { u8 *pos = eid; #ifdef CONFIG_INTERWORKING u8 *len; unsigned int i, count; if (!hapd->conf->interworking || hapd->conf->roaming_consortium == NULL || hapd->conf->roaming_consortium_count == 0) return eid; *pos++ = WLAN_EID_ROAMING_CONSORTIUM; len = pos++; /* Number of ANQP OIs (in addition to the max 3 listed here) */ if (hapd->conf->roaming_consortium_count > 3 + 255) *pos++ = 255; else if (hapd->conf->roaming_consortium_count > 3) *pos++ = hapd->conf->roaming_consortium_count - 3; else *pos++ = 0; /* OU #1 and #2 Lengths */ *pos = hapd->conf->roaming_consortium[0].len; if (hapd->conf->roaming_consortium_count > 1) *pos |= hapd->conf->roaming_consortium[1].len << 4; pos++; if (hapd->conf->roaming_consortium_count > 3) count = 3; else count = hapd->conf->roaming_consortium_count; for (i = 0; i < count; i++) { os_memcpy(pos, hapd->conf->roaming_consortium[i].oi, hapd->conf->roaming_consortium[i].len); pos += hapd->conf->roaming_consortium[i].len; } *len = pos - len - 1; #endif /* CONFIG_INTERWORKING */ return pos; } u8 * hostapd_eid_time_adv(struct hostapd_data *hapd, u8 *eid) { if (hapd->conf->time_advertisement != 2) return eid; if (hapd->time_adv == NULL && hostapd_update_time_adv(hapd) < 0) return eid; if (hapd->time_adv == NULL) return eid; os_memcpy(eid, wpabuf_head(hapd->time_adv), wpabuf_len(hapd->time_adv)); eid += wpabuf_len(hapd->time_adv); return eid; } u8 * hostapd_eid_time_zone(struct hostapd_data *hapd, u8 *eid) { size_t len; if (hapd->conf->time_advertisement != 2) return eid; len = os_strlen(hapd->conf->time_zone); *eid++ = WLAN_EID_TIME_ZONE; *eid++ = len; os_memcpy(eid, hapd->conf->time_zone, len); eid += len; return eid; } int hostapd_update_time_adv(struct hostapd_data *hapd) { const int elen = 2 + 1 + 10 + 5 + 1; struct os_time t; struct os_tm tm; u8 *pos; if (hapd->conf->time_advertisement != 2) return 0; if (os_get_time(&t) < 0 || os_gmtime(t.sec, &tm) < 0) return -1; if (!hapd->time_adv) { hapd->time_adv = wpabuf_alloc(elen); if (hapd->time_adv == NULL) return -1; pos = wpabuf_put(hapd->time_adv, elen); } else pos = wpabuf_mhead_u8(hapd->time_adv); *pos++ = WLAN_EID_TIME_ADVERTISEMENT; *pos++ = 1 + 10 + 5 + 1; *pos++ = 2; /* UTC time at which the TSF timer is 0 */ /* Time Value at TSF 0 */ /* FIX: need to calculate this based on the current TSF value */ WPA_PUT_LE16(pos, tm.year); /* Year */ pos += 2; *pos++ = tm.month; /* Month */ *pos++ = tm.day; /* Day of month */ *pos++ = tm.hour; /* Hours */ *pos++ = tm.min; /* Minutes */ *pos++ = tm.sec; /* Seconds */ WPA_PUT_LE16(pos, 0); /* Milliseconds (not used) */ pos += 2; *pos++ = 0; /* Reserved */ /* Time Error */ /* TODO: fill in an estimate on the error */ *pos++ = 0; *pos++ = 0; *pos++ = 0; *pos++ = 0; *pos++ = 0; *pos++ = hapd->time_update_counter++; return 0; } u8 * hostapd_eid_bss_max_idle_period(struct hostapd_data *hapd, u8 *eid) { u8 *pos = eid; #ifdef CONFIG_WNM if (hapd->conf->ap_max_inactivity > 0) { unsigned int val; *pos++ = WLAN_EID_BSS_MAX_IDLE_PERIOD; *pos++ = 3; val = hapd->conf->ap_max_inactivity; if (val > 68000) val = 68000; val *= 1000; val /= 1024; if (val == 0) val = 1; if (val > 65535) val = 65535; WPA_PUT_LE16(pos, val); pos += 2; *pos++ = 0x00; /* TODO: Protected Keep-Alive Required */ } #endif /* CONFIG_WNM */ return pos; } wpa-2.1/src/ap/ieee802_11_vht.c000066400000000000000000000110511227414666700160360ustar00rootroot00000000000000/* * hostapd / IEEE 802.11ac VHT * Copyright (c) 2002-2009, Jouni Malinen * * This program is free software; you can redistribute it and/or modify * it under the terms of BSD license * * See README and COPYING for more details. */ #include "utils/includes.h" #include "utils/common.h" #include "common/ieee802_11_defs.h" #include "hostapd.h" #include "ap_config.h" #include "sta_info.h" #include "beacon.h" #include "ieee802_11.h" u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid) { struct ieee80211_vht_capabilities *cap; u8 *pos = eid; if (!hapd->iconf->ieee80211ac || !hapd->iface->current_mode || hapd->conf->disable_11ac) return eid; *pos++ = WLAN_EID_VHT_CAP; *pos++ = sizeof(*cap); cap = (struct ieee80211_vht_capabilities *) pos; os_memset(cap, 0, sizeof(*cap)); cap->vht_capabilities_info = host_to_le32( hapd->iface->conf->vht_capab); /* Supported MCS set comes from hw */ os_memcpy(&cap->vht_supported_mcs_set, hapd->iface->current_mode->vht_mcs_set, 8); pos += sizeof(*cap); return pos; } u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid) { struct ieee80211_vht_operation *oper; u8 *pos = eid; if (!hapd->iconf->ieee80211ac || hapd->conf->disable_11ac) return eid; *pos++ = WLAN_EID_VHT_OPERATION; *pos++ = sizeof(*oper); oper = (struct ieee80211_vht_operation *) pos; os_memset(oper, 0, sizeof(*oper)); /* * center freq = 5 GHz + (5 * index) * So index 42 gives center freq 5.210 GHz * which is channel 42 in 5G band */ oper->vht_op_info_chan_center_freq_seg0_idx = hapd->iconf->vht_oper_centr_freq_seg0_idx; oper->vht_op_info_chan_center_freq_seg1_idx = hapd->iconf->vht_oper_centr_freq_seg1_idx; oper->vht_op_info_chwidth = hapd->iconf->vht_oper_chwidth; /* VHT Basic MCS set comes from hw */ /* Hard code 1 stream, MCS0-7 is a min Basic VHT MCS rates */ oper->vht_basic_mcs_set = host_to_le16(0xfffc); pos += sizeof(*oper); return pos; } u16 copy_sta_vht_capab(struct hostapd_data *hapd, struct sta_info *sta, const u8 *vht_capab, size_t vht_capab_len) { /* Disable VHT caps for STAs associated to no-VHT BSSes. */ if (!vht_capab || vht_capab_len < sizeof(struct ieee80211_vht_capabilities) || hapd->conf->disable_11ac) { sta->flags &= ~WLAN_STA_VHT; os_free(sta->vht_capabilities); sta->vht_capabilities = NULL; return WLAN_STATUS_SUCCESS; } if (sta->vht_capabilities == NULL) { sta->vht_capabilities = os_zalloc(sizeof(struct ieee80211_vht_capabilities)); if (sta->vht_capabilities == NULL) return WLAN_STATUS_UNSPECIFIED_FAILURE; } sta->flags |= WLAN_STA_VHT; os_memcpy(sta->vht_capabilities, vht_capab, sizeof(struct ieee80211_vht_capabilities)); return WLAN_STATUS_SUCCESS; } void hostapd_get_vht_capab(struct hostapd_data *hapd, struct ieee80211_vht_capabilities *vht_cap, struct ieee80211_vht_capabilities *neg_vht_cap) { u32 cap, own_cap, sym_caps; if (vht_cap == NULL) return; os_memcpy(neg_vht_cap, vht_cap, sizeof(*neg_vht_cap)); cap = le_to_host32(neg_vht_cap->vht_capabilities_info); own_cap = hapd->iconf->vht_capab; /* mask out symmetric VHT capabilities we don't support */ sym_caps = VHT_CAP_SHORT_GI_80 | VHT_CAP_SHORT_GI_160; cap &= ~sym_caps | (own_cap & sym_caps); /* mask out beamformer/beamformee caps if not supported */ if (!(own_cap & VHT_CAP_SU_BEAMFORMER_CAPABLE)) cap &= ~(VHT_CAP_SU_BEAMFORMEE_CAPABLE | VHT_CAP_BEAMFORMEE_STS_MAX); if (!(own_cap & VHT_CAP_SU_BEAMFORMEE_CAPABLE)) cap &= ~(VHT_CAP_SU_BEAMFORMER_CAPABLE | VHT_CAP_SOUNDING_DIMENSION_MAX); if (!(own_cap & VHT_CAP_MU_BEAMFORMER_CAPABLE)) cap &= ~VHT_CAP_MU_BEAMFORMEE_CAPABLE; if (!(own_cap & VHT_CAP_MU_BEAMFORMEE_CAPABLE)) cap &= ~VHT_CAP_MU_BEAMFORMER_CAPABLE; /* mask channel widths we don't support */ switch (own_cap & VHT_CAP_SUPP_CHAN_WIDTH_MASK) { case VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ: break; case VHT_CAP_SUPP_CHAN_WIDTH_160MHZ: if (cap & VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ) { cap &= ~VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ; cap |= VHT_CAP_SUPP_CHAN_WIDTH_160MHZ; } break; default: cap &= ~VHT_CAP_SUPP_CHAN_WIDTH_MASK; break; } if (!(cap & VHT_CAP_SUPP_CHAN_WIDTH_MASK)) cap &= ~VHT_CAP_SHORT_GI_160; /* * if we don't support RX STBC, mask out TX STBC in the STA's HT caps * if we don't support TX STBC, mask out RX STBC in the STA's HT caps */ if (!(own_cap & VHT_CAP_RXSTBC_MASK)) cap &= ~VHT_CAP_TXSTBC; if (!(own_cap & VHT_CAP_TXSTBC)) cap &= ~VHT_CAP_RXSTBC_MASK; neg_vht_cap->vht_capabilities_info = host_to_le32(cap); } wpa-2.1/src/ap/ieee802_1x.c000066400000000000000000001671621227414666700153030ustar00rootroot00000000000000/* * hostapd / IEEE 802.1X-2004 Authenticator * Copyright (c) 2002-2012, 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/eloop.h" #include "crypto/md5.h" #include "crypto/crypto.h" #include "crypto/random.h" #include "common/ieee802_11_defs.h" #include "radius/radius.h" #include "radius/radius_client.h" #include "eap_server/eap.h" #include "eap_common/eap_wsc_common.h" #include "eapol_auth/eapol_auth_sm.h" #include "eapol_auth/eapol_auth_sm_i.h" #include "p2p/p2p.h" #include "hostapd.h" #include "accounting.h" #include "sta_info.h" #include "wpa_auth.h" #include "preauth_auth.h" #include "pmksa_cache_auth.h" #include "ap_config.h" #include "ap_drv_ops.h" #include "wps_hostapd.h" #include "ieee802_1x.h" static void ieee802_1x_finished(struct hostapd_data *hapd, struct sta_info *sta, int success); static void ieee802_1x_send(struct hostapd_data *hapd, struct sta_info *sta, u8 type, const u8 *data, size_t datalen) { u8 *buf; struct ieee802_1x_hdr *xhdr; size_t len; int encrypt = 0; len = sizeof(*xhdr) + datalen; buf = os_zalloc(len); if (buf == NULL) { wpa_printf(MSG_ERROR, "malloc() failed for " "ieee802_1x_send(len=%lu)", (unsigned long) len); return; } xhdr = (struct ieee802_1x_hdr *) buf; xhdr->version = hapd->conf->eapol_version; xhdr->type = type; xhdr->length = host_to_be16(datalen); if (datalen > 0 && data != NULL) os_memcpy(xhdr + 1, data, datalen); if (wpa_auth_pairwise_set(sta->wpa_sm)) encrypt = 1; if (sta->flags & WLAN_STA_PREAUTH) { rsn_preauth_send(hapd, sta, buf, len); } else { hostapd_drv_hapd_send_eapol( hapd, sta->addr, buf, len, encrypt, hostapd_sta_flags_to_drv(sta->flags)); } os_free(buf); } void ieee802_1x_set_sta_authorized(struct hostapd_data *hapd, struct sta_info *sta, int authorized) { int res; if (sta->flags & WLAN_STA_PREAUTH) return; if (authorized) { ap_sta_set_authorized(hapd, sta, 1); res = hostapd_set_authorized(hapd, sta, 1); hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, HOSTAPD_LEVEL_DEBUG, "authorizing port"); } else { ap_sta_set_authorized(hapd, sta, 0); res = hostapd_set_authorized(hapd, sta, 0); hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, HOSTAPD_LEVEL_DEBUG, "unauthorizing port"); } if (res && errno != ENOENT) { wpa_printf(MSG_DEBUG, "Could not set station " MACSTR " flags for kernel driver (errno=%d).", MAC2STR(sta->addr), errno); } if (authorized) { os_get_reltime(&sta->connected_time); accounting_sta_start(hapd, sta); } } static void ieee802_1x_tx_key_one(struct hostapd_data *hapd, struct sta_info *sta, int idx, int broadcast, u8 *key_data, size_t key_len) { u8 *buf, *ekey; struct ieee802_1x_hdr *hdr; struct ieee802_1x_eapol_key *key; size_t len, ekey_len; struct eapol_state_machine *sm = sta->eapol_sm; if (sm == NULL) return; len = sizeof(*key) + key_len; buf = os_zalloc(sizeof(*hdr) + len); if (buf == NULL) return; hdr = (struct ieee802_1x_hdr *) buf; key = (struct ieee802_1x_eapol_key *) (hdr + 1); key->type = EAPOL_KEY_TYPE_RC4; WPA_PUT_BE16(key->key_length, key_len); wpa_get_ntp_timestamp(key->replay_counter); if (random_get_bytes(key->key_iv, sizeof(key->key_iv))) { wpa_printf(MSG_ERROR, "Could not get random numbers"); os_free(buf); return; } key->key_index = idx | (broadcast ? 0 : BIT(7)); if (hapd->conf->eapol_key_index_workaround) { /* According to some information, WinXP Supplicant seems to * interpret bit7 as an indication whether the key is to be * activated, so make it possible to enable workaround that * sets this bit for all keys. */ key->key_index |= BIT(7); } /* Key is encrypted using "Key-IV + MSK[0..31]" as the RC4-key and * MSK[32..63] is used to sign the message. */ if (sm->eap_if->eapKeyData == NULL || sm->eap_if->eapKeyDataLen < 64) { wpa_printf(MSG_ERROR, "No eapKeyData available for encrypting " "and signing EAPOL-Key"); os_free(buf); return; } os_memcpy((u8 *) (key + 1), key_data, key_len); ekey_len = sizeof(key->key_iv) + 32; ekey = os_malloc(ekey_len); if (ekey == NULL) { wpa_printf(MSG_ERROR, "Could not encrypt key"); os_free(buf); return; } os_memcpy(ekey, key->key_iv, sizeof(key->key_iv)); os_memcpy(ekey + sizeof(key->key_iv), sm->eap_if->eapKeyData, 32); rc4_skip(ekey, ekey_len, 0, (u8 *) (key + 1), key_len); os_free(ekey); /* This header is needed here for HMAC-MD5, but it will be regenerated * in ieee802_1x_send() */ hdr->version = hapd->conf->eapol_version; hdr->type = IEEE802_1X_TYPE_EAPOL_KEY; hdr->length = host_to_be16(len); hmac_md5(sm->eap_if->eapKeyData + 32, 32, buf, sizeof(*hdr) + len, key->key_signature); wpa_printf(MSG_DEBUG, "IEEE 802.1X: Sending EAPOL-Key to " MACSTR " (%s index=%d)", MAC2STR(sm->addr), broadcast ? "broadcast" : "unicast", idx); ieee802_1x_send(hapd, sta, IEEE802_1X_TYPE_EAPOL_KEY, (u8 *) key, len); if (sta->eapol_sm) sta->eapol_sm->dot1xAuthEapolFramesTx++; os_free(buf); } void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta) { struct eapol_authenticator *eapol = hapd->eapol_auth; struct eapol_state_machine *sm = sta->eapol_sm; if (sm == NULL || !sm->eap_if->eapKeyData) return; wpa_printf(MSG_DEBUG, "IEEE 802.1X: Sending EAPOL-Key(s) to " MACSTR, MAC2STR(sta->addr)); #ifndef CONFIG_NO_VLAN if (sta->vlan_id > 0 && sta->vlan_id <= MAX_VLAN_ID) { wpa_printf(MSG_ERROR, "Using WEP with vlans is not supported."); return; } #endif /* CONFIG_NO_VLAN */ if (eapol->default_wep_key) { ieee802_1x_tx_key_one(hapd, sta, eapol->default_wep_key_idx, 1, eapol->default_wep_key, hapd->conf->default_wep_key_len); } if (hapd->conf->individual_wep_key_len > 0) { u8 *ikey; ikey = os_malloc(hapd->conf->individual_wep_key_len); if (ikey == NULL || random_get_bytes(ikey, hapd->conf->individual_wep_key_len)) { wpa_printf(MSG_ERROR, "Could not generate random " "individual WEP key."); os_free(ikey); return; } wpa_hexdump_key(MSG_DEBUG, "Individual WEP key", ikey, hapd->conf->individual_wep_key_len); ieee802_1x_tx_key_one(hapd, sta, 0, 0, ikey, hapd->conf->individual_wep_key_len); /* TODO: set encryption in TX callback, i.e., only after STA * has ACKed EAPOL-Key frame */ if (hostapd_drv_set_key(hapd->conf->iface, hapd, WPA_ALG_WEP, sta->addr, 0, 1, NULL, 0, ikey, hapd->conf->individual_wep_key_len)) { wpa_printf(MSG_ERROR, "Could not set individual WEP " "encryption."); } os_free(ikey); } } const char *radius_mode_txt(struct hostapd_data *hapd) { switch (hapd->iface->conf->hw_mode) { case HOSTAPD_MODE_IEEE80211AD: return "802.11ad"; case HOSTAPD_MODE_IEEE80211A: return "802.11a"; case HOSTAPD_MODE_IEEE80211G: return "802.11g"; case HOSTAPD_MODE_IEEE80211B: default: return "802.11b"; } } int radius_sta_rate(struct hostapd_data *hapd, struct sta_info *sta) { int i; u8 rate = 0; for (i = 0; i < sta->supported_rates_len; i++) if ((sta->supported_rates[i] & 0x7f) > rate) rate = sta->supported_rates[i] & 0x7f; return rate; } #ifndef CONFIG_NO_RADIUS static void ieee802_1x_learn_identity(struct hostapd_data *hapd, struct eapol_state_machine *sm, const u8 *eap, size_t len) { const u8 *identity; size_t identity_len; if (len <= sizeof(struct eap_hdr) || eap[sizeof(struct eap_hdr)] != EAP_TYPE_IDENTITY) return; identity = eap_get_identity(sm->eap, &identity_len); if (identity == NULL) return; /* Save station identity for future RADIUS packets */ os_free(sm->identity); sm->identity = (u8 *) dup_binstr(identity, identity_len); if (sm->identity == NULL) { sm->identity_len = 0; return; } sm->identity_len = identity_len; hostapd_logger(hapd, sm->addr, HOSTAPD_MODULE_IEEE8021X, HOSTAPD_LEVEL_DEBUG, "STA identity '%s'", sm->identity); sm->dot1xAuthEapolRespIdFramesRx++; } static int add_common_radius_sta_attr(struct hostapd_data *hapd, struct hostapd_radius_attr *req_attr, struct sta_info *sta, struct radius_msg *msg) { char buf[128]; if (!hostapd_config_get_radius_attr(req_attr, RADIUS_ATTR_NAS_PORT) && !radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT, sta->aid)) { wpa_printf(MSG_ERROR, "Could not add NAS-Port"); return -1; } os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT, MAC2STR(sta->addr)); buf[sizeof(buf) - 1] = '\0'; if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID, (u8 *) buf, os_strlen(buf))) { wpa_printf(MSG_ERROR, "Could not add Calling-Station-Id"); return -1; } if (sta->flags & WLAN_STA_PREAUTH) { os_strlcpy(buf, "IEEE 802.11i Pre-Authentication", sizeof(buf)); } else { os_snprintf(buf, sizeof(buf), "CONNECT %d%sMbps %s", radius_sta_rate(hapd, sta) / 2, (radius_sta_rate(hapd, sta) & 1) ? ".5" : "", radius_mode_txt(hapd)); buf[sizeof(buf) - 1] = '\0'; } if (!hostapd_config_get_radius_attr(req_attr, RADIUS_ATTR_CONNECT_INFO) && !radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO, (u8 *) buf, os_strlen(buf))) { wpa_printf(MSG_ERROR, "Could not add Connect-Info"); return -1; } if (sta->acct_session_id_hi || sta->acct_session_id_lo) { os_snprintf(buf, sizeof(buf), "%08X-%08X", sta->acct_session_id_hi, sta->acct_session_id_lo); if (!radius_msg_add_attr(msg, RADIUS_ATTR_ACCT_SESSION_ID, (u8 *) buf, os_strlen(buf))) { wpa_printf(MSG_ERROR, "Could not add Acct-Session-Id"); return -1; } } return 0; } int add_common_radius_attr(struct hostapd_data *hapd, struct hostapd_radius_attr *req_attr, struct sta_info *sta, struct radius_msg *msg) { char buf[128]; struct hostapd_radius_attr *attr; if (!hostapd_config_get_radius_attr(req_attr, RADIUS_ATTR_NAS_IP_ADDRESS) && hapd->conf->own_ip_addr.af == AF_INET && !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IP_ADDRESS, (u8 *) &hapd->conf->own_ip_addr.u.v4, 4)) { wpa_printf(MSG_ERROR, "Could not add NAS-IP-Address"); return -1; } #ifdef CONFIG_IPV6 if (!hostapd_config_get_radius_attr(req_attr, RADIUS_ATTR_NAS_IPV6_ADDRESS) && hapd->conf->own_ip_addr.af == AF_INET6 && !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IPV6_ADDRESS, (u8 *) &hapd->conf->own_ip_addr.u.v6, 16)) { wpa_printf(MSG_ERROR, "Could not add NAS-IPv6-Address"); return -1; } #endif /* CONFIG_IPV6 */ if (!hostapd_config_get_radius_attr(req_attr, RADIUS_ATTR_NAS_IDENTIFIER) && hapd->conf->nas_identifier && !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IDENTIFIER, (u8 *) hapd->conf->nas_identifier, os_strlen(hapd->conf->nas_identifier))) { wpa_printf(MSG_ERROR, "Could not add NAS-Identifier"); return -1; } os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":%s", MAC2STR(hapd->own_addr), wpa_ssid_txt(hapd->conf->ssid.ssid, hapd->conf->ssid.ssid_len)); buf[sizeof(buf) - 1] = '\0'; if (!hostapd_config_get_radius_attr(req_attr, RADIUS_ATTR_CALLED_STATION_ID) && !radius_msg_add_attr(msg, RADIUS_ATTR_CALLED_STATION_ID, (u8 *) buf, os_strlen(buf))) { wpa_printf(MSG_ERROR, "Could not add Called-Station-Id"); return -1; } if (!hostapd_config_get_radius_attr(req_attr, RADIUS_ATTR_NAS_PORT_TYPE) && !radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT_TYPE, RADIUS_NAS_PORT_TYPE_IEEE_802_11)) { wpa_printf(MSG_ERROR, "Could not add NAS-Port-Type"); return -1; } if (sta && add_common_radius_sta_attr(hapd, req_attr, sta, msg) < 0) return -1; for (attr = req_attr; attr; attr = attr->next) { if (!radius_msg_add_attr(msg, attr->type, wpabuf_head(attr->val), wpabuf_len(attr->val))) { wpa_printf(MSG_ERROR, "Could not add RADIUS " "attribute"); return -1; } } return 0; } static void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd, struct sta_info *sta, const u8 *eap, size_t len) { struct radius_msg *msg; struct eapol_state_machine *sm = sta->eapol_sm; if (sm == NULL) return; ieee802_1x_learn_identity(hapd, sm, eap, len); wpa_printf(MSG_DEBUG, "Encapsulating EAP message into a RADIUS " "packet"); sm->radius_identifier = radius_client_get_id(hapd->radius); msg = radius_msg_new(RADIUS_CODE_ACCESS_REQUEST, sm->radius_identifier); if (msg == NULL) { wpa_printf(MSG_INFO, "Could not create new RADIUS packet"); return; } radius_msg_make_authenticator(msg, (u8 *) sta, sizeof(*sta)); if (sm->identity && !radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME, sm->identity, sm->identity_len)) { wpa_printf(MSG_INFO, "Could not add User-Name"); goto fail; } if (add_common_radius_attr(hapd, hapd->conf->radius_auth_req_attr, sta, msg) < 0) goto fail; /* TODO: should probably check MTU from driver config; 2304 is max for * IEEE 802.11, but use 1400 to avoid problems with too large packets */ if (!hostapd_config_get_radius_attr(hapd->conf->radius_auth_req_attr, RADIUS_ATTR_FRAMED_MTU) && !radius_msg_add_attr_int32(msg, RADIUS_ATTR_FRAMED_MTU, 1400)) { wpa_printf(MSG_INFO, "Could not add Framed-MTU"); goto fail; } if (eap && !radius_msg_add_eap(msg, eap, len)) { wpa_printf(MSG_INFO, "Could not add EAP-Message"); goto fail; } /* State attribute must be copied if and only if this packet is * Access-Request reply to the previous Access-Challenge */ if (sm->last_recv_radius && radius_msg_get_hdr(sm->last_recv_radius)->code == RADIUS_CODE_ACCESS_CHALLENGE) { int res = radius_msg_copy_attr(msg, sm->last_recv_radius, RADIUS_ATTR_STATE); if (res < 0) { wpa_printf(MSG_INFO, "Could not copy State attribute from previous Access-Challenge"); goto fail; } if (res > 0) { wpa_printf(MSG_DEBUG, "Copied RADIUS State Attribute"); } } if (hapd->conf->radius_request_cui) { const u8 *cui; size_t cui_len; /* Add previously learned CUI or nul CUI to request CUI */ if (sm->radius_cui) { cui = wpabuf_head(sm->radius_cui); cui_len = wpabuf_len(sm->radius_cui); } else { cui = (const u8 *) "\0"; cui_len = 1; } if (!radius_msg_add_attr(msg, RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, cui, cui_len)) { wpa_printf(MSG_ERROR, "Could not add CUI"); goto fail; } } if (radius_client_send(hapd->radius, msg, RADIUS_AUTH, sta->addr) < 0) goto fail; return; fail: radius_msg_free(msg); } #endif /* CONFIG_NO_RADIUS */ static void handle_eap_response(struct hostapd_data *hapd, struct sta_info *sta, struct eap_hdr *eap, size_t len) { u8 type, *data; struct eapol_state_machine *sm = sta->eapol_sm; if (sm == NULL) return; data = (u8 *) (eap + 1); if (len < sizeof(*eap) + 1) { wpa_printf(MSG_INFO, "handle_eap_response: too short response data"); return; } sm->eap_type_supp = type = data[0]; hostapd_logger(hapd, sm->addr, HOSTAPD_MODULE_IEEE8021X, HOSTAPD_LEVEL_DEBUG, "received EAP packet (code=%d " "id=%d len=%d) from STA: EAP Response-%s (%d)", eap->code, eap->identifier, be_to_host16(eap->length), eap_server_get_name(0, type), type); sm->dot1xAuthEapolRespFramesRx++; wpabuf_free(sm->eap_if->eapRespData); sm->eap_if->eapRespData = wpabuf_alloc_copy(eap, len); sm->eapolEap = TRUE; } /* Process incoming EAP packet from Supplicant */ static void handle_eap(struct hostapd_data *hapd, struct sta_info *sta, u8 *buf, size_t len) { struct eap_hdr *eap; u16 eap_len; if (len < sizeof(*eap)) { wpa_printf(MSG_INFO, " too short EAP packet"); return; } eap = (struct eap_hdr *) buf; eap_len = be_to_host16(eap->length); wpa_printf(MSG_DEBUG, "EAP: code=%d identifier=%d length=%d", eap->code, eap->identifier, eap_len); if (eap_len < sizeof(*eap)) { wpa_printf(MSG_DEBUG, " Invalid EAP length"); return; } else if (eap_len > len) { wpa_printf(MSG_DEBUG, " Too short frame to contain this EAP " "packet"); return; } else if (eap_len < len) { wpa_printf(MSG_DEBUG, " Ignoring %lu extra bytes after EAP " "packet", (unsigned long) len - eap_len); } switch (eap->code) { case EAP_CODE_REQUEST: wpa_printf(MSG_DEBUG, " (request)"); return; case EAP_CODE_RESPONSE: wpa_printf(MSG_DEBUG, " (response)"); handle_eap_response(hapd, sta, eap, eap_len); break; case EAP_CODE_SUCCESS: wpa_printf(MSG_DEBUG, " (success)"); return; case EAP_CODE_FAILURE: wpa_printf(MSG_DEBUG, " (failure)"); return; default: wpa_printf(MSG_DEBUG, " (unknown code)"); return; } } static struct eapol_state_machine * ieee802_1x_alloc_eapol_sm(struct hostapd_data *hapd, struct sta_info *sta) { int flags = 0; if (sta->flags & WLAN_STA_PREAUTH) flags |= EAPOL_SM_PREAUTH; if (sta->wpa_sm) { flags |= EAPOL_SM_USES_WPA; if (wpa_auth_sta_get_pmksa(sta->wpa_sm)) flags |= EAPOL_SM_FROM_PMKSA_CACHE; } return eapol_auth_alloc(hapd->eapol_auth, sta->addr, flags, sta->wps_ie, sta->p2p_ie, sta, sta->identity, sta->radius_cui); } /** * ieee802_1x_receive - Process the EAPOL frames from the Supplicant * @hapd: hostapd BSS data * @sa: Source address (sender of the EAPOL frame) * @buf: EAPOL frame * @len: Length of buf in octets * * This function is called for each incoming EAPOL frame from the interface */ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, size_t len) { struct sta_info *sta; struct ieee802_1x_hdr *hdr; struct ieee802_1x_eapol_key *key; u16 datalen; struct rsn_pmksa_cache_entry *pmksa; int key_mgmt; if (!hapd->conf->ieee802_1x && !hapd->conf->wpa && !hapd->conf->wps_state) return; wpa_printf(MSG_DEBUG, "IEEE 802.1X: %lu bytes from " MACSTR, (unsigned long) len, MAC2STR(sa)); sta = ap_get_sta(hapd, sa); if (!sta || (!(sta->flags & (WLAN_STA_ASSOC | WLAN_STA_PREAUTH)) && !(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_WIRED))) { wpa_printf(MSG_DEBUG, "IEEE 802.1X data frame from not " "associated/Pre-authenticating STA"); return; } if (len < sizeof(*hdr)) { wpa_printf(MSG_INFO, " too short IEEE 802.1X packet"); return; } hdr = (struct ieee802_1x_hdr *) buf; datalen = be_to_host16(hdr->length); wpa_printf(MSG_DEBUG, " IEEE 802.1X: version=%d type=%d length=%d", hdr->version, hdr->type, datalen); if (len - sizeof(*hdr) < datalen) { wpa_printf(MSG_INFO, " frame too short for this IEEE 802.1X packet"); if (sta->eapol_sm) sta->eapol_sm->dot1xAuthEapLengthErrorFramesRx++; return; } if (len - sizeof(*hdr) > datalen) { wpa_printf(MSG_DEBUG, " ignoring %lu extra octets after " "IEEE 802.1X packet", (unsigned long) len - sizeof(*hdr) - datalen); } if (sta->eapol_sm) { sta->eapol_sm->dot1xAuthLastEapolFrameVersion = hdr->version; sta->eapol_sm->dot1xAuthEapolFramesRx++; } key = (struct ieee802_1x_eapol_key *) (hdr + 1); if (datalen >= sizeof(struct ieee802_1x_eapol_key) && hdr->type == IEEE802_1X_TYPE_EAPOL_KEY && (key->type == EAPOL_KEY_TYPE_WPA || key->type == EAPOL_KEY_TYPE_RSN)) { wpa_receive(hapd->wpa_auth, sta->wpa_sm, (u8 *) hdr, sizeof(*hdr) + datalen); return; } if (!hapd->conf->ieee802_1x && !(sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS))) { wpa_printf(MSG_DEBUG, "IEEE 802.1X: Ignore EAPOL message - " "802.1X not enabled and WPS not used"); return; } key_mgmt = wpa_auth_sta_key_mgmt(sta->wpa_sm); if (key_mgmt != -1 && wpa_key_mgmt_wpa_psk(key_mgmt)) { wpa_printf(MSG_DEBUG, "IEEE 802.1X: Ignore EAPOL message - " "STA is using PSK"); return; } if (!sta->eapol_sm) { sta->eapol_sm = ieee802_1x_alloc_eapol_sm(hapd, sta); if (!sta->eapol_sm) return; #ifdef CONFIG_WPS if (!hapd->conf->ieee802_1x) { u32 wflags = sta->flags & (WLAN_STA_WPS | WLAN_STA_WPS2 | WLAN_STA_MAYBE_WPS); if (wflags == WLAN_STA_MAYBE_WPS || wflags == (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS)) { /* * Delay EAPOL frame transmission until a * possible WPS STA initiates the handshake * with EAPOL-Start. Only allow the wait to be * skipped if the STA is known to support WPS * 2.0. */ wpa_printf(MSG_DEBUG, "WPS: Do not start " "EAPOL until EAPOL-Start is " "received"); sta->eapol_sm->flags |= EAPOL_SM_WAIT_START; } } #endif /* CONFIG_WPS */ sta->eapol_sm->eap_if->portEnabled = TRUE; } /* since we support version 1, we can ignore version field and proceed * as specified in version 1 standard [IEEE Std 802.1X-2001, 7.5.5] */ /* TODO: actually, we are not version 1 anymore.. However, Version 2 * does not change frame contents, so should be ok to process frames * more or less identically. Some changes might be needed for * verification of fields. */ switch (hdr->type) { case IEEE802_1X_TYPE_EAP_PACKET: handle_eap(hapd, sta, (u8 *) (hdr + 1), datalen); break; case IEEE802_1X_TYPE_EAPOL_START: hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, HOSTAPD_LEVEL_DEBUG, "received EAPOL-Start " "from STA"); sta->eapol_sm->flags &= ~EAPOL_SM_WAIT_START; pmksa = wpa_auth_sta_get_pmksa(sta->wpa_sm); if (pmksa) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, HOSTAPD_LEVEL_DEBUG, "cached PMKSA " "available - ignore it since " "STA sent EAPOL-Start"); wpa_auth_sta_clear_pmksa(sta->wpa_sm, pmksa); } sta->eapol_sm->eapolStart = TRUE; sta->eapol_sm->dot1xAuthEapolStartFramesRx++; eap_server_clear_identity(sta->eapol_sm->eap); wpa_auth_sm_event(sta->wpa_sm, WPA_REAUTH_EAPOL); break; case IEEE802_1X_TYPE_EAPOL_LOGOFF: hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, HOSTAPD_LEVEL_DEBUG, "received EAPOL-Logoff " "from STA"); sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST; accounting_sta_stop(hapd, sta); sta->eapol_sm->eapolLogoff = TRUE; sta->eapol_sm->dot1xAuthEapolLogoffFramesRx++; eap_server_clear_identity(sta->eapol_sm->eap); break; case IEEE802_1X_TYPE_EAPOL_KEY: wpa_printf(MSG_DEBUG, " EAPOL-Key"); if (!ap_sta_is_authorized(sta)) { wpa_printf(MSG_DEBUG, " Dropped key data from " "unauthorized Supplicant"); break; } break; case IEEE802_1X_TYPE_EAPOL_ENCAPSULATED_ASF_ALERT: wpa_printf(MSG_DEBUG, " EAPOL-Encapsulated-ASF-Alert"); /* TODO: implement support for this; show data */ break; default: wpa_printf(MSG_DEBUG, " unknown IEEE 802.1X packet type"); sta->eapol_sm->dot1xAuthInvalidEapolFramesRx++; break; } eapol_auth_step(sta->eapol_sm); } /** * ieee802_1x_new_station - Start IEEE 802.1X authentication * @hapd: hostapd BSS data * @sta: The station * * This function is called to start IEEE 802.1X authentication when a new * station completes IEEE 802.11 association. */ void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta) { struct rsn_pmksa_cache_entry *pmksa; int reassoc = 1; int force_1x = 0; int key_mgmt; #ifdef CONFIG_WPS if (hapd->conf->wps_state && hapd->conf->wpa && (sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS))) { /* * Need to enable IEEE 802.1X/EAPOL state machines for possible * WPS handshake even if IEEE 802.1X/EAPOL is not used for * authentication in this BSS. */ force_1x = 1; } #endif /* CONFIG_WPS */ if (!force_1x && !hapd->conf->ieee802_1x) { wpa_printf(MSG_DEBUG, "IEEE 802.1X: Ignore STA - " "802.1X not enabled or forced for WPS"); /* * Clear any possible EAPOL authenticator state to support * reassociation change from WPS to PSK. */ ieee802_1x_free_station(sta); return; } key_mgmt = wpa_auth_sta_key_mgmt(sta->wpa_sm); if (key_mgmt != -1 && wpa_key_mgmt_wpa_psk(key_mgmt)) { wpa_printf(MSG_DEBUG, "IEEE 802.1X: Ignore STA - using PSK"); /* * Clear any possible EAPOL authenticator state to support * reassociation change from WPA-EAP to PSK. */ ieee802_1x_free_station(sta); return; } if (sta->eapol_sm == NULL) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, HOSTAPD_LEVEL_DEBUG, "start authentication"); sta->eapol_sm = ieee802_1x_alloc_eapol_sm(hapd, sta); if (sta->eapol_sm == NULL) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, HOSTAPD_LEVEL_INFO, "failed to allocate state machine"); return; } reassoc = 0; } #ifdef CONFIG_WPS sta->eapol_sm->flags &= ~EAPOL_SM_WAIT_START; if (!hapd->conf->ieee802_1x && !(sta->flags & WLAN_STA_WPS2)) { /* * Delay EAPOL frame transmission until a possible WPS STA * initiates the handshake with EAPOL-Start. Only allow the * wait to be skipped if the STA is known to support WPS 2.0. */ wpa_printf(MSG_DEBUG, "WPS: Do not start EAPOL until " "EAPOL-Start is received"); sta->eapol_sm->flags |= EAPOL_SM_WAIT_START; } #endif /* CONFIG_WPS */ sta->eapol_sm->eap_if->portEnabled = TRUE; #ifdef CONFIG_IEEE80211R if (sta->auth_alg == WLAN_AUTH_FT) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, HOSTAPD_LEVEL_DEBUG, "PMK from FT - skip IEEE 802.1X/EAP"); /* Setup EAPOL state machines to already authenticated state * because of existing FT information from R0KH. */ sta->eapol_sm->keyRun = TRUE; sta->eapol_sm->eap_if->eapKeyAvailable = TRUE; sta->eapol_sm->auth_pae_state = AUTH_PAE_AUTHENTICATING; sta->eapol_sm->be_auth_state = BE_AUTH_SUCCESS; sta->eapol_sm->authSuccess = TRUE; sta->eapol_sm->authFail = FALSE; if (sta->eapol_sm->eap) eap_sm_notify_cached(sta->eapol_sm->eap); /* TODO: get vlan_id from R0KH using RRB message */ return; } #endif /* CONFIG_IEEE80211R */ pmksa = wpa_auth_sta_get_pmksa(sta->wpa_sm); if (pmksa) { int old_vlanid; hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, HOSTAPD_LEVEL_DEBUG, "PMK from PMKSA cache - skip IEEE 802.1X/EAP"); /* Setup EAPOL state machines to already authenticated state * because of existing PMKSA information in the cache. */ sta->eapol_sm->keyRun = TRUE; sta->eapol_sm->eap_if->eapKeyAvailable = TRUE; sta->eapol_sm->auth_pae_state = AUTH_PAE_AUTHENTICATING; sta->eapol_sm->be_auth_state = BE_AUTH_SUCCESS; sta->eapol_sm->authSuccess = TRUE; sta->eapol_sm->authFail = FALSE; if (sta->eapol_sm->eap) eap_sm_notify_cached(sta->eapol_sm->eap); old_vlanid = sta->vlan_id; pmksa_cache_to_eapol_data(pmksa, sta->eapol_sm); if (sta->ssid->dynamic_vlan == DYNAMIC_VLAN_DISABLED) sta->vlan_id = 0; ap_sta_bind_vlan(hapd, sta, old_vlanid); } else { if (reassoc) { /* * Force EAPOL state machines to start * re-authentication without having to wait for the * Supplicant to send EAPOL-Start. */ sta->eapol_sm->reAuthenticate = TRUE; } eapol_auth_step(sta->eapol_sm); } } void ieee802_1x_free_station(struct sta_info *sta) { struct eapol_state_machine *sm = sta->eapol_sm; if (sm == NULL) return; sta->eapol_sm = NULL; #ifndef CONFIG_NO_RADIUS radius_msg_free(sm->last_recv_radius); radius_free_class(&sm->radius_class); wpabuf_free(sm->radius_cui); #endif /* CONFIG_NO_RADIUS */ os_free(sm->identity); eapol_auth_free(sm); } #ifndef CONFIG_NO_RADIUS static void ieee802_1x_decapsulate_radius(struct hostapd_data *hapd, struct sta_info *sta) { struct wpabuf *eap; const struct eap_hdr *hdr; int eap_type = -1; char buf[64]; struct radius_msg *msg; struct eapol_state_machine *sm = sta->eapol_sm; if (sm == NULL || sm->last_recv_radius == NULL) { if (sm) sm->eap_if->aaaEapNoReq = TRUE; return; } msg = sm->last_recv_radius; eap = radius_msg_get_eap(msg); if (eap == NULL) { /* RFC 3579, Chap. 2.6.3: * RADIUS server SHOULD NOT send Access-Reject/no EAP-Message * attribute */ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, HOSTAPD_LEVEL_WARNING, "could not extract " "EAP-Message from RADIUS message"); sm->eap_if->aaaEapNoReq = TRUE; return; } if (wpabuf_len(eap) < sizeof(*hdr)) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, HOSTAPD_LEVEL_WARNING, "too short EAP packet " "received from authentication server"); wpabuf_free(eap); sm->eap_if->aaaEapNoReq = TRUE; return; } if (wpabuf_len(eap) > sizeof(*hdr)) eap_type = (wpabuf_head_u8(eap))[sizeof(*hdr)]; hdr = wpabuf_head(eap); switch (hdr->code) { case EAP_CODE_REQUEST: if (eap_type >= 0) sm->eap_type_authsrv = eap_type; os_snprintf(buf, sizeof(buf), "EAP-Request-%s (%d)", eap_type >= 0 ? eap_server_get_name(0, eap_type) : "??", eap_type); break; case EAP_CODE_RESPONSE: os_snprintf(buf, sizeof(buf), "EAP Response-%s (%d)", eap_type >= 0 ? eap_server_get_name(0, eap_type) : "??", eap_type); break; case EAP_CODE_SUCCESS: os_strlcpy(buf, "EAP Success", sizeof(buf)); break; case EAP_CODE_FAILURE: os_strlcpy(buf, "EAP Failure", sizeof(buf)); break; default: os_strlcpy(buf, "unknown EAP code", sizeof(buf)); break; } buf[sizeof(buf) - 1] = '\0'; hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, HOSTAPD_LEVEL_DEBUG, "decapsulated EAP packet (code=%d " "id=%d len=%d) from RADIUS server: %s", hdr->code, hdr->identifier, be_to_host16(hdr->length), buf); sm->eap_if->aaaEapReq = TRUE; wpabuf_free(sm->eap_if->aaaEapReqData); sm->eap_if->aaaEapReqData = eap; } static void ieee802_1x_get_keys(struct hostapd_data *hapd, struct sta_info *sta, struct radius_msg *msg, struct radius_msg *req, const u8 *shared_secret, size_t shared_secret_len) { struct radius_ms_mppe_keys *keys; struct eapol_state_machine *sm = sta->eapol_sm; if (sm == NULL) return; keys = radius_msg_get_ms_keys(msg, req, shared_secret, shared_secret_len); if (keys && keys->send && keys->recv) { size_t len = keys->send_len + keys->recv_len; wpa_hexdump_key(MSG_DEBUG, "MS-MPPE-Send-Key", keys->send, keys->send_len); wpa_hexdump_key(MSG_DEBUG, "MS-MPPE-Recv-Key", keys->recv, keys->recv_len); os_free(sm->eap_if->aaaEapKeyData); sm->eap_if->aaaEapKeyData = os_malloc(len); if (sm->eap_if->aaaEapKeyData) { os_memcpy(sm->eap_if->aaaEapKeyData, keys->recv, keys->recv_len); os_memcpy(sm->eap_if->aaaEapKeyData + keys->recv_len, keys->send, keys->send_len); sm->eap_if->aaaEapKeyDataLen = len; sm->eap_if->aaaEapKeyAvailable = TRUE; } } if (keys) { os_free(keys->send); os_free(keys->recv); os_free(keys); } } static void ieee802_1x_store_radius_class(struct hostapd_data *hapd, struct sta_info *sta, struct radius_msg *msg) { u8 *class; size_t class_len; struct eapol_state_machine *sm = sta->eapol_sm; int count, i; struct radius_attr_data *nclass; size_t nclass_count; if (!hapd->conf->radius->acct_server || hapd->radius == NULL || sm == NULL) return; radius_free_class(&sm->radius_class); count = radius_msg_count_attr(msg, RADIUS_ATTR_CLASS, 1); if (count <= 0) return; nclass = os_calloc(count, sizeof(struct radius_attr_data)); if (nclass == NULL) return; nclass_count = 0; class = NULL; for (i = 0; i < count; i++) { do { if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CLASS, &class, &class_len, class) < 0) { i = count; break; } } while (class_len < 1); nclass[nclass_count].data = os_malloc(class_len); if (nclass[nclass_count].data == NULL) break; os_memcpy(nclass[nclass_count].data, class, class_len); nclass[nclass_count].len = class_len; nclass_count++; } sm->radius_class.attr = nclass; sm->radius_class.count = nclass_count; wpa_printf(MSG_DEBUG, "IEEE 802.1X: Stored %lu RADIUS Class " "attributes for " MACSTR, (unsigned long) sm->radius_class.count, MAC2STR(sta->addr)); } /* Update sta->identity based on User-Name attribute in Access-Accept */ static void ieee802_1x_update_sta_identity(struct hostapd_data *hapd, struct sta_info *sta, struct radius_msg *msg) { u8 *buf, *identity; size_t len; struct eapol_state_machine *sm = sta->eapol_sm; if (sm == NULL) return; if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_USER_NAME, &buf, &len, NULL) < 0) return; identity = (u8 *) dup_binstr(buf, len); if (identity == NULL) return; hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, HOSTAPD_LEVEL_DEBUG, "old identity '%s' updated with " "User-Name from Access-Accept '%s'", sm->identity ? (char *) sm->identity : "N/A", (char *) identity); os_free(sm->identity); sm->identity = identity; sm->identity_len = len; } /* Update CUI based on Chargeable-User-Identity attribute in Access-Accept */ static void ieee802_1x_update_sta_cui(struct hostapd_data *hapd, struct sta_info *sta, struct radius_msg *msg) { struct eapol_state_machine *sm = sta->eapol_sm; struct wpabuf *cui; u8 *buf; size_t len; if (sm == NULL) return; if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, &buf, &len, NULL) < 0) return; cui = wpabuf_alloc_copy(buf, len); if (cui == NULL) return; wpabuf_free(sm->radius_cui); sm->radius_cui = cui; } struct sta_id_search { u8 identifier; struct eapol_state_machine *sm; }; static int ieee802_1x_select_radius_identifier(struct hostapd_data *hapd, struct sta_info *sta, void *ctx) { struct sta_id_search *id_search = ctx; struct eapol_state_machine *sm = sta->eapol_sm; if (sm && sm->radius_identifier >= 0 && sm->radius_identifier == id_search->identifier) { id_search->sm = sm; return 1; } return 0; } static struct eapol_state_machine * ieee802_1x_search_radius_identifier(struct hostapd_data *hapd, u8 identifier) { struct sta_id_search id_search; id_search.identifier = identifier; id_search.sm = NULL; ap_for_each_sta(hapd, ieee802_1x_select_radius_identifier, &id_search); return id_search.sm; } /** * ieee802_1x_receive_auth - Process RADIUS frames from Authentication Server * @msg: RADIUS response message * @req: RADIUS request message * @shared_secret: RADIUS shared secret * @shared_secret_len: Length of shared_secret in octets * @data: Context data (struct hostapd_data *) * Returns: Processing status */ static RadiusRxResult ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, const u8 *shared_secret, size_t shared_secret_len, void *data) { struct hostapd_data *hapd = data; struct sta_info *sta; u32 session_timeout = 0, termination_action, acct_interim_interval; int session_timeout_set, old_vlanid = 0; struct eapol_state_machine *sm; int override_eapReq = 0; struct radius_hdr *hdr = radius_msg_get_hdr(msg); sm = ieee802_1x_search_radius_identifier(hapd, hdr->identifier); if (sm == NULL) { wpa_printf(MSG_DEBUG, "IEEE 802.1X: Could not find matching " "station for this RADIUS message"); return RADIUS_RX_UNKNOWN; } sta = sm->sta; /* RFC 2869, Ch. 5.13: valid Message-Authenticator attribute MUST be * present when packet contains an EAP-Message attribute */ if (hdr->code == RADIUS_CODE_ACCESS_REJECT && radius_msg_get_attr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR, NULL, 0) < 0 && radius_msg_get_attr(msg, RADIUS_ATTR_EAP_MESSAGE, NULL, 0) < 0) { wpa_printf(MSG_DEBUG, "Allowing RADIUS Access-Reject without " "Message-Authenticator since it does not include " "EAP-Message"); } else if (radius_msg_verify(msg, shared_secret, shared_secret_len, req, 1)) { wpa_printf(MSG_INFO, "Incoming RADIUS packet did not have correct Message-Authenticator - dropped"); return RADIUS_RX_INVALID_AUTHENTICATOR; } if (hdr->code != RADIUS_CODE_ACCESS_ACCEPT && hdr->code != RADIUS_CODE_ACCESS_REJECT && hdr->code != RADIUS_CODE_ACCESS_CHALLENGE) { wpa_printf(MSG_INFO, "Unknown RADIUS message code"); return RADIUS_RX_UNKNOWN; } sm->radius_identifier = -1; wpa_printf(MSG_DEBUG, "RADIUS packet matching with station " MACSTR, MAC2STR(sta->addr)); radius_msg_free(sm->last_recv_radius); sm->last_recv_radius = msg; session_timeout_set = !radius_msg_get_attr_int32(msg, RADIUS_ATTR_SESSION_TIMEOUT, &session_timeout); if (radius_msg_get_attr_int32(msg, RADIUS_ATTR_TERMINATION_ACTION, &termination_action)) termination_action = RADIUS_TERMINATION_ACTION_DEFAULT; if (hapd->conf->acct_interim_interval == 0 && hdr->code == RADIUS_CODE_ACCESS_ACCEPT && radius_msg_get_attr_int32(msg, RADIUS_ATTR_ACCT_INTERIM_INTERVAL, &acct_interim_interval) == 0) { if (acct_interim_interval < 60) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, HOSTAPD_LEVEL_INFO, "ignored too small " "Acct-Interim-Interval %d", acct_interim_interval); } else sta->acct_interim_interval = acct_interim_interval; } switch (hdr->code) { case RADIUS_CODE_ACCESS_ACCEPT: if (sta->ssid->dynamic_vlan == DYNAMIC_VLAN_DISABLED) sta->vlan_id = 0; #ifndef CONFIG_NO_VLAN else { old_vlanid = sta->vlan_id; sta->vlan_id = radius_msg_get_vlanid(msg); } if (sta->vlan_id > 0 && hostapd_vlan_id_valid(hapd->conf->vlan, sta->vlan_id)) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_INFO, "VLAN ID %d", sta->vlan_id); } else if (sta->ssid->dynamic_vlan == DYNAMIC_VLAN_REQUIRED) { sta->eapol_sm->authFail = TRUE; hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, HOSTAPD_LEVEL_INFO, "authentication " "server did not include required VLAN " "ID in Access-Accept"); break; } #endif /* CONFIG_NO_VLAN */ if (ap_sta_bind_vlan(hapd, sta, old_vlanid) < 0) break; /* RFC 3580, Ch. 3.17 */ if (session_timeout_set && termination_action == RADIUS_TERMINATION_ACTION_RADIUS_REQUEST) { sm->reAuthPeriod = session_timeout; } else if (session_timeout_set) ap_sta_session_timeout(hapd, sta, session_timeout); sm->eap_if->aaaSuccess = TRUE; override_eapReq = 1; ieee802_1x_get_keys(hapd, sta, msg, req, shared_secret, shared_secret_len); ieee802_1x_store_radius_class(hapd, sta, msg); ieee802_1x_update_sta_identity(hapd, sta, msg); ieee802_1x_update_sta_cui(hapd, sta, msg); if (sm->eap_if->eapKeyAvailable && wpa_auth_pmksa_add(sta->wpa_sm, sm->eapol_key_crypt, session_timeout_set ? (int) session_timeout : -1, sm) == 0) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, HOSTAPD_LEVEL_DEBUG, "Added PMKSA cache entry"); } break; case RADIUS_CODE_ACCESS_REJECT: sm->eap_if->aaaFail = TRUE; override_eapReq = 1; break; case RADIUS_CODE_ACCESS_CHALLENGE: sm->eap_if->aaaEapReq = TRUE; if (session_timeout_set) { /* RFC 2869, Ch. 2.3.2; RFC 3580, Ch. 3.17 */ sm->eap_if->aaaMethodTimeout = session_timeout; hostapd_logger(hapd, sm->addr, HOSTAPD_MODULE_IEEE8021X, HOSTAPD_LEVEL_DEBUG, "using EAP timeout of %d seconds (from " "RADIUS)", sm->eap_if->aaaMethodTimeout); } else { /* * Use dynamic retransmission behavior per EAP * specification. */ sm->eap_if->aaaMethodTimeout = 0; } break; } ieee802_1x_decapsulate_radius(hapd, sta); if (override_eapReq) sm->eap_if->aaaEapReq = FALSE; eapol_auth_step(sm); return RADIUS_RX_QUEUED; } #endif /* CONFIG_NO_RADIUS */ void ieee802_1x_abort_auth(struct hostapd_data *hapd, struct sta_info *sta) { struct eapol_state_machine *sm = sta->eapol_sm; if (sm == NULL) return; hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, HOSTAPD_LEVEL_DEBUG, "aborting authentication"); #ifndef CONFIG_NO_RADIUS radius_msg_free(sm->last_recv_radius); sm->last_recv_radius = NULL; #endif /* CONFIG_NO_RADIUS */ if (sm->eap_if->eapTimeout) { /* * Disconnect the STA since it did not reply to the last EAP * request and we cannot continue EAP processing (EAP-Failure * could only be sent if the EAP peer actually replied). */ wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "EAP Timeout, STA " MACSTR, MAC2STR(sta->addr)); sm->eap_if->portEnabled = FALSE; ap_sta_disconnect(hapd, sta, sta->addr, WLAN_REASON_PREV_AUTH_NOT_VALID); } } static int ieee802_1x_rekey_broadcast(struct hostapd_data *hapd) { struct eapol_authenticator *eapol = hapd->eapol_auth; if (hapd->conf->default_wep_key_len < 1) return 0; os_free(eapol->default_wep_key); eapol->default_wep_key = os_malloc(hapd->conf->default_wep_key_len); if (eapol->default_wep_key == NULL || random_get_bytes(eapol->default_wep_key, hapd->conf->default_wep_key_len)) { wpa_printf(MSG_INFO, "Could not generate random WEP key"); os_free(eapol->default_wep_key); eapol->default_wep_key = NULL; return -1; } wpa_hexdump_key(MSG_DEBUG, "IEEE 802.1X: New default WEP key", eapol->default_wep_key, hapd->conf->default_wep_key_len); return 0; } static int ieee802_1x_sta_key_available(struct hostapd_data *hapd, struct sta_info *sta, void *ctx) { if (sta->eapol_sm) { sta->eapol_sm->eap_if->eapKeyAvailable = TRUE; eapol_auth_step(sta->eapol_sm); } return 0; } static void ieee802_1x_rekey(void *eloop_ctx, void *timeout_ctx) { struct hostapd_data *hapd = eloop_ctx; struct eapol_authenticator *eapol = hapd->eapol_auth; if (eapol->default_wep_key_idx >= 3) eapol->default_wep_key_idx = hapd->conf->individual_wep_key_len > 0 ? 1 : 0; else eapol->default_wep_key_idx++; wpa_printf(MSG_DEBUG, "IEEE 802.1X: New default WEP key index %d", eapol->default_wep_key_idx); if (ieee802_1x_rekey_broadcast(hapd)) { hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE8021X, HOSTAPD_LEVEL_WARNING, "failed to generate a " "new broadcast key"); os_free(eapol->default_wep_key); eapol->default_wep_key = NULL; return; } /* TODO: Could setup key for RX here, but change default TX keyid only * after new broadcast key has been sent to all stations. */ if (hostapd_drv_set_key(hapd->conf->iface, hapd, WPA_ALG_WEP, broadcast_ether_addr, eapol->default_wep_key_idx, 1, NULL, 0, eapol->default_wep_key, hapd->conf->default_wep_key_len)) { hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE8021X, HOSTAPD_LEVEL_WARNING, "failed to configure a " "new broadcast key"); os_free(eapol->default_wep_key); eapol->default_wep_key = NULL; return; } ap_for_each_sta(hapd, ieee802_1x_sta_key_available, NULL); if (hapd->conf->wep_rekeying_period > 0) { eloop_register_timeout(hapd->conf->wep_rekeying_period, 0, ieee802_1x_rekey, hapd, NULL); } } static void ieee802_1x_eapol_send(void *ctx, void *sta_ctx, u8 type, const u8 *data, size_t datalen) { #ifdef CONFIG_WPS struct sta_info *sta = sta_ctx; if ((sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS)) == WLAN_STA_MAYBE_WPS) { const u8 *identity; size_t identity_len; struct eapol_state_machine *sm = sta->eapol_sm; identity = eap_get_identity(sm->eap, &identity_len); if (identity && ((identity_len == WSC_ID_ENROLLEE_LEN && os_memcmp(identity, WSC_ID_ENROLLEE, WSC_ID_ENROLLEE_LEN) == 0) || (identity_len == WSC_ID_REGISTRAR_LEN && os_memcmp(identity, WSC_ID_REGISTRAR, WSC_ID_REGISTRAR_LEN) == 0))) { wpa_printf(MSG_DEBUG, "WPS: WLAN_STA_MAYBE_WPS -> " "WLAN_STA_WPS"); sta->flags |= WLAN_STA_WPS; } } #endif /* CONFIG_WPS */ ieee802_1x_send(ctx, sta_ctx, type, data, datalen); } static void ieee802_1x_aaa_send(void *ctx, void *sta_ctx, const u8 *data, size_t datalen) { #ifndef CONFIG_NO_RADIUS struct hostapd_data *hapd = ctx; struct sta_info *sta = sta_ctx; ieee802_1x_encapsulate_radius(hapd, sta, data, datalen); #endif /* CONFIG_NO_RADIUS */ } static void _ieee802_1x_finished(void *ctx, void *sta_ctx, int success, int preauth) { struct hostapd_data *hapd = ctx; struct sta_info *sta = sta_ctx; if (preauth) rsn_preauth_finished(hapd, sta, success); else ieee802_1x_finished(hapd, sta, success); } static int ieee802_1x_get_eap_user(void *ctx, const u8 *identity, size_t identity_len, int phase2, struct eap_user *user) { struct hostapd_data *hapd = ctx; const struct hostapd_eap_user *eap_user; int i; eap_user = hostapd_get_eap_user(hapd, identity, identity_len, phase2); if (eap_user == NULL) return -1; os_memset(user, 0, sizeof(*user)); user->phase2 = phase2; for (i = 0; i < EAP_MAX_METHODS; i++) { user->methods[i].vendor = eap_user->methods[i].vendor; user->methods[i].method = eap_user->methods[i].method; } if (eap_user->password) { user->password = os_malloc(eap_user->password_len); if (user->password == NULL) return -1; os_memcpy(user->password, eap_user->password, eap_user->password_len); user->password_len = eap_user->password_len; user->password_hash = eap_user->password_hash; } user->force_version = eap_user->force_version; user->ttls_auth = eap_user->ttls_auth; return 0; } static int ieee802_1x_sta_entry_alive(void *ctx, const u8 *addr) { struct hostapd_data *hapd = ctx; struct sta_info *sta; sta = ap_get_sta(hapd, addr); if (sta == NULL || sta->eapol_sm == NULL) return 0; return 1; } static void ieee802_1x_logger(void *ctx, const u8 *addr, eapol_logger_level level, const char *txt) { #ifndef CONFIG_NO_HOSTAPD_LOGGER struct hostapd_data *hapd = ctx; int hlevel; switch (level) { case EAPOL_LOGGER_WARNING: hlevel = HOSTAPD_LEVEL_WARNING; break; case EAPOL_LOGGER_INFO: hlevel = HOSTAPD_LEVEL_INFO; break; case EAPOL_LOGGER_DEBUG: default: hlevel = HOSTAPD_LEVEL_DEBUG; break; } hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE8021X, hlevel, "%s", txt); #endif /* CONFIG_NO_HOSTAPD_LOGGER */ } static void ieee802_1x_set_port_authorized(void *ctx, void *sta_ctx, int authorized) { struct hostapd_data *hapd = ctx; struct sta_info *sta = sta_ctx; ieee802_1x_set_sta_authorized(hapd, sta, authorized); } static void _ieee802_1x_abort_auth(void *ctx, void *sta_ctx) { struct hostapd_data *hapd = ctx; struct sta_info *sta = sta_ctx; ieee802_1x_abort_auth(hapd, sta); } static void _ieee802_1x_tx_key(void *ctx, void *sta_ctx) { struct hostapd_data *hapd = ctx; struct sta_info *sta = sta_ctx; ieee802_1x_tx_key(hapd, sta); } static void ieee802_1x_eapol_event(void *ctx, void *sta_ctx, enum eapol_event type) { /* struct hostapd_data *hapd = ctx; */ struct sta_info *sta = sta_ctx; switch (type) { case EAPOL_AUTH_SM_CHANGE: wpa_auth_sm_notify(sta->wpa_sm); break; case EAPOL_AUTH_REAUTHENTICATE: wpa_auth_sm_event(sta->wpa_sm, WPA_REAUTH_EAPOL); break; } } int ieee802_1x_init(struct hostapd_data *hapd) { int i; struct eapol_auth_config conf; struct eapol_auth_cb cb; os_memset(&conf, 0, sizeof(conf)); conf.ctx = hapd; conf.eap_reauth_period = hapd->conf->eap_reauth_period; conf.wpa = hapd->conf->wpa; conf.individual_wep_key_len = hapd->conf->individual_wep_key_len; conf.eap_server = hapd->conf->eap_server; conf.ssl_ctx = hapd->ssl_ctx; conf.msg_ctx = hapd->msg_ctx; conf.eap_sim_db_priv = hapd->eap_sim_db_priv; conf.eap_req_id_text = hapd->conf->eap_req_id_text; conf.eap_req_id_text_len = hapd->conf->eap_req_id_text_len; conf.pac_opaque_encr_key = hapd->conf->pac_opaque_encr_key; conf.eap_fast_a_id = hapd->conf->eap_fast_a_id; conf.eap_fast_a_id_len = hapd->conf->eap_fast_a_id_len; conf.eap_fast_a_id_info = hapd->conf->eap_fast_a_id_info; conf.eap_fast_prov = hapd->conf->eap_fast_prov; conf.pac_key_lifetime = hapd->conf->pac_key_lifetime; conf.pac_key_refresh_time = hapd->conf->pac_key_refresh_time; conf.eap_sim_aka_result_ind = hapd->conf->eap_sim_aka_result_ind; conf.tnc = hapd->conf->tnc; conf.wps = hapd->wps; conf.fragment_size = hapd->conf->fragment_size; conf.pwd_group = hapd->conf->pwd_group; conf.pbc_in_m1 = hapd->conf->pbc_in_m1; if (hapd->conf->server_id) { conf.server_id = (const u8 *) hapd->conf->server_id; conf.server_id_len = os_strlen(hapd->conf->server_id); } else { conf.server_id = (const u8 *) "hostapd"; conf.server_id_len = 7; } os_memset(&cb, 0, sizeof(cb)); cb.eapol_send = ieee802_1x_eapol_send; cb.aaa_send = ieee802_1x_aaa_send; cb.finished = _ieee802_1x_finished; cb.get_eap_user = ieee802_1x_get_eap_user; cb.sta_entry_alive = ieee802_1x_sta_entry_alive; cb.logger = ieee802_1x_logger; cb.set_port_authorized = ieee802_1x_set_port_authorized; cb.abort_auth = _ieee802_1x_abort_auth; cb.tx_key = _ieee802_1x_tx_key; cb.eapol_event = ieee802_1x_eapol_event; hapd->eapol_auth = eapol_auth_init(&conf, &cb); if (hapd->eapol_auth == NULL) return -1; if ((hapd->conf->ieee802_1x || hapd->conf->wpa) && hostapd_set_drv_ieee8021x(hapd, hapd->conf->iface, 1)) return -1; #ifndef CONFIG_NO_RADIUS if (radius_client_register(hapd->radius, RADIUS_AUTH, ieee802_1x_receive_auth, hapd)) return -1; #endif /* CONFIG_NO_RADIUS */ if (hapd->conf->default_wep_key_len) { for (i = 0; i < 4; i++) hostapd_drv_set_key(hapd->conf->iface, hapd, WPA_ALG_NONE, NULL, i, 0, NULL, 0, NULL, 0); ieee802_1x_rekey(hapd, NULL); if (hapd->eapol_auth->default_wep_key == NULL) return -1; } return 0; } void ieee802_1x_deinit(struct hostapd_data *hapd) { eloop_cancel_timeout(ieee802_1x_rekey, hapd, NULL); if (hapd->driver != NULL && (hapd->conf->ieee802_1x || hapd->conf->wpa)) hostapd_set_drv_ieee8021x(hapd, hapd->conf->iface, 0); eapol_auth_deinit(hapd->eapol_auth); hapd->eapol_auth = NULL; } int ieee802_1x_tx_status(struct hostapd_data *hapd, struct sta_info *sta, const u8 *buf, size_t len, int ack) { struct ieee80211_hdr *hdr; u8 *pos; const unsigned char rfc1042_hdr[ETH_ALEN] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 }; if (sta == NULL) return -1; if (len < sizeof(*hdr) + sizeof(rfc1042_hdr) + 2) return 0; hdr = (struct ieee80211_hdr *) buf; pos = (u8 *) (hdr + 1); if (os_memcmp(pos, rfc1042_hdr, sizeof(rfc1042_hdr)) != 0) return 0; pos += sizeof(rfc1042_hdr); if (WPA_GET_BE16(pos) != ETH_P_PAE) return 0; pos += 2; return ieee802_1x_eapol_tx_status(hapd, sta, pos, buf + len - pos, ack); } int ieee802_1x_eapol_tx_status(struct hostapd_data *hapd, struct sta_info *sta, const u8 *buf, int len, int ack) { const struct ieee802_1x_hdr *xhdr = (const struct ieee802_1x_hdr *) buf; const u8 *pos = buf + sizeof(*xhdr); struct ieee802_1x_eapol_key *key; if (len < (int) sizeof(*xhdr)) return 0; wpa_printf(MSG_DEBUG, "IEEE 802.1X: " MACSTR " TX status - version=%d " "type=%d length=%d - ack=%d", MAC2STR(sta->addr), xhdr->version, xhdr->type, be_to_host16(xhdr->length), ack); if (xhdr->type != IEEE802_1X_TYPE_EAPOL_KEY) return 0; if (pos + sizeof(struct wpa_eapol_key) <= buf + len) { const struct wpa_eapol_key *wpa; wpa = (const struct wpa_eapol_key *) pos; if (wpa->type == EAPOL_KEY_TYPE_RSN || wpa->type == EAPOL_KEY_TYPE_WPA) wpa_auth_eapol_key_tx_status(hapd->wpa_auth, sta->wpa_sm, ack); } /* EAPOL EAP-Packet packets are eventually re-sent by either Supplicant * or Authenticator state machines, but EAPOL-Key packets are not * retransmitted in case of failure. Try to re-send failed EAPOL-Key * packets couple of times because otherwise STA keys become * unsynchronized with AP. */ if (!ack && pos + sizeof(*key) <= buf + len) { key = (struct ieee802_1x_eapol_key *) pos; hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, HOSTAPD_LEVEL_DEBUG, "did not Ack EAPOL-Key " "frame (%scast index=%d)", key->key_index & BIT(7) ? "uni" : "broad", key->key_index & ~BIT(7)); /* TODO: re-send EAPOL-Key couple of times (with short delay * between them?). If all attempt fail, report error and * deauthenticate STA so that it will get new keys when * authenticating again (e.g., after returning in range). * Separate limit/transmit state needed both for unicast and * broadcast keys(?) */ } /* TODO: could move unicast key configuration from ieee802_1x_tx_key() * to here and change the key only if the EAPOL-Key packet was Acked. */ return 1; } u8 * ieee802_1x_get_identity(struct eapol_state_machine *sm, size_t *len) { if (sm == NULL || sm->identity == NULL) return NULL; *len = sm->identity_len; return sm->identity; } u8 * ieee802_1x_get_radius_class(struct eapol_state_machine *sm, size_t *len, int idx) { if (sm == NULL || sm->radius_class.attr == NULL || idx >= (int) sm->radius_class.count) return NULL; *len = sm->radius_class.attr[idx].len; return sm->radius_class.attr[idx].data; } struct wpabuf * ieee802_1x_get_radius_cui(struct eapol_state_machine *sm) { if (sm == NULL) return NULL; return sm->radius_cui; } const u8 * ieee802_1x_get_key(struct eapol_state_machine *sm, size_t *len) { *len = 0; if (sm == NULL) return NULL; *len = sm->eap_if->eapKeyDataLen; return sm->eap_if->eapKeyData; } void ieee802_1x_notify_port_enabled(struct eapol_state_machine *sm, int enabled) { if (sm == NULL) return; sm->eap_if->portEnabled = enabled ? TRUE : FALSE; eapol_auth_step(sm); } void ieee802_1x_notify_port_valid(struct eapol_state_machine *sm, int valid) { if (sm == NULL) return; sm->portValid = valid ? TRUE : FALSE; eapol_auth_step(sm); } void ieee802_1x_notify_pre_auth(struct eapol_state_machine *sm, int pre_auth) { if (sm == NULL) return; if (pre_auth) sm->flags |= EAPOL_SM_PREAUTH; else sm->flags &= ~EAPOL_SM_PREAUTH; } static const char * bool_txt(Boolean bool) { return bool ? "TRUE" : "FALSE"; } int ieee802_1x_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen) { /* TODO */ return 0; } int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, char *buf, size_t buflen) { int len = 0, ret; struct eapol_state_machine *sm = sta->eapol_sm; struct os_reltime diff; if (sm == NULL) return 0; ret = os_snprintf(buf + len, buflen - len, "dot1xPaePortNumber=%d\n" "dot1xPaePortProtocolVersion=%d\n" "dot1xPaePortCapabilities=1\n" "dot1xPaePortInitialize=%d\n" "dot1xPaePortReauthenticate=FALSE\n", sta->aid, EAPOL_VERSION, sm->initialize); if (ret < 0 || (size_t) ret >= buflen - len) return len; len += ret; /* dot1xAuthConfigTable */ ret = os_snprintf(buf + len, buflen - len, "dot1xAuthPaeState=%d\n" "dot1xAuthBackendAuthState=%d\n" "dot1xAuthAdminControlledDirections=%d\n" "dot1xAuthOperControlledDirections=%d\n" "dot1xAuthAuthControlledPortStatus=%d\n" "dot1xAuthAuthControlledPortControl=%d\n" "dot1xAuthQuietPeriod=%u\n" "dot1xAuthServerTimeout=%u\n" "dot1xAuthReAuthPeriod=%u\n" "dot1xAuthReAuthEnabled=%s\n" "dot1xAuthKeyTxEnabled=%s\n", sm->auth_pae_state + 1, sm->be_auth_state + 1, sm->adminControlledDirections, sm->operControlledDirections, sm->authPortStatus, sm->portControl, sm->quietPeriod, sm->serverTimeout, sm->reAuthPeriod, bool_txt(sm->reAuthEnabled), bool_txt(sm->keyTxEnabled)); if (ret < 0 || (size_t) ret >= buflen - len) return len; len += ret; /* dot1xAuthStatsTable */ ret = os_snprintf(buf + len, buflen - len, "dot1xAuthEapolFramesRx=%u\n" "dot1xAuthEapolFramesTx=%u\n" "dot1xAuthEapolStartFramesRx=%u\n" "dot1xAuthEapolLogoffFramesRx=%u\n" "dot1xAuthEapolRespIdFramesRx=%u\n" "dot1xAuthEapolRespFramesRx=%u\n" "dot1xAuthEapolReqIdFramesTx=%u\n" "dot1xAuthEapolReqFramesTx=%u\n" "dot1xAuthInvalidEapolFramesRx=%u\n" "dot1xAuthEapLengthErrorFramesRx=%u\n" "dot1xAuthLastEapolFrameVersion=%u\n" "dot1xAuthLastEapolFrameSource=" MACSTR "\n", sm->dot1xAuthEapolFramesRx, sm->dot1xAuthEapolFramesTx, sm->dot1xAuthEapolStartFramesRx, sm->dot1xAuthEapolLogoffFramesRx, sm->dot1xAuthEapolRespIdFramesRx, sm->dot1xAuthEapolRespFramesRx, sm->dot1xAuthEapolReqIdFramesTx, sm->dot1xAuthEapolReqFramesTx, sm->dot1xAuthInvalidEapolFramesRx, sm->dot1xAuthEapLengthErrorFramesRx, sm->dot1xAuthLastEapolFrameVersion, MAC2STR(sm->addr)); if (ret < 0 || (size_t) ret >= buflen - len) return len; len += ret; /* dot1xAuthDiagTable */ ret = os_snprintf(buf + len, buflen - len, "dot1xAuthEntersConnecting=%u\n" "dot1xAuthEapLogoffsWhileConnecting=%u\n" "dot1xAuthEntersAuthenticating=%u\n" "dot1xAuthAuthSuccessesWhileAuthenticating=%u\n" "dot1xAuthAuthTimeoutsWhileAuthenticating=%u\n" "dot1xAuthAuthFailWhileAuthenticating=%u\n" "dot1xAuthAuthEapStartsWhileAuthenticating=%u\n" "dot1xAuthAuthEapLogoffWhileAuthenticating=%u\n" "dot1xAuthAuthReauthsWhileAuthenticated=%u\n" "dot1xAuthAuthEapStartsWhileAuthenticated=%u\n" "dot1xAuthAuthEapLogoffWhileAuthenticated=%u\n" "dot1xAuthBackendResponses=%u\n" "dot1xAuthBackendAccessChallenges=%u\n" "dot1xAuthBackendOtherRequestsToSupplicant=%u\n" "dot1xAuthBackendAuthSuccesses=%u\n" "dot1xAuthBackendAuthFails=%u\n", sm->authEntersConnecting, sm->authEapLogoffsWhileConnecting, sm->authEntersAuthenticating, sm->authAuthSuccessesWhileAuthenticating, sm->authAuthTimeoutsWhileAuthenticating, sm->authAuthFailWhileAuthenticating, sm->authAuthEapStartsWhileAuthenticating, sm->authAuthEapLogoffWhileAuthenticating, sm->authAuthReauthsWhileAuthenticated, sm->authAuthEapStartsWhileAuthenticated, sm->authAuthEapLogoffWhileAuthenticated, sm->backendResponses, sm->backendAccessChallenges, sm->backendOtherRequestsToSupplicant, sm->backendAuthSuccesses, sm->backendAuthFails); if (ret < 0 || (size_t) ret >= buflen - len) return len; len += ret; /* dot1xAuthSessionStatsTable */ os_reltime_age(&sta->acct_session_start, &diff); ret = os_snprintf(buf + len, buflen - len, /* TODO: dot1xAuthSessionOctetsRx */ /* TODO: dot1xAuthSessionOctetsTx */ /* TODO: dot1xAuthSessionFramesRx */ /* TODO: dot1xAuthSessionFramesTx */ "dot1xAuthSessionId=%08X-%08X\n" "dot1xAuthSessionAuthenticMethod=%d\n" "dot1xAuthSessionTime=%u\n" "dot1xAuthSessionTerminateCause=999\n" "dot1xAuthSessionUserName=%s\n", sta->acct_session_id_hi, sta->acct_session_id_lo, (wpa_key_mgmt_wpa_ieee8021x( wpa_auth_sta_key_mgmt(sta->wpa_sm))) ? 1 : 2, (unsigned int) diff.sec, sm->identity); if (ret < 0 || (size_t) ret >= buflen - len) return len; len += ret; ret = os_snprintf(buf + len, buflen - len, "last_eap_type_as=%d (%s)\n" "last_eap_type_sta=%d (%s)\n", sm->eap_type_authsrv, eap_server_get_name(0, sm->eap_type_authsrv), sm->eap_type_supp, eap_server_get_name(0, sm->eap_type_supp)); if (ret < 0 || (size_t) ret >= buflen - len) return len; len += ret; return len; } static void ieee802_1x_finished(struct hostapd_data *hapd, struct sta_info *sta, int success) { const u8 *key; size_t len; /* TODO: get PMKLifetime from WPA parameters */ static const int dot11RSNAConfigPMKLifetime = 43200; key = ieee802_1x_get_key(sta->eapol_sm, &len); if (success && key && len >= PMK_LEN && wpa_auth_pmksa_add(sta->wpa_sm, key, dot11RSNAConfigPMKLifetime, sta->eapol_sm) == 0) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, HOSTAPD_LEVEL_DEBUG, "Added PMKSA cache entry (IEEE 802.1X)"); } if (!success) { /* * Many devices require deauthentication after WPS provisioning * and some may not be be able to do that themselves, so * disconnect the client here. In addition, this may also * benefit IEEE 802.1X/EAPOL authentication cases, too since * the EAPOL PAE state machine would remain in HELD state for * considerable amount of time and some EAP methods, like * EAP-FAST with anonymous provisioning, may require another * EAPOL authentication to be started to complete connection. */ wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "IEEE 802.1X: Force " "disconnection after EAP-Failure"); /* Add a small sleep to increase likelihood of previously * requested EAP-Failure TX getting out before this should the * driver reorder operations. */ os_sleep(0, 10000); ap_sta_disconnect(hapd, sta, sta->addr, WLAN_REASON_IEEE_802_1X_AUTH_FAILED); hostapd_wps_eap_completed(hapd); } } wpa-2.1/src/ap/ieee802_1x.h000066400000000000000000000046371227414666700153050ustar00rootroot00000000000000/* * hostapd / IEEE 802.1X-2004 Authenticator * Copyright (c) 2002-2012, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef IEEE802_1X_H #define IEEE802_1X_H struct hostapd_data; struct sta_info; struct eapol_state_machine; struct hostapd_config; struct hostapd_bss_config; struct hostapd_radius_attr; struct radius_msg; void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, size_t len); void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta); void ieee802_1x_free_station(struct sta_info *sta); void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta); void ieee802_1x_abort_auth(struct hostapd_data *hapd, struct sta_info *sta); void ieee802_1x_set_sta_authorized(struct hostapd_data *hapd, struct sta_info *sta, int authorized); void ieee802_1x_dump_state(FILE *f, const char *prefix, struct sta_info *sta); int ieee802_1x_init(struct hostapd_data *hapd); void ieee802_1x_deinit(struct hostapd_data *hapd); int ieee802_1x_tx_status(struct hostapd_data *hapd, struct sta_info *sta, const u8 *buf, size_t len, int ack); int ieee802_1x_eapol_tx_status(struct hostapd_data *hapd, struct sta_info *sta, const u8 *data, int len, int ack); u8 * ieee802_1x_get_identity(struct eapol_state_machine *sm, size_t *len); u8 * ieee802_1x_get_radius_class(struct eapol_state_machine *sm, size_t *len, int idx); struct wpabuf * ieee802_1x_get_radius_cui(struct eapol_state_machine *sm); const u8 * ieee802_1x_get_key(struct eapol_state_machine *sm, size_t *len); void ieee802_1x_notify_port_enabled(struct eapol_state_machine *sm, int enabled); void ieee802_1x_notify_port_valid(struct eapol_state_machine *sm, int valid); void ieee802_1x_notify_pre_auth(struct eapol_state_machine *sm, int pre_auth); int ieee802_1x_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen); int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, char *buf, size_t buflen); void hostapd_get_ntp_timestamp(u8 *buf); char *eap_type_text(u8 type); const char *radius_mode_txt(struct hostapd_data *hapd); int radius_sta_rate(struct hostapd_data *hapd, struct sta_info *sta); int add_common_radius_attr(struct hostapd_data *hapd, struct hostapd_radius_attr *req_attr, struct sta_info *sta, struct radius_msg *msg); #endif /* IEEE802_1X_H */ wpa-2.1/src/ap/p2p_hostapd.c000066400000000000000000000051251227414666700157430ustar00rootroot00000000000000/* * hostapd / P2P integration * Copyright (c) 2009-2010, Atheros Communications * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "utils/includes.h" #include "utils/common.h" #include "common/ieee802_11_defs.h" #include "p2p/p2p.h" #include "hostapd.h" #include "ap_config.h" #include "ap_drv_ops.h" #include "sta_info.h" #include "p2p_hostapd.h" #ifdef CONFIG_P2P int hostapd_p2p_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, char *buf, size_t buflen) { if (sta->p2p_ie == NULL) return 0; return p2p_ie_text(sta->p2p_ie, buf, buf + buflen); } int hostapd_p2p_set_noa(struct hostapd_data *hapd, u8 count, int start, int duration) { wpa_printf(MSG_DEBUG, "P2P: Set NoA parameters: count=%u start=%d " "duration=%d", count, start, duration); if (count == 0) { hapd->noa_enabled = 0; hapd->noa_start = 0; hapd->noa_duration = 0; } if (count != 255) { wpa_printf(MSG_DEBUG, "P2P: Non-periodic NoA - set " "NoA parameters"); return hostapd_driver_set_noa(hapd, count, start, duration); } hapd->noa_enabled = 1; hapd->noa_start = start; hapd->noa_duration = duration; if (hapd->num_sta_no_p2p == 0) { wpa_printf(MSG_DEBUG, "P2P: No legacy STAs connected - update " "periodic NoA parameters"); return hostapd_driver_set_noa(hapd, count, start, duration); } wpa_printf(MSG_DEBUG, "P2P: Legacy STA(s) connected - do not enable " "periodic NoA"); return 0; } void hostapd_p2p_non_p2p_sta_connected(struct hostapd_data *hapd) { wpa_printf(MSG_DEBUG, "P2P: First non-P2P device connected"); if (hapd->noa_enabled) { wpa_printf(MSG_DEBUG, "P2P: Disable periodic NoA"); hostapd_driver_set_noa(hapd, 0, 0, 0); } } void hostapd_p2p_non_p2p_sta_disconnected(struct hostapd_data *hapd) { wpa_printf(MSG_DEBUG, "P2P: Last non-P2P device disconnected"); if (hapd->noa_enabled) { wpa_printf(MSG_DEBUG, "P2P: Enable periodic NoA"); hostapd_driver_set_noa(hapd, 255, hapd->noa_start, hapd->noa_duration); } } #endif /* CONFIG_P2P */ #ifdef CONFIG_P2P_MANAGER u8 * hostapd_eid_p2p_manage(struct hostapd_data *hapd, u8 *eid) { u8 bitmap; *eid++ = WLAN_EID_VENDOR_SPECIFIC; *eid++ = 4 + 3 + 1; WPA_PUT_BE24(eid, OUI_WFA); eid += 3; *eid++ = P2P_OUI_TYPE; *eid++ = P2P_ATTR_MANAGEABILITY; WPA_PUT_LE16(eid, 1); eid += 2; bitmap = P2P_MAN_DEVICE_MANAGEMENT; if (hapd->conf->p2p & P2P_ALLOW_CROSS_CONNECTION) bitmap |= P2P_MAN_CROSS_CONNECTION_PERMITTED; bitmap |= P2P_MAN_COEXISTENCE_OPTIONAL; *eid++ = bitmap; return eid; } #endif /* CONFIG_P2P_MANAGER */ wpa-2.1/src/ap/p2p_hostapd.h000066400000000000000000000015701227414666700157500ustar00rootroot00000000000000/* * hostapd / P2P integration * Copyright (c) 2009-2010, Atheros Communications * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef P2P_HOSTAPD_H #define P2P_HOSTAPD_H #ifdef CONFIG_P2P int hostapd_p2p_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, char *buf, size_t buflen); int hostapd_p2p_set_noa(struct hostapd_data *hapd, u8 count, int start, int duration); void hostapd_p2p_non_p2p_sta_connected(struct hostapd_data *hapd); void hostapd_p2p_non_p2p_sta_disconnected(struct hostapd_data *hapd); #else /* CONFIG_P2P */ static inline int hostapd_p2p_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, char *buf, size_t buflen) { return 0; } #endif /* CONFIG_P2P */ u8 * hostapd_eid_p2p_manage(struct hostapd_data *hapd, u8 *eid); #endif /* P2P_HOSTAPD_H */ wpa-2.1/src/ap/peerkey_auth.c000066400000000000000000000246531227414666700162140ustar00rootroot00000000000000/* * hostapd - PeerKey for Direct Link Setup (DLS) * Copyright (c) 2006-2009, 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/eloop.h" #include "crypto/sha1.h" #include "crypto/sha256.h" #include "crypto/random.h" #include "wpa_auth.h" #include "wpa_auth_i.h" #include "wpa_auth_ie.h" #ifdef CONFIG_PEERKEY static void wpa_stsl_step(void *eloop_ctx, void *timeout_ctx) { #if 0 struct wpa_authenticator *wpa_auth = eloop_ctx; struct wpa_stsl_negotiation *neg = timeout_ctx; #endif /* TODO: ? */ } struct wpa_stsl_search { const u8 *addr; struct wpa_state_machine *sm; }; static int wpa_stsl_select_sta(struct wpa_state_machine *sm, void *ctx) { struct wpa_stsl_search *search = ctx; if (os_memcmp(search->addr, sm->addr, ETH_ALEN) == 0) { search->sm = sm; return 1; } return 0; } static void wpa_smk_send_error(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, const u8 *peer, u16 mui, u16 error_type) { u8 kde[2 + RSN_SELECTOR_LEN + ETH_ALEN + 2 + RSN_SELECTOR_LEN + sizeof(struct rsn_error_kde)]; u8 *pos; struct rsn_error_kde error; wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, "Sending SMK Error"); pos = kde; if (peer) { pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peer, ETH_ALEN, NULL, 0); } error.mui = host_to_be16(mui); error.error_type = host_to_be16(error_type); pos = wpa_add_kde(pos, RSN_KEY_DATA_ERROR, (u8 *) &error, sizeof(error), NULL, 0); __wpa_send_eapol(wpa_auth, sm, WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC | WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_ERROR, NULL, NULL, kde, pos - kde, 0, 0, 0); } void wpa_smk_m1(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, struct wpa_eapol_key *key) { struct wpa_eapol_ie_parse kde; struct wpa_stsl_search search; u8 *buf, *pos; size_t buf_len; if (wpa_parse_kde_ies((const u8 *) (key + 1), WPA_GET_BE16(key->key_data_length), &kde) < 0) { wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M1"); return; } if (kde.rsn_ie == NULL || kde.mac_addr == NULL || kde.mac_addr_len < ETH_ALEN) { wpa_printf(MSG_INFO, "RSN: No RSN IE or MAC address KDE in " "SMK M1"); return; } /* Initiator = sm->addr; Peer = kde.mac_addr */ search.addr = kde.mac_addr; search.sm = NULL; if (wpa_auth_for_each_sta(wpa_auth, wpa_stsl_select_sta, &search) == 0 || search.sm == NULL) { wpa_printf(MSG_DEBUG, "RSN: SMK handshake with " MACSTR " aborted - STA not associated anymore", MAC2STR(kde.mac_addr)); wpa_smk_send_error(wpa_auth, sm, kde.mac_addr, STK_MUI_SMK, STK_ERR_STA_NR); /* FIX: wpa_stsl_remove(wpa_auth, neg); */ return; } buf_len = kde.rsn_ie_len + 2 + RSN_SELECTOR_LEN + ETH_ALEN; buf = os_malloc(buf_len); if (buf == NULL) return; /* Initiator RSN IE */ os_memcpy(buf, kde.rsn_ie, kde.rsn_ie_len); pos = buf + kde.rsn_ie_len; /* Initiator MAC Address */ pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, sm->addr, ETH_ALEN, NULL, 0); /* SMK M2: * EAPOL-Key(S=1, M=1, A=1, I=0, K=0, SM=1, KeyRSC=0, Nonce=INonce, * MIC=MIC, DataKDs=(RSNIE_I, MAC_I KDE) */ wpa_auth_logger(wpa_auth, search.sm->addr, LOGGER_DEBUG, "Sending SMK M2"); __wpa_send_eapol(wpa_auth, search.sm, WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC | WPA_KEY_INFO_ACK | WPA_KEY_INFO_SMK_MESSAGE, NULL, key->key_nonce, buf, pos - buf, 0, 0, 0); os_free(buf); } static void wpa_send_smk_m4(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, struct wpa_eapol_key *key, struct wpa_eapol_ie_parse *kde, const u8 *smk) { u8 *buf, *pos; size_t buf_len; u32 lifetime; /* SMK M4: * EAPOL-Key(S=1, M=1, A=0, I=1, K=0, SM=1, KeyRSC=0, Nonce=PNonce, * MIC=MIC, DataKDs=(MAC_I KDE, INonce KDE, SMK KDE, * Lifetime KDE) */ buf_len = 2 + RSN_SELECTOR_LEN + ETH_ALEN + 2 + RSN_SELECTOR_LEN + WPA_NONCE_LEN + 2 + RSN_SELECTOR_LEN + PMK_LEN + WPA_NONCE_LEN + 2 + RSN_SELECTOR_LEN + sizeof(lifetime); pos = buf = os_malloc(buf_len); if (buf == NULL) return; /* Initiator MAC Address */ pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, kde->mac_addr, ETH_ALEN, NULL, 0); /* Initiator Nonce */ pos = wpa_add_kde(pos, RSN_KEY_DATA_NONCE, kde->nonce, WPA_NONCE_LEN, NULL, 0); /* SMK with PNonce */ pos = wpa_add_kde(pos, RSN_KEY_DATA_SMK, smk, PMK_LEN, key->key_nonce, WPA_NONCE_LEN); /* Lifetime */ lifetime = htonl(43200); /* dot11RSNAConfigSMKLifetime */ pos = wpa_add_kde(pos, RSN_KEY_DATA_LIFETIME, (u8 *) &lifetime, sizeof(lifetime), NULL, 0); wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, "Sending SMK M4"); __wpa_send_eapol(wpa_auth, sm, WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC | WPA_KEY_INFO_INSTALL | WPA_KEY_INFO_SMK_MESSAGE, NULL, key->key_nonce, buf, pos - buf, 0, 1, 0); os_free(buf); } static void wpa_send_smk_m5(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, struct wpa_eapol_key *key, struct wpa_eapol_ie_parse *kde, const u8 *smk, const u8 *peer) { u8 *buf, *pos; size_t buf_len; u32 lifetime; /* SMK M5: * EAPOL-Key(S=1, M=1, A=0, I=0, K=0, SM=1, KeyRSC=0, Nonce=INonce, * MIC=MIC, DataKDs=(RSNIE_P, MAC_P KDE, PNonce, SMK KDE, * Lifetime KDE)) */ buf_len = kde->rsn_ie_len + 2 + RSN_SELECTOR_LEN + ETH_ALEN + 2 + RSN_SELECTOR_LEN + WPA_NONCE_LEN + 2 + RSN_SELECTOR_LEN + PMK_LEN + WPA_NONCE_LEN + 2 + RSN_SELECTOR_LEN + sizeof(lifetime); pos = buf = os_malloc(buf_len); if (buf == NULL) return; /* Peer RSN IE */ os_memcpy(buf, kde->rsn_ie, kde->rsn_ie_len); pos = buf + kde->rsn_ie_len; /* Peer MAC Address */ pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peer, ETH_ALEN, NULL, 0); /* PNonce */ pos = wpa_add_kde(pos, RSN_KEY_DATA_NONCE, key->key_nonce, WPA_NONCE_LEN, NULL, 0); /* SMK and INonce */ pos = wpa_add_kde(pos, RSN_KEY_DATA_SMK, smk, PMK_LEN, kde->nonce, WPA_NONCE_LEN); /* Lifetime */ lifetime = htonl(43200); /* dot11RSNAConfigSMKLifetime */ pos = wpa_add_kde(pos, RSN_KEY_DATA_LIFETIME, (u8 *) &lifetime, sizeof(lifetime), NULL, 0); wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, "Sending SMK M5"); __wpa_send_eapol(wpa_auth, sm, WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC | WPA_KEY_INFO_SMK_MESSAGE, NULL, kde->nonce, buf, pos - buf, 0, 1, 0); os_free(buf); } void wpa_smk_m3(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, struct wpa_eapol_key *key) { struct wpa_eapol_ie_parse kde; struct wpa_stsl_search search; u8 smk[32], buf[ETH_ALEN + 8 + 2 * WPA_NONCE_LEN], *pos; if (wpa_parse_kde_ies((const u8 *) (key + 1), WPA_GET_BE16(key->key_data_length), &kde) < 0) { wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M3"); return; } if (kde.rsn_ie == NULL || kde.mac_addr == NULL || kde.mac_addr_len < ETH_ALEN || kde.nonce == NULL || kde.nonce_len < WPA_NONCE_LEN) { wpa_printf(MSG_INFO, "RSN: No RSN IE, MAC address KDE, or " "Nonce KDE in SMK M3"); return; } /* Peer = sm->addr; Initiator = kde.mac_addr; * Peer Nonce = key->key_nonce; Initiator Nonce = kde.nonce */ search.addr = kde.mac_addr; search.sm = NULL; if (wpa_auth_for_each_sta(wpa_auth, wpa_stsl_select_sta, &search) == 0 || search.sm == NULL) { wpa_printf(MSG_DEBUG, "RSN: SMK handshake with " MACSTR " aborted - STA not associated anymore", MAC2STR(kde.mac_addr)); wpa_smk_send_error(wpa_auth, sm, kde.mac_addr, STK_MUI_SMK, STK_ERR_STA_NR); /* FIX: wpa_stsl_remove(wpa_auth, neg); */ return; } if (random_get_bytes(smk, PMK_LEN)) { wpa_printf(MSG_DEBUG, "RSN: Failed to generate SMK"); return; } /* SMK = PRF-256(Random number, "SMK Derivation", * AA || Time || INonce || PNonce) */ os_memcpy(buf, wpa_auth->addr, ETH_ALEN); pos = buf + ETH_ALEN; wpa_get_ntp_timestamp(pos); pos += 8; os_memcpy(pos, kde.nonce, WPA_NONCE_LEN); pos += WPA_NONCE_LEN; os_memcpy(pos, key->key_nonce, WPA_NONCE_LEN); #ifdef CONFIG_IEEE80211W sha256_prf(smk, PMK_LEN, "SMK Derivation", buf, sizeof(buf), smk, PMK_LEN); #else /* CONFIG_IEEE80211W */ sha1_prf(smk, PMK_LEN, "SMK Derivation", buf, sizeof(buf), smk, PMK_LEN); #endif /* CONFIG_IEEE80211W */ wpa_hexdump_key(MSG_DEBUG, "RSN: SMK", smk, PMK_LEN); wpa_send_smk_m4(wpa_auth, sm, key, &kde, smk); wpa_send_smk_m5(wpa_auth, search.sm, key, &kde, smk, sm->addr); /* Authenticator does not need SMK anymore and it is required to forget * it. */ os_memset(smk, 0, sizeof(*smk)); } void wpa_smk_error(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, struct wpa_eapol_key *key) { struct wpa_eapol_ie_parse kde; struct wpa_stsl_search search; struct rsn_error_kde error; u16 mui, error_type; if (wpa_parse_kde_ies((const u8 *) (key + 1), WPA_GET_BE16(key->key_data_length), &kde) < 0) { wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK Error"); return; } if (kde.mac_addr == NULL || kde.mac_addr_len < ETH_ALEN || kde.error == NULL || kde.error_len < sizeof(error)) { wpa_printf(MSG_INFO, "RSN: No MAC address or Error KDE in " "SMK Error"); return; } search.addr = kde.mac_addr; search.sm = NULL; if (wpa_auth_for_each_sta(wpa_auth, wpa_stsl_select_sta, &search) == 0 || search.sm == NULL) { wpa_printf(MSG_DEBUG, "RSN: Peer STA " MACSTR " not " "associated for SMK Error message from " MACSTR, MAC2STR(kde.mac_addr), MAC2STR(sm->addr)); return; } os_memcpy(&error, kde.error, sizeof(error)); mui = be_to_host16(error.mui); error_type = be_to_host16(error.error_type); wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, "STA reported SMK Error: Peer " MACSTR " MUI %d Error Type %d", MAC2STR(kde.mac_addr), mui, error_type); wpa_smk_send_error(wpa_auth, search.sm, sm->addr, mui, error_type); } int wpa_stsl_remove(struct wpa_authenticator *wpa_auth, struct wpa_stsl_negotiation *neg) { struct wpa_stsl_negotiation *pos, *prev; if (wpa_auth == NULL) return -1; pos = wpa_auth->stsl_negotiations; prev = NULL; while (pos) { if (pos == neg) { if (prev) prev->next = pos->next; else wpa_auth->stsl_negotiations = pos->next; eloop_cancel_timeout(wpa_stsl_step, wpa_auth, pos); os_free(pos); return 0; } prev = pos; pos = pos->next; } return -1; } #endif /* CONFIG_PEERKEY */ wpa-2.1/src/ap/pmksa_cache_auth.c000066400000000000000000000261651227414666700170060ustar00rootroot00000000000000/* * hostapd - PMKSA cache for IEEE 802.11i RSN * Copyright (c) 2004-2008, 2012, 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/eloop.h" #include "eapol_auth/eapol_auth_sm.h" #include "eapol_auth/eapol_auth_sm_i.h" #include "sta_info.h" #include "ap_config.h" #include "pmksa_cache_auth.h" static const int pmksa_cache_max_entries = 1024; static const int dot11RSNAConfigPMKLifetime = 43200; struct rsn_pmksa_cache { #define PMKID_HASH_SIZE 128 #define PMKID_HASH(pmkid) (unsigned int) ((pmkid)[0] & 0x7f) struct rsn_pmksa_cache_entry *pmkid[PMKID_HASH_SIZE]; struct rsn_pmksa_cache_entry *pmksa; int pmksa_count; void (*free_cb)(struct rsn_pmksa_cache_entry *entry, void *ctx); void *ctx; }; static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa); static void _pmksa_cache_free_entry(struct rsn_pmksa_cache_entry *entry) { if (entry == NULL) return; os_free(entry->identity); wpabuf_free(entry->cui); #ifndef CONFIG_NO_RADIUS radius_free_class(&entry->radius_class); #endif /* CONFIG_NO_RADIUS */ os_free(entry); } void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa, struct rsn_pmksa_cache_entry *entry) { struct rsn_pmksa_cache_entry *pos, *prev; pmksa->pmksa_count--; pmksa->free_cb(entry, pmksa->ctx); pos = pmksa->pmkid[PMKID_HASH(entry->pmkid)]; prev = NULL; while (pos) { if (pos == entry) { if (prev != NULL) { prev->hnext = pos->hnext; } else { pmksa->pmkid[PMKID_HASH(entry->pmkid)] = pos->hnext; } break; } prev = pos; pos = pos->hnext; } pos = pmksa->pmksa; prev = NULL; while (pos) { if (pos == entry) { if (prev != NULL) prev->next = pos->next; else pmksa->pmksa = pos->next; break; } prev = pos; pos = pos->next; } _pmksa_cache_free_entry(entry); } static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx) { struct rsn_pmksa_cache *pmksa = eloop_ctx; struct os_reltime now; os_get_reltime(&now); while (pmksa->pmksa && pmksa->pmksa->expiration <= now.sec) { wpa_printf(MSG_DEBUG, "RSN: expired PMKSA cache entry for " MACSTR, MAC2STR(pmksa->pmksa->spa)); pmksa_cache_free_entry(pmksa, pmksa->pmksa); } pmksa_cache_set_expiration(pmksa); } static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa) { int sec; struct os_reltime now; eloop_cancel_timeout(pmksa_cache_expire, pmksa, NULL); if (pmksa->pmksa == NULL) return; os_get_reltime(&now); sec = pmksa->pmksa->expiration - now.sec; if (sec < 0) sec = 0; eloop_register_timeout(sec + 1, 0, pmksa_cache_expire, pmksa, NULL); } static void pmksa_cache_from_eapol_data(struct rsn_pmksa_cache_entry *entry, struct eapol_state_machine *eapol) { if (eapol == NULL) return; if (eapol->identity) { entry->identity = os_malloc(eapol->identity_len); if (entry->identity) { entry->identity_len = eapol->identity_len; os_memcpy(entry->identity, eapol->identity, eapol->identity_len); } } if (eapol->radius_cui) entry->cui = wpabuf_dup(eapol->radius_cui); #ifndef CONFIG_NO_RADIUS radius_copy_class(&entry->radius_class, &eapol->radius_class); #endif /* CONFIG_NO_RADIUS */ entry->eap_type_authsrv = eapol->eap_type_authsrv; entry->vlan_id = ((struct sta_info *) eapol->sta)->vlan_id; } void pmksa_cache_to_eapol_data(struct rsn_pmksa_cache_entry *entry, struct eapol_state_machine *eapol) { if (entry == NULL || eapol == NULL) return; if (entry->identity) { os_free(eapol->identity); eapol->identity = os_malloc(entry->identity_len); if (eapol->identity) { eapol->identity_len = entry->identity_len; os_memcpy(eapol->identity, entry->identity, entry->identity_len); } wpa_hexdump_ascii(MSG_DEBUG, "STA identity from PMKSA", eapol->identity, eapol->identity_len); } if (entry->cui) { wpabuf_free(eapol->radius_cui); eapol->radius_cui = wpabuf_dup(entry->cui); } #ifndef CONFIG_NO_RADIUS radius_free_class(&eapol->radius_class); radius_copy_class(&eapol->radius_class, &entry->radius_class); #endif /* CONFIG_NO_RADIUS */ if (eapol->radius_class.attr) { wpa_printf(MSG_DEBUG, "Copied %lu Class attribute(s) from " "PMKSA", (unsigned long) eapol->radius_class.count); } eapol->eap_type_authsrv = entry->eap_type_authsrv; ((struct sta_info *) eapol->sta)->vlan_id = entry->vlan_id; } static void pmksa_cache_link_entry(struct rsn_pmksa_cache *pmksa, struct rsn_pmksa_cache_entry *entry) { struct rsn_pmksa_cache_entry *pos, *prev; /* Add the new entry; order by expiration time */ pos = pmksa->pmksa; prev = NULL; while (pos) { if (pos->expiration > entry->expiration) break; prev = pos; pos = pos->next; } if (prev == NULL) { entry->next = pmksa->pmksa; pmksa->pmksa = entry; } else { entry->next = prev->next; prev->next = entry; } entry->hnext = pmksa->pmkid[PMKID_HASH(entry->pmkid)]; pmksa->pmkid[PMKID_HASH(entry->pmkid)] = entry; pmksa->pmksa_count++; if (prev == NULL) pmksa_cache_set_expiration(pmksa); wpa_printf(MSG_DEBUG, "RSN: added PMKSA cache entry for " MACSTR, MAC2STR(entry->spa)); wpa_hexdump(MSG_DEBUG, "RSN: added PMKID", entry->pmkid, PMKID_LEN); } /** * pmksa_cache_auth_add - Add a PMKSA cache entry * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init() * @pmk: The new pairwise master key * @pmk_len: PMK length in bytes, usually PMK_LEN (32) * @aa: Authenticator address * @spa: Supplicant address * @session_timeout: Session timeout * @eapol: Pointer to EAPOL state machine data * @akmp: WPA_KEY_MGMT_* used in key derivation * Returns: Pointer to the added PMKSA cache entry or %NULL on error * * This function create a PMKSA entry for a new PMK and adds it to the PMKSA * cache. If an old entry is already in the cache for the same Supplicant, * this entry will be replaced with the new entry. PMKID will be calculated * based on the PMK. */ struct rsn_pmksa_cache_entry * pmksa_cache_auth_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, const u8 *aa, const u8 *spa, int session_timeout, struct eapol_state_machine *eapol, int akmp) { struct rsn_pmksa_cache_entry *entry, *pos; struct os_reltime now; if (pmk_len > PMK_LEN) return NULL; entry = os_zalloc(sizeof(*entry)); if (entry == NULL) return NULL; os_memcpy(entry->pmk, pmk, pmk_len); entry->pmk_len = pmk_len; rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid, wpa_key_mgmt_sha256(akmp)); os_get_reltime(&now); entry->expiration = now.sec; if (session_timeout > 0) entry->expiration += session_timeout; else entry->expiration += dot11RSNAConfigPMKLifetime; entry->akmp = akmp; os_memcpy(entry->spa, spa, ETH_ALEN); pmksa_cache_from_eapol_data(entry, eapol); /* Replace an old entry for the same STA (if found) with the new entry */ pos = pmksa_cache_auth_get(pmksa, spa, NULL); if (pos) pmksa_cache_free_entry(pmksa, pos); if (pmksa->pmksa_count >= pmksa_cache_max_entries && pmksa->pmksa) { /* Remove the oldest entry to make room for the new entry */ wpa_printf(MSG_DEBUG, "RSN: removed the oldest PMKSA cache " "entry (for " MACSTR ") to make room for new one", MAC2STR(pmksa->pmksa->spa)); pmksa_cache_free_entry(pmksa, pmksa->pmksa); } pmksa_cache_link_entry(pmksa, entry); return entry; } struct rsn_pmksa_cache_entry * pmksa_cache_add_okc(struct rsn_pmksa_cache *pmksa, const struct rsn_pmksa_cache_entry *old_entry, const u8 *aa, const u8 *pmkid) { struct rsn_pmksa_cache_entry *entry; entry = os_zalloc(sizeof(*entry)); if (entry == NULL) return NULL; os_memcpy(entry->pmkid, pmkid, PMKID_LEN); os_memcpy(entry->pmk, old_entry->pmk, old_entry->pmk_len); entry->pmk_len = old_entry->pmk_len; entry->expiration = old_entry->expiration; entry->akmp = old_entry->akmp; os_memcpy(entry->spa, old_entry->spa, ETH_ALEN); entry->opportunistic = 1; if (old_entry->identity) { entry->identity = os_malloc(old_entry->identity_len); if (entry->identity) { entry->identity_len = old_entry->identity_len; os_memcpy(entry->identity, old_entry->identity, old_entry->identity_len); } } if (old_entry->cui) entry->cui = wpabuf_dup(old_entry->cui); #ifndef CONFIG_NO_RADIUS radius_copy_class(&entry->radius_class, &old_entry->radius_class); #endif /* CONFIG_NO_RADIUS */ entry->eap_type_authsrv = old_entry->eap_type_authsrv; entry->vlan_id = old_entry->vlan_id; entry->opportunistic = 1; pmksa_cache_link_entry(pmksa, entry); return entry; } /** * pmksa_cache_auth_deinit - Free all entries in PMKSA cache * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init() */ void pmksa_cache_auth_deinit(struct rsn_pmksa_cache *pmksa) { struct rsn_pmksa_cache_entry *entry, *prev; int i; if (pmksa == NULL) return; entry = pmksa->pmksa; while (entry) { prev = entry; entry = entry->next; _pmksa_cache_free_entry(prev); } eloop_cancel_timeout(pmksa_cache_expire, pmksa, NULL); for (i = 0; i < PMKID_HASH_SIZE; i++) pmksa->pmkid[i] = NULL; os_free(pmksa); } /** * pmksa_cache_auth_get - Fetch a PMKSA cache entry * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init() * @spa: Supplicant address or %NULL to match any * @pmkid: PMKID or %NULL to match any * Returns: Pointer to PMKSA cache entry or %NULL if no match was found */ struct rsn_pmksa_cache_entry * pmksa_cache_auth_get(struct rsn_pmksa_cache *pmksa, const u8 *spa, const u8 *pmkid) { struct rsn_pmksa_cache_entry *entry; if (pmkid) entry = pmksa->pmkid[PMKID_HASH(pmkid)]; else entry = pmksa->pmksa; while (entry) { if ((spa == NULL || os_memcmp(entry->spa, spa, ETH_ALEN) == 0) && (pmkid == NULL || os_memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0)) return entry; entry = pmkid ? entry->hnext : entry->next; } return NULL; } /** * pmksa_cache_get_okc - Fetch a PMKSA cache entry using OKC * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init() * @aa: Authenticator address * @spa: Supplicant address * @pmkid: PMKID * Returns: Pointer to PMKSA cache entry or %NULL if no match was found * * Use opportunistic key caching (OKC) to find a PMK for a supplicant. */ struct rsn_pmksa_cache_entry * pmksa_cache_get_okc( struct rsn_pmksa_cache *pmksa, const u8 *aa, const u8 *spa, const u8 *pmkid) { struct rsn_pmksa_cache_entry *entry; u8 new_pmkid[PMKID_LEN]; entry = pmksa->pmksa; while (entry) { if (os_memcmp(entry->spa, spa, ETH_ALEN) != 0) continue; rsn_pmkid(entry->pmk, entry->pmk_len, aa, spa, new_pmkid, wpa_key_mgmt_sha256(entry->akmp)); if (os_memcmp(new_pmkid, pmkid, PMKID_LEN) == 0) return entry; entry = entry->next; } return NULL; } /** * pmksa_cache_auth_init - Initialize PMKSA cache * @free_cb: Callback function to be called when a PMKSA cache entry is freed * @ctx: Context pointer for free_cb function * Returns: Pointer to PMKSA cache data or %NULL on failure */ struct rsn_pmksa_cache * pmksa_cache_auth_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry, void *ctx), void *ctx) { struct rsn_pmksa_cache *pmksa; pmksa = os_zalloc(sizeof(*pmksa)); if (pmksa) { pmksa->free_cb = free_cb; pmksa->ctx = ctx; } return pmksa; } wpa-2.1/src/ap/pmksa_cache_auth.h000066400000000000000000000034631227414666700170070ustar00rootroot00000000000000/* * hostapd - PMKSA cache for IEEE 802.11i RSN * Copyright (c) 2004-2008, 2012, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef PMKSA_CACHE_H #define PMKSA_CACHE_H #include "radius/radius.h" /** * struct rsn_pmksa_cache_entry - PMKSA cache entry */ struct rsn_pmksa_cache_entry { struct rsn_pmksa_cache_entry *next, *hnext; u8 pmkid[PMKID_LEN]; u8 pmk[PMK_LEN]; size_t pmk_len; os_time_t expiration; int akmp; /* WPA_KEY_MGMT_* */ u8 spa[ETH_ALEN]; u8 *identity; size_t identity_len; struct wpabuf *cui; struct radius_class_data radius_class; u8 eap_type_authsrv; int vlan_id; int opportunistic; }; struct rsn_pmksa_cache; struct rsn_pmksa_cache * pmksa_cache_auth_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry, void *ctx), void *ctx); void pmksa_cache_auth_deinit(struct rsn_pmksa_cache *pmksa); struct rsn_pmksa_cache_entry * pmksa_cache_auth_get(struct rsn_pmksa_cache *pmksa, const u8 *spa, const u8 *pmkid); struct rsn_pmksa_cache_entry * pmksa_cache_get_okc( struct rsn_pmksa_cache *pmksa, const u8 *spa, const u8 *aa, const u8 *pmkid); struct rsn_pmksa_cache_entry * pmksa_cache_auth_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, const u8 *aa, const u8 *spa, int session_timeout, struct eapol_state_machine *eapol, int akmp); struct rsn_pmksa_cache_entry * pmksa_cache_add_okc(struct rsn_pmksa_cache *pmksa, const struct rsn_pmksa_cache_entry *old_entry, const u8 *aa, const u8 *pmkid); void pmksa_cache_to_eapol_data(struct rsn_pmksa_cache_entry *entry, struct eapol_state_machine *eapol); void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa, struct rsn_pmksa_cache_entry *entry); #endif /* PMKSA_CACHE_H */ wpa-2.1/src/ap/preauth_auth.c000066400000000000000000000147421227414666700162160ustar00rootroot00000000000000/* * hostapd - Authenticator for IEEE 802.11i RSN pre-authentication * Copyright (c) 2004-2007, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "utils/includes.h" #ifdef CONFIG_RSN_PREAUTH #include "utils/common.h" #include "utils/eloop.h" #include "l2_packet/l2_packet.h" #include "common/wpa_common.h" #include "eapol_auth/eapol_auth_sm.h" #include "eapol_auth/eapol_auth_sm_i.h" #include "hostapd.h" #include "ap_config.h" #include "ieee802_1x.h" #include "sta_info.h" #include "wpa_auth.h" #include "preauth_auth.h" #ifndef ETH_P_PREAUTH #define ETH_P_PREAUTH 0x88C7 /* IEEE 802.11i pre-authentication */ #endif /* ETH_P_PREAUTH */ static const int dot11RSNAConfigPMKLifetime = 43200; struct rsn_preauth_interface { struct rsn_preauth_interface *next; struct hostapd_data *hapd; struct l2_packet_data *l2; char *ifname; int ifindex; }; static void rsn_preauth_receive(void *ctx, const u8 *src_addr, const u8 *buf, size_t len) { struct rsn_preauth_interface *piface = ctx; struct hostapd_data *hapd = piface->hapd; struct ieee802_1x_hdr *hdr; struct sta_info *sta; struct l2_ethhdr *ethhdr; wpa_printf(MSG_DEBUG, "RSN: receive pre-auth packet " "from interface '%s'", piface->ifname); if (len < sizeof(*ethhdr) + sizeof(*hdr)) { wpa_printf(MSG_DEBUG, "RSN: too short pre-auth packet " "(len=%lu)", (unsigned long) len); return; } ethhdr = (struct l2_ethhdr *) buf; hdr = (struct ieee802_1x_hdr *) (ethhdr + 1); if (os_memcmp(ethhdr->h_dest, hapd->own_addr, ETH_ALEN) != 0) { wpa_printf(MSG_DEBUG, "RSN: pre-auth for foreign address " MACSTR, MAC2STR(ethhdr->h_dest)); return; } sta = ap_get_sta(hapd, ethhdr->h_source); if (sta && (sta->flags & WLAN_STA_ASSOC)) { wpa_printf(MSG_DEBUG, "RSN: pre-auth for already association " "STA " MACSTR, MAC2STR(sta->addr)); return; } if (!sta && hdr->type == IEEE802_1X_TYPE_EAPOL_START) { sta = ap_sta_add(hapd, ethhdr->h_source); if (sta == NULL) return; sta->flags = WLAN_STA_PREAUTH; ieee802_1x_new_station(hapd, sta); if (sta->eapol_sm == NULL) { ap_free_sta(hapd, sta); sta = NULL; } else { sta->eapol_sm->radius_identifier = -1; sta->eapol_sm->portValid = TRUE; sta->eapol_sm->flags |= EAPOL_SM_PREAUTH; } } if (sta == NULL) return; sta->preauth_iface = piface; ieee802_1x_receive(hapd, ethhdr->h_source, (u8 *) (ethhdr + 1), len - sizeof(*ethhdr)); } static int rsn_preauth_iface_add(struct hostapd_data *hapd, const char *ifname) { struct rsn_preauth_interface *piface; wpa_printf(MSG_DEBUG, "RSN pre-auth interface '%s'", ifname); piface = os_zalloc(sizeof(*piface)); if (piface == NULL) return -1; piface->hapd = hapd; piface->ifname = os_strdup(ifname); if (piface->ifname == NULL) { goto fail1; } piface->l2 = l2_packet_init(piface->ifname, NULL, ETH_P_PREAUTH, rsn_preauth_receive, piface, 1); if (piface->l2 == NULL) { wpa_printf(MSG_ERROR, "Failed to open register layer 2 access " "to ETH_P_PREAUTH"); goto fail2; } piface->next = hapd->preauth_iface; hapd->preauth_iface = piface; return 0; fail2: os_free(piface->ifname); fail1: os_free(piface); return -1; } void rsn_preauth_iface_deinit(struct hostapd_data *hapd) { struct rsn_preauth_interface *piface, *prev; piface = hapd->preauth_iface; hapd->preauth_iface = NULL; while (piface) { prev = piface; piface = piface->next; l2_packet_deinit(prev->l2); os_free(prev->ifname); os_free(prev); } } int rsn_preauth_iface_init(struct hostapd_data *hapd) { char *tmp, *start, *end; if (hapd->conf->rsn_preauth_interfaces == NULL) return 0; tmp = os_strdup(hapd->conf->rsn_preauth_interfaces); if (tmp == NULL) return -1; start = tmp; for (;;) { while (*start == ' ') start++; if (*start == '\0') break; end = os_strchr(start, ' '); if (end) *end = '\0'; if (rsn_preauth_iface_add(hapd, start)) { rsn_preauth_iface_deinit(hapd); os_free(tmp); return -1; } if (end) start = end + 1; else break; } os_free(tmp); return 0; } static void rsn_preauth_finished_cb(void *eloop_ctx, void *timeout_ctx) { struct hostapd_data *hapd = eloop_ctx; struct sta_info *sta = timeout_ctx; wpa_printf(MSG_DEBUG, "RSN: Removing pre-authentication STA entry for " MACSTR, MAC2STR(sta->addr)); ap_free_sta(hapd, sta); } void rsn_preauth_finished(struct hostapd_data *hapd, struct sta_info *sta, int success) { const u8 *key; size_t len; hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, HOSTAPD_LEVEL_INFO, "pre-authentication %s", success ? "succeeded" : "failed"); key = ieee802_1x_get_key(sta->eapol_sm, &len); if (len > PMK_LEN) len = PMK_LEN; if (success && key) { if (wpa_auth_pmksa_add_preauth(hapd->wpa_auth, key, len, sta->addr, dot11RSNAConfigPMKLifetime, sta->eapol_sm) == 0) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, HOSTAPD_LEVEL_DEBUG, "added PMKSA cache entry (pre-auth)"); } else { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, HOSTAPD_LEVEL_DEBUG, "failed to add PMKSA cache entry " "(pre-auth)"); } } /* * Finish STA entry removal from timeout in order to avoid freeing * STA data before the caller has finished processing. */ eloop_register_timeout(0, 0, rsn_preauth_finished_cb, hapd, sta); } void rsn_preauth_send(struct hostapd_data *hapd, struct sta_info *sta, u8 *buf, size_t len) { struct rsn_preauth_interface *piface; struct l2_ethhdr *ethhdr; piface = hapd->preauth_iface; while (piface) { if (piface == sta->preauth_iface) break; piface = piface->next; } if (piface == NULL) { wpa_printf(MSG_DEBUG, "RSN: Could not find pre-authentication " "interface for " MACSTR, MAC2STR(sta->addr)); return; } ethhdr = os_malloc(sizeof(*ethhdr) + len); if (ethhdr == NULL) return; os_memcpy(ethhdr->h_dest, sta->addr, ETH_ALEN); os_memcpy(ethhdr->h_source, hapd->own_addr, ETH_ALEN); ethhdr->h_proto = host_to_be16(ETH_P_PREAUTH); os_memcpy(ethhdr + 1, buf, len); if (l2_packet_send(piface->l2, sta->addr, ETH_P_PREAUTH, (u8 *) ethhdr, sizeof(*ethhdr) + len) < 0) { wpa_printf(MSG_ERROR, "Failed to send preauth packet using " "l2_packet_send\n"); } os_free(ethhdr); } void rsn_preauth_free_station(struct hostapd_data *hapd, struct sta_info *sta) { eloop_cancel_timeout(rsn_preauth_finished_cb, hapd, sta); } #endif /* CONFIG_RSN_PREAUTH */ wpa-2.1/src/ap/preauth_auth.h000066400000000000000000000024061227414666700162150ustar00rootroot00000000000000/* * hostapd - Authenticator for IEEE 802.11i RSN pre-authentication * Copyright (c) 2004-2005, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef PREAUTH_H #define PREAUTH_H #ifdef CONFIG_RSN_PREAUTH int rsn_preauth_iface_init(struct hostapd_data *hapd); void rsn_preauth_iface_deinit(struct hostapd_data *hapd); void rsn_preauth_finished(struct hostapd_data *hapd, struct sta_info *sta, int success); void rsn_preauth_send(struct hostapd_data *hapd, struct sta_info *sta, u8 *buf, size_t len); void rsn_preauth_free_station(struct hostapd_data *hapd, struct sta_info *sta); #else /* CONFIG_RSN_PREAUTH */ static inline int rsn_preauth_iface_init(struct hostapd_data *hapd) { return 0; } static inline void rsn_preauth_iface_deinit(struct hostapd_data *hapd) { } static inline void rsn_preauth_finished(struct hostapd_data *hapd, struct sta_info *sta, int success) { } static inline void rsn_preauth_send(struct hostapd_data *hapd, struct sta_info *sta, u8 *buf, size_t len) { } static inline void rsn_preauth_free_station(struct hostapd_data *hapd, struct sta_info *sta) { } #endif /* CONFIG_RSN_PREAUTH */ #endif /* PREAUTH_H */ wpa-2.1/src/ap/sta_info.c000066400000000000000000000726751227414666700153400ustar00rootroot00000000000000/* * hostapd / Station table * Copyright (c) 2002-2013, 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/eloop.h" #include "common/ieee802_11_defs.h" #include "common/wpa_ctrl.h" #include "common/sae.h" #include "radius/radius.h" #include "radius/radius_client.h" #include "p2p/p2p.h" #include "hostapd.h" #include "accounting.h" #include "ieee802_1x.h" #include "ieee802_11.h" #include "ieee802_11_auth.h" #include "wpa_auth.h" #include "preauth_auth.h" #include "ap_config.h" #include "beacon.h" #include "ap_mlme.h" #include "vlan_init.h" #include "p2p_hostapd.h" #include "ap_drv_ops.h" #include "gas_serv.h" #include "sta_info.h" static void ap_sta_remove_in_other_bss(struct hostapd_data *hapd, struct sta_info *sta); static void ap_handle_session_timer(void *eloop_ctx, void *timeout_ctx); static void ap_sta_deauth_cb_timeout(void *eloop_ctx, void *timeout_ctx); static void ap_sta_disassoc_cb_timeout(void *eloop_ctx, void *timeout_ctx); #ifdef CONFIG_IEEE80211W static void ap_sa_query_timer(void *eloop_ctx, void *timeout_ctx); #endif /* CONFIG_IEEE80211W */ static int ap_sta_remove(struct hostapd_data *hapd, struct sta_info *sta); int ap_for_each_sta(struct hostapd_data *hapd, int (*cb)(struct hostapd_data *hapd, struct sta_info *sta, void *ctx), void *ctx) { struct sta_info *sta; for (sta = hapd->sta_list; sta; sta = sta->next) { if (cb(hapd, sta, ctx)) return 1; } return 0; } struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta) { struct sta_info *s; s = hapd->sta_hash[STA_HASH(sta)]; while (s != NULL && os_memcmp(s->addr, sta, 6) != 0) s = s->hnext; return s; } #ifdef CONFIG_P2P struct sta_info * ap_get_sta_p2p(struct hostapd_data *hapd, const u8 *addr) { struct sta_info *sta; for (sta = hapd->sta_list; sta; sta = sta->next) { const u8 *p2p_dev_addr; if (sta->p2p_ie == NULL) continue; p2p_dev_addr = p2p_get_go_dev_addr(sta->p2p_ie); if (p2p_dev_addr == NULL) continue; if (os_memcmp(p2p_dev_addr, addr, ETH_ALEN) == 0) return sta; } return NULL; } #endif /* CONFIG_P2P */ static void ap_sta_list_del(struct hostapd_data *hapd, struct sta_info *sta) { struct sta_info *tmp; if (hapd->sta_list == sta) { hapd->sta_list = sta->next; return; } tmp = hapd->sta_list; while (tmp != NULL && tmp->next != sta) tmp = tmp->next; if (tmp == NULL) { wpa_printf(MSG_DEBUG, "Could not remove STA " MACSTR " from " "list.", MAC2STR(sta->addr)); } else tmp->next = sta->next; } void ap_sta_hash_add(struct hostapd_data *hapd, struct sta_info *sta) { sta->hnext = hapd->sta_hash[STA_HASH(sta->addr)]; hapd->sta_hash[STA_HASH(sta->addr)] = sta; } static void ap_sta_hash_del(struct hostapd_data *hapd, struct sta_info *sta) { struct sta_info *s; s = hapd->sta_hash[STA_HASH(sta->addr)]; if (s == NULL) return; if (os_memcmp(s->addr, sta->addr, 6) == 0) { hapd->sta_hash[STA_HASH(sta->addr)] = s->hnext; return; } while (s->hnext != NULL && os_memcmp(s->hnext->addr, sta->addr, ETH_ALEN) != 0) s = s->hnext; if (s->hnext != NULL) s->hnext = s->hnext->hnext; else wpa_printf(MSG_DEBUG, "AP: could not remove STA " MACSTR " from hash table", MAC2STR(sta->addr)); } void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) { int set_beacon = 0; accounting_sta_stop(hapd, sta); /* just in case */ ap_sta_set_authorized(hapd, sta, 0); if (sta->flags & WLAN_STA_WDS) hostapd_set_wds_sta(hapd, NULL, sta->addr, sta->aid, 0); if (!(sta->flags & WLAN_STA_PREAUTH)) hostapd_drv_sta_remove(hapd, sta->addr); ap_sta_hash_del(hapd, sta); ap_sta_list_del(hapd, sta); if (sta->aid > 0) hapd->sta_aid[(sta->aid - 1) / 32] &= ~BIT((sta->aid - 1) % 32); hapd->num_sta--; if (sta->nonerp_set) { sta->nonerp_set = 0; hapd->iface->num_sta_non_erp--; if (hapd->iface->num_sta_non_erp == 0) set_beacon++; } if (sta->no_short_slot_time_set) { sta->no_short_slot_time_set = 0; hapd->iface->num_sta_no_short_slot_time--; if (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G && hapd->iface->num_sta_no_short_slot_time == 0) set_beacon++; } if (sta->no_short_preamble_set) { sta->no_short_preamble_set = 0; hapd->iface->num_sta_no_short_preamble--; if (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G && hapd->iface->num_sta_no_short_preamble == 0) set_beacon++; } if (sta->no_ht_gf_set) { sta->no_ht_gf_set = 0; hapd->iface->num_sta_ht_no_gf--; } if (sta->no_ht_set) { sta->no_ht_set = 0; hapd->iface->num_sta_no_ht--; } if (sta->ht_20mhz_set) { sta->ht_20mhz_set = 0; hapd->iface->num_sta_ht_20mhz--; } #ifdef CONFIG_P2P if (sta->no_p2p_set) { sta->no_p2p_set = 0; hapd->num_sta_no_p2p--; if (hapd->num_sta_no_p2p == 0) hostapd_p2p_non_p2p_sta_disconnected(hapd); } #endif /* CONFIG_P2P */ #if defined(NEED_AP_MLME) && defined(CONFIG_IEEE80211N) if (hostapd_ht_operation_update(hapd->iface) > 0) set_beacon++; #endif /* NEED_AP_MLME && CONFIG_IEEE80211N */ if (set_beacon) ieee802_11_set_beacons(hapd->iface); wpa_printf(MSG_DEBUG, "%s: cancel ap_handle_timer for " MACSTR, __func__, MAC2STR(sta->addr)); eloop_cancel_timeout(ap_handle_timer, hapd, sta); eloop_cancel_timeout(ap_handle_session_timer, hapd, sta); eloop_cancel_timeout(ap_sta_deauth_cb_timeout, hapd, sta); eloop_cancel_timeout(ap_sta_disassoc_cb_timeout, hapd, sta); ieee802_1x_free_station(sta); wpa_auth_sta_deinit(sta->wpa_sm); rsn_preauth_free_station(hapd, sta); #ifndef CONFIG_NO_RADIUS if (hapd->radius) radius_client_flush_auth(hapd->radius, sta->addr); #endif /* CONFIG_NO_RADIUS */ os_free(sta->last_assoc_req); os_free(sta->challenge); #ifdef CONFIG_IEEE80211W os_free(sta->sa_query_trans_id); eloop_cancel_timeout(ap_sa_query_timer, hapd, sta); #endif /* CONFIG_IEEE80211W */ #ifdef CONFIG_P2P p2p_group_notif_disassoc(hapd->p2p_group, sta->addr); #endif /* CONFIG_P2P */ #ifdef CONFIG_INTERWORKING if (sta->gas_dialog) { int i; for (i = 0; i < GAS_DIALOG_MAX; i++) gas_serv_dialog_clear(&sta->gas_dialog[i]); os_free(sta->gas_dialog); } #endif /* CONFIG_INTERWORKING */ wpabuf_free(sta->wps_ie); wpabuf_free(sta->p2p_ie); wpabuf_free(sta->hs20_ie); os_free(sta->ht_capabilities); os_free(sta->vht_capabilities); hostapd_free_psk_list(sta->psk); os_free(sta->identity); os_free(sta->radius_cui); #ifdef CONFIG_SAE sae_clear_data(sta->sae); os_free(sta->sae); #endif /* CONFIG_SAE */ os_free(sta); } void hostapd_free_stas(struct hostapd_data *hapd) { struct sta_info *sta, *prev; sta = hapd->sta_list; while (sta) { prev = sta; if (sta->flags & WLAN_STA_AUTH) { mlme_deauthenticate_indication( hapd, sta, WLAN_REASON_UNSPECIFIED); } sta = sta->next; wpa_printf(MSG_DEBUG, "Removing station " MACSTR, MAC2STR(prev->addr)); ap_free_sta(hapd, prev); } } /** * ap_handle_timer - Per STA timer handler * @eloop_ctx: struct hostapd_data * * @timeout_ctx: struct sta_info * * * This function is called to check station activity and to remove inactive * stations. */ void ap_handle_timer(void *eloop_ctx, void *timeout_ctx) { struct hostapd_data *hapd = eloop_ctx; struct sta_info *sta = timeout_ctx; unsigned long next_time = 0; int reason; wpa_printf(MSG_DEBUG, "%s: " MACSTR " flags=0x%x timeout_next=%d", __func__, MAC2STR(sta->addr), sta->flags, sta->timeout_next); if (sta->timeout_next == STA_REMOVE) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "deauthenticated due to " "local deauth request"); ap_free_sta(hapd, sta); return; } if ((sta->flags & WLAN_STA_ASSOC) && (sta->timeout_next == STA_NULLFUNC || sta->timeout_next == STA_DISASSOC)) { int inactive_sec; /* * Add random value to timeout so that we don't end up bouncing * all stations at the same time if we have lots of associated * stations that are idle (but keep re-associating). */ int fuzz = os_random() % 20; inactive_sec = hostapd_drv_get_inact_sec(hapd, sta->addr); if (inactive_sec == -1) { wpa_msg(hapd->msg_ctx, MSG_DEBUG, "Check inactivity: Could not " "get station info from kernel driver for " MACSTR, MAC2STR(sta->addr)); /* * The driver may not support this functionality. * Anyway, try again after the next inactivity timeout, * but do not disconnect the station now. */ next_time = hapd->conf->ap_max_inactivity + fuzz; } else if (inactive_sec < hapd->conf->ap_max_inactivity && sta->flags & WLAN_STA_ASSOC) { /* station activity detected; reset timeout state */ wpa_msg(hapd->msg_ctx, MSG_DEBUG, "Station " MACSTR " has been active %is ago", MAC2STR(sta->addr), inactive_sec); sta->timeout_next = STA_NULLFUNC; next_time = hapd->conf->ap_max_inactivity + fuzz - inactive_sec; } else { wpa_msg(hapd->msg_ctx, MSG_DEBUG, "Station " MACSTR " has been " "inactive too long: %d sec, max allowed: %d", MAC2STR(sta->addr), inactive_sec, hapd->conf->ap_max_inactivity); if (hapd->conf->skip_inactivity_poll) sta->timeout_next = STA_DISASSOC; } } if ((sta->flags & WLAN_STA_ASSOC) && sta->timeout_next == STA_DISASSOC && !(sta->flags & WLAN_STA_PENDING_POLL) && !hapd->conf->skip_inactivity_poll) { wpa_msg(hapd->msg_ctx, MSG_DEBUG, "Station " MACSTR " has ACKed data poll", MAC2STR(sta->addr)); /* data nullfunc frame poll did not produce TX errors; assume * station ACKed it */ sta->timeout_next = STA_NULLFUNC; next_time = hapd->conf->ap_max_inactivity; } if (next_time) { wpa_printf(MSG_DEBUG, "%s: register ap_handle_timer timeout " "for " MACSTR " (%lu seconds)", __func__, MAC2STR(sta->addr), next_time); eloop_register_timeout(next_time, 0, ap_handle_timer, hapd, sta); return; } if (sta->timeout_next == STA_NULLFUNC && (sta->flags & WLAN_STA_ASSOC)) { wpa_printf(MSG_DEBUG, " Polling STA"); sta->flags |= WLAN_STA_PENDING_POLL; hostapd_drv_poll_client(hapd, hapd->own_addr, sta->addr, sta->flags & WLAN_STA_WMM); } else if (sta->timeout_next != STA_REMOVE) { int deauth = sta->timeout_next == STA_DEAUTH; wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "Timeout, sending %s info to STA " MACSTR, deauth ? "deauthentication" : "disassociation", MAC2STR(sta->addr)); if (deauth) { hostapd_drv_sta_deauth( hapd, sta->addr, WLAN_REASON_PREV_AUTH_NOT_VALID); } else { reason = (sta->timeout_next == STA_DISASSOC) ? WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY : WLAN_REASON_PREV_AUTH_NOT_VALID; hostapd_drv_sta_disassoc(hapd, sta->addr, reason); } } switch (sta->timeout_next) { case STA_NULLFUNC: sta->timeout_next = STA_DISASSOC; wpa_printf(MSG_DEBUG, "%s: register ap_handle_timer timeout " "for " MACSTR " (%d seconds - AP_DISASSOC_DELAY)", __func__, MAC2STR(sta->addr), AP_DISASSOC_DELAY); eloop_register_timeout(AP_DISASSOC_DELAY, 0, ap_handle_timer, hapd, sta); break; case STA_DISASSOC: case STA_DISASSOC_FROM_CLI: ap_sta_set_authorized(hapd, sta, 0); sta->flags &= ~WLAN_STA_ASSOC; ieee802_1x_notify_port_enabled(sta->eapol_sm, 0); if (!sta->acct_terminate_cause) sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_IDLE_TIMEOUT; accounting_sta_stop(hapd, sta); ieee802_1x_free_station(sta); hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "disassociated due to " "inactivity"); reason = (sta->timeout_next == STA_DISASSOC) ? WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY : WLAN_REASON_PREV_AUTH_NOT_VALID; sta->timeout_next = STA_DEAUTH; wpa_printf(MSG_DEBUG, "%s: register ap_handle_timer timeout " "for " MACSTR " (%d seconds - AP_DEAUTH_DELAY)", __func__, MAC2STR(sta->addr), AP_DEAUTH_DELAY); eloop_register_timeout(AP_DEAUTH_DELAY, 0, ap_handle_timer, hapd, sta); mlme_disassociate_indication(hapd, sta, reason); break; case STA_DEAUTH: case STA_REMOVE: hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "deauthenticated due to " "inactivity (timer DEAUTH/REMOVE)"); if (!sta->acct_terminate_cause) sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_IDLE_TIMEOUT; mlme_deauthenticate_indication( hapd, sta, WLAN_REASON_PREV_AUTH_NOT_VALID); ap_free_sta(hapd, sta); break; } } static void ap_handle_session_timer(void *eloop_ctx, void *timeout_ctx) { struct hostapd_data *hapd = eloop_ctx; struct sta_info *sta = timeout_ctx; u8 addr[ETH_ALEN]; if (!(sta->flags & WLAN_STA_AUTH)) { if (sta->flags & WLAN_STA_GAS) { wpa_printf(MSG_DEBUG, "GAS: Remove temporary STA " "entry " MACSTR, MAC2STR(sta->addr)); ap_free_sta(hapd, sta); } return; } mlme_deauthenticate_indication(hapd, sta, WLAN_REASON_PREV_AUTH_NOT_VALID); hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "deauthenticated due to " "session timeout"); sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_SESSION_TIMEOUT; os_memcpy(addr, sta->addr, ETH_ALEN); ap_free_sta(hapd, sta); hostapd_drv_sta_deauth(hapd, addr, WLAN_REASON_PREV_AUTH_NOT_VALID); } void ap_sta_replenish_timeout(struct hostapd_data *hapd, struct sta_info *sta, u32 session_timeout) { if (eloop_replenish_timeout(session_timeout, 0, ap_handle_session_timer, hapd, sta) == 1) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "setting session timeout " "to %d seconds", session_timeout); } } void ap_sta_session_timeout(struct hostapd_data *hapd, struct sta_info *sta, u32 session_timeout) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "setting session timeout to %d " "seconds", session_timeout); eloop_cancel_timeout(ap_handle_session_timer, hapd, sta); eloop_register_timeout(session_timeout, 0, ap_handle_session_timer, hapd, sta); } void ap_sta_no_session_timeout(struct hostapd_data *hapd, struct sta_info *sta) { eloop_cancel_timeout(ap_handle_session_timer, hapd, sta); } struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr) { struct sta_info *sta; sta = ap_get_sta(hapd, addr); if (sta) return sta; wpa_printf(MSG_DEBUG, " New STA"); if (hapd->num_sta >= hapd->conf->max_num_sta) { /* FIX: might try to remove some old STAs first? */ wpa_printf(MSG_DEBUG, "no more room for new STAs (%d/%d)", hapd->num_sta, hapd->conf->max_num_sta); return NULL; } sta = os_zalloc(sizeof(struct sta_info)); if (sta == NULL) { wpa_printf(MSG_ERROR, "malloc failed"); return NULL; } sta->acct_interim_interval = hapd->conf->acct_interim_interval; accounting_sta_get_id(hapd, sta); if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_INACTIVITY_TIMER)) { wpa_printf(MSG_DEBUG, "%s: register ap_handle_timer timeout " "for " MACSTR " (%d seconds - ap_max_inactivity)", __func__, MAC2STR(addr), hapd->conf->ap_max_inactivity); eloop_register_timeout(hapd->conf->ap_max_inactivity, 0, ap_handle_timer, hapd, sta); } /* initialize STA info data */ os_memcpy(sta->addr, addr, ETH_ALEN); sta->next = hapd->sta_list; hapd->sta_list = sta; hapd->num_sta++; ap_sta_hash_add(hapd, sta); sta->ssid = &hapd->conf->ssid; ap_sta_remove_in_other_bss(hapd, sta); return sta; } static int ap_sta_remove(struct hostapd_data *hapd, struct sta_info *sta) { ieee802_1x_notify_port_enabled(sta->eapol_sm, 0); wpa_printf(MSG_DEBUG, "Removing STA " MACSTR " from kernel driver", MAC2STR(sta->addr)); if (hostapd_drv_sta_remove(hapd, sta->addr) && sta->flags & WLAN_STA_ASSOC) { wpa_printf(MSG_DEBUG, "Could not remove station " MACSTR " from kernel driver.", MAC2STR(sta->addr)); return -1; } return 0; } static void ap_sta_remove_in_other_bss(struct hostapd_data *hapd, struct sta_info *sta) { struct hostapd_iface *iface = hapd->iface; size_t i; for (i = 0; i < iface->num_bss; i++) { struct hostapd_data *bss = iface->bss[i]; struct sta_info *sta2; /* bss should always be set during operation, but it may be * NULL during reconfiguration. Assume the STA is not * associated to another BSS in that case to avoid NULL pointer * dereferences. */ if (bss == hapd || bss == NULL) continue; sta2 = ap_get_sta(bss, sta->addr); if (!sta2) continue; ap_sta_disconnect(bss, sta2, sta2->addr, WLAN_REASON_PREV_AUTH_NOT_VALID); } } static void ap_sta_disassoc_cb_timeout(void *eloop_ctx, void *timeout_ctx) { struct hostapd_data *hapd = eloop_ctx; struct sta_info *sta = timeout_ctx; ap_sta_remove(hapd, sta); mlme_disassociate_indication(hapd, sta, sta->disassoc_reason); } void ap_sta_disassociate(struct hostapd_data *hapd, struct sta_info *sta, u16 reason) { wpa_printf(MSG_DEBUG, "%s: disassociate STA " MACSTR, hapd->conf->iface, MAC2STR(sta->addr)); sta->flags &= ~(WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK); ap_sta_set_authorized(hapd, sta, 0); sta->timeout_next = STA_DEAUTH; wpa_printf(MSG_DEBUG, "%s: reschedule ap_handle_timer timeout " "for " MACSTR " (%d seconds - " "AP_MAX_INACTIVITY_AFTER_DISASSOC)", __func__, MAC2STR(sta->addr), AP_MAX_INACTIVITY_AFTER_DISASSOC); eloop_cancel_timeout(ap_handle_timer, hapd, sta); eloop_register_timeout(AP_MAX_INACTIVITY_AFTER_DISASSOC, 0, ap_handle_timer, hapd, sta); accounting_sta_stop(hapd, sta); ieee802_1x_free_station(sta); sta->disassoc_reason = reason; sta->flags |= WLAN_STA_PENDING_DISASSOC_CB; eloop_cancel_timeout(ap_sta_disassoc_cb_timeout, hapd, sta); eloop_register_timeout(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS ? 2 : 0, 0, ap_sta_disassoc_cb_timeout, hapd, sta); } static void ap_sta_deauth_cb_timeout(void *eloop_ctx, void *timeout_ctx) { struct hostapd_data *hapd = eloop_ctx; struct sta_info *sta = timeout_ctx; ap_sta_remove(hapd, sta); mlme_deauthenticate_indication(hapd, sta, sta->deauth_reason); } void ap_sta_deauthenticate(struct hostapd_data *hapd, struct sta_info *sta, u16 reason) { wpa_printf(MSG_DEBUG, "%s: deauthenticate STA " MACSTR, hapd->conf->iface, MAC2STR(sta->addr)); sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC); ap_sta_set_authorized(hapd, sta, 0); sta->timeout_next = STA_REMOVE; wpa_printf(MSG_DEBUG, "%s: reschedule ap_handle_timer timeout " "for " MACSTR " (%d seconds - " "AP_MAX_INACTIVITY_AFTER_DEAUTH)", __func__, MAC2STR(sta->addr), AP_MAX_INACTIVITY_AFTER_DEAUTH); eloop_cancel_timeout(ap_handle_timer, hapd, sta); eloop_register_timeout(AP_MAX_INACTIVITY_AFTER_DEAUTH, 0, ap_handle_timer, hapd, sta); accounting_sta_stop(hapd, sta); ieee802_1x_free_station(sta); sta->deauth_reason = reason; sta->flags |= WLAN_STA_PENDING_DEAUTH_CB; eloop_cancel_timeout(ap_sta_deauth_cb_timeout, hapd, sta); eloop_register_timeout(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS ? 2 : 0, 0, ap_sta_deauth_cb_timeout, hapd, sta); } #ifdef CONFIG_WPS int ap_sta_wps_cancel(struct hostapd_data *hapd, struct sta_info *sta, void *ctx) { if (sta && (sta->flags & WLAN_STA_WPS)) { ap_sta_deauthenticate(hapd, sta, WLAN_REASON_PREV_AUTH_NOT_VALID); wpa_printf(MSG_DEBUG, "WPS: %s: Deauth sta=" MACSTR, __func__, MAC2STR(sta->addr)); return 1; } return 0; } #endif /* CONFIG_WPS */ int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta, int old_vlanid) { #ifndef CONFIG_NO_VLAN const char *iface; struct hostapd_vlan *vlan = NULL; int ret; /* * Do not proceed furthur if the vlan id remains same. We do not want * duplicate dynamic vlan entries. */ if (sta->vlan_id == old_vlanid) return 0; /* * During 1x reauth, if the vlan id changes, then remove the old id and * proceed furthur to add the new one. */ if (old_vlanid > 0) vlan_remove_dynamic(hapd, old_vlanid); iface = hapd->conf->iface; if (sta->ssid->vlan[0]) iface = sta->ssid->vlan; if (sta->ssid->dynamic_vlan == DYNAMIC_VLAN_DISABLED) sta->vlan_id = 0; else if (sta->vlan_id > 0) { struct hostapd_vlan *wildcard_vlan = NULL; vlan = hapd->conf->vlan; while (vlan) { if (vlan->vlan_id == sta->vlan_id) break; if (vlan->vlan_id == VLAN_ID_WILDCARD) wildcard_vlan = vlan; vlan = vlan->next; } if (!vlan) vlan = wildcard_vlan; if (vlan) iface = vlan->ifname; } if (sta->vlan_id > 0 && vlan == NULL) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "could not find VLAN for " "binding station to (vlan_id=%d)", sta->vlan_id); return -1; } else if (sta->vlan_id > 0 && vlan->vlan_id == VLAN_ID_WILDCARD) { vlan = vlan_add_dynamic(hapd, vlan, sta->vlan_id); if (vlan == NULL) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "could not add " "dynamic VLAN interface for vlan_id=%d", sta->vlan_id); return -1; } iface = vlan->ifname; if (vlan_setup_encryption_dyn(hapd, sta->ssid, iface) != 0) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "could not " "configure encryption for dynamic VLAN " "interface for vlan_id=%d", sta->vlan_id); } hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "added new dynamic VLAN " "interface '%s'", iface); } else if (vlan && vlan->vlan_id == sta->vlan_id) { if (sta->vlan_id > 0) { vlan->dynamic_vlan++; hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "updated existing " "dynamic VLAN interface '%s'", iface); } /* * Update encryption configuration for statically generated * VLAN interface. This is only used for static WEP * configuration for the case where hostapd did not yet know * which keys are to be used when the interface was added. */ if (vlan_setup_encryption_dyn(hapd, sta->ssid, iface) != 0) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "could not " "configure encryption for VLAN " "interface for vlan_id=%d", sta->vlan_id); } } hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "binding station to interface " "'%s'", iface); if (wpa_auth_sta_set_vlan(sta->wpa_sm, sta->vlan_id) < 0) wpa_printf(MSG_INFO, "Failed to update VLAN-ID for WPA"); ret = hostapd_drv_set_sta_vlan(iface, hapd, sta->addr, sta->vlan_id); if (ret < 0) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "could not bind the STA " "entry to vlan_id=%d", sta->vlan_id); } return ret; #else /* CONFIG_NO_VLAN */ return 0; #endif /* CONFIG_NO_VLAN */ } #ifdef CONFIG_IEEE80211W int ap_check_sa_query_timeout(struct hostapd_data *hapd, struct sta_info *sta) { u32 tu; struct os_reltime now, passed; os_get_reltime(&now); os_reltime_sub(&now, &sta->sa_query_start, &passed); tu = (passed.sec * 1000000 + passed.usec) / 1024; if (hapd->conf->assoc_sa_query_max_timeout < tu) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "association SA Query timed out"); sta->sa_query_timed_out = 1; os_free(sta->sa_query_trans_id); sta->sa_query_trans_id = NULL; sta->sa_query_count = 0; eloop_cancel_timeout(ap_sa_query_timer, hapd, sta); return 1; } return 0; } static void ap_sa_query_timer(void *eloop_ctx, void *timeout_ctx) { struct hostapd_data *hapd = eloop_ctx; struct sta_info *sta = timeout_ctx; unsigned int timeout, sec, usec; u8 *trans_id, *nbuf; if (sta->sa_query_count > 0 && ap_check_sa_query_timeout(hapd, sta)) return; nbuf = os_realloc_array(sta->sa_query_trans_id, sta->sa_query_count + 1, WLAN_SA_QUERY_TR_ID_LEN); if (nbuf == NULL) return; if (sta->sa_query_count == 0) { /* Starting a new SA Query procedure */ os_get_reltime(&sta->sa_query_start); } trans_id = nbuf + sta->sa_query_count * WLAN_SA_QUERY_TR_ID_LEN; sta->sa_query_trans_id = nbuf; sta->sa_query_count++; os_get_random(trans_id, WLAN_SA_QUERY_TR_ID_LEN); timeout = hapd->conf->assoc_sa_query_retry_timeout; sec = ((timeout / 1000) * 1024) / 1000; usec = (timeout % 1000) * 1024; eloop_register_timeout(sec, usec, ap_sa_query_timer, hapd, sta); hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "association SA Query attempt %d", sta->sa_query_count); ieee802_11_send_sa_query_req(hapd, sta->addr, trans_id); } void ap_sta_start_sa_query(struct hostapd_data *hapd, struct sta_info *sta) { ap_sa_query_timer(hapd, sta); } void ap_sta_stop_sa_query(struct hostapd_data *hapd, struct sta_info *sta) { eloop_cancel_timeout(ap_sa_query_timer, hapd, sta); os_free(sta->sa_query_trans_id); sta->sa_query_trans_id = NULL; sta->sa_query_count = 0; } #endif /* CONFIG_IEEE80211W */ void ap_sta_set_authorized(struct hostapd_data *hapd, struct sta_info *sta, int authorized) { const u8 *dev_addr = NULL; char buf[100]; #ifdef CONFIG_P2P u8 addr[ETH_ALEN]; u8 ip_addr_buf[4]; #endif /* CONFIG_P2P */ if (!!authorized == !!(sta->flags & WLAN_STA_AUTHORIZED)) return; #ifdef CONFIG_P2P if (hapd->p2p_group == NULL) { if (sta->p2p_ie != NULL && p2p_parse_dev_addr_in_p2p_ie(sta->p2p_ie, addr) == 0) dev_addr = addr; } else dev_addr = p2p_group_get_dev_addr(hapd->p2p_group, sta->addr); #endif /* CONFIG_P2P */ if (dev_addr) os_snprintf(buf, sizeof(buf), MACSTR " p2p_dev_addr=" MACSTR, MAC2STR(sta->addr), MAC2STR(dev_addr)); else os_snprintf(buf, sizeof(buf), MACSTR, MAC2STR(sta->addr)); if (authorized) { char ip_addr[100]; ip_addr[0] = '\0'; #ifdef CONFIG_P2P if (wpa_auth_get_ip_addr(sta->wpa_sm, ip_addr_buf) == 0) { os_snprintf(ip_addr, sizeof(ip_addr), " ip_addr=%u.%u.%u.%u", ip_addr_buf[0], ip_addr_buf[1], ip_addr_buf[2], ip_addr_buf[3]); } #endif /* CONFIG_P2P */ wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_CONNECTED "%s%s", buf, ip_addr); if (hapd->msg_ctx_parent && hapd->msg_ctx_parent != hapd->msg_ctx) wpa_msg_no_global(hapd->msg_ctx_parent, MSG_INFO, AP_STA_CONNECTED "%s%s", buf, ip_addr); sta->flags |= WLAN_STA_AUTHORIZED; } else { wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_DISCONNECTED "%s", buf); if (hapd->msg_ctx_parent && hapd->msg_ctx_parent != hapd->msg_ctx) wpa_msg_no_global(hapd->msg_ctx_parent, MSG_INFO, AP_STA_DISCONNECTED "%s", buf); sta->flags &= ~WLAN_STA_AUTHORIZED; } if (hapd->sta_authorized_cb) hapd->sta_authorized_cb(hapd->sta_authorized_cb_ctx, sta->addr, authorized, dev_addr); } void ap_sta_disconnect(struct hostapd_data *hapd, struct sta_info *sta, const u8 *addr, u16 reason) { if (sta == NULL && addr) sta = ap_get_sta(hapd, addr); if (addr) hostapd_drv_sta_deauth(hapd, addr, reason); if (sta == NULL) return; ap_sta_set_authorized(hapd, sta, 0); wpa_auth_sm_event(sta->wpa_sm, WPA_DEAUTH); ieee802_1x_notify_port_enabled(sta->eapol_sm, 0); sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC); wpa_printf(MSG_DEBUG, "%s: reschedule ap_handle_timer timeout " "for " MACSTR " (%d seconds - " "AP_MAX_INACTIVITY_AFTER_DEAUTH)", __func__, MAC2STR(sta->addr), AP_MAX_INACTIVITY_AFTER_DEAUTH); eloop_cancel_timeout(ap_handle_timer, hapd, sta); eloop_register_timeout(AP_MAX_INACTIVITY_AFTER_DEAUTH, 0, ap_handle_timer, hapd, sta); sta->timeout_next = STA_REMOVE; sta->deauth_reason = reason; sta->flags |= WLAN_STA_PENDING_DEAUTH_CB; eloop_cancel_timeout(ap_sta_deauth_cb_timeout, hapd, sta); eloop_register_timeout(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS ? 2 : 0, 0, ap_sta_deauth_cb_timeout, hapd, sta); } void ap_sta_deauth_cb(struct hostapd_data *hapd, struct sta_info *sta) { if (!(sta->flags & WLAN_STA_PENDING_DEAUTH_CB)) { wpa_printf(MSG_DEBUG, "Ignore deauth cb for test frame"); return; } sta->flags &= ~WLAN_STA_PENDING_DEAUTH_CB; eloop_cancel_timeout(ap_sta_deauth_cb_timeout, hapd, sta); ap_sta_deauth_cb_timeout(hapd, sta); } void ap_sta_disassoc_cb(struct hostapd_data *hapd, struct sta_info *sta) { if (!(sta->flags & WLAN_STA_PENDING_DISASSOC_CB)) { wpa_printf(MSG_DEBUG, "Ignore disassoc cb for test frame"); return; } sta->flags &= ~WLAN_STA_PENDING_DISASSOC_CB; eloop_cancel_timeout(ap_sta_disassoc_cb_timeout, hapd, sta); ap_sta_disassoc_cb_timeout(hapd, sta); } int ap_sta_flags_txt(u32 flags, char *buf, size_t buflen) { int res; buf[0] = '\0'; res = os_snprintf(buf, buflen, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", (flags & WLAN_STA_AUTH ? "[AUTH]" : ""), (flags & WLAN_STA_ASSOC ? "[ASSOC]" : ""), (flags & WLAN_STA_AUTHORIZED ? "[AUTHORIZED]" : ""), (flags & WLAN_STA_PENDING_POLL ? "[PENDING_POLL" : ""), (flags & WLAN_STA_SHORT_PREAMBLE ? "[SHORT_PREAMBLE]" : ""), (flags & WLAN_STA_PREAUTH ? "[PREAUTH]" : ""), (flags & WLAN_STA_WMM ? "[WMM]" : ""), (flags & WLAN_STA_MFP ? "[MFP]" : ""), (flags & WLAN_STA_WPS ? "[WPS]" : ""), (flags & WLAN_STA_MAYBE_WPS ? "[MAYBE_WPS]" : ""), (flags & WLAN_STA_WDS ? "[WDS]" : ""), (flags & WLAN_STA_NONERP ? "[NonERP]" : ""), (flags & WLAN_STA_WPS2 ? "[WPS2]" : ""), (flags & WLAN_STA_GAS ? "[GAS]" : ""), (flags & WLAN_STA_VHT ? "[VHT]" : ""), (flags & WLAN_STA_WNM_SLEEP_MODE ? "[WNM_SLEEP_MODE]" : "")); return res; } wpa-2.1/src/ap/sta_info.h000066400000000000000000000154501227414666700153310ustar00rootroot00000000000000/* * hostapd / Station table * Copyright (c) 2002-2011, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef STA_INFO_H #define STA_INFO_H /* STA flags */ #define WLAN_STA_AUTH BIT(0) #define WLAN_STA_ASSOC BIT(1) #define WLAN_STA_AUTHORIZED BIT(5) #define WLAN_STA_PENDING_POLL BIT(6) /* pending activity poll not ACKed */ #define WLAN_STA_SHORT_PREAMBLE BIT(7) #define WLAN_STA_PREAUTH BIT(8) #define WLAN_STA_WMM BIT(9) #define WLAN_STA_MFP BIT(10) #define WLAN_STA_HT BIT(11) #define WLAN_STA_WPS BIT(12) #define WLAN_STA_MAYBE_WPS BIT(13) #define WLAN_STA_WDS BIT(14) #define WLAN_STA_ASSOC_REQ_OK BIT(15) #define WLAN_STA_WPS2 BIT(16) #define WLAN_STA_GAS BIT(17) #define WLAN_STA_VHT BIT(18) #define WLAN_STA_WNM_SLEEP_MODE BIT(19) #define WLAN_STA_PENDING_DISASSOC_CB BIT(29) #define WLAN_STA_PENDING_DEAUTH_CB BIT(30) #define WLAN_STA_NONERP BIT(31) /* Maximum number of supported rates (from both Supported Rates and Extended * Supported Rates IEs). */ #define WLAN_SUPP_RATES_MAX 32 struct sta_info { struct sta_info *next; /* next entry in sta list */ struct sta_info *hnext; /* next entry in hash table list */ u8 addr[6]; u16 aid; /* STA's unique AID (1 .. 2007) or 0 if not yet assigned */ u32 flags; /* Bitfield of WLAN_STA_* */ u16 capability; u16 listen_interval; /* or beacon_int for APs */ u8 supported_rates[WLAN_SUPP_RATES_MAX]; int supported_rates_len; u8 qosinfo; /* Valid when WLAN_STA_WMM is set */ unsigned int nonerp_set:1; unsigned int no_short_slot_time_set:1; unsigned int no_short_preamble_set:1; unsigned int no_ht_gf_set:1; unsigned int no_ht_set:1; unsigned int ht_20mhz_set:1; unsigned int no_p2p_set:1; unsigned int qos_map_enabled:1; u16 auth_alg; u8 previous_ap[6]; enum { STA_NULLFUNC = 0, STA_DISASSOC, STA_DEAUTH, STA_REMOVE, STA_DISASSOC_FROM_CLI } timeout_next; u16 deauth_reason; u16 disassoc_reason; /* IEEE 802.1X related data */ struct eapol_state_machine *eapol_sm; /* IEEE 802.11f (IAPP) related data */ struct ieee80211_mgmt *last_assoc_req; u32 acct_session_id_hi; u32 acct_session_id_lo; struct os_reltime acct_session_start; int acct_session_started; int acct_terminate_cause; /* Acct-Terminate-Cause */ int acct_interim_interval; /* Acct-Interim-Interval */ unsigned long last_rx_bytes; unsigned long last_tx_bytes; u32 acct_input_gigawords; /* Acct-Input-Gigawords */ u32 acct_output_gigawords; /* Acct-Output-Gigawords */ u8 *challenge; /* IEEE 802.11 Shared Key Authentication Challenge */ struct wpa_state_machine *wpa_sm; struct rsn_preauth_interface *preauth_iface; struct hostapd_ssid *ssid; /* SSID selection based on (Re)AssocReq */ struct hostapd_ssid *ssid_probe; /* SSID selection based on ProbeReq */ int vlan_id; /* PSKs from RADIUS authentication server */ struct hostapd_sta_wpa_psk_short *psk; char *identity; /* User-Name from RADIUS */ char *radius_cui; /* Chargeable-User-Identity from RADIUS */ struct ieee80211_ht_capabilities *ht_capabilities; struct ieee80211_vht_capabilities *vht_capabilities; #ifdef CONFIG_IEEE80211W int sa_query_count; /* number of pending SA Query requests; * 0 = no SA Query in progress */ int sa_query_timed_out; u8 *sa_query_trans_id; /* buffer of WLAN_SA_QUERY_TR_ID_LEN * * sa_query_count octets of pending SA Query * transaction identifiers */ struct os_reltime sa_query_start; #endif /* CONFIG_IEEE80211W */ #ifdef CONFIG_INTERWORKING #define GAS_DIALOG_MAX 8 /* Max concurrent dialog number */ struct gas_dialog_info *gas_dialog; u8 gas_dialog_next; #endif /* CONFIG_INTERWORKING */ struct wpabuf *wps_ie; /* WPS IE from (Re)Association Request */ struct wpabuf *p2p_ie; /* P2P IE from (Re)Association Request */ struct wpabuf *hs20_ie; /* HS 2.0 IE from (Re)Association Request */ struct os_reltime connected_time; #ifdef CONFIG_SAE struct sae_data *sae; #endif /* CONFIG_SAE */ }; /* Default value for maximum station inactivity. After AP_MAX_INACTIVITY has * passed since last received frame from the station, a nullfunc data frame is * sent to the station. If this frame is not acknowledged and no other frames * have been received, the station will be disassociated after * AP_DISASSOC_DELAY seconds. Similarly, the station will be deauthenticated * after AP_DEAUTH_DELAY seconds has passed after disassociation. */ #define AP_MAX_INACTIVITY (5 * 60) #define AP_DISASSOC_DELAY (1) #define AP_DEAUTH_DELAY (1) /* Number of seconds to keep STA entry with Authenticated flag after it has * been disassociated. */ #define AP_MAX_INACTIVITY_AFTER_DISASSOC (1 * 30) /* Number of seconds to keep STA entry after it has been deauthenticated. */ #define AP_MAX_INACTIVITY_AFTER_DEAUTH (1 * 5) struct hostapd_data; int ap_for_each_sta(struct hostapd_data *hapd, int (*cb)(struct hostapd_data *hapd, struct sta_info *sta, void *ctx), void *ctx); struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta); struct sta_info * ap_get_sta_p2p(struct hostapd_data *hapd, const u8 *addr); void ap_sta_hash_add(struct hostapd_data *hapd, struct sta_info *sta); void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta); void hostapd_free_stas(struct hostapd_data *hapd); void ap_handle_timer(void *eloop_ctx, void *timeout_ctx); void ap_sta_replenish_timeout(struct hostapd_data *hapd, struct sta_info *sta, u32 session_timeout); void ap_sta_session_timeout(struct hostapd_data *hapd, struct sta_info *sta, u32 session_timeout); void ap_sta_no_session_timeout(struct hostapd_data *hapd, struct sta_info *sta); struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr); void ap_sta_disassociate(struct hostapd_data *hapd, struct sta_info *sta, u16 reason); void ap_sta_deauthenticate(struct hostapd_data *hapd, struct sta_info *sta, u16 reason); #ifdef CONFIG_WPS int ap_sta_wps_cancel(struct hostapd_data *hapd, struct sta_info *sta, void *ctx); #endif /* CONFIG_WPS */ int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta, int old_vlanid); void ap_sta_start_sa_query(struct hostapd_data *hapd, struct sta_info *sta); void ap_sta_stop_sa_query(struct hostapd_data *hapd, struct sta_info *sta); int ap_check_sa_query_timeout(struct hostapd_data *hapd, struct sta_info *sta); void ap_sta_disconnect(struct hostapd_data *hapd, struct sta_info *sta, const u8 *addr, u16 reason); void ap_sta_set_authorized(struct hostapd_data *hapd, struct sta_info *sta, int authorized); static inline int ap_sta_is_authorized(struct sta_info *sta) { return sta->flags & WLAN_STA_AUTHORIZED; } void ap_sta_deauth_cb(struct hostapd_data *hapd, struct sta_info *sta); void ap_sta_disassoc_cb(struct hostapd_data *hapd, struct sta_info *sta); int ap_sta_flags_txt(u32 flags, char *buf, size_t buflen); #endif /* STA_INFO_H */ wpa-2.1/src/ap/tkip_countermeasures.c000066400000000000000000000054301227414666700177720ustar00rootroot00000000000000/* * hostapd / TKIP countermeasures * Copyright (c) 2002-2012, 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/eloop.h" #include "common/ieee802_11_defs.h" #include "radius/radius.h" #include "hostapd.h" #include "sta_info.h" #include "ap_mlme.h" #include "wpa_auth.h" #include "ap_drv_ops.h" #include "tkip_countermeasures.h" static void ieee80211_tkip_countermeasures_stop(void *eloop_ctx, void *timeout_ctx) { struct hostapd_data *hapd = eloop_ctx; hapd->tkip_countermeasures = 0; hostapd_drv_set_countermeasures(hapd, 0); hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "TKIP countermeasures ended"); } static void ieee80211_tkip_countermeasures_start(struct hostapd_data *hapd) { struct sta_info *sta; hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "TKIP countermeasures initiated"); wpa_auth_countermeasures_start(hapd->wpa_auth); hapd->tkip_countermeasures = 1; hostapd_drv_set_countermeasures(hapd, 1); wpa_gtk_rekey(hapd->wpa_auth); eloop_cancel_timeout(ieee80211_tkip_countermeasures_stop, hapd, NULL); eloop_register_timeout(60, 0, ieee80211_tkip_countermeasures_stop, hapd, NULL); while ((sta = hapd->sta_list)) { sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_ADMIN_RESET; if (sta->flags & WLAN_STA_AUTH) { mlme_deauthenticate_indication( hapd, sta, WLAN_REASON_MICHAEL_MIC_FAILURE); } hostapd_drv_sta_deauth(hapd, sta->addr, WLAN_REASON_MICHAEL_MIC_FAILURE); ap_free_sta(hapd, sta); } } void ieee80211_tkip_countermeasures_deinit(struct hostapd_data *hapd) { eloop_cancel_timeout(ieee80211_tkip_countermeasures_stop, hapd, NULL); } int michael_mic_failure(struct hostapd_data *hapd, const u8 *addr, int local) { struct os_reltime now; int ret = 0; if (addr && local) { struct sta_info *sta = ap_get_sta(hapd, addr); if (sta != NULL) { wpa_auth_sta_local_mic_failure_report(sta->wpa_sm); hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "Michael MIC failure detected in " "received frame"); mlme_michaelmicfailure_indication(hapd, addr); } else { wpa_printf(MSG_DEBUG, "MLME-MICHAELMICFAILURE.indication " "for not associated STA (" MACSTR ") ignored", MAC2STR(addr)); return ret; } } os_get_reltime(&now); if (os_reltime_expired(&now, &hapd->michael_mic_failure, 60)) { hapd->michael_mic_failures = 1; } else { hapd->michael_mic_failures++; if (hapd->michael_mic_failures > 1) { ieee80211_tkip_countermeasures_start(hapd); ret = 1; } } hapd->michael_mic_failure = now; return ret; } wpa-2.1/src/ap/tkip_countermeasures.h000066400000000000000000000007031227414666700177750ustar00rootroot00000000000000/* * hostapd / TKIP countermeasures * Copyright (c) 2002-2012, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef TKIP_COUNTERMEASURES_H #define TKIP_COUNTERMEASURES_H int michael_mic_failure(struct hostapd_data *hapd, const u8 *addr, int local); void ieee80211_tkip_countermeasures_deinit(struct hostapd_data *hapd); #endif /* TKIP_COUNTERMEASURES_H */ wpa-2.1/src/ap/utils.c000066400000000000000000000036611227414666700146630ustar00rootroot00000000000000/* * AP mode helper functions * Copyright (c) 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 "common/ieee802_11_defs.h" #include "sta_info.h" #include "hostapd.h" int hostapd_register_probereq_cb(struct hostapd_data *hapd, int (*cb)(void *ctx, const u8 *sa, const u8 *da, const u8 *bssid, const u8 *ie, size_t ie_len, int ssi_signal), void *ctx) { struct hostapd_probereq_cb *n; n = os_realloc_array(hapd->probereq_cb, hapd->num_probereq_cb + 1, sizeof(struct hostapd_probereq_cb)); if (n == NULL) return -1; hapd->probereq_cb = n; n = &hapd->probereq_cb[hapd->num_probereq_cb]; hapd->num_probereq_cb++; n->cb = cb; n->ctx = ctx; return 0; } struct prune_data { struct hostapd_data *hapd; const u8 *addr; }; static int prune_associations(struct hostapd_iface *iface, void *ctx) { struct prune_data *data = ctx; struct sta_info *osta; struct hostapd_data *ohapd; size_t j; for (j = 0; j < iface->num_bss; j++) { ohapd = iface->bss[j]; if (ohapd == data->hapd) continue; osta = ap_get_sta(ohapd, data->addr); if (!osta) continue; ap_sta_disassociate(ohapd, osta, WLAN_REASON_UNSPECIFIED); } return 0; } /** * hostapd_prune_associations - Remove extraneous associations * @hapd: Pointer to BSS data for the most recent association * @addr: Associated STA address * * This function looks through all radios and BSS's for previous * (stale) associations of STA. If any are found they are removed. */ void hostapd_prune_associations(struct hostapd_data *hapd, const u8 *addr) { struct prune_data data; data.hapd = hapd; data.addr = addr; if (hapd->iface->interfaces && hapd->iface->interfaces->for_each_interface) hapd->iface->interfaces->for_each_interface( hapd->iface->interfaces, prune_associations, &data); } wpa-2.1/src/ap/vlan_init.c000066400000000000000000000621171227414666700155070ustar00rootroot00000000000000/* * hostapd / VLAN initialization * Copyright 2003, Instant802 Networks, Inc. * Copyright 2005-2006, Devicescape Software, Inc. * Copyright (c) 2009, 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 "hostapd.h" #include "ap_config.h" #include "ap_drv_ops.h" #include "vlan_init.h" #include "vlan_util.h" #ifdef CONFIG_FULL_DYNAMIC_VLAN #include #include #include #include #include #include "drivers/priv_netlink.h" #include "utils/eloop.h" struct full_dynamic_vlan { int s; /* socket on which to listen for new/removed interfaces. */ }; static int ifconfig_helper(const char *if_name, int up) { int fd; struct ifreq ifr; if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " "failed: %s", __func__, strerror(errno)); return -1; } os_memset(&ifr, 0, sizeof(ifr)); os_strlcpy(ifr.ifr_name, if_name, IFNAMSIZ); if (ioctl(fd, SIOCGIFFLAGS, &ifr) != 0) { wpa_printf(MSG_ERROR, "VLAN: %s: ioctl(SIOCGIFFLAGS) failed " "for interface %s: %s", __func__, if_name, strerror(errno)); close(fd); return -1; } if (up) ifr.ifr_flags |= IFF_UP; else ifr.ifr_flags &= ~IFF_UP; if (ioctl(fd, SIOCSIFFLAGS, &ifr) != 0) { wpa_printf(MSG_ERROR, "VLAN: %s: ioctl(SIOCSIFFLAGS) failed " "for interface %s (up=%d): %s", __func__, if_name, up, strerror(errno)); close(fd); return -1; } close(fd); return 0; } static int ifconfig_up(const char *if_name) { wpa_printf(MSG_DEBUG, "VLAN: Set interface %s up", if_name); return ifconfig_helper(if_name, 1); } static int ifconfig_down(const char *if_name) { wpa_printf(MSG_DEBUG, "VLAN: Set interface %s down", if_name); return ifconfig_helper(if_name, 0); } /* * These are only available in recent linux headers (without the leading * underscore). */ #define _GET_VLAN_REALDEV_NAME_CMD 8 #define _GET_VLAN_VID_CMD 9 /* This value should be 256 ONLY. If it is something else, then hostapd * might crash!, as this value has been hard-coded in 2.4.x kernel * bridging code. */ #define MAX_BR_PORTS 256 static int br_delif(const char *br_name, const char *if_name) { int fd; struct ifreq ifr; unsigned long args[2]; int if_index; wpa_printf(MSG_DEBUG, "VLAN: br_delif(%s, %s)", br_name, if_name); if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " "failed: %s", __func__, strerror(errno)); return -1; } if_index = if_nametoindex(if_name); if (if_index == 0) { wpa_printf(MSG_ERROR, "VLAN: %s: Failure determining " "interface index for '%s'", __func__, if_name); close(fd); return -1; } args[0] = BRCTL_DEL_IF; args[1] = if_index; os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name)); ifr.ifr_data = (__caddr_t) args; if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0 && errno != EINVAL) { /* No error if interface already removed. */ wpa_printf(MSG_ERROR, "VLAN: %s: ioctl[SIOCDEVPRIVATE," "BRCTL_DEL_IF] failed for br_name=%s if_name=%s: " "%s", __func__, br_name, if_name, strerror(errno)); close(fd); return -1; } close(fd); return 0; } /* Add interface 'if_name' to the bridge 'br_name' returns -1 on error returns 1 if the interface is already part of the bridge returns 0 otherwise */ static int br_addif(const char *br_name, const char *if_name) { int fd; struct ifreq ifr; unsigned long args[2]; int if_index; wpa_printf(MSG_DEBUG, "VLAN: br_addif(%s, %s)", br_name, if_name); if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " "failed: %s", __func__, strerror(errno)); return -1; } if_index = if_nametoindex(if_name); if (if_index == 0) { wpa_printf(MSG_ERROR, "VLAN: %s: Failure determining " "interface index for '%s'", __func__, if_name); close(fd); return -1; } args[0] = BRCTL_ADD_IF; args[1] = if_index; os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name)); ifr.ifr_data = (__caddr_t) args; if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) { if (errno == EBUSY) { /* The interface is already added. */ close(fd); return 1; } wpa_printf(MSG_ERROR, "VLAN: %s: ioctl[SIOCDEVPRIVATE," "BRCTL_ADD_IF] failed for br_name=%s if_name=%s: " "%s", __func__, br_name, if_name, strerror(errno)); close(fd); return -1; } close(fd); return 0; } static int br_delbr(const char *br_name) { int fd; unsigned long arg[2]; wpa_printf(MSG_DEBUG, "VLAN: br_delbr(%s)", br_name); if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " "failed: %s", __func__, strerror(errno)); return -1; } arg[0] = BRCTL_DEL_BRIDGE; arg[1] = (unsigned long) br_name; if (ioctl(fd, SIOCGIFBR, arg) < 0 && errno != ENXIO) { /* No error if bridge already removed. */ wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_DEL_BRIDGE failed for " "%s: %s", __func__, br_name, strerror(errno)); close(fd); return -1; } close(fd); return 0; } /* Add a bridge with the name 'br_name'. returns -1 on error returns 1 if the bridge already exists returns 0 otherwise */ static int br_addbr(const char *br_name) { int fd; unsigned long arg[4]; struct ifreq ifr; wpa_printf(MSG_DEBUG, "VLAN: br_addbr(%s)", br_name); if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " "failed: %s", __func__, strerror(errno)); return -1; } arg[0] = BRCTL_ADD_BRIDGE; arg[1] = (unsigned long) br_name; if (ioctl(fd, SIOCGIFBR, arg) < 0) { if (errno == EEXIST) { /* The bridge is already added. */ close(fd); return 1; } else { wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_ADD_BRIDGE " "failed for %s: %s", __func__, br_name, strerror(errno)); close(fd); return -1; } } /* Decrease forwarding delay to avoid EAPOL timeouts. */ os_memset(&ifr, 0, sizeof(ifr)); os_strlcpy(ifr.ifr_name, br_name, IFNAMSIZ); arg[0] = BRCTL_SET_BRIDGE_FORWARD_DELAY; arg[1] = 1; arg[2] = 0; arg[3] = 0; ifr.ifr_data = (char *) &arg; if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) { wpa_printf(MSG_ERROR, "VLAN: %s: " "BRCTL_SET_BRIDGE_FORWARD_DELAY (1 sec) failed for " "%s: %s", __func__, br_name, strerror(errno)); /* Continue anyway */ } close(fd); return 0; } static int br_getnumports(const char *br_name) { int fd; int i; int port_cnt = 0; unsigned long arg[4]; int ifindices[MAX_BR_PORTS]; struct ifreq ifr; if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " "failed: %s", __func__, strerror(errno)); return -1; } arg[0] = BRCTL_GET_PORT_LIST; arg[1] = (unsigned long) ifindices; arg[2] = MAX_BR_PORTS; arg[3] = 0; os_memset(ifindices, 0, sizeof(ifindices)); os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name)); ifr.ifr_data = (__caddr_t) arg; if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) { wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_GET_PORT_LIST " "failed for %s: %s", __func__, br_name, strerror(errno)); close(fd); return -1; } for (i = 1; i < MAX_BR_PORTS; i++) { if (ifindices[i] > 0) { port_cnt++; } } close(fd); return port_cnt; } #ifndef CONFIG_VLAN_NETLINK int vlan_rem(const char *if_name) { int fd; struct vlan_ioctl_args if_request; wpa_printf(MSG_DEBUG, "VLAN: vlan_rem(%s)", if_name); if ((os_strlen(if_name) + 1) > sizeof(if_request.device1)) { wpa_printf(MSG_ERROR, "VLAN: Interface name too long: '%s'", if_name); return -1; } if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " "failed: %s", __func__, strerror(errno)); return -1; } os_memset(&if_request, 0, sizeof(if_request)); os_strlcpy(if_request.device1, if_name, sizeof(if_request.device1)); if_request.cmd = DEL_VLAN_CMD; if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) { wpa_printf(MSG_ERROR, "VLAN: %s: DEL_VLAN_CMD failed for %s: " "%s", __func__, if_name, strerror(errno)); close(fd); return -1; } close(fd); return 0; } /* Add a vlan interface with VLAN ID 'vid' and tagged interface 'if_name'. returns -1 on error returns 1 if the interface already exists returns 0 otherwise */ int vlan_add(const char *if_name, int vid, const char *vlan_if_name) { int fd; struct vlan_ioctl_args if_request; wpa_printf(MSG_DEBUG, "VLAN: vlan_add(if_name=%s, vid=%d)", if_name, vid); ifconfig_up(if_name); if ((os_strlen(if_name) + 1) > sizeof(if_request.device1)) { wpa_printf(MSG_ERROR, "VLAN: Interface name too long: '%s'", if_name); return -1; } if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " "failed: %s", __func__, strerror(errno)); return -1; } os_memset(&if_request, 0, sizeof(if_request)); /* Determine if a suitable vlan device already exists. */ os_snprintf(if_request.device1, sizeof(if_request.device1), "vlan%d", vid); if_request.cmd = _GET_VLAN_VID_CMD; if (ioctl(fd, SIOCSIFVLAN, &if_request) == 0) { if (if_request.u.VID == vid) { if_request.cmd = _GET_VLAN_REALDEV_NAME_CMD; if (ioctl(fd, SIOCSIFVLAN, &if_request) == 0 && os_strncmp(if_request.u.device2, if_name, sizeof(if_request.u.device2)) == 0) { close(fd); wpa_printf(MSG_DEBUG, "VLAN: vlan_add: " "if_name %s exists already", if_request.device1); return 1; } } } /* A suitable vlan device does not already exist, add one. */ os_memset(&if_request, 0, sizeof(if_request)); os_strlcpy(if_request.device1, if_name, sizeof(if_request.device1)); if_request.u.VID = vid; if_request.cmd = ADD_VLAN_CMD; if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) { wpa_printf(MSG_ERROR, "VLAN: %s: ADD_VLAN_CMD failed for %s: " "%s", __func__, if_request.device1, strerror(errno)); close(fd); return -1; } close(fd); return 0; } static int vlan_set_name_type(unsigned int name_type) { int fd; struct vlan_ioctl_args if_request; wpa_printf(MSG_DEBUG, "VLAN: vlan_set_name_type(name_type=%u)", name_type); if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " "failed: %s", __func__, strerror(errno)); return -1; } os_memset(&if_request, 0, sizeof(if_request)); if_request.u.name_type = name_type; if_request.cmd = SET_VLAN_NAME_TYPE_CMD; if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) { wpa_printf(MSG_ERROR, "VLAN: %s: SET_VLAN_NAME_TYPE_CMD " "name_type=%u failed: %s", __func__, name_type, strerror(errno)); close(fd); return -1; } close(fd); return 0; } #endif /* CONFIG_VLAN_NETLINK */ /** * Increase the usage counter for given parent/ifname combination. * If create is set, then this iface is added to the global list. * Returns * -1 on error * 0 if iface is not in list * 1 if iface is in list (was there or has been added) */ static int hapd_get_dynamic_iface(const char *parent, const char *ifname, int create, struct hostapd_data *hapd) { size_t i; struct hostapd_dynamic_iface *j = NULL, **tmp; struct hapd_interfaces *hapd_global = hapd->iface->interfaces; if (!parent) parent = ""; for (i = 0; i < hapd_global->count_dynamic; i++) { j = hapd_global->dynamic_iface[i]; if (os_strncmp(j->iface, ifname, sizeof(j->iface)) == 0 && os_strncmp(j->parent, parent, sizeof(j->parent)) == 0) break; } if (i < hapd_global->count_dynamic) { j->usage++; return 1; } /* new entry required */ if (!create) return 0; j = os_zalloc(sizeof(*j)); if (!j) return -1; os_strlcpy(j->iface, ifname, sizeof(j->iface)); os_strlcpy(j->parent, parent, sizeof(j->parent)); tmp = os_realloc_array(hapd_global->dynamic_iface, i + 1, sizeof(*hapd_global->dynamic_iface)); if (!tmp) { wpa_printf(MSG_ERROR, "VLAN: Failed to allocate memory in %s", __func__); return -1; } hapd_global->count_dynamic++; hapd_global->dynamic_iface = tmp; hapd_global->dynamic_iface[i] = j; return 1; } /** * Decrease the usage counter for given ifname. * Returns * -1 on error or if iface was not found * 0 if iface was found and is still present * 1 if iface was removed from global list */ static int hapd_put_dynamic_iface(const char *parent, const char *ifname, struct hostapd_data *hapd) { size_t i; struct hostapd_dynamic_iface *j = NULL, **tmp; struct hapd_interfaces *hapd_glob = hapd->iface->interfaces; if (!parent) parent = ""; for (i = 0; i < hapd_glob->count_dynamic; i++) { j = hapd_glob->dynamic_iface[i]; if (os_strncmp(j->iface, ifname, sizeof(j->iface)) == 0 && os_strncmp(j->parent, parent, sizeof(j->parent)) == 0) break; } if (i == hapd_glob->count_dynamic) { /* * Interface not in global list. This can happen if alloc in * _get_ failed. */ return -1; } if (j->usage > 0) { j->usage--; return 0; } os_free(j); for (; i < hapd_glob->count_dynamic - 1; i++) hapd_glob->dynamic_iface[i] = hapd_glob->dynamic_iface[i + 1]; hapd_glob->dynamic_iface[hapd_glob->count_dynamic - 1] = NULL; hapd_glob->count_dynamic--; if (hapd_glob->count_dynamic == 0) { os_free(hapd_glob->dynamic_iface); hapd_glob->dynamic_iface = NULL; return 1; } tmp = os_realloc_array(hapd_glob->dynamic_iface, hapd_glob->count_dynamic, sizeof(*hapd_glob->dynamic_iface)); if (!tmp) { wpa_printf(MSG_ERROR, "VLAN: Failed to release memory in %s", __func__); return -1; } hapd_glob->dynamic_iface = tmp; return 1; } static void vlan_newlink(char *ifname, struct hostapd_data *hapd) { char vlan_ifname[IFNAMSIZ]; char br_name[IFNAMSIZ]; struct hostapd_vlan *vlan = hapd->conf->vlan; char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface; int vlan_naming = hapd->conf->ssid.vlan_naming; int ret; wpa_printf(MSG_DEBUG, "VLAN: vlan_newlink(%s)", ifname); while (vlan) { if (os_strcmp(ifname, vlan->ifname) == 0) { if (hapd->conf->vlan_bridge[0]) { os_snprintf(br_name, sizeof(br_name), "%s%d", hapd->conf->vlan_bridge, vlan->vlan_id); } else if (tagged_interface) { os_snprintf(br_name, sizeof(br_name), "br%s.%d", tagged_interface, vlan->vlan_id); } else { os_snprintf(br_name, sizeof(br_name), "brvlan%d", vlan->vlan_id); } ret = br_addbr(br_name); if (hapd_get_dynamic_iface(NULL, br_name, ret == 0, hapd)) vlan->clean |= DVLAN_CLEAN_BR; ifconfig_up(br_name); if (tagged_interface) { if (vlan_naming == DYNAMIC_VLAN_NAMING_WITH_DEVICE) os_snprintf(vlan_ifname, sizeof(vlan_ifname), "%s.%d", tagged_interface, vlan->vlan_id); else os_snprintf(vlan_ifname, sizeof(vlan_ifname), "vlan%d", vlan->vlan_id); ifconfig_up(tagged_interface); ret = vlan_add(tagged_interface, vlan->vlan_id, vlan_ifname); if (hapd_get_dynamic_iface(NULL, vlan_ifname, ret == 0, hapd)) vlan->clean |= DVLAN_CLEAN_VLAN; ret = br_addif(br_name, vlan_ifname); if (hapd_get_dynamic_iface(br_name, vlan_ifname, ret == 0, hapd)) vlan->clean |= DVLAN_CLEAN_VLAN_PORT; ifconfig_up(vlan_ifname); } ret = br_addif(br_name, ifname); if (hapd_get_dynamic_iface(br_name, ifname, ret == 0, hapd)) vlan->clean |= DVLAN_CLEAN_WLAN_PORT; ifconfig_up(ifname); break; } vlan = vlan->next; } } static void vlan_dellink(char *ifname, struct hostapd_data *hapd) { char vlan_ifname[IFNAMSIZ]; char br_name[IFNAMSIZ]; struct hostapd_vlan *first, *prev, *vlan = hapd->conf->vlan; char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface; int vlan_naming = hapd->conf->ssid.vlan_naming; wpa_printf(MSG_DEBUG, "VLAN: vlan_dellink(%s)", ifname); first = prev = vlan; while (vlan) { if (os_strcmp(ifname, vlan->ifname) == 0) { if (hapd->conf->vlan_bridge[0]) { os_snprintf(br_name, sizeof(br_name), "%s%d", hapd->conf->vlan_bridge, vlan->vlan_id); } else if (tagged_interface) { os_snprintf(br_name, sizeof(br_name), "br%s.%d", tagged_interface, vlan->vlan_id); } else { os_snprintf(br_name, sizeof(br_name), "brvlan%d", vlan->vlan_id); } if ((vlan->clean & DVLAN_CLEAN_WLAN_PORT) && hapd_put_dynamic_iface(br_name, vlan->ifname, hapd)) br_delif(br_name, vlan->ifname); if (tagged_interface) { if (vlan_naming == DYNAMIC_VLAN_NAMING_WITH_DEVICE) os_snprintf(vlan_ifname, sizeof(vlan_ifname), "%s.%d", tagged_interface, vlan->vlan_id); else os_snprintf(vlan_ifname, sizeof(vlan_ifname), "vlan%d", vlan->vlan_id); if ((vlan->clean & DVLAN_CLEAN_VLAN_PORT) && hapd_put_dynamic_iface(br_name, vlan_ifname, hapd)) br_delif(br_name, vlan_ifname); ifconfig_down(vlan_ifname); if ((vlan->clean & DVLAN_CLEAN_VLAN) && hapd_put_dynamic_iface(NULL, vlan_ifname, hapd)) vlan_rem(vlan_ifname); } if ((vlan->clean & DVLAN_CLEAN_BR) && hapd_put_dynamic_iface(NULL, br_name, hapd) && br_getnumports(br_name) == 0) { ifconfig_down(br_name); br_delbr(br_name); } if (vlan == first) { hapd->conf->vlan = vlan->next; } else { prev->next = vlan->next; } os_free(vlan); break; } prev = vlan; vlan = vlan->next; } } static void vlan_read_ifnames(struct nlmsghdr *h, size_t len, int del, struct hostapd_data *hapd) { struct ifinfomsg *ifi; int attrlen, nlmsg_len, rta_len; struct rtattr *attr; if (len < sizeof(*ifi)) return; ifi = NLMSG_DATA(h); nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg)); attrlen = h->nlmsg_len - nlmsg_len; if (attrlen < 0) return; attr = (struct rtattr *) (((char *) ifi) + nlmsg_len); rta_len = RTA_ALIGN(sizeof(struct rtattr)); while (RTA_OK(attr, attrlen)) { char ifname[IFNAMSIZ + 1]; if (attr->rta_type == IFLA_IFNAME) { int n = attr->rta_len - rta_len; if (n < 0) break; os_memset(ifname, 0, sizeof(ifname)); if ((size_t) n > sizeof(ifname)) n = sizeof(ifname); os_memcpy(ifname, ((char *) attr) + rta_len, n); if (del) vlan_dellink(ifname, hapd); else vlan_newlink(ifname, hapd); } attr = RTA_NEXT(attr, attrlen); } } static void vlan_event_receive(int sock, void *eloop_ctx, void *sock_ctx) { char buf[8192]; int left; struct sockaddr_nl from; socklen_t fromlen; struct nlmsghdr *h; struct hostapd_data *hapd = eloop_ctx; fromlen = sizeof(from); left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT, (struct sockaddr *) &from, &fromlen); if (left < 0) { if (errno != EINTR && errno != EAGAIN) wpa_printf(MSG_ERROR, "VLAN: %s: recvfrom failed: %s", __func__, strerror(errno)); return; } h = (struct nlmsghdr *) buf; while (left >= (int) sizeof(*h)) { int len, plen; len = h->nlmsg_len; plen = len - sizeof(*h); if (len > left || plen < 0) { wpa_printf(MSG_DEBUG, "VLAN: Malformed netlink " "message: len=%d left=%d plen=%d", len, left, plen); break; } switch (h->nlmsg_type) { case RTM_NEWLINK: vlan_read_ifnames(h, plen, 0, hapd); break; case RTM_DELLINK: vlan_read_ifnames(h, plen, 1, hapd); break; } len = NLMSG_ALIGN(len); left -= len; h = (struct nlmsghdr *) ((char *) h + len); } if (left > 0) { wpa_printf(MSG_DEBUG, "VLAN: %s: %d extra bytes in the end of " "netlink message", __func__, left); } } static struct full_dynamic_vlan * full_dynamic_vlan_init(struct hostapd_data *hapd) { struct sockaddr_nl local; struct full_dynamic_vlan *priv; priv = os_zalloc(sizeof(*priv)); if (priv == NULL) return NULL; #ifndef CONFIG_VLAN_NETLINK vlan_set_name_type(hapd->conf->ssid.vlan_naming == DYNAMIC_VLAN_NAMING_WITH_DEVICE ? VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD : VLAN_NAME_TYPE_PLUS_VID_NO_PAD); #endif /* CONFIG_VLAN_NETLINK */ priv->s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (priv->s < 0) { wpa_printf(MSG_ERROR, "VLAN: %s: socket(PF_NETLINK,SOCK_RAW," "NETLINK_ROUTE) failed: %s", __func__, strerror(errno)); os_free(priv); return NULL; } os_memset(&local, 0, sizeof(local)); local.nl_family = AF_NETLINK; local.nl_groups = RTMGRP_LINK; if (bind(priv->s, (struct sockaddr *) &local, sizeof(local)) < 0) { wpa_printf(MSG_ERROR, "VLAN: %s: bind(netlink) failed: %s", __func__, strerror(errno)); close(priv->s); os_free(priv); return NULL; } if (eloop_register_read_sock(priv->s, vlan_event_receive, hapd, NULL)) { close(priv->s); os_free(priv); return NULL; } return priv; } static void full_dynamic_vlan_deinit(struct full_dynamic_vlan *priv) { if (priv == NULL) return; eloop_unregister_read_sock(priv->s); close(priv->s); os_free(priv); } #endif /* CONFIG_FULL_DYNAMIC_VLAN */ int vlan_setup_encryption_dyn(struct hostapd_data *hapd, struct hostapd_ssid *mssid, const char *dyn_vlan) { int i; if (dyn_vlan == NULL) return 0; /* Static WEP keys are set here; IEEE 802.1X and WPA uses their own * functions for setting up dynamic broadcast keys. */ for (i = 0; i < 4; i++) { if (mssid->wep.key[i] && hostapd_drv_set_key(dyn_vlan, hapd, WPA_ALG_WEP, NULL, i, i == mssid->wep.idx, NULL, 0, mssid->wep.key[i], mssid->wep.len[i])) { wpa_printf(MSG_ERROR, "VLAN: Could not set WEP " "encryption for dynamic VLAN"); return -1; } } return 0; } static int vlan_dynamic_add(struct hostapd_data *hapd, struct hostapd_vlan *vlan) { while (vlan) { if (vlan->vlan_id != VLAN_ID_WILDCARD) { if (hostapd_vlan_if_add(hapd, vlan->ifname)) { if (errno != EEXIST) { wpa_printf(MSG_ERROR, "VLAN: Could " "not add VLAN %s: %s", vlan->ifname, strerror(errno)); return -1; } } #ifdef CONFIG_FULL_DYNAMIC_VLAN ifconfig_up(vlan->ifname); #endif /* CONFIG_FULL_DYNAMIC_VLAN */ } vlan = vlan->next; } return 0; } static void vlan_dynamic_remove(struct hostapd_data *hapd, struct hostapd_vlan *vlan) { struct hostapd_vlan *next; while (vlan) { next = vlan->next; if (vlan->vlan_id != VLAN_ID_WILDCARD && hostapd_vlan_if_remove(hapd, vlan->ifname)) { wpa_printf(MSG_ERROR, "VLAN: Could not remove VLAN " "iface: %s: %s", vlan->ifname, strerror(errno)); } #ifdef CONFIG_FULL_DYNAMIC_VLAN if (vlan->clean) vlan_dellink(vlan->ifname, hapd); #endif /* CONFIG_FULL_DYNAMIC_VLAN */ vlan = next; } } int vlan_init(struct hostapd_data *hapd) { #ifdef CONFIG_FULL_DYNAMIC_VLAN hapd->full_dynamic_vlan = full_dynamic_vlan_init(hapd); #endif /* CONFIG_FULL_DYNAMIC_VLAN */ if (hapd->conf->ssid.dynamic_vlan != DYNAMIC_VLAN_DISABLED && !hapd->conf->vlan) { /* dynamic vlans enabled but no (or empty) vlan_file given */ struct hostapd_vlan *vlan; vlan = os_zalloc(sizeof(*vlan)); if (vlan == NULL) { wpa_printf(MSG_ERROR, "Out of memory while assigning " "VLAN interfaces"); return -1; } vlan->vlan_id = VLAN_ID_WILDCARD; os_snprintf(vlan->ifname, sizeof(vlan->ifname), "%s.#", hapd->conf->iface); vlan->next = hapd->conf->vlan; hapd->conf->vlan = vlan; } if (vlan_dynamic_add(hapd, hapd->conf->vlan)) return -1; return 0; } void vlan_deinit(struct hostapd_data *hapd) { vlan_dynamic_remove(hapd, hapd->conf->vlan); #ifdef CONFIG_FULL_DYNAMIC_VLAN full_dynamic_vlan_deinit(hapd->full_dynamic_vlan); hapd->full_dynamic_vlan = NULL; #endif /* CONFIG_FULL_DYNAMIC_VLAN */ } struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd, struct hostapd_vlan *vlan, int vlan_id) { struct hostapd_vlan *n; char *ifname, *pos; if (vlan == NULL || vlan_id <= 0 || vlan_id > MAX_VLAN_ID || vlan->vlan_id != VLAN_ID_WILDCARD) return NULL; wpa_printf(MSG_DEBUG, "VLAN: %s(vlan_id=%d ifname=%s)", __func__, vlan_id, vlan->ifname); ifname = os_strdup(vlan->ifname); if (ifname == NULL) return NULL; pos = os_strchr(ifname, '#'); if (pos == NULL) { os_free(ifname); return NULL; } *pos++ = '\0'; n = os_zalloc(sizeof(*n)); if (n == NULL) { os_free(ifname); return NULL; } n->vlan_id = vlan_id; n->dynamic_vlan = 1; os_snprintf(n->ifname, sizeof(n->ifname), "%s%d%s", ifname, vlan_id, pos); os_free(ifname); if (hostapd_vlan_if_add(hapd, n->ifname)) { os_free(n); return NULL; } n->next = hapd->conf->vlan; hapd->conf->vlan = n; #ifdef CONFIG_FULL_DYNAMIC_VLAN ifconfig_up(n->ifname); #endif /* CONFIG_FULL_DYNAMIC_VLAN */ return n; } int vlan_remove_dynamic(struct hostapd_data *hapd, int vlan_id) { struct hostapd_vlan *vlan; if (vlan_id <= 0 || vlan_id > MAX_VLAN_ID) return 1; wpa_printf(MSG_DEBUG, "VLAN: %s(vlan_id=%d)", __func__, vlan_id); vlan = hapd->conf->vlan; while (vlan) { if (vlan->vlan_id == vlan_id && vlan->dynamic_vlan > 0) { vlan->dynamic_vlan--; break; } vlan = vlan->next; } if (vlan == NULL) return 1; if (vlan->dynamic_vlan == 0) hostapd_vlan_if_remove(hapd, vlan->ifname); return 0; } wpa-2.1/src/ap/vlan_init.h000066400000000000000000000024701227414666700155100ustar00rootroot00000000000000/* * hostapd / VLAN initialization * Copyright 2003, Instant802 Networks, Inc. * Copyright 2005, Devicescape Software, Inc. * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef VLAN_INIT_H #define VLAN_INIT_H #ifndef CONFIG_NO_VLAN int vlan_init(struct hostapd_data *hapd); void vlan_deinit(struct hostapd_data *hapd); struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd, struct hostapd_vlan *vlan, int vlan_id); int vlan_remove_dynamic(struct hostapd_data *hapd, int vlan_id); int vlan_setup_encryption_dyn(struct hostapd_data *hapd, struct hostapd_ssid *mssid, const char *dyn_vlan); #else /* CONFIG_NO_VLAN */ static inline int vlan_init(struct hostapd_data *hapd) { return 0; } static inline void vlan_deinit(struct hostapd_data *hapd) { } static inline struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd, struct hostapd_vlan *vlan, int vlan_id) { return NULL; } static inline int vlan_remove_dynamic(struct hostapd_data *hapd, int vlan_id) { return -1; } static inline int vlan_setup_encryption_dyn(struct hostapd_data *hapd, struct hostapd_ssid *mssid, const char *dyn_vlan) { return -1; } #endif /* CONFIG_NO_VLAN */ #endif /* VLAN_INIT_H */ wpa-2.1/src/ap/vlan_util.c000066400000000000000000000101771227414666700155200ustar00rootroot00000000000000/* * hostapd / VLAN netlink api * Copyright (c) 2012, Michael Braun * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "utils/includes.h" #include #include #include #include #include #include #include #include #include "utils/common.h" #include "utils/eloop.h" #include "hostapd.h" #include "vlan_util.h" /* * Add a vlan interface with name 'vlan_if_name', VLAN ID 'vid' and * tagged interface 'if_name'. * * returns -1 on error * returns 1 if the interface already exists * returns 0 otherwise */ int vlan_add(const char *if_name, int vid, const char *vlan_if_name) { int ret = -1; struct nl_sock *handle = NULL; struct nl_cache *cache = NULL; struct rtnl_link *rlink = NULL; int if_idx = 0; wpa_printf(MSG_DEBUG, "VLAN: vlan_add(if_name=%s, vid=%d, " "vlan_if_name=%s)", if_name, vid, vlan_if_name); if ((os_strlen(if_name) + 1) > IFNAMSIZ) { wpa_printf(MSG_ERROR, "VLAN: Interface name too long: '%s'", if_name); return -1; } if ((os_strlen(vlan_if_name) + 1) > IFNAMSIZ) { wpa_printf(MSG_ERROR, "VLAN: Interface name too long: '%s'", vlan_if_name); return -1; } handle = nl_socket_alloc(); if (!handle) { wpa_printf(MSG_ERROR, "VLAN: failed to open netlink socket"); goto vlan_add_error; } if (nl_connect(handle, NETLINK_ROUTE) < 0) { wpa_printf(MSG_ERROR, "VLAN: failed to connect to netlink"); goto vlan_add_error; } if (rtnl_link_alloc_cache(handle, AF_UNSPEC, &cache) < 0) { cache = NULL; wpa_printf(MSG_ERROR, "VLAN: failed to alloc cache"); goto vlan_add_error; } if (!(if_idx = rtnl_link_name2i(cache, if_name))) { /* link does not exist */ wpa_printf(MSG_ERROR, "VLAN: interface %s does not exist", if_name); goto vlan_add_error; } if ((rlink = rtnl_link_get_by_name(cache, vlan_if_name))) { /* link does exist */ rtnl_link_put(rlink); rlink = NULL; wpa_printf(MSG_ERROR, "VLAN: interface %s already exists", vlan_if_name); ret = 1; goto vlan_add_error; } rlink = rtnl_link_alloc(); if (!rlink) { wpa_printf(MSG_ERROR, "VLAN: failed to allocate new link"); goto vlan_add_error; } if (rtnl_link_set_type(rlink, "vlan") < 0) { wpa_printf(MSG_ERROR, "VLAN: failed to set link type"); goto vlan_add_error; } rtnl_link_set_link(rlink, if_idx); rtnl_link_set_name(rlink, vlan_if_name); if (rtnl_link_vlan_set_id(rlink, vid) < 0) { wpa_printf(MSG_ERROR, "VLAN: failed to set link vlan id"); goto vlan_add_error; } if (rtnl_link_add(handle, rlink, NLM_F_CREATE) < 0) { wpa_printf(MSG_ERROR, "VLAN: failed to create link %s for " "vlan %d on %s (%d)", vlan_if_name, vid, if_name, if_idx); goto vlan_add_error; } ret = 0; vlan_add_error: if (rlink) rtnl_link_put(rlink); if (cache) nl_cache_free(cache); if (handle) nl_socket_free(handle); return ret; } int vlan_rem(const char *if_name) { int ret = -1; struct nl_sock *handle = NULL; struct nl_cache *cache = NULL; struct rtnl_link *rlink = NULL; wpa_printf(MSG_DEBUG, "VLAN: vlan_rem(if_name=%s)", if_name); handle = nl_socket_alloc(); if (!handle) { wpa_printf(MSG_ERROR, "VLAN: failed to open netlink socket"); goto vlan_rem_error; } if (nl_connect(handle, NETLINK_ROUTE) < 0) { wpa_printf(MSG_ERROR, "VLAN: failed to connect to netlink"); goto vlan_rem_error; } if (rtnl_link_alloc_cache(handle, AF_UNSPEC, &cache) < 0) { cache = NULL; wpa_printf(MSG_ERROR, "VLAN: failed to alloc cache"); goto vlan_rem_error; } if (!(rlink = rtnl_link_get_by_name(cache, if_name))) { /* link does not exist */ wpa_printf(MSG_ERROR, "VLAN: interface %s does not exists", if_name); goto vlan_rem_error; } if (rtnl_link_delete(handle, rlink) < 0) { wpa_printf(MSG_ERROR, "VLAN: failed to remove link %s", if_name); goto vlan_rem_error; } ret = 0; vlan_rem_error: if (rlink) rtnl_link_put(rlink); if (cache) nl_cache_free(cache); if (handle) nl_socket_free(handle); return ret; } wpa-2.1/src/ap/vlan_util.h000066400000000000000000000005761227414666700155270ustar00rootroot00000000000000/* * hostapd / VLAN netlink api * Copyright (c) 2012, Michael Braun * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef VLAN_UTIL_H #define VLAN_UTIL_H int vlan_add(const char *if_name, int vid, const char *vlan_if_name); int vlan_rem(const char *if_name); #endif /* VLAN_UTIL_H */ wpa-2.1/src/ap/wmm.c000066400000000000000000000235641227414666700143270ustar00rootroot00000000000000/* * hostapd / WMM (Wi-Fi Multimedia) * Copyright 2002-2003, Instant802 Networks, Inc. * Copyright 2005-2006, Devicescape Software, Inc. * Copyright (c) 2009, 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 "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" #include "hostapd.h" #include "ieee802_11.h" #include "sta_info.h" #include "ap_config.h" #include "ap_drv_ops.h" #include "wmm.h" /* TODO: maintain separate sequence and fragment numbers for each AC * TODO: IGMP snooping to track which multicasts to forward - and use QOS-DATA * if only WMM stations are receiving a certain group */ static inline u8 wmm_aci_aifsn(int aifsn, int acm, int aci) { u8 ret; ret = (aifsn << WMM_AC_AIFNS_SHIFT) & WMM_AC_AIFSN_MASK; if (acm) ret |= WMM_AC_ACM; ret |= (aci << WMM_AC_ACI_SHIFT) & WMM_AC_ACI_MASK; return ret; } static inline u8 wmm_ecw(int ecwmin, int ecwmax) { return ((ecwmin << WMM_AC_ECWMIN_SHIFT) & WMM_AC_ECWMIN_MASK) | ((ecwmax << WMM_AC_ECWMAX_SHIFT) & WMM_AC_ECWMAX_MASK); } /* * Add WMM Parameter Element to Beacon, Probe Response, and (Re)Association * Response frames. */ u8 * hostapd_eid_wmm(struct hostapd_data *hapd, u8 *eid) { u8 *pos = eid; struct wmm_parameter_element *wmm = (struct wmm_parameter_element *) (pos + 2); int e; if (!hapd->conf->wmm_enabled) return eid; eid[0] = WLAN_EID_VENDOR_SPECIFIC; wmm->oui[0] = 0x00; wmm->oui[1] = 0x50; wmm->oui[2] = 0xf2; wmm->oui_type = WMM_OUI_TYPE; wmm->oui_subtype = WMM_OUI_SUBTYPE_PARAMETER_ELEMENT; wmm->version = WMM_VERSION; wmm->qos_info = hapd->parameter_set_count & 0xf; if (hapd->conf->wmm_uapsd && (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_UAPSD)) wmm->qos_info |= 0x80; wmm->reserved = 0; /* fill in a parameter set record for each AC */ for (e = 0; e < 4; e++) { struct wmm_ac_parameter *ac = &wmm->ac[e]; struct hostapd_wmm_ac_params *acp = &hapd->iconf->wmm_ac_params[e]; ac->aci_aifsn = wmm_aci_aifsn(acp->aifs, acp->admission_control_mandatory, e); ac->cw = wmm_ecw(acp->cwmin, acp->cwmax); ac->txop_limit = host_to_le16(acp->txop_limit); } pos = (u8 *) (wmm + 1); eid[1] = pos - eid - 2; /* element length */ return pos; } /* * This function is called when a station sends an association request with * WMM info element. The function returns 1 on success or 0 on any error in WMM * element. eid does not include Element ID and Length octets. */ int hostapd_eid_wmm_valid(struct hostapd_data *hapd, const u8 *eid, size_t len) { struct wmm_information_element *wmm; wpa_hexdump(MSG_MSGDUMP, "WMM IE", eid, len); if (len < sizeof(struct wmm_information_element)) { wpa_printf(MSG_DEBUG, "Too short WMM IE (len=%lu)", (unsigned long) len); return 0; } wmm = (struct wmm_information_element *) eid; wpa_printf(MSG_DEBUG, "Validating WMM IE: OUI %02x:%02x:%02x " "OUI type %d OUI sub-type %d version %d QoS info 0x%x", wmm->oui[0], wmm->oui[1], wmm->oui[2], wmm->oui_type, wmm->oui_subtype, wmm->version, wmm->qos_info); if (wmm->oui_subtype != WMM_OUI_SUBTYPE_INFORMATION_ELEMENT || wmm->version != WMM_VERSION) { wpa_printf(MSG_DEBUG, "Unsupported WMM IE Subtype/Version"); return 0; } return 1; } static void wmm_send_action(struct hostapd_data *hapd, const u8 *addr, const struct wmm_tspec_element *tspec, u8 action_code, u8 dialogue_token, u8 status_code) { u8 buf[256]; struct ieee80211_mgmt *m = (struct ieee80211_mgmt *) buf; struct wmm_tspec_element *t = (struct wmm_tspec_element *) m->u.action.u.wmm_action.variable; int len; hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "action response - reason %d", status_code); os_memset(buf, 0, sizeof(buf)); m->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_ACTION); os_memcpy(m->da, addr, ETH_ALEN); os_memcpy(m->sa, hapd->own_addr, ETH_ALEN); os_memcpy(m->bssid, hapd->own_addr, ETH_ALEN); m->u.action.category = WLAN_ACTION_WMM; m->u.action.u.wmm_action.action_code = action_code; m->u.action.u.wmm_action.dialog_token = dialogue_token; m->u.action.u.wmm_action.status_code = status_code; os_memcpy(t, tspec, sizeof(struct wmm_tspec_element)); len = ((u8 *) (t + 1)) - buf; if (hostapd_drv_send_mlme(hapd, m, len, 0) < 0) wpa_printf(MSG_INFO, "wmm_send_action: send failed"); } int wmm_process_tspec(struct wmm_tspec_element *tspec) { int medium_time, pps, duration; int up, psb, dir, tid; u16 val, surplus; up = (tspec->ts_info[1] >> 3) & 0x07; psb = (tspec->ts_info[1] >> 2) & 0x01; dir = (tspec->ts_info[0] >> 5) & 0x03; tid = (tspec->ts_info[0] >> 1) & 0x0f; wpa_printf(MSG_DEBUG, "WMM: TS Info: UP=%d PSB=%d Direction=%d TID=%d", up, psb, dir, tid); val = le_to_host16(tspec->nominal_msdu_size); wpa_printf(MSG_DEBUG, "WMM: Nominal MSDU Size: %d%s", val & 0x7fff, val & 0x8000 ? " (fixed)" : ""); wpa_printf(MSG_DEBUG, "WMM: Mean Data Rate: %u bps", le_to_host32(tspec->mean_data_rate)); wpa_printf(MSG_DEBUG, "WMM: Minimum PHY Rate: %u bps", le_to_host32(tspec->minimum_phy_rate)); val = le_to_host16(tspec->surplus_bandwidth_allowance); wpa_printf(MSG_DEBUG, "WMM: Surplus Bandwidth Allowance: %u.%04u", val >> 13, 10000 * (val & 0x1fff) / 0x2000); val = le_to_host16(tspec->nominal_msdu_size); if (val == 0) { wpa_printf(MSG_DEBUG, "WMM: Invalid Nominal MSDU Size (0)"); return WMM_ADDTS_STATUS_INVALID_PARAMETERS; } /* pps = Ceiling((Mean Data Rate / 8) / Nominal MSDU Size) */ pps = ((le_to_host32(tspec->mean_data_rate) / 8) + val - 1) / val; wpa_printf(MSG_DEBUG, "WMM: Packets-per-second estimate for TSPEC: %d", pps); if (le_to_host32(tspec->minimum_phy_rate) < 1000000) { wpa_printf(MSG_DEBUG, "WMM: Too small Minimum PHY Rate"); return WMM_ADDTS_STATUS_INVALID_PARAMETERS; } duration = (le_to_host16(tspec->nominal_msdu_size) & 0x7fff) * 8 / (le_to_host32(tspec->minimum_phy_rate) / 1000000) + 50 /* FIX: proper SIFS + ACK duration */; /* unsigned binary number with an implicit binary point after the * leftmost 3 bits, i.e., 0x2000 = 1.0 */ surplus = le_to_host16(tspec->surplus_bandwidth_allowance); if (surplus <= 0x2000) { wpa_printf(MSG_DEBUG, "WMM: Surplus Bandwidth Allowance not " "greater than unity"); return WMM_ADDTS_STATUS_INVALID_PARAMETERS; } medium_time = surplus * pps * duration / 0x2000; wpa_printf(MSG_DEBUG, "WMM: Estimated medium time: %u", medium_time); /* * TODO: store list of granted (and still active) TSPECs and check * whether there is available medium time for this request. For now, * just refuse requests that would by themselves take very large * portion of the available bandwidth. */ if (medium_time > 750000) { wpa_printf(MSG_DEBUG, "WMM: Refuse TSPEC request for over " "75%% of available bandwidth"); return WMM_ADDTS_STATUS_REFUSED; } /* Convert to 32 microseconds per second unit */ tspec->medium_time = host_to_le16(medium_time / 32); return WMM_ADDTS_STATUS_ADMISSION_ACCEPTED; } static void wmm_addts_req(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, struct wmm_tspec_element *tspec, size_t len) { const u8 *end = ((const u8 *) mgmt) + len; int res; if ((const u8 *) (tspec + 1) > end) { wpa_printf(MSG_DEBUG, "WMM: TSPEC overflow in ADDTS Request"); return; } wpa_printf(MSG_DEBUG, "WMM: ADDTS Request (Dialog Token %d) for TSPEC " "from " MACSTR, mgmt->u.action.u.wmm_action.dialog_token, MAC2STR(mgmt->sa)); res = wmm_process_tspec(tspec); wpa_printf(MSG_DEBUG, "WMM: ADDTS processing result: %d", res); wmm_send_action(hapd, mgmt->sa, tspec, WMM_ACTION_CODE_ADDTS_RESP, mgmt->u.action.u.wmm_action.dialog_token, res); } void hostapd_wmm_action(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, size_t len) { int action_code; int left = len - IEEE80211_HDRLEN - 4; const u8 *pos = ((const u8 *) mgmt) + IEEE80211_HDRLEN + 4; struct ieee802_11_elems elems; struct sta_info *sta = ap_get_sta(hapd, mgmt->sa); /* check that the request comes from a valid station */ if (!sta || (sta->flags & (WLAN_STA_ASSOC | WLAN_STA_WMM)) != (WLAN_STA_ASSOC | WLAN_STA_WMM)) { hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "wmm action received is not from associated wmm" " station"); /* TODO: respond with action frame refused status code */ return; } /* extract the tspec info element */ if (ieee802_11_parse_elems(pos, left, &elems, 1) == ParseFailed) { hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "hostapd_wmm_action - could not parse wmm " "action"); /* TODO: respond with action frame invalid parameters status * code */ return; } if (!elems.wmm_tspec || elems.wmm_tspec_len != (sizeof(struct wmm_tspec_element) - 2)) { hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "hostapd_wmm_action - missing or wrong length " "tspec"); /* TODO: respond with action frame invalid parameters status * code */ return; } /* TODO: check the request is for an AC with ACM set, if not, refuse * request */ action_code = mgmt->u.action.u.wmm_action.action_code; switch (action_code) { case WMM_ACTION_CODE_ADDTS_REQ: wmm_addts_req(hapd, mgmt, (struct wmm_tspec_element *) (elems.wmm_tspec - 2), len); return; #if 0 /* TODO: needed for client implementation */ case WMM_ACTION_CODE_ADDTS_RESP: wmm_setup_request(hapd, mgmt, len); return; /* TODO: handle station teardown requests */ case WMM_ACTION_CODE_DELTS: wmm_teardown(hapd, mgmt, len); return; #endif } hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "hostapd_wmm_action - unknown action code %d", action_code); } wpa-2.1/src/ap/wmm.h000066400000000000000000000012151227414666700143210ustar00rootroot00000000000000/* * hostapd / WMM (Wi-Fi Multimedia) * Copyright 2002-2003, Instant802 Networks, Inc. * Copyright 2005-2006, Devicescape Software, Inc. * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef WME_H #define WME_H struct ieee80211_mgmt; struct wmm_tspec_element; u8 * hostapd_eid_wmm(struct hostapd_data *hapd, u8 *eid); int hostapd_eid_wmm_valid(struct hostapd_data *hapd, const u8 *eid, size_t len); void hostapd_wmm_action(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, size_t len); int wmm_process_tspec(struct wmm_tspec_element *tspec); #endif /* WME_H */ wpa-2.1/src/ap/wnm_ap.c000066400000000000000000000352251227414666700150050ustar00rootroot00000000000000/* * hostapd - WNM * Copyright (c) 2011-2013, Qualcomm Atheros, Inc. * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "utils/includes.h" #include "utils/common.h" #include "utils/eloop.h" #include "common/ieee802_11_defs.h" #include "ap/hostapd.h" #include "ap/sta_info.h" #include "ap/ap_config.h" #include "ap/ap_drv_ops.h" #include "ap/wpa_auth.h" #include "wnm_ap.h" #define MAX_TFS_IE_LEN 1024 /* get the TFS IE from driver */ static int ieee80211_11_get_tfs_ie(struct hostapd_data *hapd, const u8 *addr, u8 *buf, u16 *buf_len, enum wnm_oper oper) { wpa_printf(MSG_DEBUG, "%s: TFS get operation %d", __func__, oper); return hostapd_drv_wnm_oper(hapd, oper, addr, buf, buf_len); } /* set the TFS IE to driver */ static int ieee80211_11_set_tfs_ie(struct hostapd_data *hapd, const u8 *addr, u8 *buf, u16 *buf_len, enum wnm_oper oper) { wpa_printf(MSG_DEBUG, "%s: TFS set operation %d", __func__, oper); return hostapd_drv_wnm_oper(hapd, oper, addr, buf, buf_len); } /* MLME-SLEEPMODE.response */ static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd, const u8 *addr, u8 dialog_token, u8 action_type, u16 intval) { struct ieee80211_mgmt *mgmt; int res; size_t len; size_t gtk_elem_len = 0; size_t igtk_elem_len = 0; struct wnm_sleep_element wnmsleep_ie; u8 *wnmtfs_ie; u8 wnmsleep_ie_len; u16 wnmtfs_ie_len; u8 *pos; struct sta_info *sta; enum wnm_oper tfs_oper = action_type == WNM_SLEEP_MODE_ENTER ? WNM_SLEEP_TFS_RESP_IE_ADD : WNM_SLEEP_TFS_RESP_IE_NONE; sta = ap_get_sta(hapd, addr); if (sta == NULL) { wpa_printf(MSG_DEBUG, "%s: station not found", __func__); return -EINVAL; } /* WNM-Sleep Mode IE */ os_memset(&wnmsleep_ie, 0, sizeof(struct wnm_sleep_element)); wnmsleep_ie_len = sizeof(struct wnm_sleep_element); wnmsleep_ie.eid = WLAN_EID_WNMSLEEP; wnmsleep_ie.len = wnmsleep_ie_len - 2; wnmsleep_ie.action_type = action_type; wnmsleep_ie.status = WNM_STATUS_SLEEP_ACCEPT; wnmsleep_ie.intval = host_to_le16(intval); /* TFS IE(s) */ wnmtfs_ie = os_zalloc(MAX_TFS_IE_LEN); if (wnmtfs_ie == NULL) return -1; if (ieee80211_11_get_tfs_ie(hapd, addr, wnmtfs_ie, &wnmtfs_ie_len, tfs_oper)) { wnmtfs_ie_len = 0; os_free(wnmtfs_ie); wnmtfs_ie = NULL; } #define MAX_GTK_SUBELEM_LEN 45 #define MAX_IGTK_SUBELEM_LEN 26 mgmt = os_zalloc(sizeof(*mgmt) + wnmsleep_ie_len + MAX_GTK_SUBELEM_LEN + MAX_IGTK_SUBELEM_LEN); if (mgmt == NULL) { wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for " "WNM-Sleep Response action frame"); return -1; } os_memcpy(mgmt->da, addr, ETH_ALEN); os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN); os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN); mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_ACTION); mgmt->u.action.category = WLAN_ACTION_WNM; mgmt->u.action.u.wnm_sleep_resp.action = WNM_SLEEP_MODE_RESP; mgmt->u.action.u.wnm_sleep_resp.dialogtoken = dialog_token; pos = (u8 *)mgmt->u.action.u.wnm_sleep_resp.variable; /* add key data if MFP is enabled */ if (!wpa_auth_uses_mfp(sta->wpa_sm) || action_type != WNM_SLEEP_MODE_EXIT) { mgmt->u.action.u.wnm_sleep_resp.keydata_len = 0; } else { gtk_elem_len = wpa_wnmsleep_gtk_subelem(sta->wpa_sm, pos); pos += gtk_elem_len; wpa_printf(MSG_DEBUG, "Pass 4, gtk_len = %d", (int) gtk_elem_len); #ifdef CONFIG_IEEE80211W res = wpa_wnmsleep_igtk_subelem(sta->wpa_sm, pos); if (res < 0) { os_free(wnmtfs_ie); os_free(mgmt); return -1; } igtk_elem_len = res; pos += igtk_elem_len; wpa_printf(MSG_DEBUG, "Pass 4 igtk_len = %d", (int) igtk_elem_len); #endif /* CONFIG_IEEE80211W */ WPA_PUT_LE16((u8 *) &mgmt->u.action.u.wnm_sleep_resp.keydata_len, gtk_elem_len + igtk_elem_len); } os_memcpy(pos, &wnmsleep_ie, wnmsleep_ie_len); /* copy TFS IE here */ pos += wnmsleep_ie_len; if (wnmtfs_ie) os_memcpy(pos, wnmtfs_ie, wnmtfs_ie_len); len = 1 + sizeof(mgmt->u.action.u.wnm_sleep_resp) + gtk_elem_len + igtk_elem_len + wnmsleep_ie_len + wnmtfs_ie_len; /* In driver, response frame should be forced to sent when STA is in * PS mode */ res = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, mgmt->da, &mgmt->u.action.category, len); if (!res) { wpa_printf(MSG_DEBUG, "Successfully send WNM-Sleep Response " "frame"); /* when entering wnmsleep * 1. pause the node in driver * 2. mark the node so that AP won't update GTK/IGTK during * WNM Sleep */ if (wnmsleep_ie.status == WNM_STATUS_SLEEP_ACCEPT && wnmsleep_ie.action_type == WNM_SLEEP_MODE_ENTER) { sta->flags |= WLAN_STA_WNM_SLEEP_MODE; hostapd_drv_wnm_oper(hapd, WNM_SLEEP_ENTER_CONFIRM, addr, NULL, NULL); wpa_set_wnmsleep(sta->wpa_sm, 1); } /* when exiting wnmsleep * 1. unmark the node * 2. start GTK/IGTK update if MFP is not used * 3. unpause the node in driver */ if ((wnmsleep_ie.status == WNM_STATUS_SLEEP_ACCEPT || wnmsleep_ie.status == WNM_STATUS_SLEEP_EXIT_ACCEPT_GTK_UPDATE) && wnmsleep_ie.action_type == WNM_SLEEP_MODE_EXIT) { sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE; wpa_set_wnmsleep(sta->wpa_sm, 0); hostapd_drv_wnm_oper(hapd, WNM_SLEEP_EXIT_CONFIRM, addr, NULL, NULL); if (!wpa_auth_uses_mfp(sta->wpa_sm)) wpa_wnmsleep_rekey_gtk(sta->wpa_sm); } } else wpa_printf(MSG_DEBUG, "Fail to send WNM-Sleep Response frame"); #undef MAX_GTK_SUBELEM_LEN #undef MAX_IGTK_SUBELEM_LEN os_free(wnmtfs_ie); os_free(mgmt); return res; } static void ieee802_11_rx_wnmsleep_req(struct hostapd_data *hapd, const u8 *addr, const u8 *frm, int len) { /* Dialog Token [1] | WNM-Sleep Mode IE | TFS Response IE */ const u8 *pos = frm; u8 dialog_token; struct wnm_sleep_element *wnmsleep_ie = NULL; /* multiple TFS Req IE (assuming consecutive) */ u8 *tfsreq_ie_start = NULL; u8 *tfsreq_ie_end = NULL; u16 tfsreq_ie_len = 0; dialog_token = *pos++; while (pos + 1 < frm + len) { u8 ie_len = pos[1]; if (pos + 2 + ie_len > frm + len) break; if (*pos == WLAN_EID_WNMSLEEP) wnmsleep_ie = (struct wnm_sleep_element *) pos; else if (*pos == WLAN_EID_TFS_REQ) { if (!tfsreq_ie_start) tfsreq_ie_start = (u8 *) pos; tfsreq_ie_end = (u8 *) pos; } else wpa_printf(MSG_DEBUG, "WNM: EID %d not recognized", *pos); pos += ie_len + 2; } if (!wnmsleep_ie) { wpa_printf(MSG_DEBUG, "No WNM-Sleep IE found"); return; } if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_ENTER && tfsreq_ie_start && tfsreq_ie_end && tfsreq_ie_end - tfsreq_ie_start >= 0) { tfsreq_ie_len = (tfsreq_ie_end + tfsreq_ie_end[1] + 2) - tfsreq_ie_start; wpa_printf(MSG_DEBUG, "TFS Req IE(s) found"); /* pass the TFS Req IE(s) to driver for processing */ if (ieee80211_11_set_tfs_ie(hapd, addr, tfsreq_ie_start, &tfsreq_ie_len, WNM_SLEEP_TFS_REQ_IE_SET)) wpa_printf(MSG_DEBUG, "Fail to set TFS Req IE"); } ieee802_11_send_wnmsleep_resp(hapd, addr, dialog_token, wnmsleep_ie->action_type, le_to_host16(wnmsleep_ie->intval)); if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT) { /* clear the tfs after sending the resp frame */ ieee80211_11_set_tfs_ie(hapd, addr, tfsreq_ie_start, &tfsreq_ie_len, WNM_SLEEP_TFS_IE_DEL); } } static int ieee802_11_send_bss_trans_mgmt_request(struct hostapd_data *hapd, const u8 *addr, u8 dialog_token, const char *url) { struct ieee80211_mgmt *mgmt; size_t url_len, len; u8 *pos; int res; if (url) url_len = os_strlen(url); else url_len = 0; mgmt = os_zalloc(sizeof(*mgmt) + (url_len ? 1 + url_len : 0)); if (mgmt == NULL) return -1; os_memcpy(mgmt->da, addr, ETH_ALEN); os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN); os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN); mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_ACTION); mgmt->u.action.category = WLAN_ACTION_WNM; mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ; mgmt->u.action.u.bss_tm_req.dialog_token = dialog_token; mgmt->u.action.u.bss_tm_req.req_mode = 0; mgmt->u.action.u.bss_tm_req.disassoc_timer = host_to_le16(0); mgmt->u.action.u.bss_tm_req.validity_interval = 1; pos = mgmt->u.action.u.bss_tm_req.variable; if (url) { *pos++ += url_len; os_memcpy(pos, url, url_len); pos += url_len; } wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Request to " MACSTR " dialog_token=%u req_mode=0x%x disassoc_timer=%u " "validity_interval=%u", MAC2STR(addr), dialog_token, mgmt->u.action.u.bss_tm_req.req_mode, le_to_host16(mgmt->u.action.u.bss_tm_req.disassoc_timer), mgmt->u.action.u.bss_tm_req.validity_interval); len = pos - &mgmt->u.action.category; res = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, mgmt->da, &mgmt->u.action.category, len); os_free(mgmt); return res; } static void ieee802_11_rx_bss_trans_mgmt_query(struct hostapd_data *hapd, const u8 *addr, const u8 *frm, size_t len) { u8 dialog_token, reason; const u8 *pos, *end; if (len < 2) { wpa_printf(MSG_DEBUG, "WNM: Ignore too short BSS Transition Management Query from " MACSTR, MAC2STR(addr)); return; } pos = frm; end = pos + len; dialog_token = *pos++; reason = *pos++; wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Query from " MACSTR " dialog_token=%u reason=%u", MAC2STR(addr), dialog_token, reason); wpa_hexdump(MSG_DEBUG, "WNM: BSS Transition Candidate List Entries", pos, end - pos); ieee802_11_send_bss_trans_mgmt_request(hapd, addr, dialog_token, NULL); } static void ieee802_11_rx_bss_trans_mgmt_resp(struct hostapd_data *hapd, const u8 *addr, const u8 *frm, size_t len) { u8 dialog_token, status_code, bss_termination_delay; const u8 *pos, *end; if (len < 3) { wpa_printf(MSG_DEBUG, "WNM: Ignore too short BSS Transition Management Response from " MACSTR, MAC2STR(addr)); return; } pos = frm; end = pos + len; dialog_token = *pos++; status_code = *pos++; bss_termination_delay = *pos++; wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Response from " MACSTR " dialog_token=%u status_code=%u " "bss_termination_delay=%u", MAC2STR(addr), dialog_token, status_code, bss_termination_delay); if (status_code == WNM_BSS_TM_ACCEPT) { if (end - pos < ETH_ALEN) { wpa_printf(MSG_DEBUG, "WNM: not enough room for Target BSSID field"); return; } wpa_printf(MSG_DEBUG, "WNM: Target BSSID: " MACSTR, MAC2STR(pos)); pos += ETH_ALEN; } wpa_hexdump(MSG_DEBUG, "WNM: BSS Transition Candidate List Entries", pos, end - pos); } int ieee802_11_rx_wnm_action_ap(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, size_t len) { u8 action; const u8 *payload; size_t plen; if (len < IEEE80211_HDRLEN + 2) return -1; payload = &mgmt->u.action.category; payload++; action = *payload++; plen = (((const u8 *) mgmt) + len) - payload; switch (action) { case WNM_BSS_TRANS_MGMT_QUERY: ieee802_11_rx_bss_trans_mgmt_query(hapd, mgmt->sa, payload, plen); return 0; case WNM_BSS_TRANS_MGMT_RESP: ieee802_11_rx_bss_trans_mgmt_resp(hapd, mgmt->sa, payload, plen); return 0; case WNM_SLEEP_MODE_REQ: ieee802_11_rx_wnmsleep_req(hapd, mgmt->sa, payload, plen); return 0; } wpa_printf(MSG_DEBUG, "WNM: Unsupported WNM Action %u from " MACSTR, action, MAC2STR(mgmt->sa)); return -1; } int wnm_send_disassoc_imminent(struct hostapd_data *hapd, struct sta_info *sta, int disassoc_timer) { u8 buf[1000], *pos; struct ieee80211_mgmt *mgmt; os_memset(buf, 0, sizeof(buf)); mgmt = (struct ieee80211_mgmt *) buf; mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_ACTION); os_memcpy(mgmt->da, sta->addr, ETH_ALEN); os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN); os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN); mgmt->u.action.category = WLAN_ACTION_WNM; mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ; mgmt->u.action.u.bss_tm_req.dialog_token = 1; mgmt->u.action.u.bss_tm_req.req_mode = WNM_BSS_TM_REQ_DISASSOC_IMMINENT; mgmt->u.action.u.bss_tm_req.disassoc_timer = host_to_le16(disassoc_timer); mgmt->u.action.u.bss_tm_req.validity_interval = 0; pos = mgmt->u.action.u.bss_tm_req.variable; wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Request frame to indicate imminent disassociation (disassoc_timer=%d) to " MACSTR, disassoc_timer, MAC2STR(sta->addr)); if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0) < 0) { wpa_printf(MSG_DEBUG, "Failed to send BSS Transition " "Management Request frame"); return -1; } return 0; } int wnm_send_ess_disassoc_imminent(struct hostapd_data *hapd, struct sta_info *sta, const char *url, int disassoc_timer) { u8 buf[1000], *pos; struct ieee80211_mgmt *mgmt; size_t url_len; os_memset(buf, 0, sizeof(buf)); mgmt = (struct ieee80211_mgmt *) buf; mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_ACTION); os_memcpy(mgmt->da, sta->addr, ETH_ALEN); os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN); os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN); mgmt->u.action.category = WLAN_ACTION_WNM; mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ; mgmt->u.action.u.bss_tm_req.dialog_token = 1; mgmt->u.action.u.bss_tm_req.req_mode = WNM_BSS_TM_REQ_DISASSOC_IMMINENT | WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT; mgmt->u.action.u.bss_tm_req.disassoc_timer = host_to_le16(disassoc_timer); mgmt->u.action.u.bss_tm_req.validity_interval = 0x01; pos = mgmt->u.action.u.bss_tm_req.variable; /* Session Information URL */ url_len = os_strlen(url); if (url_len > 255) return -1; *pos++ = url_len; os_memcpy(pos, url, url_len); pos += url_len; if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0) < 0) { wpa_printf(MSG_DEBUG, "Failed to send BSS Transition " "Management Request frame"); return -1; } /* send disassociation frame after time-out */ if (disassoc_timer) { int timeout, beacon_int; /* * Prevent STA from reconnecting using cached PMKSA to force * full authentication with the authentication server (which may * decide to reject the connection), */ wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr); beacon_int = hapd->iconf->beacon_int; if (beacon_int < 1) beacon_int = 100; /* best guess */ /* Calculate timeout in ms based on beacon_int in TU */ timeout = disassoc_timer * beacon_int * 128 / 125; wpa_printf(MSG_DEBUG, "Disassociation timer for " MACSTR " set to %d ms", MAC2STR(sta->addr), timeout); sta->timeout_next = STA_DISASSOC_FROM_CLI; eloop_cancel_timeout(ap_handle_timer, hapd, sta); eloop_register_timeout(timeout / 1000, timeout % 1000 * 1000, ap_handle_timer, hapd, sta); } return 0; } wpa-2.1/src/ap/wnm_ap.h000066400000000000000000000012151227414666700150020ustar00rootroot00000000000000/* * IEEE 802.11v WNM related functions and structures * Copyright (c) 2011-2013, Qualcomm Atheros, Inc. * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef WNM_AP_H #define WNM_AP_H struct sta_info; int ieee802_11_rx_wnm_action_ap(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, size_t len); int wnm_send_disassoc_imminent(struct hostapd_data *hapd, struct sta_info *sta, int disassoc_timer); int wnm_send_ess_disassoc_imminent(struct hostapd_data *hapd, struct sta_info *sta, const char *url, int disassoc_timer); #endif /* WNM_AP_H */ wpa-2.1/src/ap/wpa_auth.c000066400000000000000000002526301227414666700153350ustar00rootroot00000000000000/* * IEEE 802.11 RSN / WPA Authenticator * Copyright (c) 2004-2013, 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/eloop.h" #include "utils/state_machine.h" #include "utils/bitfield.h" #include "common/ieee802_11_defs.h" #include "crypto/aes_wrap.h" #include "crypto/crypto.h" #include "crypto/sha1.h" #include "crypto/sha256.h" #include "crypto/random.h" #include "eapol_auth/eapol_auth_sm.h" #include "ap_config.h" #include "ieee802_11.h" #include "wpa_auth.h" #include "pmksa_cache_auth.h" #include "wpa_auth_i.h" #include "wpa_auth_ie.h" #define STATE_MACHINE_DATA struct wpa_state_machine #define STATE_MACHINE_DEBUG_PREFIX "WPA" #define STATE_MACHINE_ADDR sm->addr static void wpa_send_eapol_timeout(void *eloop_ctx, void *timeout_ctx); static int wpa_sm_step(struct wpa_state_machine *sm); static int wpa_verify_key_mic(struct wpa_ptk *PTK, u8 *data, size_t data_len); static void wpa_sm_call_step(void *eloop_ctx, void *timeout_ctx); static void wpa_group_sm_step(struct wpa_authenticator *wpa_auth, struct wpa_group *group); static void wpa_request_new_ptk(struct wpa_state_machine *sm); static int wpa_gtk_update(struct wpa_authenticator *wpa_auth, struct wpa_group *group); static int wpa_group_config_group_keys(struct wpa_authenticator *wpa_auth, struct wpa_group *group); static const u32 dot11RSNAConfigGroupUpdateCount = 4; static const u32 dot11RSNAConfigPairwiseUpdateCount = 4; static const u32 eapol_key_timeout_first = 100; /* ms */ static const u32 eapol_key_timeout_subseq = 1000; /* ms */ static const u32 eapol_key_timeout_first_group = 500; /* ms */ /* TODO: make these configurable */ static const int dot11RSNAConfigPMKLifetime = 43200; static const int dot11RSNAConfigPMKReauthThreshold = 70; static const int dot11RSNAConfigSATimeout = 60; static inline int wpa_auth_mic_failure_report( struct wpa_authenticator *wpa_auth, const u8 *addr) { if (wpa_auth->cb.mic_failure_report) return wpa_auth->cb.mic_failure_report(wpa_auth->cb.ctx, addr); return 0; } static inline void wpa_auth_set_eapol(struct wpa_authenticator *wpa_auth, const u8 *addr, wpa_eapol_variable var, int value) { if (wpa_auth->cb.set_eapol) wpa_auth->cb.set_eapol(wpa_auth->cb.ctx, addr, var, value); } static inline int wpa_auth_get_eapol(struct wpa_authenticator *wpa_auth, const u8 *addr, wpa_eapol_variable var) { if (wpa_auth->cb.get_eapol == NULL) return -1; return wpa_auth->cb.get_eapol(wpa_auth->cb.ctx, addr, var); } static inline const u8 * wpa_auth_get_psk(struct wpa_authenticator *wpa_auth, const u8 *addr, const u8 *p2p_dev_addr, const u8 *prev_psk) { if (wpa_auth->cb.get_psk == NULL) return NULL; return wpa_auth->cb.get_psk(wpa_auth->cb.ctx, addr, p2p_dev_addr, prev_psk); } static inline int wpa_auth_get_msk(struct wpa_authenticator *wpa_auth, const u8 *addr, u8 *msk, size_t *len) { if (wpa_auth->cb.get_msk == NULL) return -1; return wpa_auth->cb.get_msk(wpa_auth->cb.ctx, addr, msk, len); } static inline int wpa_auth_set_key(struct wpa_authenticator *wpa_auth, int vlan_id, enum wpa_alg alg, const u8 *addr, int idx, u8 *key, size_t key_len) { if (wpa_auth->cb.set_key == NULL) return -1; return wpa_auth->cb.set_key(wpa_auth->cb.ctx, vlan_id, alg, addr, idx, key, key_len); } static inline int wpa_auth_get_seqnum(struct wpa_authenticator *wpa_auth, const u8 *addr, int idx, u8 *seq) { if (wpa_auth->cb.get_seqnum == NULL) return -1; return wpa_auth->cb.get_seqnum(wpa_auth->cb.ctx, addr, idx, seq); } static inline int wpa_auth_send_eapol(struct wpa_authenticator *wpa_auth, const u8 *addr, const u8 *data, size_t data_len, int encrypt) { if (wpa_auth->cb.send_eapol == NULL) return -1; return wpa_auth->cb.send_eapol(wpa_auth->cb.ctx, addr, data, data_len, encrypt); } int wpa_auth_for_each_sta(struct wpa_authenticator *wpa_auth, int (*cb)(struct wpa_state_machine *sm, void *ctx), void *cb_ctx) { if (wpa_auth->cb.for_each_sta == NULL) return 0; return wpa_auth->cb.for_each_sta(wpa_auth->cb.ctx, cb, cb_ctx); } int wpa_auth_for_each_auth(struct wpa_authenticator *wpa_auth, int (*cb)(struct wpa_authenticator *a, void *ctx), void *cb_ctx) { if (wpa_auth->cb.for_each_auth == NULL) return 0; return wpa_auth->cb.for_each_auth(wpa_auth->cb.ctx, cb, cb_ctx); } void wpa_auth_logger(struct wpa_authenticator *wpa_auth, const u8 *addr, logger_level level, const char *txt) { if (wpa_auth->cb.logger == NULL) return; wpa_auth->cb.logger(wpa_auth->cb.ctx, addr, level, txt); } void wpa_auth_vlogger(struct wpa_authenticator *wpa_auth, const u8 *addr, logger_level level, const char *fmt, ...) { char *format; int maxlen; va_list ap; if (wpa_auth->cb.logger == NULL) return; maxlen = os_strlen(fmt) + 100; format = os_malloc(maxlen); if (!format) return; va_start(ap, fmt); vsnprintf(format, maxlen, fmt, ap); va_end(ap); wpa_auth_logger(wpa_auth, addr, level, format); os_free(format); } static void wpa_sta_disconnect(struct wpa_authenticator *wpa_auth, const u8 *addr) { if (wpa_auth->cb.disconnect == NULL) return; wpa_printf(MSG_DEBUG, "wpa_sta_disconnect STA " MACSTR, MAC2STR(addr)); wpa_auth->cb.disconnect(wpa_auth->cb.ctx, addr, WLAN_REASON_PREV_AUTH_NOT_VALID); } static int wpa_use_aes_cmac(struct wpa_state_machine *sm) { int ret = 0; #ifdef CONFIG_IEEE80211R if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) ret = 1; #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_IEEE80211W if (wpa_key_mgmt_sha256(sm->wpa_key_mgmt)) ret = 1; #endif /* CONFIG_IEEE80211W */ return ret; } static void wpa_rekey_gmk(void *eloop_ctx, void *timeout_ctx) { struct wpa_authenticator *wpa_auth = eloop_ctx; if (random_get_bytes(wpa_auth->group->GMK, WPA_GMK_LEN)) { wpa_printf(MSG_ERROR, "Failed to get random data for WPA " "initialization."); } else { wpa_auth_logger(wpa_auth, NULL, LOGGER_DEBUG, "GMK rekeyd"); wpa_hexdump_key(MSG_DEBUG, "GMK", wpa_auth->group->GMK, WPA_GMK_LEN); } if (wpa_auth->conf.wpa_gmk_rekey) { eloop_register_timeout(wpa_auth->conf.wpa_gmk_rekey, 0, wpa_rekey_gmk, wpa_auth, NULL); } } static void wpa_rekey_gtk(void *eloop_ctx, void *timeout_ctx) { struct wpa_authenticator *wpa_auth = eloop_ctx; struct wpa_group *group; wpa_auth_logger(wpa_auth, NULL, LOGGER_DEBUG, "rekeying GTK"); for (group = wpa_auth->group; group; group = group->next) { group->GTKReKey = TRUE; do { group->changed = FALSE; wpa_group_sm_step(wpa_auth, group); } while (group->changed); } if (wpa_auth->conf.wpa_group_rekey) { eloop_register_timeout(wpa_auth->conf.wpa_group_rekey, 0, wpa_rekey_gtk, wpa_auth, NULL); } } static void wpa_rekey_ptk(void *eloop_ctx, void *timeout_ctx) { struct wpa_authenticator *wpa_auth = eloop_ctx; struct wpa_state_machine *sm = timeout_ctx; wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, "rekeying PTK"); wpa_request_new_ptk(sm); wpa_sm_step(sm); } static int wpa_auth_pmksa_clear_cb(struct wpa_state_machine *sm, void *ctx) { if (sm->pmksa == ctx) sm->pmksa = NULL; return 0; } static void wpa_auth_pmksa_free_cb(struct rsn_pmksa_cache_entry *entry, void *ctx) { struct wpa_authenticator *wpa_auth = ctx; wpa_auth_for_each_sta(wpa_auth, wpa_auth_pmksa_clear_cb, entry); } static int wpa_group_init_gmk_and_counter(struct wpa_authenticator *wpa_auth, struct wpa_group *group) { u8 buf[ETH_ALEN + 8 + sizeof(unsigned long)]; u8 rkey[32]; unsigned long ptr; if (random_get_bytes(group->GMK, WPA_GMK_LEN) < 0) return -1; wpa_hexdump_key(MSG_DEBUG, "GMK", group->GMK, WPA_GMK_LEN); /* * Counter = PRF-256(Random number, "Init Counter", * Local MAC Address || Time) */ os_memcpy(buf, wpa_auth->addr, ETH_ALEN); wpa_get_ntp_timestamp(buf + ETH_ALEN); ptr = (unsigned long) group; os_memcpy(buf + ETH_ALEN + 8, &ptr, sizeof(ptr)); if (random_get_bytes(rkey, sizeof(rkey)) < 0) return -1; if (sha1_prf(rkey, sizeof(rkey), "Init Counter", buf, sizeof(buf), group->Counter, WPA_NONCE_LEN) < 0) return -1; wpa_hexdump_key(MSG_DEBUG, "Key Counter", group->Counter, WPA_NONCE_LEN); return 0; } static struct wpa_group * wpa_group_init(struct wpa_authenticator *wpa_auth, int vlan_id, int delay_init) { struct wpa_group *group; group = os_zalloc(sizeof(struct wpa_group)); if (group == NULL) return NULL; group->GTKAuthenticator = TRUE; group->vlan_id = vlan_id; group->GTK_len = wpa_cipher_key_len(wpa_auth->conf.wpa_group); if (random_pool_ready() != 1) { wpa_printf(MSG_INFO, "WPA: Not enough entropy in random pool " "for secure operations - update keys later when " "the first station connects"); } /* * Set initial GMK/Counter value here. The actual values that will be * used in negotiations will be set once the first station tries to * connect. This allows more time for collecting additional randomness * on embedded devices. */ if (wpa_group_init_gmk_and_counter(wpa_auth, group) < 0) { wpa_printf(MSG_ERROR, "Failed to get random data for WPA " "initialization."); os_free(group); return NULL; } group->GInit = TRUE; if (delay_init) { wpa_printf(MSG_DEBUG, "WPA: Delay group state machine start " "until Beacon frames have been configured"); /* Initialization is completed in wpa_init_keys(). */ } else { wpa_group_sm_step(wpa_auth, group); group->GInit = FALSE; wpa_group_sm_step(wpa_auth, group); } return group; } /** * wpa_init - Initialize WPA authenticator * @addr: Authenticator address * @conf: Configuration for WPA authenticator * @cb: Callback functions for WPA authenticator * Returns: Pointer to WPA authenticator data or %NULL on failure */ struct wpa_authenticator * wpa_init(const u8 *addr, struct wpa_auth_config *conf, struct wpa_auth_callbacks *cb) { struct wpa_authenticator *wpa_auth; wpa_auth = os_zalloc(sizeof(struct wpa_authenticator)); if (wpa_auth == NULL) return NULL; os_memcpy(wpa_auth->addr, addr, ETH_ALEN); os_memcpy(&wpa_auth->conf, conf, sizeof(*conf)); os_memcpy(&wpa_auth->cb, cb, sizeof(*cb)); if (wpa_auth_gen_wpa_ie(wpa_auth)) { wpa_printf(MSG_ERROR, "Could not generate WPA IE."); os_free(wpa_auth); return NULL; } wpa_auth->group = wpa_group_init(wpa_auth, 0, 1); if (wpa_auth->group == NULL) { os_free(wpa_auth->wpa_ie); os_free(wpa_auth); return NULL; } wpa_auth->pmksa = pmksa_cache_auth_init(wpa_auth_pmksa_free_cb, wpa_auth); if (wpa_auth->pmksa == NULL) { wpa_printf(MSG_ERROR, "PMKSA cache initialization failed."); os_free(wpa_auth->wpa_ie); os_free(wpa_auth); return NULL; } #ifdef CONFIG_IEEE80211R wpa_auth->ft_pmk_cache = wpa_ft_pmk_cache_init(); if (wpa_auth->ft_pmk_cache == NULL) { wpa_printf(MSG_ERROR, "FT PMK cache initialization failed."); os_free(wpa_auth->wpa_ie); pmksa_cache_auth_deinit(wpa_auth->pmksa); os_free(wpa_auth); return NULL; } #endif /* CONFIG_IEEE80211R */ if (wpa_auth->conf.wpa_gmk_rekey) { eloop_register_timeout(wpa_auth->conf.wpa_gmk_rekey, 0, wpa_rekey_gmk, wpa_auth, NULL); } if (wpa_auth->conf.wpa_group_rekey) { eloop_register_timeout(wpa_auth->conf.wpa_group_rekey, 0, wpa_rekey_gtk, wpa_auth, NULL); } #ifdef CONFIG_P2P if (WPA_GET_BE32(conf->ip_addr_start)) { int count = WPA_GET_BE32(conf->ip_addr_end) - WPA_GET_BE32(conf->ip_addr_start) + 1; if (count > 1000) count = 1000; if (count > 0) wpa_auth->ip_pool = bitfield_alloc(count); } #endif /* CONFIG_P2P */ return wpa_auth; } int wpa_init_keys(struct wpa_authenticator *wpa_auth) { struct wpa_group *group = wpa_auth->group; wpa_printf(MSG_DEBUG, "WPA: Start group state machine to set initial " "keys"); wpa_group_sm_step(wpa_auth, group); group->GInit = FALSE; wpa_group_sm_step(wpa_auth, group); if (group->wpa_group_state == WPA_GROUP_FATAL_FAILURE) return -1; return 0; } /** * wpa_deinit - Deinitialize WPA authenticator * @wpa_auth: Pointer to WPA authenticator data from wpa_init() */ void wpa_deinit(struct wpa_authenticator *wpa_auth) { struct wpa_group *group, *prev; eloop_cancel_timeout(wpa_rekey_gmk, wpa_auth, NULL); eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL); #ifdef CONFIG_PEERKEY while (wpa_auth->stsl_negotiations) wpa_stsl_remove(wpa_auth, wpa_auth->stsl_negotiations); #endif /* CONFIG_PEERKEY */ pmksa_cache_auth_deinit(wpa_auth->pmksa); #ifdef CONFIG_IEEE80211R wpa_ft_pmk_cache_deinit(wpa_auth->ft_pmk_cache); wpa_auth->ft_pmk_cache = NULL; #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_P2P bitfield_free(wpa_auth->ip_pool); #endif /* CONFIG_P2P */ os_free(wpa_auth->wpa_ie); group = wpa_auth->group; while (group) { prev = group; group = group->next; os_free(prev); } os_free(wpa_auth); } /** * wpa_reconfig - Update WPA authenticator configuration * @wpa_auth: Pointer to WPA authenticator data from wpa_init() * @conf: Configuration for WPA authenticator */ int wpa_reconfig(struct wpa_authenticator *wpa_auth, struct wpa_auth_config *conf) { struct wpa_group *group; if (wpa_auth == NULL) return 0; os_memcpy(&wpa_auth->conf, conf, sizeof(*conf)); if (wpa_auth_gen_wpa_ie(wpa_auth)) { wpa_printf(MSG_ERROR, "Could not generate WPA IE."); return -1; } /* * Reinitialize GTK to make sure it is suitable for the new * configuration. */ group = wpa_auth->group; group->GTK_len = wpa_cipher_key_len(wpa_auth->conf.wpa_group); group->GInit = TRUE; wpa_group_sm_step(wpa_auth, group); group->GInit = FALSE; wpa_group_sm_step(wpa_auth, group); return 0; } struct wpa_state_machine * wpa_auth_sta_init(struct wpa_authenticator *wpa_auth, const u8 *addr, const u8 *p2p_dev_addr) { struct wpa_state_machine *sm; if (wpa_auth->group->wpa_group_state == WPA_GROUP_FATAL_FAILURE) return NULL; sm = os_zalloc(sizeof(struct wpa_state_machine)); if (sm == NULL) return NULL; os_memcpy(sm->addr, addr, ETH_ALEN); if (p2p_dev_addr) os_memcpy(sm->p2p_dev_addr, p2p_dev_addr, ETH_ALEN); sm->wpa_auth = wpa_auth; sm->group = wpa_auth->group; return sm; } int wpa_auth_sta_associated(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm) { if (wpa_auth == NULL || !wpa_auth->conf.wpa || sm == NULL) return -1; #ifdef CONFIG_IEEE80211R if (sm->ft_completed) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, "FT authentication already completed - do not " "start 4-way handshake"); return 0; } #endif /* CONFIG_IEEE80211R */ if (sm->started) { os_memset(&sm->key_replay, 0, sizeof(sm->key_replay)); sm->ReAuthenticationRequest = TRUE; return wpa_sm_step(sm); } wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, "start authentication"); sm->started = 1; sm->Init = TRUE; if (wpa_sm_step(sm) == 1) return 1; /* should not really happen */ sm->Init = FALSE; sm->AuthenticationRequest = TRUE; return wpa_sm_step(sm); } void wpa_auth_sta_no_wpa(struct wpa_state_machine *sm) { /* WPA/RSN was not used - clear WPA state. This is needed if the STA * reassociates back to the same AP while the previous entry for the * STA has not yet been removed. */ if (sm == NULL) return; sm->wpa_key_mgmt = 0; } static void wpa_free_sta_sm(struct wpa_state_machine *sm) { #ifdef CONFIG_P2P if (WPA_GET_BE32(sm->ip_addr)) { u32 start; wpa_printf(MSG_DEBUG, "P2P: Free assigned IP " "address %u.%u.%u.%u from " MACSTR, sm->ip_addr[0], sm->ip_addr[1], sm->ip_addr[2], sm->ip_addr[3], MAC2STR(sm->addr)); start = WPA_GET_BE32(sm->wpa_auth->conf.ip_addr_start); bitfield_clear(sm->wpa_auth->ip_pool, WPA_GET_BE32(sm->ip_addr) - start); } #endif /* CONFIG_P2P */ if (sm->GUpdateStationKeys) { sm->group->GKeyDoneStations--; sm->GUpdateStationKeys = FALSE; } #ifdef CONFIG_IEEE80211R os_free(sm->assoc_resp_ftie); #endif /* CONFIG_IEEE80211R */ os_free(sm->last_rx_eapol_key); os_free(sm->wpa_ie); os_free(sm); } void wpa_auth_sta_deinit(struct wpa_state_machine *sm) { if (sm == NULL) return; if (sm->wpa_auth->conf.wpa_strict_rekey && sm->has_GTK) { wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, "strict rekeying - force GTK rekey since STA " "is leaving"); eloop_cancel_timeout(wpa_rekey_gtk, sm->wpa_auth, NULL); eloop_register_timeout(0, 500000, wpa_rekey_gtk, sm->wpa_auth, NULL); } eloop_cancel_timeout(wpa_send_eapol_timeout, sm->wpa_auth, sm); sm->pending_1_of_4_timeout = 0; eloop_cancel_timeout(wpa_sm_call_step, sm, NULL); eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm); if (sm->in_step_loop) { /* Must not free state machine while wpa_sm_step() is running. * Freeing will be completed in the end of wpa_sm_step(). */ wpa_printf(MSG_DEBUG, "WPA: Registering pending STA state " "machine deinit for " MACSTR, MAC2STR(sm->addr)); sm->pending_deinit = 1; } else wpa_free_sta_sm(sm); } static void wpa_request_new_ptk(struct wpa_state_machine *sm) { if (sm == NULL) return; sm->PTKRequest = TRUE; sm->PTK_valid = 0; } static int wpa_replay_counter_valid(struct wpa_key_replay_counter *ctr, const u8 *replay_counter) { int i; for (i = 0; i < RSNA_MAX_EAPOL_RETRIES; i++) { if (!ctr[i].valid) break; if (os_memcmp(replay_counter, ctr[i].counter, WPA_REPLAY_COUNTER_LEN) == 0) return 1; } return 0; } static void wpa_replay_counter_mark_invalid(struct wpa_key_replay_counter *ctr, const u8 *replay_counter) { int i; for (i = 0; i < RSNA_MAX_EAPOL_RETRIES; i++) { if (ctr[i].valid && (replay_counter == NULL || os_memcmp(replay_counter, ctr[i].counter, WPA_REPLAY_COUNTER_LEN) == 0)) ctr[i].valid = FALSE; } } #ifdef CONFIG_IEEE80211R static int ft_check_msg_2_of_4(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, struct wpa_eapol_ie_parse *kde) { struct wpa_ie_data ie; struct rsn_mdie *mdie; if (wpa_parse_wpa_ie_rsn(kde->rsn_ie, kde->rsn_ie_len, &ie) < 0 || ie.num_pmkid != 1 || ie.pmkid == NULL) { wpa_printf(MSG_DEBUG, "FT: No PMKR1Name in " "FT 4-way handshake message 2/4"); return -1; } os_memcpy(sm->sup_pmk_r1_name, ie.pmkid, PMKID_LEN); wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name from Supplicant", sm->sup_pmk_r1_name, PMKID_LEN); if (!kde->mdie || !kde->ftie) { wpa_printf(MSG_DEBUG, "FT: No %s in FT 4-way handshake " "message 2/4", kde->mdie ? "FTIE" : "MDIE"); return -1; } mdie = (struct rsn_mdie *) (kde->mdie + 2); if (kde->mdie[1] < sizeof(struct rsn_mdie) || os_memcmp(wpa_auth->conf.mobility_domain, mdie->mobility_domain, MOBILITY_DOMAIN_ID_LEN) != 0) { wpa_printf(MSG_DEBUG, "FT: MDIE mismatch"); return -1; } if (sm->assoc_resp_ftie && (kde->ftie[1] != sm->assoc_resp_ftie[1] || os_memcmp(kde->ftie, sm->assoc_resp_ftie, 2 + sm->assoc_resp_ftie[1]) != 0)) { wpa_printf(MSG_DEBUG, "FT: FTIE mismatch"); wpa_hexdump(MSG_DEBUG, "FT: FTIE in EAPOL-Key msg 2/4", kde->ftie, kde->ftie_len); wpa_hexdump(MSG_DEBUG, "FT: FTIE in (Re)AssocResp", sm->assoc_resp_ftie, 2 + sm->assoc_resp_ftie[1]); return -1; } return 0; } #endif /* CONFIG_IEEE80211R */ static int wpa_receive_error_report(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, int group) { /* Supplicant reported a Michael MIC error */ wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, "received EAPOL-Key Error Request " "(STA detected Michael MIC failure (group=%d))", group); if (group && wpa_auth->conf.wpa_group != WPA_CIPHER_TKIP) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, "ignore Michael MIC failure report since " "group cipher is not TKIP"); } else if (!group && sm->pairwise != WPA_CIPHER_TKIP) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, "ignore Michael MIC failure report since " "pairwise cipher is not TKIP"); } else { if (wpa_auth_mic_failure_report(wpa_auth, sm->addr) > 0) return 1; /* STA entry was removed */ sm->dot11RSNAStatsTKIPRemoteMICFailures++; wpa_auth->dot11RSNAStatsTKIPRemoteMICFailures++; } /* * Error report is not a request for a new key handshake, but since * Authenticator may do it, let's change the keys now anyway. */ wpa_request_new_ptk(sm); return 0; } void wpa_receive(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, u8 *data, size_t data_len) { struct ieee802_1x_hdr *hdr; struct wpa_eapol_key *key; u16 key_info, key_data_length; enum { PAIRWISE_2, PAIRWISE_4, GROUP_2, REQUEST, SMK_M1, SMK_M3, SMK_ERROR } msg; char *msgtxt; struct wpa_eapol_ie_parse kde; int ft; const u8 *eapol_key_ie; size_t eapol_key_ie_len; if (wpa_auth == NULL || !wpa_auth->conf.wpa || sm == NULL) return; if (data_len < sizeof(*hdr) + sizeof(*key)) return; hdr = (struct ieee802_1x_hdr *) data; key = (struct wpa_eapol_key *) (hdr + 1); key_info = WPA_GET_BE16(key->key_info); key_data_length = WPA_GET_BE16(key->key_data_length); wpa_printf(MSG_DEBUG, "WPA: Received EAPOL-Key from " MACSTR " key_info=0x%x type=%u key_data_length=%u", MAC2STR(sm->addr), key_info, key->type, key_data_length); if (key_data_length > data_len - sizeof(*hdr) - sizeof(*key)) { wpa_printf(MSG_INFO, "WPA: Invalid EAPOL-Key frame - " "key_data overflow (%d > %lu)", key_data_length, (unsigned long) (data_len - sizeof(*hdr) - sizeof(*key))); return; } if (sm->wpa == WPA_VERSION_WPA2) { if (key->type == EAPOL_KEY_TYPE_WPA) { /* * Some deployed station implementations seem to send * msg 4/4 with incorrect type value in WPA2 mode. */ wpa_printf(MSG_DEBUG, "Workaround: Allow EAPOL-Key " "with unexpected WPA type in RSN mode"); } else if (key->type != EAPOL_KEY_TYPE_RSN) { wpa_printf(MSG_DEBUG, "Ignore EAPOL-Key with " "unexpected type %d in RSN mode", key->type); return; } } else { if (key->type != EAPOL_KEY_TYPE_WPA) { wpa_printf(MSG_DEBUG, "Ignore EAPOL-Key with " "unexpected type %d in WPA mode", key->type); return; } } wpa_hexdump(MSG_DEBUG, "WPA: Received Key Nonce", key->key_nonce, WPA_NONCE_LEN); wpa_hexdump(MSG_DEBUG, "WPA: Received Replay Counter", key->replay_counter, WPA_REPLAY_COUNTER_LEN); /* FIX: verify that the EAPOL-Key frame was encrypted if pairwise keys * are set */ if ((key_info & (WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_REQUEST)) == (WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_REQUEST)) { if (key_info & WPA_KEY_INFO_ERROR) { msg = SMK_ERROR; msgtxt = "SMK Error"; } else { msg = SMK_M1; msgtxt = "SMK M1"; } } else if (key_info & WPA_KEY_INFO_SMK_MESSAGE) { msg = SMK_M3; msgtxt = "SMK M3"; } else if (key_info & WPA_KEY_INFO_REQUEST) { msg = REQUEST; msgtxt = "Request"; } else if (!(key_info & WPA_KEY_INFO_KEY_TYPE)) { msg = GROUP_2; msgtxt = "2/2 Group"; } else if (key_data_length == 0) { msg = PAIRWISE_4; msgtxt = "4/4 Pairwise"; } else { msg = PAIRWISE_2; msgtxt = "2/4 Pairwise"; } /* TODO: key_info type validation for PeerKey */ if (msg == REQUEST || msg == PAIRWISE_2 || msg == PAIRWISE_4 || msg == GROUP_2) { u16 ver = key_info & WPA_KEY_INFO_TYPE_MASK; if (sm->pairwise == WPA_CIPHER_CCMP || sm->pairwise == WPA_CIPHER_GCMP) { if (wpa_use_aes_cmac(sm) && ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_WARNING, "advertised support for " "AES-128-CMAC, but did not " "use it"); return; } if (!wpa_use_aes_cmac(sm) && ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_WARNING, "did not use HMAC-SHA1-AES " "with CCMP/GCMP"); return; } } } if (key_info & WPA_KEY_INFO_REQUEST) { if (sm->req_replay_counter_used && os_memcmp(key->replay_counter, sm->req_replay_counter, WPA_REPLAY_COUNTER_LEN) <= 0) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_WARNING, "received EAPOL-Key request with " "replayed counter"); return; } } if (!(key_info & WPA_KEY_INFO_REQUEST) && !wpa_replay_counter_valid(sm->key_replay, key->replay_counter)) { int i; if (msg == PAIRWISE_2 && wpa_replay_counter_valid(sm->prev_key_replay, key->replay_counter) && sm->wpa_ptk_state == WPA_PTK_PTKINITNEGOTIATING && os_memcmp(sm->SNonce, key->key_nonce, WPA_NONCE_LEN) != 0) { /* * Some supplicant implementations (e.g., Windows XP * WZC) update SNonce for each EAPOL-Key 2/4. This * breaks the workaround on accepting any of the * pending requests, so allow the SNonce to be updated * even if we have already sent out EAPOL-Key 3/4. */ wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG, "Process SNonce update from STA " "based on retransmitted EAPOL-Key " "1/4"); sm->update_snonce = 1; wpa_replay_counter_mark_invalid(sm->prev_key_replay, key->replay_counter); goto continue_processing; } if (msg == PAIRWISE_2 && wpa_replay_counter_valid(sm->prev_key_replay, key->replay_counter) && sm->wpa_ptk_state == WPA_PTK_PTKINITNEGOTIATING) { wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG, "ignore retransmitted EAPOL-Key %s - " "SNonce did not change", msgtxt); } else { wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG, "received EAPOL-Key %s with " "unexpected replay counter", msgtxt); } for (i = 0; i < RSNA_MAX_EAPOL_RETRIES; i++) { if (!sm->key_replay[i].valid) break; wpa_hexdump(MSG_DEBUG, "pending replay counter", sm->key_replay[i].counter, WPA_REPLAY_COUNTER_LEN); } wpa_hexdump(MSG_DEBUG, "received replay counter", key->replay_counter, WPA_REPLAY_COUNTER_LEN); return; } continue_processing: switch (msg) { case PAIRWISE_2: if (sm->wpa_ptk_state != WPA_PTK_PTKSTART && sm->wpa_ptk_state != WPA_PTK_PTKCALCNEGOTIATING && (!sm->update_snonce || sm->wpa_ptk_state != WPA_PTK_PTKINITNEGOTIATING)) { wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, "received EAPOL-Key msg 2/4 in " "invalid state (%d) - dropped", sm->wpa_ptk_state); return; } random_add_randomness(key->key_nonce, WPA_NONCE_LEN); if (sm->group->reject_4way_hs_for_entropy) { /* * The system did not have enough entropy to generate * strong random numbers. Reject the first 4-way * handshake(s) and collect some entropy based on the * information from it. Once enough entropy is * available, the next atempt will trigger GMK/Key * Counter update and the station will be allowed to * continue. */ wpa_printf(MSG_DEBUG, "WPA: Reject 4-way handshake to " "collect more entropy for random number " "generation"); random_mark_pool_ready(); wpa_sta_disconnect(wpa_auth, sm->addr); return; } if (wpa_parse_kde_ies((u8 *) (key + 1), key_data_length, &kde) < 0) { wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, "received EAPOL-Key msg 2/4 with " "invalid Key Data contents"); return; } if (kde.rsn_ie) { eapol_key_ie = kde.rsn_ie; eapol_key_ie_len = kde.rsn_ie_len; } else { eapol_key_ie = kde.wpa_ie; eapol_key_ie_len = kde.wpa_ie_len; } ft = sm->wpa == WPA_VERSION_WPA2 && wpa_key_mgmt_ft(sm->wpa_key_mgmt); if (sm->wpa_ie == NULL || wpa_compare_rsn_ie(ft, sm->wpa_ie, sm->wpa_ie_len, eapol_key_ie, eapol_key_ie_len)) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, "WPA IE from (Re)AssocReq did not " "match with msg 2/4"); if (sm->wpa_ie) { wpa_hexdump(MSG_DEBUG, "WPA IE in AssocReq", sm->wpa_ie, sm->wpa_ie_len); } wpa_hexdump(MSG_DEBUG, "WPA IE in msg 2/4", eapol_key_ie, eapol_key_ie_len); /* MLME-DEAUTHENTICATE.request */ wpa_sta_disconnect(wpa_auth, sm->addr); return; } #ifdef CONFIG_IEEE80211R if (ft && ft_check_msg_2_of_4(wpa_auth, sm, &kde) < 0) { wpa_sta_disconnect(wpa_auth, sm->addr); return; } #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_P2P if (kde.ip_addr_req && kde.ip_addr_req[0] && wpa_auth->ip_pool && WPA_GET_BE32(sm->ip_addr) == 0) { int idx; wpa_printf(MSG_DEBUG, "P2P: IP address requested in " "EAPOL-Key exchange"); idx = bitfield_get_first_zero(wpa_auth->ip_pool); if (idx >= 0) { u32 start = WPA_GET_BE32(wpa_auth->conf. ip_addr_start); bitfield_set(wpa_auth->ip_pool, idx); WPA_PUT_BE32(sm->ip_addr, start + idx); wpa_printf(MSG_DEBUG, "P2P: Assigned IP " "address %u.%u.%u.%u to " MACSTR, sm->ip_addr[0], sm->ip_addr[1], sm->ip_addr[2], sm->ip_addr[3], MAC2STR(sm->addr)); } } #endif /* CONFIG_P2P */ break; case PAIRWISE_4: if (sm->wpa_ptk_state != WPA_PTK_PTKINITNEGOTIATING || !sm->PTK_valid) { wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, "received EAPOL-Key msg 4/4 in " "invalid state (%d) - dropped", sm->wpa_ptk_state); return; } break; case GROUP_2: if (sm->wpa_ptk_group_state != WPA_PTK_GROUP_REKEYNEGOTIATING || !sm->PTK_valid) { wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, "received EAPOL-Key msg 2/2 in " "invalid state (%d) - dropped", sm->wpa_ptk_group_state); return; } break; #ifdef CONFIG_PEERKEY case SMK_M1: case SMK_M3: case SMK_ERROR: if (!wpa_auth->conf.peerkey) { wpa_printf(MSG_DEBUG, "RSN: SMK M1/M3/Error, but " "PeerKey use disabled - ignoring message"); return; } if (!sm->PTK_valid) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, "received EAPOL-Key msg SMK in " "invalid state - dropped"); return; } break; #else /* CONFIG_PEERKEY */ case SMK_M1: case SMK_M3: case SMK_ERROR: return; /* STSL disabled - ignore SMK messages */ #endif /* CONFIG_PEERKEY */ case REQUEST: break; } wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG, "received EAPOL-Key frame (%s)", msgtxt); if (key_info & WPA_KEY_INFO_ACK) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, "received invalid EAPOL-Key: Key Ack set"); return; } if (!(key_info & WPA_KEY_INFO_MIC)) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, "received invalid EAPOL-Key: Key MIC not set"); return; } sm->MICVerified = FALSE; if (sm->PTK_valid && !sm->update_snonce) { if (wpa_verify_key_mic(&sm->PTK, data, data_len)) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, "received EAPOL-Key with invalid MIC"); return; } sm->MICVerified = TRUE; eloop_cancel_timeout(wpa_send_eapol_timeout, wpa_auth, sm); sm->pending_1_of_4_timeout = 0; } if (key_info & WPA_KEY_INFO_REQUEST) { if (sm->MICVerified) { sm->req_replay_counter_used = 1; os_memcpy(sm->req_replay_counter, key->replay_counter, WPA_REPLAY_COUNTER_LEN); } else { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, "received EAPOL-Key request with " "invalid MIC"); return; } /* * TODO: should decrypt key data field if encryption was used; * even though MAC address KDE is not normally encrypted, * supplicant is allowed to encrypt it. */ if (msg == SMK_ERROR) { #ifdef CONFIG_PEERKEY wpa_smk_error(wpa_auth, sm, key); #endif /* CONFIG_PEERKEY */ return; } else if (key_info & WPA_KEY_INFO_ERROR) { if (wpa_receive_error_report( wpa_auth, sm, !(key_info & WPA_KEY_INFO_KEY_TYPE)) > 0) return; /* STA entry was removed */ } else if (key_info & WPA_KEY_INFO_KEY_TYPE) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, "received EAPOL-Key Request for new " "4-Way Handshake"); wpa_request_new_ptk(sm); #ifdef CONFIG_PEERKEY } else if (msg == SMK_M1) { wpa_smk_m1(wpa_auth, sm, key); #endif /* CONFIG_PEERKEY */ } else if (key_data_length > 0 && wpa_parse_kde_ies((const u8 *) (key + 1), key_data_length, &kde) == 0 && kde.mac_addr) { } else { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, "received EAPOL-Key Request for GTK " "rekeying"); eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL); wpa_rekey_gtk(wpa_auth, NULL); } } else { /* Do not allow the same key replay counter to be reused. */ wpa_replay_counter_mark_invalid(sm->key_replay, key->replay_counter); if (msg == PAIRWISE_2) { /* * Maintain a copy of the pending EAPOL-Key frames in * case the EAPOL-Key frame was retransmitted. This is * needed to allow EAPOL-Key msg 2/4 reply to another * pending msg 1/4 to update the SNonce to work around * unexpected supplicant behavior. */ os_memcpy(sm->prev_key_replay, sm->key_replay, sizeof(sm->key_replay)); } else { os_memset(sm->prev_key_replay, 0, sizeof(sm->prev_key_replay)); } /* * Make sure old valid counters are not accepted anymore and * do not get copied again. */ wpa_replay_counter_mark_invalid(sm->key_replay, NULL); } #ifdef CONFIG_PEERKEY if (msg == SMK_M3) { wpa_smk_m3(wpa_auth, sm, key); return; } #endif /* CONFIG_PEERKEY */ os_free(sm->last_rx_eapol_key); sm->last_rx_eapol_key = os_malloc(data_len); if (sm->last_rx_eapol_key == NULL) return; os_memcpy(sm->last_rx_eapol_key, data, data_len); sm->last_rx_eapol_key_len = data_len; sm->rx_eapol_key_secure = !!(key_info & WPA_KEY_INFO_SECURE); sm->EAPOLKeyReceived = TRUE; sm->EAPOLKeyPairwise = !!(key_info & WPA_KEY_INFO_KEY_TYPE); sm->EAPOLKeyRequest = !!(key_info & WPA_KEY_INFO_REQUEST); os_memcpy(sm->SNonce, key->key_nonce, WPA_NONCE_LEN); wpa_sm_step(sm); } static int wpa_gmk_to_gtk(const u8 *gmk, const char *label, const u8 *addr, const u8 *gnonce, u8 *gtk, size_t gtk_len) { u8 data[ETH_ALEN + WPA_NONCE_LEN + 8 + 16]; u8 *pos; int ret = 0; /* GTK = PRF-X(GMK, "Group key expansion", * AA || GNonce || Time || random data) * The example described in the IEEE 802.11 standard uses only AA and * GNonce as inputs here. Add some more entropy since this derivation * is done only at the Authenticator and as such, does not need to be * exactly same. */ os_memcpy(data, addr, ETH_ALEN); os_memcpy(data + ETH_ALEN, gnonce, WPA_NONCE_LEN); pos = data + ETH_ALEN + WPA_NONCE_LEN; wpa_get_ntp_timestamp(pos); pos += 8; if (random_get_bytes(pos, 16) < 0) ret = -1; #ifdef CONFIG_IEEE80211W sha256_prf(gmk, WPA_GMK_LEN, label, data, sizeof(data), gtk, gtk_len); #else /* CONFIG_IEEE80211W */ if (sha1_prf(gmk, WPA_GMK_LEN, label, data, sizeof(data), gtk, gtk_len) < 0) ret = -1; #endif /* CONFIG_IEEE80211W */ return ret; } static void wpa_send_eapol_timeout(void *eloop_ctx, void *timeout_ctx) { struct wpa_authenticator *wpa_auth = eloop_ctx; struct wpa_state_machine *sm = timeout_ctx; sm->pending_1_of_4_timeout = 0; wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, "EAPOL-Key timeout"); sm->TimeoutEvt = TRUE; wpa_sm_step(sm); } void __wpa_send_eapol(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, int key_info, const u8 *key_rsc, const u8 *nonce, const u8 *kde, size_t kde_len, int keyidx, int encr, int force_version) { struct ieee802_1x_hdr *hdr; struct wpa_eapol_key *key; size_t len; int alg; int key_data_len, pad_len = 0; u8 *buf, *pos; int version, pairwise; int i; len = sizeof(struct ieee802_1x_hdr) + sizeof(struct wpa_eapol_key); if (force_version) version = force_version; else if (wpa_use_aes_cmac(sm)) version = WPA_KEY_INFO_TYPE_AES_128_CMAC; else if (sm->pairwise != WPA_CIPHER_TKIP) version = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES; else version = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4; pairwise = !!(key_info & WPA_KEY_INFO_KEY_TYPE); wpa_printf(MSG_DEBUG, "WPA: Send EAPOL(version=%d secure=%d mic=%d " "ack=%d install=%d pairwise=%d kde_len=%lu keyidx=%d " "encr=%d)", version, (key_info & WPA_KEY_INFO_SECURE) ? 1 : 0, (key_info & WPA_KEY_INFO_MIC) ? 1 : 0, (key_info & WPA_KEY_INFO_ACK) ? 1 : 0, (key_info & WPA_KEY_INFO_INSTALL) ? 1 : 0, pairwise, (unsigned long) kde_len, keyidx, encr); key_data_len = kde_len; if ((version == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES || version == WPA_KEY_INFO_TYPE_AES_128_CMAC) && encr) { pad_len = key_data_len % 8; if (pad_len) pad_len = 8 - pad_len; key_data_len += pad_len + 8; } len += key_data_len; hdr = os_zalloc(len); if (hdr == NULL) return; hdr->version = wpa_auth->conf.eapol_version; hdr->type = IEEE802_1X_TYPE_EAPOL_KEY; hdr->length = host_to_be16(len - sizeof(*hdr)); key = (struct wpa_eapol_key *) (hdr + 1); key->type = sm->wpa == WPA_VERSION_WPA2 ? EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA; key_info |= version; if (encr && sm->wpa == WPA_VERSION_WPA2) key_info |= WPA_KEY_INFO_ENCR_KEY_DATA; if (sm->wpa != WPA_VERSION_WPA2) key_info |= keyidx << WPA_KEY_INFO_KEY_INDEX_SHIFT; WPA_PUT_BE16(key->key_info, key_info); alg = pairwise ? sm->pairwise : wpa_auth->conf.wpa_group; WPA_PUT_BE16(key->key_length, wpa_cipher_key_len(alg)); if (key_info & WPA_KEY_INFO_SMK_MESSAGE) WPA_PUT_BE16(key->key_length, 0); /* FIX: STSL: what to use as key_replay_counter? */ for (i = RSNA_MAX_EAPOL_RETRIES - 1; i > 0; i--) { sm->key_replay[i].valid = sm->key_replay[i - 1].valid; os_memcpy(sm->key_replay[i].counter, sm->key_replay[i - 1].counter, WPA_REPLAY_COUNTER_LEN); } inc_byte_array(sm->key_replay[0].counter, WPA_REPLAY_COUNTER_LEN); os_memcpy(key->replay_counter, sm->key_replay[0].counter, WPA_REPLAY_COUNTER_LEN); sm->key_replay[0].valid = TRUE; if (nonce) os_memcpy(key->key_nonce, nonce, WPA_NONCE_LEN); if (key_rsc) os_memcpy(key->key_rsc, key_rsc, WPA_KEY_RSC_LEN); if (kde && !encr) { os_memcpy(key + 1, kde, kde_len); WPA_PUT_BE16(key->key_data_length, kde_len); } else if (encr && kde) { buf = os_zalloc(key_data_len); if (buf == NULL) { os_free(hdr); return; } pos = buf; os_memcpy(pos, kde, kde_len); pos += kde_len; if (pad_len) *pos++ = 0xdd; wpa_hexdump_key(MSG_DEBUG, "Plaintext EAPOL-Key Key Data", buf, key_data_len); if (version == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES || version == WPA_KEY_INFO_TYPE_AES_128_CMAC) { if (aes_wrap(sm->PTK.kek, (key_data_len - 8) / 8, buf, (u8 *) (key + 1))) { os_free(hdr); os_free(buf); return; } WPA_PUT_BE16(key->key_data_length, key_data_len); } else { u8 ek[32]; os_memcpy(key->key_iv, sm->group->Counter + WPA_NONCE_LEN - 16, 16); inc_byte_array(sm->group->Counter, WPA_NONCE_LEN); os_memcpy(ek, key->key_iv, 16); os_memcpy(ek + 16, sm->PTK.kek, 16); os_memcpy(key + 1, buf, key_data_len); rc4_skip(ek, 32, 256, (u8 *) (key + 1), key_data_len); WPA_PUT_BE16(key->key_data_length, key_data_len); } os_free(buf); } if (key_info & WPA_KEY_INFO_MIC) { if (!sm->PTK_valid) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, "PTK not valid when sending EAPOL-Key " "frame"); os_free(hdr); return; } wpa_eapol_key_mic(sm->PTK.kck, version, (u8 *) hdr, len, key->key_mic); #ifdef CONFIG_TESTING_OPTIONS if (!pairwise && wpa_auth->conf.corrupt_gtk_rekey_mic_probability > 0.0d && drand48() < wpa_auth->conf.corrupt_gtk_rekey_mic_probability) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, "Corrupting group EAPOL-Key Key MIC"); key->key_mic[0]++; } #endif /* CONFIG_TESTING_OPTIONS */ } wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_inc_EapolFramesTx, 1); wpa_auth_send_eapol(wpa_auth, sm->addr, (u8 *) hdr, len, sm->pairwise_set); os_free(hdr); } static void wpa_send_eapol(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, int key_info, const u8 *key_rsc, const u8 *nonce, const u8 *kde, size_t kde_len, int keyidx, int encr) { int timeout_ms; int pairwise = key_info & WPA_KEY_INFO_KEY_TYPE; int ctr; if (sm == NULL) return; __wpa_send_eapol(wpa_auth, sm, key_info, key_rsc, nonce, kde, kde_len, keyidx, encr, 0); ctr = pairwise ? sm->TimeoutCtr : sm->GTimeoutCtr; if (ctr == 1 && wpa_auth->conf.tx_status) timeout_ms = pairwise ? eapol_key_timeout_first : eapol_key_timeout_first_group; else timeout_ms = eapol_key_timeout_subseq; if (pairwise && ctr == 1 && !(key_info & WPA_KEY_INFO_MIC)) sm->pending_1_of_4_timeout = 1; wpa_printf(MSG_DEBUG, "WPA: Use EAPOL-Key timeout of %u ms (retry " "counter %d)", timeout_ms, ctr); eloop_register_timeout(timeout_ms / 1000, (timeout_ms % 1000) * 1000, wpa_send_eapol_timeout, wpa_auth, sm); } static int wpa_verify_key_mic(struct wpa_ptk *PTK, u8 *data, size_t data_len) { struct ieee802_1x_hdr *hdr; struct wpa_eapol_key *key; u16 key_info; int ret = 0; u8 mic[16]; if (data_len < sizeof(*hdr) + sizeof(*key)) return -1; hdr = (struct ieee802_1x_hdr *) data; key = (struct wpa_eapol_key *) (hdr + 1); key_info = WPA_GET_BE16(key->key_info); os_memcpy(mic, key->key_mic, 16); os_memset(key->key_mic, 0, 16); if (wpa_eapol_key_mic(PTK->kck, key_info & WPA_KEY_INFO_TYPE_MASK, data, data_len, key->key_mic) || os_memcmp(mic, key->key_mic, 16) != 0) ret = -1; os_memcpy(key->key_mic, mic, 16); return ret; } void wpa_remove_ptk(struct wpa_state_machine *sm) { sm->PTK_valid = FALSE; os_memset(&sm->PTK, 0, sizeof(sm->PTK)); wpa_auth_set_key(sm->wpa_auth, 0, WPA_ALG_NONE, sm->addr, 0, NULL, 0); sm->pairwise_set = FALSE; eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm); } int wpa_auth_sm_event(struct wpa_state_machine *sm, wpa_event event) { int remove_ptk = 1; if (sm == NULL) return -1; wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, "event %d notification", event); switch (event) { case WPA_AUTH: case WPA_ASSOC: break; case WPA_DEAUTH: case WPA_DISASSOC: sm->DeauthenticationRequest = TRUE; break; case WPA_REAUTH: case WPA_REAUTH_EAPOL: if (!sm->started) { /* * When using WPS, we may end up here if the STA * manages to re-associate without the previous STA * entry getting removed. Consequently, we need to make * sure that the WPA state machines gets initialized * properly at this point. */ wpa_printf(MSG_DEBUG, "WPA state machine had not been " "started - initialize now"); sm->started = 1; sm->Init = TRUE; if (wpa_sm_step(sm) == 1) return 1; /* should not really happen */ sm->Init = FALSE; sm->AuthenticationRequest = TRUE; break; } if (sm->GUpdateStationKeys) { /* * Reauthentication cancels the pending group key * update for this STA. */ sm->group->GKeyDoneStations--; sm->GUpdateStationKeys = FALSE; sm->PtkGroupInit = TRUE; } sm->ReAuthenticationRequest = TRUE; break; case WPA_ASSOC_FT: #ifdef CONFIG_IEEE80211R wpa_printf(MSG_DEBUG, "FT: Retry PTK configuration " "after association"); wpa_ft_install_ptk(sm); /* Using FT protocol, not WPA auth state machine */ sm->ft_completed = 1; return 0; #else /* CONFIG_IEEE80211R */ break; #endif /* CONFIG_IEEE80211R */ } #ifdef CONFIG_IEEE80211R sm->ft_completed = 0; #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_IEEE80211W if (sm->mgmt_frame_prot && event == WPA_AUTH) remove_ptk = 0; #endif /* CONFIG_IEEE80211W */ if (remove_ptk) { sm->PTK_valid = FALSE; os_memset(&sm->PTK, 0, sizeof(sm->PTK)); if (event != WPA_REAUTH_EAPOL) wpa_remove_ptk(sm); } return wpa_sm_step(sm); } SM_STATE(WPA_PTK, INITIALIZE) { SM_ENTRY_MA(WPA_PTK, INITIALIZE, wpa_ptk); if (sm->Init) { /* Init flag is not cleared here, so avoid busy * loop by claiming nothing changed. */ sm->changed = FALSE; } sm->keycount = 0; if (sm->GUpdateStationKeys) sm->group->GKeyDoneStations--; sm->GUpdateStationKeys = FALSE; if (sm->wpa == WPA_VERSION_WPA) sm->PInitAKeys = FALSE; if (1 /* Unicast cipher supported AND (ESS OR ((IBSS or WDS) and * Local AA > Remote AA)) */) { sm->Pair = TRUE; } wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portEnabled, 0); wpa_remove_ptk(sm); wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portValid, 0); sm->TimeoutCtr = 0; if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) { wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_authorized, 0); } } SM_STATE(WPA_PTK, DISCONNECT) { SM_ENTRY_MA(WPA_PTK, DISCONNECT, wpa_ptk); sm->Disconnect = FALSE; wpa_sta_disconnect(sm->wpa_auth, sm->addr); } SM_STATE(WPA_PTK, DISCONNECTED) { SM_ENTRY_MA(WPA_PTK, DISCONNECTED, wpa_ptk); sm->DeauthenticationRequest = FALSE; } SM_STATE(WPA_PTK, AUTHENTICATION) { SM_ENTRY_MA(WPA_PTK, AUTHENTICATION, wpa_ptk); os_memset(&sm->PTK, 0, sizeof(sm->PTK)); sm->PTK_valid = FALSE; wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portControl_Auto, 1); wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portEnabled, 1); sm->AuthenticationRequest = FALSE; } static void wpa_group_ensure_init(struct wpa_authenticator *wpa_auth, struct wpa_group *group) { if (group->first_sta_seen) return; /* * System has run bit further than at the time hostapd was started * potentially very early during boot up. This provides better chances * of collecting more randomness on embedded systems. Re-initialize the * GMK and Counter here to improve their strength if there was not * enough entropy available immediately after system startup. */ wpa_printf(MSG_DEBUG, "WPA: Re-initialize GMK/Counter on first " "station"); if (random_pool_ready() != 1) { wpa_printf(MSG_INFO, "WPA: Not enough entropy in random pool " "to proceed - reject first 4-way handshake"); group->reject_4way_hs_for_entropy = TRUE; } else { group->first_sta_seen = TRUE; group->reject_4way_hs_for_entropy = FALSE; } wpa_group_init_gmk_and_counter(wpa_auth, group); wpa_gtk_update(wpa_auth, group); wpa_group_config_group_keys(wpa_auth, group); } SM_STATE(WPA_PTK, AUTHENTICATION2) { SM_ENTRY_MA(WPA_PTK, AUTHENTICATION2, wpa_ptk); wpa_group_ensure_init(sm->wpa_auth, sm->group); sm->ReAuthenticationRequest = FALSE; /* * Definition of ANonce selection in IEEE Std 802.11i-2004 is somewhat * ambiguous. The Authenticator state machine uses a counter that is * incremented by one for each 4-way handshake. However, the security * analysis of 4-way handshake points out that unpredictable nonces * help in preventing precomputation attacks. Instead of the state * machine definition, use an unpredictable nonce value here to provide * stronger protection against potential precomputation attacks. */ if (random_get_bytes(sm->ANonce, WPA_NONCE_LEN)) { wpa_printf(MSG_ERROR, "WPA: Failed to get random data for " "ANonce."); sm->Disconnect = TRUE; return; } wpa_hexdump(MSG_DEBUG, "WPA: Assign ANonce", sm->ANonce, WPA_NONCE_LEN); /* IEEE 802.11i does not clear TimeoutCtr here, but this is more * logical place than INITIALIZE since AUTHENTICATION2 can be * re-entered on ReAuthenticationRequest without going through * INITIALIZE. */ sm->TimeoutCtr = 0; } SM_STATE(WPA_PTK, INITPMK) { u8 msk[2 * PMK_LEN]; size_t len = 2 * PMK_LEN; SM_ENTRY_MA(WPA_PTK, INITPMK, wpa_ptk); #ifdef CONFIG_IEEE80211R sm->xxkey_len = 0; #endif /* CONFIG_IEEE80211R */ if (sm->pmksa) { wpa_printf(MSG_DEBUG, "WPA: PMK from PMKSA cache"); os_memcpy(sm->PMK, sm->pmksa->pmk, PMK_LEN); } else if (wpa_auth_get_msk(sm->wpa_auth, sm->addr, msk, &len) == 0) { wpa_printf(MSG_DEBUG, "WPA: PMK from EAPOL state machine " "(len=%lu)", (unsigned long) len); os_memcpy(sm->PMK, msk, PMK_LEN); #ifdef CONFIG_IEEE80211R if (len >= 2 * PMK_LEN) { os_memcpy(sm->xxkey, msk + PMK_LEN, PMK_LEN); sm->xxkey_len = PMK_LEN; } #endif /* CONFIG_IEEE80211R */ } else { wpa_printf(MSG_DEBUG, "WPA: Could not get PMK"); } sm->req_replay_counter_used = 0; /* IEEE 802.11i does not set keyRun to FALSE, but not doing this * will break reauthentication since EAPOL state machines may not be * get into AUTHENTICATING state that clears keyRun before WPA state * machine enters AUTHENTICATION2 state and goes immediately to INITPMK * state and takes PMK from the previously used AAA Key. This will * eventually fail in 4-Way Handshake because Supplicant uses PMK * derived from the new AAA Key. Setting keyRun = FALSE here seems to * be good workaround for this issue. */ wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_keyRun, 0); } SM_STATE(WPA_PTK, INITPSK) { const u8 *psk; SM_ENTRY_MA(WPA_PTK, INITPSK, wpa_ptk); psk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, sm->p2p_dev_addr, NULL); if (psk) { os_memcpy(sm->PMK, psk, PMK_LEN); #ifdef CONFIG_IEEE80211R os_memcpy(sm->xxkey, psk, PMK_LEN); sm->xxkey_len = PMK_LEN; #endif /* CONFIG_IEEE80211R */ } sm->req_replay_counter_used = 0; } SM_STATE(WPA_PTK, PTKSTART) { u8 buf[2 + RSN_SELECTOR_LEN + PMKID_LEN], *pmkid = NULL; size_t pmkid_len = 0; SM_ENTRY_MA(WPA_PTK, PTKSTART, wpa_ptk); sm->PTKRequest = FALSE; sm->TimeoutEvt = FALSE; sm->TimeoutCtr++; if (sm->TimeoutCtr > (int) dot11RSNAConfigPairwiseUpdateCount) { /* No point in sending the EAPOL-Key - we will disconnect * immediately following this. */ return; } wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, "sending 1/4 msg of 4-Way Handshake"); /* * TODO: Could add PMKID even with WPA2-PSK, but only if there is only * one possible PSK for this STA. */ if (sm->wpa == WPA_VERSION_WPA2 && wpa_key_mgmt_wpa_ieee8021x(sm->wpa_key_mgmt)) { pmkid = buf; pmkid_len = 2 + RSN_SELECTOR_LEN + PMKID_LEN; pmkid[0] = WLAN_EID_VENDOR_SPECIFIC; pmkid[1] = RSN_SELECTOR_LEN + PMKID_LEN; RSN_SELECTOR_PUT(&pmkid[2], RSN_KEY_DATA_PMKID); if (sm->pmksa) os_memcpy(&pmkid[2 + RSN_SELECTOR_LEN], sm->pmksa->pmkid, PMKID_LEN); else { /* * Calculate PMKID since no PMKSA cache entry was * available with pre-calculated PMKID. */ rsn_pmkid(sm->PMK, PMK_LEN, sm->wpa_auth->addr, sm->addr, &pmkid[2 + RSN_SELECTOR_LEN], wpa_key_mgmt_sha256(sm->wpa_key_mgmt)); } } wpa_send_eapol(sm->wpa_auth, sm, WPA_KEY_INFO_ACK | WPA_KEY_INFO_KEY_TYPE, NULL, sm->ANonce, pmkid, pmkid_len, 0, 0); } static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *pmk, struct wpa_ptk *ptk) { size_t ptk_len = sm->pairwise != WPA_CIPHER_TKIP ? 48 : 64; #ifdef CONFIG_IEEE80211R if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) return wpa_auth_derive_ptk_ft(sm, pmk, ptk, ptk_len); #endif /* CONFIG_IEEE80211R */ wpa_pmk_to_ptk(pmk, PMK_LEN, "Pairwise key expansion", sm->wpa_auth->addr, sm->addr, sm->ANonce, sm->SNonce, (u8 *) ptk, ptk_len, wpa_key_mgmt_sha256(sm->wpa_key_mgmt)); return 0; } SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) { struct wpa_ptk PTK; int ok = 0; const u8 *pmk = NULL; SM_ENTRY_MA(WPA_PTK, PTKCALCNEGOTIATING, wpa_ptk); sm->EAPOLKeyReceived = FALSE; sm->update_snonce = FALSE; /* WPA with IEEE 802.1X: use the derived PMK from EAP * WPA-PSK: iterate through possible PSKs and select the one matching * the packet */ for (;;) { if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) { pmk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, sm->p2p_dev_addr, pmk); if (pmk == NULL) break; } else pmk = sm->PMK; wpa_derive_ptk(sm, pmk, &PTK); if (wpa_verify_key_mic(&PTK, sm->last_rx_eapol_key, sm->last_rx_eapol_key_len) == 0) { ok = 1; break; } if (!wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) break; } if (!ok) { wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, "invalid MIC in msg 2/4 of 4-Way Handshake"); return; } #ifdef CONFIG_IEEE80211R if (sm->wpa == WPA_VERSION_WPA2 && wpa_key_mgmt_ft(sm->wpa_key_mgmt)) { /* * Verify that PMKR1Name from EAPOL-Key message 2/4 matches * with the value we derived. */ if (os_memcmp(sm->sup_pmk_r1_name, sm->pmk_r1_name, WPA_PMK_NAME_LEN) != 0) { wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, "PMKR1Name mismatch in FT 4-way " "handshake"); wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name from " "Supplicant", sm->sup_pmk_r1_name, WPA_PMK_NAME_LEN); wpa_hexdump(MSG_DEBUG, "FT: Derived PMKR1Name", sm->pmk_r1_name, WPA_PMK_NAME_LEN); return; } } #endif /* CONFIG_IEEE80211R */ sm->pending_1_of_4_timeout = 0; eloop_cancel_timeout(wpa_send_eapol_timeout, sm->wpa_auth, sm); if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) { /* PSK may have changed from the previous choice, so update * state machine data based on whatever PSK was selected here. */ os_memcpy(sm->PMK, pmk, PMK_LEN); } sm->MICVerified = TRUE; os_memcpy(&sm->PTK, &PTK, sizeof(PTK)); sm->PTK_valid = TRUE; } SM_STATE(WPA_PTK, PTKCALCNEGOTIATING2) { SM_ENTRY_MA(WPA_PTK, PTKCALCNEGOTIATING2, wpa_ptk); sm->TimeoutCtr = 0; } #ifdef CONFIG_IEEE80211W static int ieee80211w_kde_len(struct wpa_state_machine *sm) { if (sm->mgmt_frame_prot) { return 2 + RSN_SELECTOR_LEN + sizeof(struct wpa_igtk_kde); } return 0; } static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos) { struct wpa_igtk_kde igtk; struct wpa_group *gsm = sm->group; u8 rsc[WPA_KEY_RSC_LEN]; if (!sm->mgmt_frame_prot) return pos; igtk.keyid[0] = gsm->GN_igtk; igtk.keyid[1] = 0; if (gsm->wpa_group_state != WPA_GROUP_SETKEYSDONE || wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, rsc) < 0) os_memset(igtk.pn, 0, sizeof(igtk.pn)); else os_memcpy(igtk.pn, rsc, sizeof(igtk.pn)); os_memcpy(igtk.igtk, gsm->IGTK[gsm->GN_igtk - 4], WPA_IGTK_LEN); if (sm->wpa_auth->conf.disable_gtk) { /* * Provide unique random IGTK to each STA to prevent use of * IGTK in the BSS. */ if (random_get_bytes(igtk.igtk, WPA_IGTK_LEN) < 0) return pos; } pos = wpa_add_kde(pos, RSN_KEY_DATA_IGTK, (const u8 *) &igtk, sizeof(igtk), NULL, 0); return pos; } #else /* CONFIG_IEEE80211W */ static int ieee80211w_kde_len(struct wpa_state_machine *sm) { return 0; } static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos) { return pos; } #endif /* CONFIG_IEEE80211W */ SM_STATE(WPA_PTK, PTKINITNEGOTIATING) { u8 rsc[WPA_KEY_RSC_LEN], *_rsc, *gtk, *kde, *pos, dummy_gtk[32]; size_t gtk_len, kde_len; struct wpa_group *gsm = sm->group; u8 *wpa_ie; int wpa_ie_len, secure, keyidx, encr = 0; SM_ENTRY_MA(WPA_PTK, PTKINITNEGOTIATING, wpa_ptk); sm->TimeoutEvt = FALSE; sm->TimeoutCtr++; if (sm->TimeoutCtr > (int) dot11RSNAConfigPairwiseUpdateCount) { /* No point in sending the EAPOL-Key - we will disconnect * immediately following this. */ return; } /* Send EAPOL(1, 1, 1, Pair, P, RSC, ANonce, MIC(PTK), RSNIE, [MDIE], GTK[GN], IGTK, [FTIE], [TIE * 2]) */ os_memset(rsc, 0, WPA_KEY_RSC_LEN); wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, rsc); /* If FT is used, wpa_auth->wpa_ie includes both RSNIE and MDIE */ wpa_ie = sm->wpa_auth->wpa_ie; wpa_ie_len = sm->wpa_auth->wpa_ie_len; if (sm->wpa == WPA_VERSION_WPA && (sm->wpa_auth->conf.wpa & WPA_PROTO_RSN) && wpa_ie_len > wpa_ie[1] + 2 && wpa_ie[0] == WLAN_EID_RSN) { /* WPA-only STA, remove RSN IE */ wpa_ie = wpa_ie + wpa_ie[1] + 2; wpa_ie_len = wpa_ie[1] + 2; } wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, "sending 3/4 msg of 4-Way Handshake"); if (sm->wpa == WPA_VERSION_WPA2) { /* WPA2 send GTK in the 4-way handshake */ secure = 1; gtk = gsm->GTK[gsm->GN - 1]; gtk_len = gsm->GTK_len; if (sm->wpa_auth->conf.disable_gtk) { /* * Provide unique random GTK to each STA to prevent use * of GTK in the BSS. */ if (random_get_bytes(dummy_gtk, gtk_len) < 0) return; gtk = dummy_gtk; } keyidx = gsm->GN; _rsc = rsc; encr = 1; } else { /* WPA does not include GTK in msg 3/4 */ secure = 0; gtk = NULL; gtk_len = 0; keyidx = 0; _rsc = NULL; if (sm->rx_eapol_key_secure) { /* * It looks like Windows 7 supplicant tries to use * Secure bit in msg 2/4 after having reported Michael * MIC failure and it then rejects the 4-way handshake * if msg 3/4 does not set Secure bit. Work around this * by setting the Secure bit here even in the case of * WPA if the supplicant used it first. */ wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, "STA used Secure bit in WPA msg 2/4 - " "set Secure for 3/4 as workaround"); secure = 1; } } kde_len = wpa_ie_len + ieee80211w_kde_len(sm); if (gtk) kde_len += 2 + RSN_SELECTOR_LEN + 2 + gtk_len; #ifdef CONFIG_IEEE80211R if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) { kde_len += 2 + PMKID_LEN; /* PMKR1Name into RSN IE */ kde_len += 300; /* FTIE + 2 * TIE */ } #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_P2P if (WPA_GET_BE32(sm->ip_addr) > 0) kde_len += 2 + RSN_SELECTOR_LEN + 3 * 4; #endif /* CONFIG_P2P */ kde = os_malloc(kde_len); if (kde == NULL) return; pos = kde; os_memcpy(pos, wpa_ie, wpa_ie_len); pos += wpa_ie_len; #ifdef CONFIG_IEEE80211R if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) { int res = wpa_insert_pmkid(kde, pos - kde, sm->pmk_r1_name); if (res < 0) { wpa_printf(MSG_ERROR, "FT: Failed to insert " "PMKR1Name into RSN IE in EAPOL-Key data"); os_free(kde); return; } pos += res; } #endif /* CONFIG_IEEE80211R */ if (gtk) { u8 hdr[2]; hdr[0] = keyidx & 0x03; hdr[1] = 0; pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2, gtk, gtk_len); } pos = ieee80211w_kde_add(sm, pos); #ifdef CONFIG_IEEE80211R if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) { int res; struct wpa_auth_config *conf; conf = &sm->wpa_auth->conf; res = wpa_write_ftie(conf, conf->r0_key_holder, conf->r0_key_holder_len, NULL, NULL, pos, kde + kde_len - pos, NULL, 0); if (res < 0) { wpa_printf(MSG_ERROR, "FT: Failed to insert FTIE " "into EAPOL-Key Key Data"); os_free(kde); return; } pos += res; /* TIE[ReassociationDeadline] (TU) */ *pos++ = WLAN_EID_TIMEOUT_INTERVAL; *pos++ = 5; *pos++ = WLAN_TIMEOUT_REASSOC_DEADLINE; WPA_PUT_LE32(pos, conf->reassociation_deadline); pos += 4; /* TIE[KeyLifetime] (seconds) */ *pos++ = WLAN_EID_TIMEOUT_INTERVAL; *pos++ = 5; *pos++ = WLAN_TIMEOUT_KEY_LIFETIME; WPA_PUT_LE32(pos, conf->r0_key_lifetime * 60); pos += 4; } #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_P2P if (WPA_GET_BE32(sm->ip_addr) > 0) { u8 addr[3 * 4]; os_memcpy(addr, sm->ip_addr, 4); os_memcpy(addr + 4, sm->wpa_auth->conf.ip_addr_mask, 4); os_memcpy(addr + 8, sm->wpa_auth->conf.ip_addr_go, 4); pos = wpa_add_kde(pos, WFA_KEY_DATA_IP_ADDR_ALLOC, addr, sizeof(addr), NULL, 0); } #endif /* CONFIG_P2P */ wpa_send_eapol(sm->wpa_auth, sm, (secure ? WPA_KEY_INFO_SECURE : 0) | WPA_KEY_INFO_MIC | WPA_KEY_INFO_ACK | WPA_KEY_INFO_INSTALL | WPA_KEY_INFO_KEY_TYPE, _rsc, sm->ANonce, kde, pos - kde, keyidx, encr); os_free(kde); } SM_STATE(WPA_PTK, PTKINITDONE) { SM_ENTRY_MA(WPA_PTK, PTKINITDONE, wpa_ptk); sm->EAPOLKeyReceived = FALSE; if (sm->Pair) { enum wpa_alg alg = wpa_cipher_to_alg(sm->pairwise); int klen = wpa_cipher_key_len(sm->pairwise); if (wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr, 0, sm->PTK.tk1, klen)) { wpa_sta_disconnect(sm->wpa_auth, sm->addr); return; } /* FIX: MLME-SetProtection.Request(TA, Tx_Rx) */ sm->pairwise_set = TRUE; if (sm->wpa_auth->conf.wpa_ptk_rekey) { eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm); eloop_register_timeout(sm->wpa_auth->conf. wpa_ptk_rekey, 0, wpa_rekey_ptk, sm->wpa_auth, sm); } if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) { wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_authorized, 1); } } if (0 /* IBSS == TRUE */) { sm->keycount++; if (sm->keycount == 2) { wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portValid, 1); } } else { wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portValid, 1); } wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_keyAvailable, 0); wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_keyDone, 1); if (sm->wpa == WPA_VERSION_WPA) sm->PInitAKeys = TRUE; else sm->has_GTK = TRUE; wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_INFO, "pairwise key handshake completed (%s)", sm->wpa == WPA_VERSION_WPA ? "WPA" : "RSN"); #ifdef CONFIG_IEEE80211R wpa_ft_push_pmk_r1(sm->wpa_auth, sm->addr); #endif /* CONFIG_IEEE80211R */ } SM_STEP(WPA_PTK) { struct wpa_authenticator *wpa_auth = sm->wpa_auth; if (sm->Init) SM_ENTER(WPA_PTK, INITIALIZE); else if (sm->Disconnect /* || FIX: dot11RSNAConfigSALifetime timeout */) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, "WPA_PTK: sm->Disconnect"); SM_ENTER(WPA_PTK, DISCONNECT); } else if (sm->DeauthenticationRequest) SM_ENTER(WPA_PTK, DISCONNECTED); else if (sm->AuthenticationRequest) SM_ENTER(WPA_PTK, AUTHENTICATION); else if (sm->ReAuthenticationRequest) SM_ENTER(WPA_PTK, AUTHENTICATION2); else if (sm->PTKRequest) SM_ENTER(WPA_PTK, PTKSTART); else switch (sm->wpa_ptk_state) { case WPA_PTK_INITIALIZE: break; case WPA_PTK_DISCONNECT: SM_ENTER(WPA_PTK, DISCONNECTED); break; case WPA_PTK_DISCONNECTED: SM_ENTER(WPA_PTK, INITIALIZE); break; case WPA_PTK_AUTHENTICATION: SM_ENTER(WPA_PTK, AUTHENTICATION2); break; case WPA_PTK_AUTHENTICATION2: if (wpa_key_mgmt_wpa_ieee8021x(sm->wpa_key_mgmt) && wpa_auth_get_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_keyRun) > 0) SM_ENTER(WPA_PTK, INITPMK); else if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) /* FIX: && 802.1X::keyRun */) SM_ENTER(WPA_PTK, INITPSK); break; case WPA_PTK_INITPMK: if (wpa_auth_get_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_keyAvailable) > 0) SM_ENTER(WPA_PTK, PTKSTART); else { wpa_auth->dot11RSNA4WayHandshakeFailures++; wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_INFO, "INITPMK - keyAvailable = false"); SM_ENTER(WPA_PTK, DISCONNECT); } break; case WPA_PTK_INITPSK: if (wpa_auth_get_psk(sm->wpa_auth, sm->addr, sm->p2p_dev_addr, NULL)) SM_ENTER(WPA_PTK, PTKSTART); else { wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_INFO, "no PSK configured for the STA"); wpa_auth->dot11RSNA4WayHandshakeFailures++; SM_ENTER(WPA_PTK, DISCONNECT); } break; case WPA_PTK_PTKSTART: if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest && sm->EAPOLKeyPairwise) SM_ENTER(WPA_PTK, PTKCALCNEGOTIATING); else if (sm->TimeoutCtr > (int) dot11RSNAConfigPairwiseUpdateCount) { wpa_auth->dot11RSNA4WayHandshakeFailures++; wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, "PTKSTART: Retry limit %d reached", dot11RSNAConfigPairwiseUpdateCount); SM_ENTER(WPA_PTK, DISCONNECT); } else if (sm->TimeoutEvt) SM_ENTER(WPA_PTK, PTKSTART); break; case WPA_PTK_PTKCALCNEGOTIATING: if (sm->MICVerified) SM_ENTER(WPA_PTK, PTKCALCNEGOTIATING2); else if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest && sm->EAPOLKeyPairwise) SM_ENTER(WPA_PTK, PTKCALCNEGOTIATING); else if (sm->TimeoutEvt) SM_ENTER(WPA_PTK, PTKSTART); break; case WPA_PTK_PTKCALCNEGOTIATING2: SM_ENTER(WPA_PTK, PTKINITNEGOTIATING); break; case WPA_PTK_PTKINITNEGOTIATING: if (sm->update_snonce) SM_ENTER(WPA_PTK, PTKCALCNEGOTIATING); else if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest && sm->EAPOLKeyPairwise && sm->MICVerified) SM_ENTER(WPA_PTK, PTKINITDONE); else if (sm->TimeoutCtr > (int) dot11RSNAConfigPairwiseUpdateCount) { wpa_auth->dot11RSNA4WayHandshakeFailures++; wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, "PTKINITNEGOTIATING: Retry limit %d " "reached", dot11RSNAConfigPairwiseUpdateCount); SM_ENTER(WPA_PTK, DISCONNECT); } else if (sm->TimeoutEvt) SM_ENTER(WPA_PTK, PTKINITNEGOTIATING); break; case WPA_PTK_PTKINITDONE: break; } } SM_STATE(WPA_PTK_GROUP, IDLE) { SM_ENTRY_MA(WPA_PTK_GROUP, IDLE, wpa_ptk_group); if (sm->Init) { /* Init flag is not cleared here, so avoid busy * loop by claiming nothing changed. */ sm->changed = FALSE; } sm->GTimeoutCtr = 0; } SM_STATE(WPA_PTK_GROUP, REKEYNEGOTIATING) { u8 rsc[WPA_KEY_RSC_LEN]; struct wpa_group *gsm = sm->group; u8 *kde, *pos, hdr[2]; size_t kde_len; u8 *gtk, dummy_gtk[32]; SM_ENTRY_MA(WPA_PTK_GROUP, REKEYNEGOTIATING, wpa_ptk_group); sm->GTimeoutCtr++; if (sm->GTimeoutCtr > (int) dot11RSNAConfigGroupUpdateCount) { /* No point in sending the EAPOL-Key - we will disconnect * immediately following this. */ return; } if (sm->wpa == WPA_VERSION_WPA) sm->PInitAKeys = FALSE; sm->TimeoutEvt = FALSE; /* Send EAPOL(1, 1, 1, !Pair, G, RSC, GNonce, MIC(PTK), GTK[GN]) */ os_memset(rsc, 0, WPA_KEY_RSC_LEN); if (gsm->wpa_group_state == WPA_GROUP_SETKEYSDONE) wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, rsc); wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, "sending 1/2 msg of Group Key Handshake"); gtk = gsm->GTK[gsm->GN - 1]; if (sm->wpa_auth->conf.disable_gtk) { /* * Provide unique random GTK to each STA to prevent use * of GTK in the BSS. */ if (random_get_bytes(dummy_gtk, gsm->GTK_len) < 0) return; gtk = dummy_gtk; } if (sm->wpa == WPA_VERSION_WPA2) { kde_len = 2 + RSN_SELECTOR_LEN + 2 + gsm->GTK_len + ieee80211w_kde_len(sm); kde = os_malloc(kde_len); if (kde == NULL) return; pos = kde; hdr[0] = gsm->GN & 0x03; hdr[1] = 0; pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2, gtk, gsm->GTK_len); pos = ieee80211w_kde_add(sm, pos); } else { kde = gtk; pos = kde + gsm->GTK_len; } wpa_send_eapol(sm->wpa_auth, sm, WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC | WPA_KEY_INFO_ACK | (!sm->Pair ? WPA_KEY_INFO_INSTALL : 0), rsc, gsm->GNonce, kde, pos - kde, gsm->GN, 1); if (sm->wpa == WPA_VERSION_WPA2) os_free(kde); } SM_STATE(WPA_PTK_GROUP, REKEYESTABLISHED) { SM_ENTRY_MA(WPA_PTK_GROUP, REKEYESTABLISHED, wpa_ptk_group); sm->EAPOLKeyReceived = FALSE; if (sm->GUpdateStationKeys) sm->group->GKeyDoneStations--; sm->GUpdateStationKeys = FALSE; sm->GTimeoutCtr = 0; /* FIX: MLME.SetProtection.Request(TA, Tx_Rx) */ wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_INFO, "group key handshake completed (%s)", sm->wpa == WPA_VERSION_WPA ? "WPA" : "RSN"); sm->has_GTK = TRUE; } SM_STATE(WPA_PTK_GROUP, KEYERROR) { SM_ENTRY_MA(WPA_PTK_GROUP, KEYERROR, wpa_ptk_group); if (sm->GUpdateStationKeys) sm->group->GKeyDoneStations--; sm->GUpdateStationKeys = FALSE; sm->Disconnect = TRUE; } SM_STEP(WPA_PTK_GROUP) { if (sm->Init || sm->PtkGroupInit) { SM_ENTER(WPA_PTK_GROUP, IDLE); sm->PtkGroupInit = FALSE; } else switch (sm->wpa_ptk_group_state) { case WPA_PTK_GROUP_IDLE: if (sm->GUpdateStationKeys || (sm->wpa == WPA_VERSION_WPA && sm->PInitAKeys)) SM_ENTER(WPA_PTK_GROUP, REKEYNEGOTIATING); break; case WPA_PTK_GROUP_REKEYNEGOTIATING: if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest && !sm->EAPOLKeyPairwise && sm->MICVerified) SM_ENTER(WPA_PTK_GROUP, REKEYESTABLISHED); else if (sm->GTimeoutCtr > (int) dot11RSNAConfigGroupUpdateCount) SM_ENTER(WPA_PTK_GROUP, KEYERROR); else if (sm->TimeoutEvt) SM_ENTER(WPA_PTK_GROUP, REKEYNEGOTIATING); break; case WPA_PTK_GROUP_KEYERROR: SM_ENTER(WPA_PTK_GROUP, IDLE); break; case WPA_PTK_GROUP_REKEYESTABLISHED: SM_ENTER(WPA_PTK_GROUP, IDLE); break; } } static int wpa_gtk_update(struct wpa_authenticator *wpa_auth, struct wpa_group *group) { int ret = 0; os_memcpy(group->GNonce, group->Counter, WPA_NONCE_LEN); inc_byte_array(group->Counter, WPA_NONCE_LEN); if (wpa_gmk_to_gtk(group->GMK, "Group key expansion", wpa_auth->addr, group->GNonce, group->GTK[group->GN - 1], group->GTK_len) < 0) ret = -1; wpa_hexdump_key(MSG_DEBUG, "GTK", group->GTK[group->GN - 1], group->GTK_len); #ifdef CONFIG_IEEE80211W if (wpa_auth->conf.ieee80211w != NO_MGMT_FRAME_PROTECTION) { os_memcpy(group->GNonce, group->Counter, WPA_NONCE_LEN); inc_byte_array(group->Counter, WPA_NONCE_LEN); if (wpa_gmk_to_gtk(group->GMK, "IGTK key expansion", wpa_auth->addr, group->GNonce, group->IGTK[group->GN_igtk - 4], WPA_IGTK_LEN) < 0) ret = -1; wpa_hexdump_key(MSG_DEBUG, "IGTK", group->IGTK[group->GN_igtk - 4], WPA_IGTK_LEN); } #endif /* CONFIG_IEEE80211W */ return ret; } static void wpa_group_gtk_init(struct wpa_authenticator *wpa_auth, struct wpa_group *group) { wpa_printf(MSG_DEBUG, "WPA: group state machine entering state " "GTK_INIT (VLAN-ID %d)", group->vlan_id); group->changed = FALSE; /* GInit is not cleared here; avoid loop */ group->wpa_group_state = WPA_GROUP_GTK_INIT; /* GTK[0..N] = 0 */ os_memset(group->GTK, 0, sizeof(group->GTK)); group->GN = 1; group->GM = 2; #ifdef CONFIG_IEEE80211W group->GN_igtk = 4; group->GM_igtk = 5; #endif /* CONFIG_IEEE80211W */ /* GTK[GN] = CalcGTK() */ wpa_gtk_update(wpa_auth, group); } static int wpa_group_update_sta(struct wpa_state_machine *sm, void *ctx) { if (ctx != NULL && ctx != sm->group) return 0; if (sm->wpa_ptk_state != WPA_PTK_PTKINITDONE) { wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, "Not in PTKINITDONE; skip Group Key update"); sm->GUpdateStationKeys = FALSE; return 0; } if (sm->GUpdateStationKeys) { /* * This should not really happen, so add a debug log entry. * Since we clear the GKeyDoneStations before the loop, the * station needs to be counted here anyway. */ wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, "GUpdateStationKeys was already set when " "marking station for GTK rekeying"); } /* Do not rekey GTK/IGTK when STA is in WNM-Sleep Mode */ if (sm->is_wnmsleep) return 0; sm->group->GKeyDoneStations++; sm->GUpdateStationKeys = TRUE; wpa_sm_step(sm); return 0; } #ifdef CONFIG_WNM /* update GTK when exiting WNM-Sleep Mode */ void wpa_wnmsleep_rekey_gtk(struct wpa_state_machine *sm) { if (sm == NULL || sm->is_wnmsleep) return; wpa_group_update_sta(sm, NULL); } void wpa_set_wnmsleep(struct wpa_state_machine *sm, int flag) { if (sm) sm->is_wnmsleep = !!flag; } int wpa_wnmsleep_gtk_subelem(struct wpa_state_machine *sm, u8 *pos) { struct wpa_group *gsm = sm->group; u8 *start = pos; /* * GTK subelement: * Sub-elem ID[1] | Length[1] | Key Info[2] | Key Length[1] | RSC[8] | * Key[5..32] */ *pos++ = WNM_SLEEP_SUBELEM_GTK; *pos++ = 11 + gsm->GTK_len; /* Key ID in B0-B1 of Key Info */ WPA_PUT_LE16(pos, gsm->GN & 0x03); pos += 2; *pos++ = gsm->GTK_len; if (wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, pos) != 0) return 0; pos += 8; os_memcpy(pos, gsm->GTK[gsm->GN - 1], gsm->GTK_len); pos += gsm->GTK_len; wpa_printf(MSG_DEBUG, "WNM: GTK Key ID %u in WNM-Sleep Mode exit", gsm->GN); wpa_hexdump_key(MSG_DEBUG, "WNM: GTK in WNM-Sleep Mode exit", gsm->GTK[gsm->GN - 1], gsm->GTK_len); return pos - start; } #ifdef CONFIG_IEEE80211W int wpa_wnmsleep_igtk_subelem(struct wpa_state_machine *sm, u8 *pos) { struct wpa_group *gsm = sm->group; u8 *start = pos; /* * IGTK subelement: * Sub-elem ID[1] | Length[1] | KeyID[2] | PN[6] | Key[16] */ *pos++ = WNM_SLEEP_SUBELEM_IGTK; *pos++ = 2 + 6 + WPA_IGTK_LEN; WPA_PUT_LE16(pos, gsm->GN_igtk); pos += 2; if (wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, pos) != 0) return 0; pos += 6; os_memcpy(pos, gsm->IGTK[gsm->GN_igtk - 4], WPA_IGTK_LEN); pos += WPA_IGTK_LEN; wpa_printf(MSG_DEBUG, "WNM: IGTK Key ID %u in WNM-Sleep Mode exit", gsm->GN_igtk); wpa_hexdump_key(MSG_DEBUG, "WNM: IGTK in WNM-Sleep Mode exit", gsm->IGTK[gsm->GN_igtk - 4], WPA_IGTK_LEN); return pos - start; } #endif /* CONFIG_IEEE80211W */ #endif /* CONFIG_WNM */ static void wpa_group_setkeys(struct wpa_authenticator *wpa_auth, struct wpa_group *group) { int tmp; wpa_printf(MSG_DEBUG, "WPA: group state machine entering state " "SETKEYS (VLAN-ID %d)", group->vlan_id); group->changed = TRUE; group->wpa_group_state = WPA_GROUP_SETKEYS; group->GTKReKey = FALSE; tmp = group->GM; group->GM = group->GN; group->GN = tmp; #ifdef CONFIG_IEEE80211W tmp = group->GM_igtk; group->GM_igtk = group->GN_igtk; group->GN_igtk = tmp; #endif /* CONFIG_IEEE80211W */ /* "GKeyDoneStations = GNoStations" is done in more robust way by * counting the STAs that are marked with GUpdateStationKeys instead of * including all STAs that could be in not-yet-completed state. */ wpa_gtk_update(wpa_auth, group); if (group->GKeyDoneStations) { wpa_printf(MSG_DEBUG, "wpa_group_setkeys: Unexpected " "GKeyDoneStations=%d when starting new GTK rekey", group->GKeyDoneStations); group->GKeyDoneStations = 0; } wpa_auth_for_each_sta(wpa_auth, wpa_group_update_sta, group); wpa_printf(MSG_DEBUG, "wpa_group_setkeys: GKeyDoneStations=%d", group->GKeyDoneStations); } static int wpa_group_config_group_keys(struct wpa_authenticator *wpa_auth, struct wpa_group *group) { int ret = 0; if (wpa_auth_set_key(wpa_auth, group->vlan_id, wpa_cipher_to_alg(wpa_auth->conf.wpa_group), broadcast_ether_addr, group->GN, group->GTK[group->GN - 1], group->GTK_len) < 0) ret = -1; #ifdef CONFIG_IEEE80211W if (wpa_auth->conf.ieee80211w != NO_MGMT_FRAME_PROTECTION && wpa_auth_set_key(wpa_auth, group->vlan_id, WPA_ALG_IGTK, broadcast_ether_addr, group->GN_igtk, group->IGTK[group->GN_igtk - 4], WPA_IGTK_LEN) < 0) ret = -1; #endif /* CONFIG_IEEE80211W */ return ret; } static int wpa_group_disconnect_cb(struct wpa_state_machine *sm, void *ctx) { if (sm->group == ctx) { wpa_printf(MSG_DEBUG, "WPA: Mark STA " MACSTR " for discconnection due to fatal failure", MAC2STR(sm->addr)); sm->Disconnect = TRUE; } return 0; } static void wpa_group_fatal_failure(struct wpa_authenticator *wpa_auth, struct wpa_group *group) { wpa_printf(MSG_DEBUG, "WPA: group state machine entering state FATAL_FAILURE"); group->changed = TRUE; group->wpa_group_state = WPA_GROUP_FATAL_FAILURE; wpa_auth_for_each_sta(wpa_auth, wpa_group_disconnect_cb, group); } static int wpa_group_setkeysdone(struct wpa_authenticator *wpa_auth, struct wpa_group *group) { wpa_printf(MSG_DEBUG, "WPA: group state machine entering state " "SETKEYSDONE (VLAN-ID %d)", group->vlan_id); group->changed = TRUE; group->wpa_group_state = WPA_GROUP_SETKEYSDONE; if (wpa_group_config_group_keys(wpa_auth, group) < 0) { wpa_group_fatal_failure(wpa_auth, group); return -1; } return 0; } static void wpa_group_sm_step(struct wpa_authenticator *wpa_auth, struct wpa_group *group) { if (group->GInit) { wpa_group_gtk_init(wpa_auth, group); } else if (group->wpa_group_state == WPA_GROUP_FATAL_FAILURE) { /* Do not allow group operations */ } else if (group->wpa_group_state == WPA_GROUP_GTK_INIT && group->GTKAuthenticator) { wpa_group_setkeysdone(wpa_auth, group); } else if (group->wpa_group_state == WPA_GROUP_SETKEYSDONE && group->GTKReKey) { wpa_group_setkeys(wpa_auth, group); } else if (group->wpa_group_state == WPA_GROUP_SETKEYS) { if (group->GKeyDoneStations == 0) wpa_group_setkeysdone(wpa_auth, group); else if (group->GTKReKey) wpa_group_setkeys(wpa_auth, group); } } static int wpa_sm_step(struct wpa_state_machine *sm) { if (sm == NULL) return 0; if (sm->in_step_loop) { /* This should not happen, but if it does, make sure we do not * end up freeing the state machine too early by exiting the * recursive call. */ wpa_printf(MSG_ERROR, "WPA: wpa_sm_step() called recursively"); return 0; } sm->in_step_loop = 1; do { if (sm->pending_deinit) break; sm->changed = FALSE; sm->wpa_auth->group->changed = FALSE; SM_STEP_RUN(WPA_PTK); if (sm->pending_deinit) break; SM_STEP_RUN(WPA_PTK_GROUP); if (sm->pending_deinit) break; wpa_group_sm_step(sm->wpa_auth, sm->group); } while (sm->changed || sm->wpa_auth->group->changed); sm->in_step_loop = 0; if (sm->pending_deinit) { wpa_printf(MSG_DEBUG, "WPA: Completing pending STA state " "machine deinit for " MACSTR, MAC2STR(sm->addr)); wpa_free_sta_sm(sm); return 1; } return 0; } static void wpa_sm_call_step(void *eloop_ctx, void *timeout_ctx) { struct wpa_state_machine *sm = eloop_ctx; wpa_sm_step(sm); } void wpa_auth_sm_notify(struct wpa_state_machine *sm) { if (sm == NULL) return; eloop_register_timeout(0, 0, wpa_sm_call_step, sm, NULL); } void wpa_gtk_rekey(struct wpa_authenticator *wpa_auth) { int tmp, i; struct wpa_group *group; if (wpa_auth == NULL) return; group = wpa_auth->group; for (i = 0; i < 2; i++) { tmp = group->GM; group->GM = group->GN; group->GN = tmp; #ifdef CONFIG_IEEE80211W tmp = group->GM_igtk; group->GM_igtk = group->GN_igtk; group->GN_igtk = tmp; #endif /* CONFIG_IEEE80211W */ wpa_gtk_update(wpa_auth, group); wpa_group_config_group_keys(wpa_auth, group); } } static const char * wpa_bool_txt(int bool) { return bool ? "TRUE" : "FALSE"; } #define RSN_SUITE "%02x-%02x-%02x-%d" #define RSN_SUITE_ARG(s) \ ((s) >> 24) & 0xff, ((s) >> 16) & 0xff, ((s) >> 8) & 0xff, (s) & 0xff int wpa_get_mib(struct wpa_authenticator *wpa_auth, char *buf, size_t buflen) { int len = 0, ret; char pmkid_txt[PMKID_LEN * 2 + 1]; #ifdef CONFIG_RSN_PREAUTH const int preauth = 1; #else /* CONFIG_RSN_PREAUTH */ const int preauth = 0; #endif /* CONFIG_RSN_PREAUTH */ if (wpa_auth == NULL) return len; ret = os_snprintf(buf + len, buflen - len, "dot11RSNAOptionImplemented=TRUE\n" "dot11RSNAPreauthenticationImplemented=%s\n" "dot11RSNAEnabled=%s\n" "dot11RSNAPreauthenticationEnabled=%s\n", wpa_bool_txt(preauth), wpa_bool_txt(wpa_auth->conf.wpa & WPA_PROTO_RSN), wpa_bool_txt(wpa_auth->conf.rsn_preauth)); if (ret < 0 || (size_t) ret >= buflen - len) return len; len += ret; wpa_snprintf_hex(pmkid_txt, sizeof(pmkid_txt), wpa_auth->dot11RSNAPMKIDUsed, PMKID_LEN); ret = os_snprintf( buf + len, buflen - len, "dot11RSNAConfigVersion=%u\n" "dot11RSNAConfigPairwiseKeysSupported=9999\n" /* FIX: dot11RSNAConfigGroupCipher */ /* FIX: dot11RSNAConfigGroupRekeyMethod */ /* FIX: dot11RSNAConfigGroupRekeyTime */ /* FIX: dot11RSNAConfigGroupRekeyPackets */ "dot11RSNAConfigGroupRekeyStrict=%u\n" "dot11RSNAConfigGroupUpdateCount=%u\n" "dot11RSNAConfigPairwiseUpdateCount=%u\n" "dot11RSNAConfigGroupCipherSize=%u\n" "dot11RSNAConfigPMKLifetime=%u\n" "dot11RSNAConfigPMKReauthThreshold=%u\n" "dot11RSNAConfigNumberOfPTKSAReplayCounters=0\n" "dot11RSNAConfigSATimeout=%u\n" "dot11RSNAAuthenticationSuiteSelected=" RSN_SUITE "\n" "dot11RSNAPairwiseCipherSelected=" RSN_SUITE "\n" "dot11RSNAGroupCipherSelected=" RSN_SUITE "\n" "dot11RSNAPMKIDUsed=%s\n" "dot11RSNAAuthenticationSuiteRequested=" RSN_SUITE "\n" "dot11RSNAPairwiseCipherRequested=" RSN_SUITE "\n" "dot11RSNAGroupCipherRequested=" RSN_SUITE "\n" "dot11RSNATKIPCounterMeasuresInvoked=%u\n" "dot11RSNA4WayHandshakeFailures=%u\n" "dot11RSNAConfigNumberOfGTKSAReplayCounters=0\n", RSN_VERSION, !!wpa_auth->conf.wpa_strict_rekey, dot11RSNAConfigGroupUpdateCount, dot11RSNAConfigPairwiseUpdateCount, wpa_cipher_key_len(wpa_auth->conf.wpa_group) * 8, dot11RSNAConfigPMKLifetime, dot11RSNAConfigPMKReauthThreshold, dot11RSNAConfigSATimeout, RSN_SUITE_ARG(wpa_auth->dot11RSNAAuthenticationSuiteSelected), RSN_SUITE_ARG(wpa_auth->dot11RSNAPairwiseCipherSelected), RSN_SUITE_ARG(wpa_auth->dot11RSNAGroupCipherSelected), pmkid_txt, RSN_SUITE_ARG(wpa_auth->dot11RSNAAuthenticationSuiteRequested), RSN_SUITE_ARG(wpa_auth->dot11RSNAPairwiseCipherRequested), RSN_SUITE_ARG(wpa_auth->dot11RSNAGroupCipherRequested), wpa_auth->dot11RSNATKIPCounterMeasuresInvoked, wpa_auth->dot11RSNA4WayHandshakeFailures); if (ret < 0 || (size_t) ret >= buflen - len) return len; len += ret; /* TODO: dot11RSNAConfigPairwiseCiphersTable */ /* TODO: dot11RSNAConfigAuthenticationSuitesTable */ /* Private MIB */ ret = os_snprintf(buf + len, buflen - len, "hostapdWPAGroupState=%d\n", wpa_auth->group->wpa_group_state); if (ret < 0 || (size_t) ret >= buflen - len) return len; len += ret; return len; } int wpa_get_mib_sta(struct wpa_state_machine *sm, char *buf, size_t buflen) { int len = 0, ret; u32 pairwise = 0; if (sm == NULL) return 0; /* TODO: FF-FF-FF-FF-FF-FF entry for broadcast/multicast stats */ /* dot11RSNAStatsEntry */ pairwise = wpa_cipher_to_suite(sm->wpa == WPA_VERSION_WPA2 ? WPA_PROTO_RSN : WPA_PROTO_WPA, sm->pairwise); if (pairwise == 0) return 0; ret = os_snprintf( buf + len, buflen - len, /* TODO: dot11RSNAStatsIndex */ "dot11RSNAStatsSTAAddress=" MACSTR "\n" "dot11RSNAStatsVersion=1\n" "dot11RSNAStatsSelectedPairwiseCipher=" RSN_SUITE "\n" /* TODO: dot11RSNAStatsTKIPICVErrors */ "dot11RSNAStatsTKIPLocalMICFailures=%u\n" "dot11RSNAStatsTKIPRemoteMICFailures=%u\n" /* TODO: dot11RSNAStatsCCMPReplays */ /* TODO: dot11RSNAStatsCCMPDecryptErrors */ /* TODO: dot11RSNAStatsTKIPReplays */, MAC2STR(sm->addr), RSN_SUITE_ARG(pairwise), sm->dot11RSNAStatsTKIPLocalMICFailures, sm->dot11RSNAStatsTKIPRemoteMICFailures); if (ret < 0 || (size_t) ret >= buflen - len) return len; len += ret; /* Private MIB */ ret = os_snprintf(buf + len, buflen - len, "hostapdWPAPTKState=%d\n" "hostapdWPAPTKGroupState=%d\n", sm->wpa_ptk_state, sm->wpa_ptk_group_state); if (ret < 0 || (size_t) ret >= buflen - len) return len; len += ret; return len; } void wpa_auth_countermeasures_start(struct wpa_authenticator *wpa_auth) { if (wpa_auth) wpa_auth->dot11RSNATKIPCounterMeasuresInvoked++; } int wpa_auth_pairwise_set(struct wpa_state_machine *sm) { return sm && sm->pairwise_set; } int wpa_auth_get_pairwise(struct wpa_state_machine *sm) { return sm->pairwise; } int wpa_auth_sta_key_mgmt(struct wpa_state_machine *sm) { if (sm == NULL) return -1; return sm->wpa_key_mgmt; } int wpa_auth_sta_wpa_version(struct wpa_state_machine *sm) { if (sm == NULL) return 0; return sm->wpa; } int wpa_auth_sta_clear_pmksa(struct wpa_state_machine *sm, struct rsn_pmksa_cache_entry *entry) { if (sm == NULL || sm->pmksa != entry) return -1; sm->pmksa = NULL; return 0; } struct rsn_pmksa_cache_entry * wpa_auth_sta_get_pmksa(struct wpa_state_machine *sm) { return sm ? sm->pmksa : NULL; } void wpa_auth_sta_local_mic_failure_report(struct wpa_state_machine *sm) { if (sm) sm->dot11RSNAStatsTKIPLocalMICFailures++; } const u8 * wpa_auth_get_wpa_ie(struct wpa_authenticator *wpa_auth, size_t *len) { if (wpa_auth == NULL) return NULL; *len = wpa_auth->wpa_ie_len; return wpa_auth->wpa_ie; } int wpa_auth_pmksa_add(struct wpa_state_machine *sm, const u8 *pmk, int session_timeout, struct eapol_state_machine *eapol) { if (sm == NULL || sm->wpa != WPA_VERSION_WPA2 || sm->wpa_auth->conf.disable_pmksa_caching) return -1; if (pmksa_cache_auth_add(sm->wpa_auth->pmksa, pmk, PMK_LEN, sm->wpa_auth->addr, sm->addr, session_timeout, eapol, sm->wpa_key_mgmt)) return 0; return -1; } int wpa_auth_pmksa_add_preauth(struct wpa_authenticator *wpa_auth, const u8 *pmk, size_t len, const u8 *sta_addr, int session_timeout, struct eapol_state_machine *eapol) { if (wpa_auth == NULL) return -1; if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, len, wpa_auth->addr, sta_addr, session_timeout, eapol, WPA_KEY_MGMT_IEEE8021X)) return 0; return -1; } void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth, const u8 *sta_addr) { struct rsn_pmksa_cache_entry *pmksa; if (wpa_auth == NULL || wpa_auth->pmksa == NULL) return; pmksa = pmksa_cache_auth_get(wpa_auth->pmksa, sta_addr, NULL); if (pmksa) { wpa_printf(MSG_DEBUG, "WPA: Remove PMKSA cache entry for " MACSTR " based on request", MAC2STR(sta_addr)); pmksa_cache_free_entry(wpa_auth->pmksa, pmksa); } } static struct wpa_group * wpa_auth_add_group(struct wpa_authenticator *wpa_auth, int vlan_id) { struct wpa_group *group; if (wpa_auth == NULL || wpa_auth->group == NULL) return NULL; wpa_printf(MSG_DEBUG, "WPA: Add group state machine for VLAN-ID %d", vlan_id); group = wpa_group_init(wpa_auth, vlan_id, 0); if (group == NULL) return NULL; group->next = wpa_auth->group->next; wpa_auth->group->next = group; return group; } int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id) { struct wpa_group *group; if (sm == NULL || sm->wpa_auth == NULL) return 0; group = sm->wpa_auth->group; while (group) { if (group->vlan_id == vlan_id) break; group = group->next; } if (group == NULL) { group = wpa_auth_add_group(sm->wpa_auth, vlan_id); if (group == NULL) return -1; } if (sm->group == group) return 0; if (group->wpa_group_state == WPA_GROUP_FATAL_FAILURE) return -1; wpa_printf(MSG_DEBUG, "WPA: Moving STA " MACSTR " to use group state " "machine for VLAN ID %d", MAC2STR(sm->addr), vlan_id); sm->group = group; return 0; } void wpa_auth_eapol_key_tx_status(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, int ack) { if (wpa_auth == NULL || sm == NULL) return; wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key TX status for STA " MACSTR " ack=%d", MAC2STR(sm->addr), ack); if (sm->pending_1_of_4_timeout && ack) { /* * Some deployed supplicant implementations update their SNonce * for each EAPOL-Key 2/4 message even within the same 4-way * handshake and then fail to use the first SNonce when * deriving the PTK. This results in unsuccessful 4-way * handshake whenever the relatively short initial timeout is * reached and EAPOL-Key 1/4 is retransmitted. Try to work * around this by increasing the timeout now that we know that * the station has received the frame. */ int timeout_ms = eapol_key_timeout_subseq; wpa_printf(MSG_DEBUG, "WPA: Increase initial EAPOL-Key 1/4 " "timeout by %u ms because of acknowledged frame", timeout_ms); eloop_cancel_timeout(wpa_send_eapol_timeout, wpa_auth, sm); eloop_register_timeout(timeout_ms / 1000, (timeout_ms % 1000) * 1000, wpa_send_eapol_timeout, wpa_auth, sm); } } int wpa_auth_uses_sae(struct wpa_state_machine *sm) { if (sm == NULL) return 0; return wpa_key_mgmt_sae(sm->wpa_key_mgmt); } int wpa_auth_uses_ft_sae(struct wpa_state_machine *sm) { if (sm == NULL) return 0; return sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_SAE; } #ifdef CONFIG_P2P int wpa_auth_get_ip_addr(struct wpa_state_machine *sm, u8 *addr) { if (sm == NULL || WPA_GET_BE32(sm->ip_addr) == 0) return -1; os_memcpy(addr, sm->ip_addr, 4); return 0; } #endif /* CONFIG_P2P */ wpa-2.1/src/ap/wpa_auth.h000066400000000000000000000243241227414666700153370ustar00rootroot00000000000000/* * hostapd - IEEE 802.11i-2004 / WPA Authenticator * Copyright (c) 2004-2007, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef WPA_AUTH_H #define WPA_AUTH_H #include "common/defs.h" #include "common/eapol_common.h" #include "common/wpa_common.h" #ifdef _MSC_VER #pragma pack(push, 1) #endif /* _MSC_VER */ /* IEEE Std 802.11r-2008, 11A.10.3 - Remote request/response frame definition */ struct ft_rrb_frame { u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */ u8 packet_type; /* FT_PACKET_REQUEST/FT_PACKET_RESPONSE */ le16 action_length; /* little endian length of action_frame */ u8 ap_address[ETH_ALEN]; /* * Followed by action_length bytes of FT Action frame (from Category * field to the end of Action Frame body. */ } STRUCT_PACKED; #define RSN_REMOTE_FRAME_TYPE_FT_RRB 1 #define FT_PACKET_REQUEST 0 #define FT_PACKET_RESPONSE 1 /* Vendor-specific types for R0KH-R1KH protocol; not defined in 802.11r */ #define FT_PACKET_R0KH_R1KH_PULL 200 #define FT_PACKET_R0KH_R1KH_RESP 201 #define FT_PACKET_R0KH_R1KH_PUSH 202 #define FT_R0KH_R1KH_PULL_DATA_LEN 44 #define FT_R0KH_R1KH_RESP_DATA_LEN 76 #define FT_R0KH_R1KH_PUSH_DATA_LEN 88 struct ft_r0kh_r1kh_pull_frame { u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */ u8 packet_type; /* FT_PACKET_R0KH_R1KH_PULL */ le16 data_length; /* little endian length of data (44) */ u8 ap_address[ETH_ALEN]; u8 nonce[16]; u8 pmk_r0_name[WPA_PMK_NAME_LEN]; u8 r1kh_id[FT_R1KH_ID_LEN]; u8 s1kh_id[ETH_ALEN]; u8 pad[4]; /* 8-octet boundary for AES key wrap */ u8 key_wrap_extra[8]; } STRUCT_PACKED; struct ft_r0kh_r1kh_resp_frame { u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */ u8 packet_type; /* FT_PACKET_R0KH_R1KH_RESP */ le16 data_length; /* little endian length of data (76) */ u8 ap_address[ETH_ALEN]; u8 nonce[16]; /* copied from pull */ u8 r1kh_id[FT_R1KH_ID_LEN]; /* copied from pull */ u8 s1kh_id[ETH_ALEN]; /* copied from pull */ u8 pmk_r1[PMK_LEN]; u8 pmk_r1_name[WPA_PMK_NAME_LEN]; le16 pairwise; u8 pad[2]; /* 8-octet boundary for AES key wrap */ u8 key_wrap_extra[8]; } STRUCT_PACKED; struct ft_r0kh_r1kh_push_frame { u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */ u8 packet_type; /* FT_PACKET_R0KH_R1KH_PUSH */ le16 data_length; /* little endian length of data (88) */ u8 ap_address[ETH_ALEN]; /* Encrypted with AES key-wrap */ u8 timestamp[4]; /* current time in seconds since unix epoch, little * endian */ u8 r1kh_id[FT_R1KH_ID_LEN]; u8 s1kh_id[ETH_ALEN]; u8 pmk_r0_name[WPA_PMK_NAME_LEN]; u8 pmk_r1[PMK_LEN]; u8 pmk_r1_name[WPA_PMK_NAME_LEN]; le16 pairwise; u8 pad[6]; /* 8-octet boundary for AES key wrap */ u8 key_wrap_extra[8]; } STRUCT_PACKED; #ifdef _MSC_VER #pragma pack(pop) #endif /* _MSC_VER */ /* per STA state machine data */ struct wpa_authenticator; struct wpa_state_machine; struct rsn_pmksa_cache_entry; struct eapol_state_machine; struct ft_remote_r0kh { struct ft_remote_r0kh *next; u8 addr[ETH_ALEN]; u8 id[FT_R0KH_ID_MAX_LEN]; size_t id_len; u8 key[16]; }; struct ft_remote_r1kh { struct ft_remote_r1kh *next; u8 addr[ETH_ALEN]; u8 id[FT_R1KH_ID_LEN]; u8 key[16]; }; struct wpa_auth_config { int wpa; int wpa_key_mgmt; int wpa_pairwise; int wpa_group; int wpa_group_rekey; int wpa_strict_rekey; int wpa_gmk_rekey; int wpa_ptk_rekey; int rsn_pairwise; int rsn_preauth; int eapol_version; int peerkey; int wmm_enabled; int wmm_uapsd; int disable_pmksa_caching; int okc; int tx_status; #ifdef CONFIG_IEEE80211W enum mfp_options ieee80211w; #endif /* CONFIG_IEEE80211W */ #ifdef CONFIG_IEEE80211R #define SSID_LEN 32 u8 ssid[SSID_LEN]; size_t ssid_len; u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN]; u8 r0_key_holder[FT_R0KH_ID_MAX_LEN]; size_t r0_key_holder_len; u8 r1_key_holder[FT_R1KH_ID_LEN]; u32 r0_key_lifetime; u32 reassociation_deadline; struct ft_remote_r0kh *r0kh_list; struct ft_remote_r1kh *r1kh_list; int pmk_r1_push; int ft_over_ds; #endif /* CONFIG_IEEE80211R */ int disable_gtk; int ap_mlme; #ifdef CONFIG_TESTING_OPTIONS double corrupt_gtk_rekey_mic_probability; #endif /* CONFIG_TESTING_OPTIONS */ #ifdef CONFIG_P2P u8 ip_addr_go[4]; u8 ip_addr_mask[4]; u8 ip_addr_start[4]; u8 ip_addr_end[4]; #endif /* CONFIG_P2P */ }; typedef enum { LOGGER_DEBUG, LOGGER_INFO, LOGGER_WARNING } logger_level; typedef enum { WPA_EAPOL_portEnabled, WPA_EAPOL_portValid, WPA_EAPOL_authorized, WPA_EAPOL_portControl_Auto, WPA_EAPOL_keyRun, WPA_EAPOL_keyAvailable, WPA_EAPOL_keyDone, WPA_EAPOL_inc_EapolFramesTx } wpa_eapol_variable; struct wpa_auth_callbacks { void *ctx; void (*logger)(void *ctx, const u8 *addr, logger_level level, const char *txt); void (*disconnect)(void *ctx, const u8 *addr, u16 reason); int (*mic_failure_report)(void *ctx, const u8 *addr); void (*set_eapol)(void *ctx, const u8 *addr, wpa_eapol_variable var, int value); int (*get_eapol)(void *ctx, const u8 *addr, wpa_eapol_variable var); const u8 * (*get_psk)(void *ctx, const u8 *addr, const u8 *p2p_dev_addr, const u8 *prev_psk); int (*get_msk)(void *ctx, const u8 *addr, u8 *msk, size_t *len); int (*set_key)(void *ctx, int vlan_id, enum wpa_alg alg, const u8 *addr, int idx, u8 *key, size_t key_len); int (*get_seqnum)(void *ctx, const u8 *addr, int idx, u8 *seq); int (*send_eapol)(void *ctx, const u8 *addr, const u8 *data, size_t data_len, int encrypt); int (*for_each_sta)(void *ctx, int (*cb)(struct wpa_state_machine *sm, void *ctx), void *cb_ctx); int (*for_each_auth)(void *ctx, int (*cb)(struct wpa_authenticator *a, void *ctx), void *cb_ctx); int (*send_ether)(void *ctx, const u8 *dst, u16 proto, const u8 *data, size_t data_len); #ifdef CONFIG_IEEE80211R struct wpa_state_machine * (*add_sta)(void *ctx, const u8 *sta_addr); int (*send_ft_action)(void *ctx, const u8 *dst, const u8 *data, size_t data_len); int (*add_tspec)(void *ctx, const u8 *sta_addr, u8 *tspec_ie, size_t tspec_ielen); #endif /* CONFIG_IEEE80211R */ }; struct wpa_authenticator * wpa_init(const u8 *addr, struct wpa_auth_config *conf, struct wpa_auth_callbacks *cb); int wpa_init_keys(struct wpa_authenticator *wpa_auth); void wpa_deinit(struct wpa_authenticator *wpa_auth); int wpa_reconfig(struct wpa_authenticator *wpa_auth, struct wpa_auth_config *conf); enum { WPA_IE_OK, WPA_INVALID_IE, WPA_INVALID_GROUP, WPA_INVALID_PAIRWISE, WPA_INVALID_AKMP, WPA_NOT_ENABLED, WPA_ALLOC_FAIL, WPA_MGMT_FRAME_PROTECTION_VIOLATION, WPA_INVALID_MGMT_GROUP_CIPHER, WPA_INVALID_MDIE, WPA_INVALID_PROTO }; int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, const u8 *wpa_ie, size_t wpa_ie_len, const u8 *mdie, size_t mdie_len); int wpa_auth_uses_mfp(struct wpa_state_machine *sm); struct wpa_state_machine * wpa_auth_sta_init(struct wpa_authenticator *wpa_auth, const u8 *addr, const u8 *p2p_dev_addr); int wpa_auth_sta_associated(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm); void wpa_auth_sta_no_wpa(struct wpa_state_machine *sm); void wpa_auth_sta_deinit(struct wpa_state_machine *sm); void wpa_receive(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, u8 *data, size_t data_len); typedef enum { WPA_AUTH, WPA_ASSOC, WPA_DISASSOC, WPA_DEAUTH, WPA_REAUTH, WPA_REAUTH_EAPOL, WPA_ASSOC_FT } wpa_event; void wpa_remove_ptk(struct wpa_state_machine *sm); int wpa_auth_sm_event(struct wpa_state_machine *sm, wpa_event event); void wpa_auth_sm_notify(struct wpa_state_machine *sm); void wpa_gtk_rekey(struct wpa_authenticator *wpa_auth); int wpa_get_mib(struct wpa_authenticator *wpa_auth, char *buf, size_t buflen); int wpa_get_mib_sta(struct wpa_state_machine *sm, char *buf, size_t buflen); void wpa_auth_countermeasures_start(struct wpa_authenticator *wpa_auth); int wpa_auth_pairwise_set(struct wpa_state_machine *sm); int wpa_auth_get_pairwise(struct wpa_state_machine *sm); int wpa_auth_sta_key_mgmt(struct wpa_state_machine *sm); int wpa_auth_sta_wpa_version(struct wpa_state_machine *sm); int wpa_auth_sta_clear_pmksa(struct wpa_state_machine *sm, struct rsn_pmksa_cache_entry *entry); struct rsn_pmksa_cache_entry * wpa_auth_sta_get_pmksa(struct wpa_state_machine *sm); void wpa_auth_sta_local_mic_failure_report(struct wpa_state_machine *sm); const u8 * wpa_auth_get_wpa_ie(struct wpa_authenticator *wpa_auth, size_t *len); int wpa_auth_pmksa_add(struct wpa_state_machine *sm, const u8 *pmk, int session_timeout, struct eapol_state_machine *eapol); int wpa_auth_pmksa_add_preauth(struct wpa_authenticator *wpa_auth, const u8 *pmk, size_t len, const u8 *sta_addr, int session_timeout, struct eapol_state_machine *eapol); void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth, const u8 *sta_addr); int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id); void wpa_auth_eapol_key_tx_status(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, int ack); #ifdef CONFIG_IEEE80211R u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos, size_t max_len, int auth_alg, const u8 *req_ies, size_t req_ies_len); void wpa_ft_process_auth(struct wpa_state_machine *sm, const u8 *bssid, u16 auth_transaction, const u8 *ies, size_t ies_len, void (*cb)(void *ctx, const u8 *dst, const u8 *bssid, u16 auth_transaction, u16 resp, const u8 *ies, size_t ies_len), void *ctx); u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies, size_t ies_len); int wpa_ft_action_rx(struct wpa_state_machine *sm, const u8 *data, size_t len); int wpa_ft_rrb_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr, const u8 *data, size_t data_len); void wpa_ft_push_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *addr); #endif /* CONFIG_IEEE80211R */ void wpa_wnmsleep_rekey_gtk(struct wpa_state_machine *sm); void wpa_set_wnmsleep(struct wpa_state_machine *sm, int flag); int wpa_wnmsleep_gtk_subelem(struct wpa_state_machine *sm, u8 *pos); int wpa_wnmsleep_igtk_subelem(struct wpa_state_machine *sm, u8 *pos); int wpa_auth_uses_sae(struct wpa_state_machine *sm); int wpa_auth_uses_ft_sae(struct wpa_state_machine *sm); int wpa_auth_get_ip_addr(struct wpa_state_machine *sm, u8 *addr); #endif /* WPA_AUTH_H */ wpa-2.1/src/ap/wpa_auth_ft.c000066400000000000000000001315111227414666700160200ustar00rootroot00000000000000/* * hostapd - IEEE 802.11r - Fast BSS Transition * Copyright (c) 2004-2009, 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 "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" #include "crypto/aes_wrap.h" #include "crypto/random.h" #include "ap_config.h" #include "ieee802_11.h" #include "wmm.h" #include "wpa_auth.h" #include "wpa_auth_i.h" #ifdef CONFIG_IEEE80211R static int wpa_ft_rrb_send(struct wpa_authenticator *wpa_auth, const u8 *dst, const u8 *data, size_t data_len) { if (wpa_auth->cb.send_ether == NULL) return -1; wpa_printf(MSG_DEBUG, "FT: RRB send to " MACSTR, MAC2STR(dst)); return wpa_auth->cb.send_ether(wpa_auth->cb.ctx, dst, ETH_P_RRB, data, data_len); } static int wpa_ft_action_send(struct wpa_authenticator *wpa_auth, const u8 *dst, const u8 *data, size_t data_len) { if (wpa_auth->cb.send_ft_action == NULL) return -1; return wpa_auth->cb.send_ft_action(wpa_auth->cb.ctx, dst, data, data_len); } static struct wpa_state_machine * wpa_ft_add_sta(struct wpa_authenticator *wpa_auth, const u8 *sta_addr) { if (wpa_auth->cb.add_sta == NULL) return NULL; return wpa_auth->cb.add_sta(wpa_auth->cb.ctx, sta_addr); } static int wpa_ft_add_tspec(struct wpa_authenticator *wpa_auth, const u8 *sta_addr, u8 *tspec_ie, size_t tspec_ielen) { if (wpa_auth->cb.add_tspec == NULL) { wpa_printf(MSG_DEBUG, "FT: add_tspec is not initialized"); return -1; } return wpa_auth->cb.add_tspec(wpa_auth->cb.ctx, sta_addr, tspec_ie, tspec_ielen); } int wpa_write_mdie(struct wpa_auth_config *conf, u8 *buf, size_t len) { u8 *pos = buf; u8 capab; if (len < 2 + sizeof(struct rsn_mdie)) return -1; *pos++ = WLAN_EID_MOBILITY_DOMAIN; *pos++ = MOBILITY_DOMAIN_ID_LEN + 1; os_memcpy(pos, conf->mobility_domain, MOBILITY_DOMAIN_ID_LEN); pos += MOBILITY_DOMAIN_ID_LEN; capab = 0; if (conf->ft_over_ds) capab |= RSN_FT_CAPAB_FT_OVER_DS; *pos++ = capab; return pos - buf; } int wpa_write_ftie(struct wpa_auth_config *conf, const u8 *r0kh_id, size_t r0kh_id_len, const u8 *anonce, const u8 *snonce, u8 *buf, size_t len, const u8 *subelem, size_t subelem_len) { u8 *pos = buf, *ielen; struct rsn_ftie *hdr; if (len < 2 + sizeof(*hdr) + 2 + FT_R1KH_ID_LEN + 2 + r0kh_id_len + subelem_len) return -1; *pos++ = WLAN_EID_FAST_BSS_TRANSITION; ielen = pos++; hdr = (struct rsn_ftie *) pos; os_memset(hdr, 0, sizeof(*hdr)); pos += sizeof(*hdr); WPA_PUT_LE16(hdr->mic_control, 0); if (anonce) os_memcpy(hdr->anonce, anonce, WPA_NONCE_LEN); if (snonce) os_memcpy(hdr->snonce, snonce, WPA_NONCE_LEN); /* Optional Parameters */ *pos++ = FTIE_SUBELEM_R1KH_ID; *pos++ = FT_R1KH_ID_LEN; os_memcpy(pos, conf->r1_key_holder, FT_R1KH_ID_LEN); pos += FT_R1KH_ID_LEN; if (r0kh_id) { *pos++ = FTIE_SUBELEM_R0KH_ID; *pos++ = r0kh_id_len; os_memcpy(pos, r0kh_id, r0kh_id_len); pos += r0kh_id_len; } if (subelem) { os_memcpy(pos, subelem, subelem_len); pos += subelem_len; } *ielen = pos - buf - 2; return pos - buf; } struct wpa_ft_pmk_r0_sa { struct wpa_ft_pmk_r0_sa *next; u8 pmk_r0[PMK_LEN]; u8 pmk_r0_name[WPA_PMK_NAME_LEN]; u8 spa[ETH_ALEN]; int pairwise; /* Pairwise cipher suite, WPA_CIPHER_* */ /* TODO: expiration, identity, radius_class, EAP type, VLAN ID */ int pmk_r1_pushed; }; struct wpa_ft_pmk_r1_sa { struct wpa_ft_pmk_r1_sa *next; u8 pmk_r1[PMK_LEN]; u8 pmk_r1_name[WPA_PMK_NAME_LEN]; u8 spa[ETH_ALEN]; int pairwise; /* Pairwise cipher suite, WPA_CIPHER_* */ /* TODO: expiration, identity, radius_class, EAP type, VLAN ID */ }; struct wpa_ft_pmk_cache { struct wpa_ft_pmk_r0_sa *pmk_r0; struct wpa_ft_pmk_r1_sa *pmk_r1; }; struct wpa_ft_pmk_cache * wpa_ft_pmk_cache_init(void) { struct wpa_ft_pmk_cache *cache; cache = os_zalloc(sizeof(*cache)); return cache; } void wpa_ft_pmk_cache_deinit(struct wpa_ft_pmk_cache *cache) { struct wpa_ft_pmk_r0_sa *r0, *r0prev; struct wpa_ft_pmk_r1_sa *r1, *r1prev; r0 = cache->pmk_r0; while (r0) { r0prev = r0; r0 = r0->next; os_memset(r0prev->pmk_r0, 0, PMK_LEN); os_free(r0prev); } r1 = cache->pmk_r1; while (r1) { r1prev = r1; r1 = r1->next; os_memset(r1prev->pmk_r1, 0, PMK_LEN); os_free(r1prev); } os_free(cache); } static int wpa_ft_store_pmk_r0(struct wpa_authenticator *wpa_auth, const u8 *spa, const u8 *pmk_r0, const u8 *pmk_r0_name, int pairwise) { struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache; struct wpa_ft_pmk_r0_sa *r0; /* TODO: add expiration and limit on number of entries in cache */ r0 = os_zalloc(sizeof(*r0)); if (r0 == NULL) return -1; os_memcpy(r0->pmk_r0, pmk_r0, PMK_LEN); os_memcpy(r0->pmk_r0_name, pmk_r0_name, WPA_PMK_NAME_LEN); os_memcpy(r0->spa, spa, ETH_ALEN); r0->pairwise = pairwise; r0->next = cache->pmk_r0; cache->pmk_r0 = r0; return 0; } static int wpa_ft_fetch_pmk_r0(struct wpa_authenticator *wpa_auth, const u8 *spa, const u8 *pmk_r0_name, u8 *pmk_r0, int *pairwise) { struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache; struct wpa_ft_pmk_r0_sa *r0; r0 = cache->pmk_r0; while (r0) { if (os_memcmp(r0->spa, spa, ETH_ALEN) == 0 && os_memcmp(r0->pmk_r0_name, pmk_r0_name, WPA_PMK_NAME_LEN) == 0) { os_memcpy(pmk_r0, r0->pmk_r0, PMK_LEN); if (pairwise) *pairwise = r0->pairwise; return 0; } r0 = r0->next; } return -1; } static int wpa_ft_store_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *spa, const u8 *pmk_r1, const u8 *pmk_r1_name, int pairwise) { struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache; struct wpa_ft_pmk_r1_sa *r1; /* TODO: add expiration and limit on number of entries in cache */ r1 = os_zalloc(sizeof(*r1)); if (r1 == NULL) return -1; os_memcpy(r1->pmk_r1, pmk_r1, PMK_LEN); os_memcpy(r1->pmk_r1_name, pmk_r1_name, WPA_PMK_NAME_LEN); os_memcpy(r1->spa, spa, ETH_ALEN); r1->pairwise = pairwise; r1->next = cache->pmk_r1; cache->pmk_r1 = r1; return 0; } static int wpa_ft_fetch_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *spa, const u8 *pmk_r1_name, u8 *pmk_r1, int *pairwise) { struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache; struct wpa_ft_pmk_r1_sa *r1; r1 = cache->pmk_r1; while (r1) { if (os_memcmp(r1->spa, spa, ETH_ALEN) == 0 && os_memcmp(r1->pmk_r1_name, pmk_r1_name, WPA_PMK_NAME_LEN) == 0) { os_memcpy(pmk_r1, r1->pmk_r1, PMK_LEN); if (pairwise) *pairwise = r1->pairwise; return 0; } r1 = r1->next; } return -1; } static int wpa_ft_pull_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *s1kh_id, const u8 *r0kh_id, size_t r0kh_id_len, const u8 *pmk_r0_name) { struct ft_remote_r0kh *r0kh; struct ft_r0kh_r1kh_pull_frame frame, f; r0kh = wpa_auth->conf.r0kh_list; while (r0kh) { if (r0kh->id_len == r0kh_id_len && os_memcmp(r0kh->id, r0kh_id, r0kh_id_len) == 0) break; r0kh = r0kh->next; } if (r0kh == NULL) return -1; wpa_printf(MSG_DEBUG, "FT: Send PMK-R1 pull request to remote R0KH " "address " MACSTR, MAC2STR(r0kh->addr)); os_memset(&frame, 0, sizeof(frame)); frame.frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB; frame.packet_type = FT_PACKET_R0KH_R1KH_PULL; frame.data_length = host_to_le16(FT_R0KH_R1KH_PULL_DATA_LEN); os_memcpy(frame.ap_address, wpa_auth->addr, ETH_ALEN); /* aes_wrap() does not support inplace encryption, so use a temporary * buffer for the data. */ if (random_get_bytes(f.nonce, sizeof(f.nonce))) { wpa_printf(MSG_DEBUG, "FT: Failed to get random data for " "nonce"); return -1; } os_memcpy(f.pmk_r0_name, pmk_r0_name, WPA_PMK_NAME_LEN); os_memcpy(f.r1kh_id, wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN); os_memcpy(f.s1kh_id, s1kh_id, ETH_ALEN); os_memset(f.pad, 0, sizeof(f.pad)); if (aes_wrap(r0kh->key, (FT_R0KH_R1KH_PULL_DATA_LEN + 7) / 8, f.nonce, frame.nonce) < 0) return -1; wpa_ft_rrb_send(wpa_auth, r0kh->addr, (u8 *) &frame, sizeof(frame)); return 0; } int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk, struct wpa_ptk *ptk, size_t ptk_len) { u8 pmk_r0[PMK_LEN], pmk_r0_name[WPA_PMK_NAME_LEN]; u8 pmk_r1[PMK_LEN]; u8 ptk_name[WPA_PMK_NAME_LEN]; const u8 *mdid = sm->wpa_auth->conf.mobility_domain; const u8 *r0kh = sm->wpa_auth->conf.r0_key_holder; size_t r0kh_len = sm->wpa_auth->conf.r0_key_holder_len; const u8 *r1kh = sm->wpa_auth->conf.r1_key_holder; const u8 *ssid = sm->wpa_auth->conf.ssid; size_t ssid_len = sm->wpa_auth->conf.ssid_len; if (sm->xxkey_len == 0) { wpa_printf(MSG_DEBUG, "FT: XXKey not available for key " "derivation"); return -1; } wpa_derive_pmk_r0(sm->xxkey, sm->xxkey_len, ssid, ssid_len, mdid, r0kh, r0kh_len, sm->addr, pmk_r0, pmk_r0_name); wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0", pmk_r0, PMK_LEN); wpa_hexdump(MSG_DEBUG, "FT: PMKR0Name", pmk_r0_name, WPA_PMK_NAME_LEN); wpa_ft_store_pmk_r0(sm->wpa_auth, sm->addr, pmk_r0, pmk_r0_name, sm->pairwise); wpa_derive_pmk_r1(pmk_r0, pmk_r0_name, r1kh, sm->addr, pmk_r1, sm->pmk_r1_name); wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", pmk_r1, PMK_LEN); wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", sm->pmk_r1_name, WPA_PMK_NAME_LEN); wpa_ft_store_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1, sm->pmk_r1_name, sm->pairwise); wpa_pmk_r1_to_ptk(pmk_r1, sm->SNonce, sm->ANonce, sm->addr, sm->wpa_auth->addr, sm->pmk_r1_name, (u8 *) ptk, ptk_len, ptk_name); wpa_hexdump_key(MSG_DEBUG, "FT: PTK", (u8 *) ptk, ptk_len); wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN); return 0; } static inline int wpa_auth_get_seqnum(struct wpa_authenticator *wpa_auth, const u8 *addr, int idx, u8 *seq) { if (wpa_auth->cb.get_seqnum == NULL) return -1; return wpa_auth->cb.get_seqnum(wpa_auth->cb.ctx, addr, idx, seq); } static u8 * wpa_ft_gtk_subelem(struct wpa_state_machine *sm, size_t *len) { u8 *subelem; struct wpa_group *gsm = sm->group; size_t subelem_len, pad_len; const u8 *key; size_t key_len; u8 keybuf[32]; key_len = gsm->GTK_len; if (key_len > sizeof(keybuf)) return NULL; /* * Pad key for AES Key Wrap if it is not multiple of 8 bytes or is less * than 16 bytes. */ pad_len = key_len % 8; if (pad_len) pad_len = 8 - pad_len; if (key_len + pad_len < 16) pad_len += 8; if (pad_len && key_len < sizeof(keybuf)) { os_memcpy(keybuf, gsm->GTK[gsm->GN - 1], key_len); os_memset(keybuf + key_len, 0, pad_len); keybuf[key_len] = 0xdd; key_len += pad_len; key = keybuf; } else key = gsm->GTK[gsm->GN - 1]; /* * Sub-elem ID[1] | Length[1] | Key Info[2] | Key Length[1] | RSC[8] | * Key[5..32]. */ subelem_len = 13 + key_len + 8; subelem = os_zalloc(subelem_len); if (subelem == NULL) return NULL; subelem[0] = FTIE_SUBELEM_GTK; subelem[1] = 11 + key_len + 8; /* Key ID in B0-B1 of Key Info */ WPA_PUT_LE16(&subelem[2], gsm->GN & 0x03); subelem[4] = gsm->GTK_len; wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, subelem + 5); if (aes_wrap(sm->PTK.kek, key_len / 8, key, subelem + 13)) { os_free(subelem); return NULL; } *len = subelem_len; return subelem; } #ifdef CONFIG_IEEE80211W static u8 * wpa_ft_igtk_subelem(struct wpa_state_machine *sm, size_t *len) { u8 *subelem, *pos; struct wpa_group *gsm = sm->group; size_t subelem_len; /* Sub-elem ID[1] | Length[1] | KeyID[2] | IPN[6] | Key Length[1] | * Key[16+8] */ subelem_len = 1 + 1 + 2 + 6 + 1 + WPA_IGTK_LEN + 8; subelem = os_zalloc(subelem_len); if (subelem == NULL) return NULL; pos = subelem; *pos++ = FTIE_SUBELEM_IGTK; *pos++ = subelem_len - 2; WPA_PUT_LE16(pos, gsm->GN_igtk); pos += 2; wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, pos); pos += 6; *pos++ = WPA_IGTK_LEN; if (aes_wrap(sm->PTK.kek, WPA_IGTK_LEN / 8, gsm->IGTK[gsm->GN_igtk - 4], pos)) { os_free(subelem); return NULL; } *len = subelem_len; return subelem; } #endif /* CONFIG_IEEE80211W */ static u8 * wpa_ft_process_rdie(struct wpa_state_machine *sm, u8 *pos, u8 *end, u8 id, u8 descr_count, const u8 *ies, size_t ies_len) { struct ieee802_11_elems parse; struct rsn_rdie *rdie; wpa_printf(MSG_DEBUG, "FT: Resource Request: id=%d descr_count=%d", id, descr_count); wpa_hexdump(MSG_MSGDUMP, "FT: Resource descriptor IE(s)", ies, ies_len); if (end - pos < (int) sizeof(*rdie)) { wpa_printf(MSG_ERROR, "FT: Not enough room for response RDIE"); return pos; } *pos++ = WLAN_EID_RIC_DATA; *pos++ = sizeof(*rdie); rdie = (struct rsn_rdie *) pos; rdie->id = id; rdie->descr_count = 0; rdie->status_code = host_to_le16(WLAN_STATUS_SUCCESS); pos += sizeof(*rdie); if (ieee802_11_parse_elems((u8 *) ies, ies_len, &parse, 1) == ParseFailed) { wpa_printf(MSG_DEBUG, "FT: Failed to parse request IEs"); rdie->status_code = host_to_le16(WLAN_STATUS_UNSPECIFIED_FAILURE); return pos; } #ifdef NEED_AP_MLME if (parse.wmm_tspec && sm->wpa_auth->conf.ap_mlme) { struct wmm_tspec_element *tspec; int res; if (parse.wmm_tspec_len + 2 < (int) sizeof(*tspec)) { wpa_printf(MSG_DEBUG, "FT: Too short WMM TSPEC IE " "(%d)", (int) parse.wmm_tspec_len); rdie->status_code = host_to_le16(WLAN_STATUS_UNSPECIFIED_FAILURE); return pos; } if (end - pos < (int) sizeof(*tspec)) { wpa_printf(MSG_ERROR, "FT: Not enough room for " "response TSPEC"); rdie->status_code = host_to_le16(WLAN_STATUS_UNSPECIFIED_FAILURE); return pos; } tspec = (struct wmm_tspec_element *) pos; os_memcpy(tspec, parse.wmm_tspec - 2, sizeof(*tspec)); res = wmm_process_tspec(tspec); wpa_printf(MSG_DEBUG, "FT: ADDTS processing result: %d", res); if (res == WMM_ADDTS_STATUS_INVALID_PARAMETERS) rdie->status_code = host_to_le16(WLAN_STATUS_INVALID_PARAMETERS); else if (res == WMM_ADDTS_STATUS_REFUSED) rdie->status_code = host_to_le16(WLAN_STATUS_REQUEST_DECLINED); else { /* TSPEC accepted; include updated TSPEC in response */ rdie->descr_count = 1; pos += sizeof(*tspec); } return pos; } #endif /* NEED_AP_MLME */ if (parse.wmm_tspec && !sm->wpa_auth->conf.ap_mlme) { struct wmm_tspec_element *tspec; int res; tspec = (struct wmm_tspec_element *) pos; os_memcpy(tspec, parse.wmm_tspec - 2, sizeof(*tspec)); res = wpa_ft_add_tspec(sm->wpa_auth, sm->addr, pos, sizeof(*tspec)); if (res >= 0) { if (res) rdie->status_code = host_to_le16(res); else { /* TSPEC accepted; include updated TSPEC in * response */ rdie->descr_count = 1; pos += sizeof(*tspec); } return pos; } } wpa_printf(MSG_DEBUG, "FT: No supported resource requested"); rdie->status_code = host_to_le16(WLAN_STATUS_UNSPECIFIED_FAILURE); return pos; } static u8 * wpa_ft_process_ric(struct wpa_state_machine *sm, u8 *pos, u8 *end, const u8 *ric, size_t ric_len) { const u8 *rpos, *start; const struct rsn_rdie *rdie; wpa_hexdump(MSG_MSGDUMP, "FT: RIC Request", ric, ric_len); rpos = ric; while (rpos + sizeof(*rdie) < ric + ric_len) { if (rpos[0] != WLAN_EID_RIC_DATA || rpos[1] < sizeof(*rdie) || rpos + 2 + rpos[1] > ric + ric_len) break; rdie = (const struct rsn_rdie *) (rpos + 2); rpos += 2 + rpos[1]; start = rpos; while (rpos + 2 <= ric + ric_len && rpos + 2 + rpos[1] <= ric + ric_len) { if (rpos[0] == WLAN_EID_RIC_DATA) break; rpos += 2 + rpos[1]; } pos = wpa_ft_process_rdie(sm, pos, end, rdie->id, rdie->descr_count, start, rpos - start); } return pos; } u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos, size_t max_len, int auth_alg, const u8 *req_ies, size_t req_ies_len) { u8 *end, *mdie, *ftie, *rsnie = NULL, *r0kh_id, *subelem = NULL; size_t mdie_len, ftie_len, rsnie_len = 0, r0kh_id_len, subelem_len = 0; int res; struct wpa_auth_config *conf; struct rsn_ftie *_ftie; struct wpa_ft_ies parse; u8 *ric_start; u8 *anonce, *snonce; if (sm == NULL) return pos; conf = &sm->wpa_auth->conf; if (!wpa_key_mgmt_ft(sm->wpa_key_mgmt)) return pos; end = pos + max_len; if (auth_alg == WLAN_AUTH_FT) { /* * RSN (only present if this is a Reassociation Response and * part of a fast BSS transition) */ res = wpa_write_rsn_ie(conf, pos, end - pos, sm->pmk_r1_name); if (res < 0) return pos; rsnie = pos; rsnie_len = res; pos += res; } /* Mobility Domain Information */ res = wpa_write_mdie(conf, pos, end - pos); if (res < 0) return pos; mdie = pos; mdie_len = res; pos += res; /* Fast BSS Transition Information */ if (auth_alg == WLAN_AUTH_FT) { subelem = wpa_ft_gtk_subelem(sm, &subelem_len); r0kh_id = sm->r0kh_id; r0kh_id_len = sm->r0kh_id_len; anonce = sm->ANonce; snonce = sm->SNonce; #ifdef CONFIG_IEEE80211W if (sm->mgmt_frame_prot) { u8 *igtk; size_t igtk_len; u8 *nbuf; igtk = wpa_ft_igtk_subelem(sm, &igtk_len); if (igtk == NULL) { os_free(subelem); return pos; } nbuf = os_realloc(subelem, subelem_len + igtk_len); if (nbuf == NULL) { os_free(subelem); os_free(igtk); return pos; } subelem = nbuf; os_memcpy(subelem + subelem_len, igtk, igtk_len); subelem_len += igtk_len; os_free(igtk); } #endif /* CONFIG_IEEE80211W */ } else { r0kh_id = conf->r0_key_holder; r0kh_id_len = conf->r0_key_holder_len; anonce = NULL; snonce = NULL; } res = wpa_write_ftie(conf, r0kh_id, r0kh_id_len, anonce, snonce, pos, end - pos, subelem, subelem_len); os_free(subelem); if (res < 0) return pos; ftie = pos; ftie_len = res; pos += res; os_free(sm->assoc_resp_ftie); sm->assoc_resp_ftie = os_malloc(ftie_len); if (sm->assoc_resp_ftie) os_memcpy(sm->assoc_resp_ftie, ftie, ftie_len); _ftie = (struct rsn_ftie *) (ftie + 2); if (auth_alg == WLAN_AUTH_FT) _ftie->mic_control[1] = 3; /* Information element count */ ric_start = pos; if (wpa_ft_parse_ies(req_ies, req_ies_len, &parse) == 0 && parse.ric) { pos = wpa_ft_process_ric(sm, pos, end, parse.ric, parse.ric_len); if (auth_alg == WLAN_AUTH_FT) _ftie->mic_control[1] += ieee802_11_ie_count(ric_start, pos - ric_start); } if (ric_start == pos) ric_start = NULL; if (auth_alg == WLAN_AUTH_FT && wpa_ft_mic(sm->PTK.kck, sm->addr, sm->wpa_auth->addr, 6, mdie, mdie_len, ftie, ftie_len, rsnie, rsnie_len, ric_start, ric_start ? pos - ric_start : 0, _ftie->mic) < 0) wpa_printf(MSG_DEBUG, "FT: Failed to calculate MIC"); return pos; } static inline int wpa_auth_set_key(struct wpa_authenticator *wpa_auth, int vlan_id, enum wpa_alg alg, const u8 *addr, int idx, u8 *key, size_t key_len) { if (wpa_auth->cb.set_key == NULL) return -1; return wpa_auth->cb.set_key(wpa_auth->cb.ctx, vlan_id, alg, addr, idx, key, key_len); } void wpa_ft_install_ptk(struct wpa_state_machine *sm) { enum wpa_alg alg; int klen; /* MLME-SETKEYS.request(PTK) */ alg = wpa_cipher_to_alg(sm->pairwise); klen = wpa_cipher_key_len(sm->pairwise); if (!wpa_cipher_valid_pairwise(sm->pairwise)) { wpa_printf(MSG_DEBUG, "FT: Unknown pairwise alg 0x%x - skip " "PTK configuration", sm->pairwise); return; } /* FIX: add STA entry to kernel/driver here? The set_key will fail * most likely without this.. At the moment, STA entry is added only * after association has been completed. This function will be called * again after association to get the PTK configured, but that could be * optimized by adding the STA entry earlier. */ if (wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr, 0, sm->PTK.tk1, klen)) return; /* FIX: MLME-SetProtection.Request(TA, Tx_Rx) */ sm->pairwise_set = TRUE; } static u16 wpa_ft_process_auth_req(struct wpa_state_machine *sm, const u8 *ies, size_t ies_len, u8 **resp_ies, size_t *resp_ies_len) { struct rsn_mdie *mdie; struct rsn_ftie *ftie; u8 pmk_r1[PMK_LEN], pmk_r1_name[WPA_PMK_NAME_LEN]; u8 ptk_name[WPA_PMK_NAME_LEN]; struct wpa_auth_config *conf; struct wpa_ft_ies parse; size_t buflen, ptk_len; int ret; u8 *pos, *end; int pairwise; *resp_ies = NULL; *resp_ies_len = 0; sm->pmk_r1_name_valid = 0; conf = &sm->wpa_auth->conf; wpa_hexdump(MSG_DEBUG, "FT: Received authentication frame IEs", ies, ies_len); if (wpa_ft_parse_ies(ies, ies_len, &parse) < 0) { wpa_printf(MSG_DEBUG, "FT: Failed to parse FT IEs"); return WLAN_STATUS_UNSPECIFIED_FAILURE; } mdie = (struct rsn_mdie *) parse.mdie; if (mdie == NULL || parse.mdie_len < sizeof(*mdie) || os_memcmp(mdie->mobility_domain, sm->wpa_auth->conf.mobility_domain, MOBILITY_DOMAIN_ID_LEN) != 0) { wpa_printf(MSG_DEBUG, "FT: Invalid MDIE"); return WLAN_STATUS_INVALID_MDIE; } ftie = (struct rsn_ftie *) parse.ftie; if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) { wpa_printf(MSG_DEBUG, "FT: Invalid FTIE"); return WLAN_STATUS_INVALID_FTIE; } os_memcpy(sm->SNonce, ftie->snonce, WPA_NONCE_LEN); if (parse.r0kh_id == NULL) { wpa_printf(MSG_DEBUG, "FT: Invalid FTIE - no R0KH-ID"); return WLAN_STATUS_INVALID_FTIE; } wpa_hexdump(MSG_DEBUG, "FT: STA R0KH-ID", parse.r0kh_id, parse.r0kh_id_len); os_memcpy(sm->r0kh_id, parse.r0kh_id, parse.r0kh_id_len); sm->r0kh_id_len = parse.r0kh_id_len; if (parse.rsn_pmkid == NULL) { wpa_printf(MSG_DEBUG, "FT: No PMKID in RSNIE"); return WLAN_STATUS_INVALID_PMKID; } wpa_hexdump(MSG_DEBUG, "FT: Requested PMKR0Name", parse.rsn_pmkid, WPA_PMK_NAME_LEN); wpa_derive_pmk_r1_name(parse.rsn_pmkid, sm->wpa_auth->conf.r1_key_holder, sm->addr, pmk_r1_name); wpa_hexdump(MSG_DEBUG, "FT: Derived requested PMKR1Name", pmk_r1_name, WPA_PMK_NAME_LEN); if (wpa_ft_fetch_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1_name, pmk_r1, &pairwise) < 0) { if (wpa_ft_pull_pmk_r1(sm->wpa_auth, sm->addr, sm->r0kh_id, sm->r0kh_id_len, parse.rsn_pmkid) < 0) { wpa_printf(MSG_DEBUG, "FT: Did not have matching " "PMK-R1 and unknown R0KH-ID"); return WLAN_STATUS_INVALID_PMKID; } /* * TODO: Should return "status pending" (and the caller should * not send out response now). The real response will be sent * once the response from R0KH is received. */ return WLAN_STATUS_INVALID_PMKID; } wpa_hexdump_key(MSG_DEBUG, "FT: Selected PMK-R1", pmk_r1, PMK_LEN); sm->pmk_r1_name_valid = 1; os_memcpy(sm->pmk_r1_name, pmk_r1_name, WPA_PMK_NAME_LEN); if (random_get_bytes(sm->ANonce, WPA_NONCE_LEN)) { wpa_printf(MSG_DEBUG, "FT: Failed to get random data for " "ANonce"); return WLAN_STATUS_UNSPECIFIED_FAILURE; } wpa_hexdump(MSG_DEBUG, "FT: Received SNonce", sm->SNonce, WPA_NONCE_LEN); wpa_hexdump(MSG_DEBUG, "FT: Generated ANonce", sm->ANonce, WPA_NONCE_LEN); ptk_len = pairwise == WPA_CIPHER_TKIP ? 64 : 48; wpa_pmk_r1_to_ptk(pmk_r1, sm->SNonce, sm->ANonce, sm->addr, sm->wpa_auth->addr, pmk_r1_name, (u8 *) &sm->PTK, ptk_len, ptk_name); wpa_hexdump_key(MSG_DEBUG, "FT: PTK", (u8 *) &sm->PTK, ptk_len); wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN); sm->pairwise = pairwise; wpa_ft_install_ptk(sm); buflen = 2 + sizeof(struct rsn_mdie) + 2 + sizeof(struct rsn_ftie) + 2 + FT_R1KH_ID_LEN + 200; *resp_ies = os_zalloc(buflen); if (*resp_ies == NULL) { return WLAN_STATUS_UNSPECIFIED_FAILURE; } pos = *resp_ies; end = *resp_ies + buflen; ret = wpa_write_rsn_ie(conf, pos, end - pos, parse.rsn_pmkid); if (ret < 0) { os_free(*resp_ies); *resp_ies = NULL; return WLAN_STATUS_UNSPECIFIED_FAILURE; } pos += ret; ret = wpa_write_mdie(conf, pos, end - pos); if (ret < 0) { os_free(*resp_ies); *resp_ies = NULL; return WLAN_STATUS_UNSPECIFIED_FAILURE; } pos += ret; ret = wpa_write_ftie(conf, parse.r0kh_id, parse.r0kh_id_len, sm->ANonce, sm->SNonce, pos, end - pos, NULL, 0); if (ret < 0) { os_free(*resp_ies); *resp_ies = NULL; return WLAN_STATUS_UNSPECIFIED_FAILURE; } pos += ret; *resp_ies_len = pos - *resp_ies; return WLAN_STATUS_SUCCESS; } void wpa_ft_process_auth(struct wpa_state_machine *sm, const u8 *bssid, u16 auth_transaction, const u8 *ies, size_t ies_len, void (*cb)(void *ctx, const u8 *dst, const u8 *bssid, u16 auth_transaction, u16 status, const u8 *ies, size_t ies_len), void *ctx) { u16 status; u8 *resp_ies; size_t resp_ies_len; if (sm == NULL) { wpa_printf(MSG_DEBUG, "FT: Received authentication frame, but " "WPA SM not available"); return; } wpa_printf(MSG_DEBUG, "FT: Received authentication frame: STA=" MACSTR " BSSID=" MACSTR " transaction=%d", MAC2STR(sm->addr), MAC2STR(bssid), auth_transaction); status = wpa_ft_process_auth_req(sm, ies, ies_len, &resp_ies, &resp_ies_len); wpa_printf(MSG_DEBUG, "FT: FT authentication response: dst=" MACSTR " auth_transaction=%d status=%d", MAC2STR(sm->addr), auth_transaction + 1, status); wpa_hexdump(MSG_DEBUG, "FT: Response IEs", resp_ies, resp_ies_len); cb(ctx, sm->addr, bssid, auth_transaction + 1, status, resp_ies, resp_ies_len); os_free(resp_ies); } u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies, size_t ies_len) { struct wpa_ft_ies parse; struct rsn_mdie *mdie; struct rsn_ftie *ftie; u8 mic[16]; unsigned int count; if (sm == NULL) return WLAN_STATUS_UNSPECIFIED_FAILURE; wpa_hexdump(MSG_DEBUG, "FT: Reassoc Req IEs", ies, ies_len); if (wpa_ft_parse_ies(ies, ies_len, &parse) < 0) { wpa_printf(MSG_DEBUG, "FT: Failed to parse FT IEs"); return WLAN_STATUS_UNSPECIFIED_FAILURE; } if (parse.rsn == NULL) { wpa_printf(MSG_DEBUG, "FT: No RSNIE in Reassoc Req"); return WLAN_STATUS_UNSPECIFIED_FAILURE; } if (parse.rsn_pmkid == NULL) { wpa_printf(MSG_DEBUG, "FT: No PMKID in RSNIE"); return WLAN_STATUS_INVALID_PMKID; } if (os_memcmp(parse.rsn_pmkid, sm->pmk_r1_name, WPA_PMK_NAME_LEN) != 0) { wpa_printf(MSG_DEBUG, "FT: PMKID in Reassoc Req did not match " "with the PMKR1Name derived from auth request"); return WLAN_STATUS_INVALID_PMKID; } mdie = (struct rsn_mdie *) parse.mdie; if (mdie == NULL || parse.mdie_len < sizeof(*mdie) || os_memcmp(mdie->mobility_domain, sm->wpa_auth->conf.mobility_domain, MOBILITY_DOMAIN_ID_LEN) != 0) { wpa_printf(MSG_DEBUG, "FT: Invalid MDIE"); return WLAN_STATUS_INVALID_MDIE; } ftie = (struct rsn_ftie *) parse.ftie; if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) { wpa_printf(MSG_DEBUG, "FT: Invalid FTIE"); return WLAN_STATUS_INVALID_FTIE; } if (os_memcmp(ftie->snonce, sm->SNonce, WPA_NONCE_LEN) != 0) { wpa_printf(MSG_DEBUG, "FT: SNonce mismatch in FTIE"); wpa_hexdump(MSG_DEBUG, "FT: Received SNonce", ftie->snonce, WPA_NONCE_LEN); wpa_hexdump(MSG_DEBUG, "FT: Expected SNonce", sm->SNonce, WPA_NONCE_LEN); return -1; } if (os_memcmp(ftie->anonce, sm->ANonce, WPA_NONCE_LEN) != 0) { wpa_printf(MSG_DEBUG, "FT: ANonce mismatch in FTIE"); wpa_hexdump(MSG_DEBUG, "FT: Received ANonce", ftie->anonce, WPA_NONCE_LEN); wpa_hexdump(MSG_DEBUG, "FT: Expected ANonce", sm->ANonce, WPA_NONCE_LEN); return -1; } if (parse.r0kh_id == NULL) { wpa_printf(MSG_DEBUG, "FT: No R0KH-ID subelem in FTIE"); return -1; } if (parse.r0kh_id_len != sm->r0kh_id_len || os_memcmp(parse.r0kh_id, sm->r0kh_id, parse.r0kh_id_len) != 0) { wpa_printf(MSG_DEBUG, "FT: R0KH-ID in FTIE did not match with " "the current R0KH-ID"); wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID in FTIE", parse.r0kh_id, parse.r0kh_id_len); wpa_hexdump(MSG_DEBUG, "FT: The current R0KH-ID", sm->r0kh_id, sm->r0kh_id_len); return -1; } if (parse.r1kh_id == NULL) { wpa_printf(MSG_DEBUG, "FT: No R1KH-ID subelem in FTIE"); return -1; } if (os_memcmp(parse.r1kh_id, sm->wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN) != 0) { wpa_printf(MSG_DEBUG, "FT: Unknown R1KH-ID used in " "ReassocReq"); wpa_hexdump(MSG_DEBUG, "FT: R1KH-ID in FTIE", parse.r1kh_id, FT_R1KH_ID_LEN); wpa_hexdump(MSG_DEBUG, "FT: Expected R1KH-ID", sm->wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN); return -1; } if (parse.rsn_pmkid == NULL || os_memcmp(parse.rsn_pmkid, sm->pmk_r1_name, WPA_PMK_NAME_LEN)) { wpa_printf(MSG_DEBUG, "FT: No matching PMKR1Name (PMKID) in " "RSNIE (pmkid=%d)", !!parse.rsn_pmkid); return -1; } count = 3; if (parse.ric) count += ieee802_11_ie_count(parse.ric, parse.ric_len); if (ftie->mic_control[1] != count) { wpa_printf(MSG_DEBUG, "FT: Unexpected IE count in MIC " "Control: received %u expected %u", ftie->mic_control[1], count); return -1; } if (wpa_ft_mic(sm->PTK.kck, sm->addr, sm->wpa_auth->addr, 5, parse.mdie - 2, parse.mdie_len + 2, parse.ftie - 2, parse.ftie_len + 2, parse.rsn - 2, parse.rsn_len + 2, parse.ric, parse.ric_len, mic) < 0) { wpa_printf(MSG_DEBUG, "FT: Failed to calculate MIC"); return WLAN_STATUS_UNSPECIFIED_FAILURE; } if (os_memcmp(mic, ftie->mic, 16) != 0) { wpa_printf(MSG_DEBUG, "FT: Invalid MIC in FTIE"); wpa_printf(MSG_DEBUG, "FT: addr=" MACSTR " auth_addr=" MACSTR, MAC2STR(sm->addr), MAC2STR(sm->wpa_auth->addr)); wpa_hexdump(MSG_MSGDUMP, "FT: Received MIC", ftie->mic, 16); wpa_hexdump(MSG_MSGDUMP, "FT: Calculated MIC", mic, 16); wpa_hexdump(MSG_MSGDUMP, "FT: MDIE", parse.mdie - 2, parse.mdie_len + 2); wpa_hexdump(MSG_MSGDUMP, "FT: FTIE", parse.ftie - 2, parse.ftie_len + 2); wpa_hexdump(MSG_MSGDUMP, "FT: RSN", parse.rsn - 2, parse.rsn_len + 2); return WLAN_STATUS_INVALID_FTIE; } return WLAN_STATUS_SUCCESS; } int wpa_ft_action_rx(struct wpa_state_machine *sm, const u8 *data, size_t len) { const u8 *sta_addr, *target_ap; const u8 *ies; size_t ies_len; u8 action; struct ft_rrb_frame *frame; if (sm == NULL) return -1; /* * data: Category[1] Action[1] STA_Address[6] Target_AP_Address[6] * FT Request action frame body[variable] */ if (len < 14) { wpa_printf(MSG_DEBUG, "FT: Too short FT Action frame " "(len=%lu)", (unsigned long) len); return -1; } action = data[1]; sta_addr = data + 2; target_ap = data + 8; ies = data + 14; ies_len = len - 14; wpa_printf(MSG_DEBUG, "FT: Received FT Action frame (STA=" MACSTR " Target AP=" MACSTR " Action=%d)", MAC2STR(sta_addr), MAC2STR(target_ap), action); if (os_memcmp(sta_addr, sm->addr, ETH_ALEN) != 0) { wpa_printf(MSG_DEBUG, "FT: Mismatch in FT Action STA address: " "STA=" MACSTR " STA-Address=" MACSTR, MAC2STR(sm->addr), MAC2STR(sta_addr)); return -1; } /* * Do some sanity checking on the target AP address (not own and not * broadcast. This could be extended to filter based on a list of known * APs in the MD (if such a list were configured). */ if ((target_ap[0] & 0x01) || os_memcmp(target_ap, sm->wpa_auth->addr, ETH_ALEN) == 0) { wpa_printf(MSG_DEBUG, "FT: Invalid Target AP in FT Action " "frame"); return -1; } wpa_hexdump(MSG_MSGDUMP, "FT: Action frame body", ies, ies_len); /* RRB - Forward action frame to the target AP */ frame = os_malloc(sizeof(*frame) + len); if (frame == NULL) return -1; frame->frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB; frame->packet_type = FT_PACKET_REQUEST; frame->action_length = host_to_le16(len); os_memcpy(frame->ap_address, sm->wpa_auth->addr, ETH_ALEN); os_memcpy(frame + 1, data, len); wpa_ft_rrb_send(sm->wpa_auth, target_ap, (u8 *) frame, sizeof(*frame) + len); os_free(frame); return 0; } static int wpa_ft_rrb_rx_request(struct wpa_authenticator *wpa_auth, const u8 *current_ap, const u8 *sta_addr, const u8 *body, size_t len) { struct wpa_state_machine *sm; u16 status; u8 *resp_ies, *pos; size_t resp_ies_len, rlen; struct ft_rrb_frame *frame; sm = wpa_ft_add_sta(wpa_auth, sta_addr); if (sm == NULL) { wpa_printf(MSG_DEBUG, "FT: Failed to add new STA based on " "RRB Request"); return -1; } wpa_hexdump(MSG_MSGDUMP, "FT: RRB Request Frame body", body, len); status = wpa_ft_process_auth_req(sm, body, len, &resp_ies, &resp_ies_len); wpa_printf(MSG_DEBUG, "FT: RRB authentication response: STA=" MACSTR " CurrentAP=" MACSTR " status=%d", MAC2STR(sm->addr), MAC2STR(current_ap), status); wpa_hexdump(MSG_DEBUG, "FT: Response IEs", resp_ies, resp_ies_len); /* RRB - Forward action frame response to the Current AP */ /* * data: Category[1] Action[1] STA_Address[6] Target_AP_Address[6] * Status_Code[2] FT Request action frame body[variable] */ rlen = 2 + 2 * ETH_ALEN + 2 + resp_ies_len; frame = os_malloc(sizeof(*frame) + rlen); if (frame == NULL) { os_free(resp_ies); return -1; } frame->frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB; frame->packet_type = FT_PACKET_RESPONSE; frame->action_length = host_to_le16(rlen); os_memcpy(frame->ap_address, wpa_auth->addr, ETH_ALEN); pos = (u8 *) (frame + 1); *pos++ = WLAN_ACTION_FT; *pos++ = 2; /* Action: Response */ os_memcpy(pos, sta_addr, ETH_ALEN); pos += ETH_ALEN; os_memcpy(pos, wpa_auth->addr, ETH_ALEN); pos += ETH_ALEN; WPA_PUT_LE16(pos, status); pos += 2; if (resp_ies) { os_memcpy(pos, resp_ies, resp_ies_len); os_free(resp_ies); } wpa_ft_rrb_send(wpa_auth, current_ap, (u8 *) frame, sizeof(*frame) + rlen); os_free(frame); return 0; } static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth, const u8 *src_addr, const u8 *data, size_t data_len) { struct ft_r0kh_r1kh_pull_frame *frame, f; struct ft_remote_r1kh *r1kh; struct ft_r0kh_r1kh_resp_frame resp, r; u8 pmk_r0[PMK_LEN]; int pairwise; wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 pull"); if (data_len < sizeof(*frame)) return -1; r1kh = wpa_auth->conf.r1kh_list; while (r1kh) { if (os_memcmp(r1kh->addr, src_addr, ETH_ALEN) == 0) break; r1kh = r1kh->next; } if (r1kh == NULL) { wpa_printf(MSG_DEBUG, "FT: No matching R1KH address found for " "PMK-R1 pull source address " MACSTR, MAC2STR(src_addr)); return -1; } frame = (struct ft_r0kh_r1kh_pull_frame *) data; /* aes_unwrap() does not support inplace decryption, so use a temporary * buffer for the data. */ if (aes_unwrap(r1kh->key, (FT_R0KH_R1KH_PULL_DATA_LEN + 7) / 8, frame->nonce, f.nonce) < 0) { wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 pull " "request from " MACSTR, MAC2STR(src_addr)); return -1; } wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - nonce", f.nonce, sizeof(f.nonce)); wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - PMKR0Name", f.pmk_r0_name, WPA_PMK_NAME_LEN); wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull - R1KH-ID=" MACSTR "S1KH-ID=" MACSTR, MAC2STR(f.r1kh_id), MAC2STR(f.s1kh_id)); os_memset(&resp, 0, sizeof(resp)); resp.frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB; resp.packet_type = FT_PACKET_R0KH_R1KH_RESP; resp.data_length = host_to_le16(FT_R0KH_R1KH_RESP_DATA_LEN); os_memcpy(resp.ap_address, wpa_auth->addr, ETH_ALEN); /* aes_wrap() does not support inplace encryption, so use a temporary * buffer for the data. */ os_memcpy(r.nonce, f.nonce, sizeof(f.nonce)); os_memcpy(r.r1kh_id, f.r1kh_id, FT_R1KH_ID_LEN); os_memcpy(r.s1kh_id, f.s1kh_id, ETH_ALEN); if (wpa_ft_fetch_pmk_r0(wpa_auth, f.s1kh_id, f.pmk_r0_name, pmk_r0, &pairwise) < 0) { wpa_printf(MSG_DEBUG, "FT: No matching PMKR0Name found for " "PMK-R1 pull"); return -1; } wpa_derive_pmk_r1(pmk_r0, f.pmk_r0_name, f.r1kh_id, f.s1kh_id, r.pmk_r1, r.pmk_r1_name); wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", r.pmk_r1, PMK_LEN); wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", r.pmk_r1_name, WPA_PMK_NAME_LEN); r.pairwise = host_to_le16(pairwise); os_memset(r.pad, 0, sizeof(r.pad)); if (aes_wrap(r1kh->key, (FT_R0KH_R1KH_RESP_DATA_LEN + 7) / 8, r.nonce, resp.nonce) < 0) { os_memset(pmk_r0, 0, PMK_LEN); return -1; } os_memset(pmk_r0, 0, PMK_LEN); wpa_ft_rrb_send(wpa_auth, src_addr, (u8 *) &resp, sizeof(resp)); return 0; } static int wpa_ft_rrb_rx_resp(struct wpa_authenticator *wpa_auth, const u8 *src_addr, const u8 *data, size_t data_len) { struct ft_r0kh_r1kh_resp_frame *frame, f; struct ft_remote_r0kh *r0kh; int pairwise; wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 pull response"); if (data_len < sizeof(*frame)) return -1; r0kh = wpa_auth->conf.r0kh_list; while (r0kh) { if (os_memcmp(r0kh->addr, src_addr, ETH_ALEN) == 0) break; r0kh = r0kh->next; } if (r0kh == NULL) { wpa_printf(MSG_DEBUG, "FT: No matching R0KH address found for " "PMK-R0 pull response source address " MACSTR, MAC2STR(src_addr)); return -1; } frame = (struct ft_r0kh_r1kh_resp_frame *) data; /* aes_unwrap() does not support inplace decryption, so use a temporary * buffer for the data. */ if (aes_unwrap(r0kh->key, (FT_R0KH_R1KH_RESP_DATA_LEN + 7) / 8, frame->nonce, f.nonce) < 0) { wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 pull " "response from " MACSTR, MAC2STR(src_addr)); return -1; } if (os_memcmp(f.r1kh_id, wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN) != 0) { wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull response did not use a " "matching R1KH-ID"); return -1; } /* TODO: verify that matches with a pending request * and call this requests callback function to finish request * processing */ pairwise = le_to_host16(f.pairwise); wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - nonce", f.nonce, sizeof(f.nonce)); wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull - R1KH-ID=" MACSTR "S1KH-ID=" MACSTR " pairwise=0x%x", MAC2STR(f.r1kh_id), MAC2STR(f.s1kh_id), pairwise); wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1 pull - PMK-R1", f.pmk_r1, PMK_LEN); wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - PMKR1Name", f.pmk_r1_name, WPA_PMK_NAME_LEN); wpa_ft_store_pmk_r1(wpa_auth, f.s1kh_id, f.pmk_r1, f.pmk_r1_name, pairwise); os_memset(f.pmk_r1, 0, PMK_LEN); return 0; } static int wpa_ft_rrb_rx_push(struct wpa_authenticator *wpa_auth, const u8 *src_addr, const u8 *data, size_t data_len) { struct ft_r0kh_r1kh_push_frame *frame, f; struct ft_remote_r0kh *r0kh; struct os_time now; os_time_t tsend; int pairwise; wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 push"); if (data_len < sizeof(*frame)) return -1; r0kh = wpa_auth->conf.r0kh_list; while (r0kh) { if (os_memcmp(r0kh->addr, src_addr, ETH_ALEN) == 0) break; r0kh = r0kh->next; } if (r0kh == NULL) { wpa_printf(MSG_DEBUG, "FT: No matching R0KH address found for " "PMK-R0 push source address " MACSTR, MAC2STR(src_addr)); return -1; } frame = (struct ft_r0kh_r1kh_push_frame *) data; /* aes_unwrap() does not support inplace decryption, so use a temporary * buffer for the data. */ if (aes_unwrap(r0kh->key, (FT_R0KH_R1KH_PUSH_DATA_LEN + 7) / 8, frame->timestamp, f.timestamp) < 0) { wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 push from " MACSTR, MAC2STR(src_addr)); return -1; } os_get_time(&now); tsend = WPA_GET_LE32(f.timestamp); if ((now.sec > tsend && now.sec - tsend > 60) || (now.sec < tsend && tsend - now.sec > 60)) { wpa_printf(MSG_DEBUG, "FT: PMK-R1 push did not have a valid " "timestamp: sender time %d own time %d\n", (int) tsend, (int) now.sec); return -1; } if (os_memcmp(f.r1kh_id, wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN) != 0) { wpa_printf(MSG_DEBUG, "FT: PMK-R1 push did not use a matching " "R1KH-ID (received " MACSTR " own " MACSTR ")", MAC2STR(f.r1kh_id), MAC2STR(wpa_auth->conf.r1_key_holder)); return -1; } pairwise = le_to_host16(f.pairwise); wpa_printf(MSG_DEBUG, "FT: PMK-R1 push - R1KH-ID=" MACSTR " S1KH-ID=" MACSTR " pairwise=0x%x", MAC2STR(f.r1kh_id), MAC2STR(f.s1kh_id), pairwise); wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1 push - PMK-R1", f.pmk_r1, PMK_LEN); wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 push - PMKR1Name", f.pmk_r1_name, WPA_PMK_NAME_LEN); wpa_ft_store_pmk_r1(wpa_auth, f.s1kh_id, f.pmk_r1, f.pmk_r1_name, pairwise); os_memset(f.pmk_r1, 0, PMK_LEN); return 0; } int wpa_ft_rrb_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr, const u8 *data, size_t data_len) { struct ft_rrb_frame *frame; u16 alen; const u8 *pos, *end, *start; u8 action; const u8 *sta_addr, *target_ap_addr; wpa_printf(MSG_DEBUG, "FT: RRB received frame from remote AP " MACSTR, MAC2STR(src_addr)); if (data_len < sizeof(*frame)) { wpa_printf(MSG_DEBUG, "FT: Too short RRB frame (data_len=%lu)", (unsigned long) data_len); return -1; } pos = data; frame = (struct ft_rrb_frame *) pos; pos += sizeof(*frame); alen = le_to_host16(frame->action_length); wpa_printf(MSG_DEBUG, "FT: RRB frame - frame_type=%d packet_type=%d " "action_length=%d ap_address=" MACSTR, frame->frame_type, frame->packet_type, alen, MAC2STR(frame->ap_address)); if (frame->frame_type != RSN_REMOTE_FRAME_TYPE_FT_RRB) { /* Discard frame per IEEE Std 802.11r-2008, 11A.10.3 */ wpa_printf(MSG_DEBUG, "FT: RRB discarded frame with " "unrecognized type %d", frame->frame_type); return -1; } if (alen > data_len - sizeof(*frame)) { wpa_printf(MSG_DEBUG, "FT: RRB frame too short for action " "frame"); return -1; } if (frame->packet_type == FT_PACKET_R0KH_R1KH_PULL) return wpa_ft_rrb_rx_pull(wpa_auth, src_addr, data, data_len); if (frame->packet_type == FT_PACKET_R0KH_R1KH_RESP) return wpa_ft_rrb_rx_resp(wpa_auth, src_addr, data, data_len); if (frame->packet_type == FT_PACKET_R0KH_R1KH_PUSH) return wpa_ft_rrb_rx_push(wpa_auth, src_addr, data, data_len); wpa_hexdump(MSG_MSGDUMP, "FT: RRB - FT Action frame", pos, alen); if (alen < 1 + 1 + 2 * ETH_ALEN) { wpa_printf(MSG_DEBUG, "FT: Too short RRB frame (not enough " "room for Action Frame body); alen=%lu", (unsigned long) alen); return -1; } start = pos; end = pos + alen; if (*pos != WLAN_ACTION_FT) { wpa_printf(MSG_DEBUG, "FT: Unexpected Action frame category " "%d", *pos); return -1; } pos++; action = *pos++; sta_addr = pos; pos += ETH_ALEN; target_ap_addr = pos; pos += ETH_ALEN; wpa_printf(MSG_DEBUG, "FT: RRB Action Frame: action=%d sta_addr=" MACSTR " target_ap_addr=" MACSTR, action, MAC2STR(sta_addr), MAC2STR(target_ap_addr)); if (frame->packet_type == FT_PACKET_REQUEST) { wpa_printf(MSG_DEBUG, "FT: FT Packet Type - Request"); if (action != 1) { wpa_printf(MSG_DEBUG, "FT: Unexpected Action %d in " "RRB Request", action); return -1; } if (os_memcmp(target_ap_addr, wpa_auth->addr, ETH_ALEN) != 0) { wpa_printf(MSG_DEBUG, "FT: Target AP address in the " "RRB Request does not match with own " "address"); return -1; } if (wpa_ft_rrb_rx_request(wpa_auth, frame->ap_address, sta_addr, pos, end - pos) < 0) return -1; } else if (frame->packet_type == FT_PACKET_RESPONSE) { u16 status_code; if (end - pos < 2) { wpa_printf(MSG_DEBUG, "FT: Not enough room for status " "code in RRB Response"); return -1; } status_code = WPA_GET_LE16(pos); pos += 2; wpa_printf(MSG_DEBUG, "FT: FT Packet Type - Response " "(status_code=%d)", status_code); if (wpa_ft_action_send(wpa_auth, sta_addr, start, alen) < 0) return -1; } else { wpa_printf(MSG_DEBUG, "FT: RRB discarded frame with unknown " "packet_type %d", frame->packet_type); return -1; } return 0; } static void wpa_ft_generate_pmk_r1(struct wpa_authenticator *wpa_auth, struct wpa_ft_pmk_r0_sa *pmk_r0, struct ft_remote_r1kh *r1kh, const u8 *s1kh_id, int pairwise) { struct ft_r0kh_r1kh_push_frame frame, f; struct os_time now; os_memset(&frame, 0, sizeof(frame)); frame.frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB; frame.packet_type = FT_PACKET_R0KH_R1KH_PUSH; frame.data_length = host_to_le16(FT_R0KH_R1KH_PUSH_DATA_LEN); os_memcpy(frame.ap_address, wpa_auth->addr, ETH_ALEN); /* aes_wrap() does not support inplace encryption, so use a temporary * buffer for the data. */ os_memcpy(f.r1kh_id, r1kh->id, FT_R1KH_ID_LEN); os_memcpy(f.s1kh_id, s1kh_id, ETH_ALEN); os_memcpy(f.pmk_r0_name, pmk_r0->pmk_r0_name, WPA_PMK_NAME_LEN); wpa_derive_pmk_r1(pmk_r0->pmk_r0, pmk_r0->pmk_r0_name, r1kh->id, s1kh_id, f.pmk_r1, f.pmk_r1_name); wpa_printf(MSG_DEBUG, "FT: R1KH-ID " MACSTR, MAC2STR(r1kh->id)); wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", f.pmk_r1, PMK_LEN); wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", f.pmk_r1_name, WPA_PMK_NAME_LEN); os_get_time(&now); WPA_PUT_LE32(f.timestamp, now.sec); f.pairwise = host_to_le16(pairwise); os_memset(f.pad, 0, sizeof(f.pad)); if (aes_wrap(r1kh->key, (FT_R0KH_R1KH_PUSH_DATA_LEN + 7) / 8, f.timestamp, frame.timestamp) < 0) return; wpa_ft_rrb_send(wpa_auth, r1kh->addr, (u8 *) &frame, sizeof(frame)); } void wpa_ft_push_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *addr) { struct wpa_ft_pmk_r0_sa *r0; struct ft_remote_r1kh *r1kh; if (!wpa_auth->conf.pmk_r1_push) return; r0 = wpa_auth->ft_pmk_cache->pmk_r0; while (r0) { if (os_memcmp(r0->spa, addr, ETH_ALEN) == 0) break; r0 = r0->next; } if (r0 == NULL || r0->pmk_r1_pushed) return; r0->pmk_r1_pushed = 1; wpa_printf(MSG_DEBUG, "FT: Deriving and pushing PMK-R1 keys to R1KHs " "for STA " MACSTR, MAC2STR(addr)); r1kh = wpa_auth->conf.r1kh_list; while (r1kh) { wpa_ft_generate_pmk_r1(wpa_auth, r0, r1kh, addr, r0->pairwise); r1kh = r1kh->next; } } #endif /* CONFIG_IEEE80211R */ wpa-2.1/src/ap/wpa_auth_glue.c000066400000000000000000000401411227414666700163410ustar00rootroot00000000000000/* * hostapd / WPA authenticator glue code * Copyright (c) 2002-2012, 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 "common/ieee802_11_defs.h" #include "common/sae.h" #include "eapol_auth/eapol_auth_sm.h" #include "eapol_auth/eapol_auth_sm_i.h" #include "eap_server/eap.h" #include "l2_packet/l2_packet.h" #include "hostapd.h" #include "ieee802_1x.h" #include "preauth_auth.h" #include "sta_info.h" #include "tkip_countermeasures.h" #include "ap_drv_ops.h" #include "ap_config.h" #include "wpa_auth.h" #include "wpa_auth_glue.h" static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf, struct hostapd_config *iconf, struct wpa_auth_config *wconf) { os_memset(wconf, 0, sizeof(*wconf)); wconf->wpa = conf->wpa; wconf->wpa_key_mgmt = conf->wpa_key_mgmt; wconf->wpa_pairwise = conf->wpa_pairwise; wconf->wpa_group = conf->wpa_group; wconf->wpa_group_rekey = conf->wpa_group_rekey; wconf->wpa_strict_rekey = conf->wpa_strict_rekey; wconf->wpa_gmk_rekey = conf->wpa_gmk_rekey; wconf->wpa_ptk_rekey = conf->wpa_ptk_rekey; wconf->rsn_pairwise = conf->rsn_pairwise; wconf->rsn_preauth = conf->rsn_preauth; wconf->eapol_version = conf->eapol_version; wconf->peerkey = conf->peerkey; wconf->wmm_enabled = conf->wmm_enabled; wconf->wmm_uapsd = conf->wmm_uapsd; wconf->disable_pmksa_caching = conf->disable_pmksa_caching; wconf->okc = conf->okc; #ifdef CONFIG_IEEE80211W wconf->ieee80211w = conf->ieee80211w; #endif /* CONFIG_IEEE80211W */ #ifdef CONFIG_IEEE80211R wconf->ssid_len = conf->ssid.ssid_len; if (wconf->ssid_len > SSID_LEN) wconf->ssid_len = SSID_LEN; os_memcpy(wconf->ssid, conf->ssid.ssid, wconf->ssid_len); os_memcpy(wconf->mobility_domain, conf->mobility_domain, MOBILITY_DOMAIN_ID_LEN); if (conf->nas_identifier && os_strlen(conf->nas_identifier) <= FT_R0KH_ID_MAX_LEN) { wconf->r0_key_holder_len = os_strlen(conf->nas_identifier); os_memcpy(wconf->r0_key_holder, conf->nas_identifier, wconf->r0_key_holder_len); } os_memcpy(wconf->r1_key_holder, conf->r1_key_holder, FT_R1KH_ID_LEN); wconf->r0_key_lifetime = conf->r0_key_lifetime; wconf->reassociation_deadline = conf->reassociation_deadline; wconf->r0kh_list = conf->r0kh_list; wconf->r1kh_list = conf->r1kh_list; wconf->pmk_r1_push = conf->pmk_r1_push; wconf->ft_over_ds = conf->ft_over_ds; #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_HS20 wconf->disable_gtk = conf->disable_dgaf; #endif /* CONFIG_HS20 */ #ifdef CONFIG_TESTING_OPTIONS wconf->corrupt_gtk_rekey_mic_probability = iconf->corrupt_gtk_rekey_mic_probability; #endif /* CONFIG_TESTING_OPTIONS */ #ifdef CONFIG_P2P os_memcpy(wconf->ip_addr_go, conf->ip_addr_go, 4); os_memcpy(wconf->ip_addr_mask, conf->ip_addr_mask, 4); os_memcpy(wconf->ip_addr_start, conf->ip_addr_start, 4); os_memcpy(wconf->ip_addr_end, conf->ip_addr_end, 4); #endif /* CONFIG_P2P */ } static void hostapd_wpa_auth_logger(void *ctx, const u8 *addr, logger_level level, const char *txt) { #ifndef CONFIG_NO_HOSTAPD_LOGGER struct hostapd_data *hapd = ctx; int hlevel; switch (level) { case LOGGER_WARNING: hlevel = HOSTAPD_LEVEL_WARNING; break; case LOGGER_INFO: hlevel = HOSTAPD_LEVEL_INFO; break; case LOGGER_DEBUG: default: hlevel = HOSTAPD_LEVEL_DEBUG; break; } hostapd_logger(hapd, addr, HOSTAPD_MODULE_WPA, hlevel, "%s", txt); #endif /* CONFIG_NO_HOSTAPD_LOGGER */ } static void hostapd_wpa_auth_disconnect(void *ctx, const u8 *addr, u16 reason) { struct hostapd_data *hapd = ctx; wpa_printf(MSG_DEBUG, "%s: WPA authenticator requests disconnect: " "STA " MACSTR " reason %d", __func__, MAC2STR(addr), reason); ap_sta_disconnect(hapd, NULL, addr, reason); } static int hostapd_wpa_auth_mic_failure_report(void *ctx, const u8 *addr) { struct hostapd_data *hapd = ctx; return michael_mic_failure(hapd, addr, 0); } static void hostapd_wpa_auth_set_eapol(void *ctx, const u8 *addr, wpa_eapol_variable var, int value) { struct hostapd_data *hapd = ctx; struct sta_info *sta = ap_get_sta(hapd, addr); if (sta == NULL) return; switch (var) { case WPA_EAPOL_portEnabled: ieee802_1x_notify_port_enabled(sta->eapol_sm, value); break; case WPA_EAPOL_portValid: ieee802_1x_notify_port_valid(sta->eapol_sm, value); break; case WPA_EAPOL_authorized: ieee802_1x_set_sta_authorized(hapd, sta, value); break; case WPA_EAPOL_portControl_Auto: if (sta->eapol_sm) sta->eapol_sm->portControl = Auto; break; case WPA_EAPOL_keyRun: if (sta->eapol_sm) sta->eapol_sm->keyRun = value ? TRUE : FALSE; break; case WPA_EAPOL_keyAvailable: if (sta->eapol_sm) sta->eapol_sm->eap_if->eapKeyAvailable = value ? TRUE : FALSE; break; case WPA_EAPOL_keyDone: if (sta->eapol_sm) sta->eapol_sm->keyDone = value ? TRUE : FALSE; break; case WPA_EAPOL_inc_EapolFramesTx: if (sta->eapol_sm) sta->eapol_sm->dot1xAuthEapolFramesTx++; break; } } static int hostapd_wpa_auth_get_eapol(void *ctx, const u8 *addr, wpa_eapol_variable var) { struct hostapd_data *hapd = ctx; struct sta_info *sta = ap_get_sta(hapd, addr); if (sta == NULL || sta->eapol_sm == NULL) return -1; switch (var) { case WPA_EAPOL_keyRun: return sta->eapol_sm->keyRun; case WPA_EAPOL_keyAvailable: return sta->eapol_sm->eap_if->eapKeyAvailable; default: return -1; } } static const u8 * hostapd_wpa_auth_get_psk(void *ctx, const u8 *addr, const u8 *p2p_dev_addr, const u8 *prev_psk) { struct hostapd_data *hapd = ctx; struct sta_info *sta = ap_get_sta(hapd, addr); const u8 *psk; #ifdef CONFIG_SAE if (sta && sta->auth_alg == WLAN_AUTH_SAE) { if (!sta->sae || prev_psk) return NULL; return sta->sae->pmk; } #endif /* CONFIG_SAE */ psk = hostapd_get_psk(hapd->conf, addr, p2p_dev_addr, prev_psk); /* * This is about to iterate over all psks, prev_psk gives the last * returned psk which should not be returned again. * logic list (all hostapd_get_psk; all sta->psk) */ if (sta && sta->psk && !psk) { struct hostapd_sta_wpa_psk_short *pos; psk = sta->psk->psk; for (pos = sta->psk; pos; pos = pos->next) { if (pos->psk == prev_psk) { psk = pos->next ? pos->next->psk : NULL; break; } } } return psk; } static int hostapd_wpa_auth_get_msk(void *ctx, const u8 *addr, u8 *msk, size_t *len) { struct hostapd_data *hapd = ctx; const u8 *key; size_t keylen; struct sta_info *sta; sta = ap_get_sta(hapd, addr); if (sta == NULL) return -1; key = ieee802_1x_get_key(sta->eapol_sm, &keylen); if (key == NULL) return -1; if (keylen > *len) keylen = *len; os_memcpy(msk, key, keylen); *len = keylen; return 0; } static int hostapd_wpa_auth_set_key(void *ctx, int vlan_id, enum wpa_alg alg, const u8 *addr, int idx, u8 *key, size_t key_len) { struct hostapd_data *hapd = ctx; const char *ifname = hapd->conf->iface; if (vlan_id > 0) { ifname = hostapd_get_vlan_id_ifname(hapd->conf->vlan, vlan_id); if (ifname == NULL) return -1; } return hostapd_drv_set_key(ifname, hapd, alg, addr, idx, 1, NULL, 0, key, key_len); } static int hostapd_wpa_auth_get_seqnum(void *ctx, const u8 *addr, int idx, u8 *seq) { struct hostapd_data *hapd = ctx; return hostapd_get_seqnum(hapd->conf->iface, hapd, addr, idx, seq); } static int hostapd_wpa_auth_send_eapol(void *ctx, const u8 *addr, const u8 *data, size_t data_len, int encrypt) { struct hostapd_data *hapd = ctx; struct sta_info *sta; u32 flags = 0; sta = ap_get_sta(hapd, addr); if (sta) flags = hostapd_sta_flags_to_drv(sta->flags); return hostapd_drv_hapd_send_eapol(hapd, addr, data, data_len, encrypt, flags); } static int hostapd_wpa_auth_for_each_sta( void *ctx, int (*cb)(struct wpa_state_machine *sm, void *ctx), void *cb_ctx) { struct hostapd_data *hapd = ctx; struct sta_info *sta; for (sta = hapd->sta_list; sta; sta = sta->next) { if (sta->wpa_sm && cb(sta->wpa_sm, cb_ctx)) return 1; } return 0; } struct wpa_auth_iface_iter_data { int (*cb)(struct wpa_authenticator *sm, void *ctx); void *cb_ctx; }; static int wpa_auth_iface_iter(struct hostapd_iface *iface, void *ctx) { struct wpa_auth_iface_iter_data *data = ctx; size_t i; for (i = 0; i < iface->num_bss; i++) { if (iface->bss[i]->wpa_auth && data->cb(iface->bss[i]->wpa_auth, data->cb_ctx)) return 1; } return 0; } static int hostapd_wpa_auth_for_each_auth( void *ctx, int (*cb)(struct wpa_authenticator *sm, void *ctx), void *cb_ctx) { struct hostapd_data *hapd = ctx; struct wpa_auth_iface_iter_data data; if (hapd->iface->interfaces == NULL || hapd->iface->interfaces->for_each_interface == NULL) return -1; data.cb = cb; data.cb_ctx = cb_ctx; return hapd->iface->interfaces->for_each_interface( hapd->iface->interfaces, wpa_auth_iface_iter, &data); } #ifdef CONFIG_IEEE80211R struct wpa_auth_ft_iface_iter_data { struct hostapd_data *src_hapd; const u8 *dst; const u8 *data; size_t data_len; }; static int hostapd_wpa_auth_ft_iter(struct hostapd_iface *iface, void *ctx) { struct wpa_auth_ft_iface_iter_data *idata = ctx; struct hostapd_data *hapd; size_t j; for (j = 0; j < iface->num_bss; j++) { hapd = iface->bss[j]; if (hapd == idata->src_hapd) continue; if (os_memcmp(hapd->own_addr, idata->dst, ETH_ALEN) == 0) { wpa_printf(MSG_DEBUG, "FT: Send RRB data directly to " "locally managed BSS " MACSTR "@%s -> " MACSTR "@%s", MAC2STR(idata->src_hapd->own_addr), idata->src_hapd->conf->iface, MAC2STR(hapd->own_addr), hapd->conf->iface); wpa_ft_rrb_rx(hapd->wpa_auth, idata->src_hapd->own_addr, idata->data, idata->data_len); return 1; } } return 0; } #endif /* CONFIG_IEEE80211R */ static int hostapd_wpa_auth_send_ether(void *ctx, const u8 *dst, u16 proto, const u8 *data, size_t data_len) { struct hostapd_data *hapd = ctx; struct l2_ethhdr *buf; int ret; #ifdef CONFIG_IEEE80211R if (proto == ETH_P_RRB && hapd->iface->interfaces && hapd->iface->interfaces->for_each_interface) { int res; struct wpa_auth_ft_iface_iter_data idata; idata.src_hapd = hapd; idata.dst = dst; idata.data = data; idata.data_len = data_len; res = hapd->iface->interfaces->for_each_interface( hapd->iface->interfaces, hostapd_wpa_auth_ft_iter, &idata); if (res == 1) return data_len; } #endif /* CONFIG_IEEE80211R */ if (hapd->driver && hapd->driver->send_ether) return hapd->driver->send_ether(hapd->drv_priv, dst, hapd->own_addr, proto, data, data_len); if (hapd->l2 == NULL) return -1; buf = os_malloc(sizeof(*buf) + data_len); if (buf == NULL) return -1; os_memcpy(buf->h_dest, dst, ETH_ALEN); os_memcpy(buf->h_source, hapd->own_addr, ETH_ALEN); buf->h_proto = host_to_be16(proto); os_memcpy(buf + 1, data, data_len); ret = l2_packet_send(hapd->l2, dst, proto, (u8 *) buf, sizeof(*buf) + data_len); os_free(buf); return ret; } #ifdef CONFIG_IEEE80211R static int hostapd_wpa_auth_send_ft_action(void *ctx, const u8 *dst, const u8 *data, size_t data_len) { struct hostapd_data *hapd = ctx; int res; struct ieee80211_mgmt *m; size_t mlen; struct sta_info *sta; sta = ap_get_sta(hapd, dst); if (sta == NULL || sta->wpa_sm == NULL) return -1; m = os_zalloc(sizeof(*m) + data_len); if (m == NULL) return -1; mlen = ((u8 *) &m->u - (u8 *) m) + data_len; m->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_ACTION); os_memcpy(m->da, dst, ETH_ALEN); os_memcpy(m->sa, hapd->own_addr, ETH_ALEN); os_memcpy(m->bssid, hapd->own_addr, ETH_ALEN); os_memcpy(&m->u, data, data_len); res = hostapd_drv_send_mlme(hapd, (u8 *) m, mlen, 0); os_free(m); return res; } static struct wpa_state_machine * hostapd_wpa_auth_add_sta(void *ctx, const u8 *sta_addr) { struct hostapd_data *hapd = ctx; struct sta_info *sta; if (hostapd_add_sta_node(hapd, sta_addr, WLAN_AUTH_FT) < 0) return NULL; sta = ap_sta_add(hapd, sta_addr); if (sta == NULL) return NULL; if (sta->wpa_sm) { sta->auth_alg = WLAN_AUTH_FT; return sta->wpa_sm; } sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, sta->addr, NULL); if (sta->wpa_sm == NULL) { ap_free_sta(hapd, sta); return NULL; } sta->auth_alg = WLAN_AUTH_FT; return sta->wpa_sm; } static void hostapd_rrb_receive(void *ctx, const u8 *src_addr, const u8 *buf, size_t len) { struct hostapd_data *hapd = ctx; struct l2_ethhdr *ethhdr; if (len < sizeof(*ethhdr)) return; ethhdr = (struct l2_ethhdr *) buf; wpa_printf(MSG_DEBUG, "FT: RRB received packet " MACSTR " -> " MACSTR, MAC2STR(ethhdr->h_source), MAC2STR(ethhdr->h_dest)); wpa_ft_rrb_rx(hapd->wpa_auth, ethhdr->h_source, buf + sizeof(*ethhdr), len - sizeof(*ethhdr)); } static int hostapd_wpa_auth_add_tspec(void *ctx, const u8 *sta_addr, u8 *tspec_ie, size_t tspec_ielen) { struct hostapd_data *hapd = ctx; return hostapd_add_tspec(hapd, sta_addr, tspec_ie, tspec_ielen); } #endif /* CONFIG_IEEE80211R */ int hostapd_setup_wpa(struct hostapd_data *hapd) { struct wpa_auth_config _conf; struct wpa_auth_callbacks cb; const u8 *wpa_ie; size_t wpa_ie_len; hostapd_wpa_auth_conf(hapd->conf, hapd->iconf, &_conf); if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_EAPOL_TX_STATUS) _conf.tx_status = 1; if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_MLME) _conf.ap_mlme = 1; os_memset(&cb, 0, sizeof(cb)); cb.ctx = hapd; cb.logger = hostapd_wpa_auth_logger; cb.disconnect = hostapd_wpa_auth_disconnect; cb.mic_failure_report = hostapd_wpa_auth_mic_failure_report; cb.set_eapol = hostapd_wpa_auth_set_eapol; cb.get_eapol = hostapd_wpa_auth_get_eapol; cb.get_psk = hostapd_wpa_auth_get_psk; cb.get_msk = hostapd_wpa_auth_get_msk; cb.set_key = hostapd_wpa_auth_set_key; cb.get_seqnum = hostapd_wpa_auth_get_seqnum; cb.send_eapol = hostapd_wpa_auth_send_eapol; cb.for_each_sta = hostapd_wpa_auth_for_each_sta; cb.for_each_auth = hostapd_wpa_auth_for_each_auth; cb.send_ether = hostapd_wpa_auth_send_ether; #ifdef CONFIG_IEEE80211R cb.send_ft_action = hostapd_wpa_auth_send_ft_action; cb.add_sta = hostapd_wpa_auth_add_sta; cb.add_tspec = hostapd_wpa_auth_add_tspec; #endif /* CONFIG_IEEE80211R */ hapd->wpa_auth = wpa_init(hapd->own_addr, &_conf, &cb); if (hapd->wpa_auth == NULL) { wpa_printf(MSG_ERROR, "WPA initialization failed."); return -1; } if (hostapd_set_privacy(hapd, 1)) { wpa_printf(MSG_ERROR, "Could not set PrivacyInvoked " "for interface %s", hapd->conf->iface); return -1; } wpa_ie = wpa_auth_get_wpa_ie(hapd->wpa_auth, &wpa_ie_len); if (hostapd_set_generic_elem(hapd, wpa_ie, wpa_ie_len)) { wpa_printf(MSG_ERROR, "Failed to configure WPA IE for " "the kernel driver."); return -1; } if (rsn_preauth_iface_init(hapd)) { wpa_printf(MSG_ERROR, "Initialization of RSN " "pre-authentication failed."); return -1; } #ifdef CONFIG_IEEE80211R if (!hostapd_drv_none(hapd)) { hapd->l2 = l2_packet_init(hapd->conf->bridge[0] ? hapd->conf->bridge : hapd->conf->iface, NULL, ETH_P_RRB, hostapd_rrb_receive, hapd, 1); if (hapd->l2 == NULL && (hapd->driver == NULL || hapd->driver->send_ether == NULL)) { wpa_printf(MSG_ERROR, "Failed to open l2_packet " "interface"); return -1; } } #endif /* CONFIG_IEEE80211R */ return 0; } void hostapd_reconfig_wpa(struct hostapd_data *hapd) { struct wpa_auth_config wpa_auth_conf; hostapd_wpa_auth_conf(hapd->conf, hapd->iconf, &wpa_auth_conf); wpa_reconfig(hapd->wpa_auth, &wpa_auth_conf); } void hostapd_deinit_wpa(struct hostapd_data *hapd) { ieee80211_tkip_countermeasures_deinit(hapd); rsn_preauth_iface_deinit(hapd); if (hapd->wpa_auth) { wpa_deinit(hapd->wpa_auth); hapd->wpa_auth = NULL; if (hostapd_set_privacy(hapd, 0)) { wpa_printf(MSG_DEBUG, "Could not disable " "PrivacyInvoked for interface %s", hapd->conf->iface); } if (hostapd_set_generic_elem(hapd, (u8 *) "", 0)) { wpa_printf(MSG_DEBUG, "Could not remove generic " "information element from interface %s", hapd->conf->iface); } } ieee802_1x_deinit(hapd); #ifdef CONFIG_IEEE80211R l2_packet_deinit(hapd->l2); hapd->l2 = NULL; #endif /* CONFIG_IEEE80211R */ } wpa-2.1/src/ap/wpa_auth_glue.h000066400000000000000000000006731227414666700163540ustar00rootroot00000000000000/* * hostapd / WPA authenticator glue code * Copyright (c) 2002-2009, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef WPA_AUTH_GLUE_H #define WPA_AUTH_GLUE_H int hostapd_setup_wpa(struct hostapd_data *hapd); void hostapd_reconfig_wpa(struct hostapd_data *hapd); void hostapd_deinit_wpa(struct hostapd_data *hapd); #endif /* WPA_AUTH_GLUE_H */ wpa-2.1/src/ap/wpa_auth_i.h000066400000000000000000000155511227414666700156510ustar00rootroot00000000000000/* * hostapd - IEEE 802.11i-2004 / WPA Authenticator: Internal definitions * Copyright (c) 2004-2007, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef WPA_AUTH_I_H #define WPA_AUTH_I_H /* max(dot11RSNAConfigGroupUpdateCount,dot11RSNAConfigPairwiseUpdateCount) */ #define RSNA_MAX_EAPOL_RETRIES 4 struct wpa_group; struct wpa_stsl_negotiation { struct wpa_stsl_negotiation *next; u8 initiator[ETH_ALEN]; u8 peer[ETH_ALEN]; }; struct wpa_state_machine { struct wpa_authenticator *wpa_auth; struct wpa_group *group; u8 addr[ETH_ALEN]; u8 p2p_dev_addr[ETH_ALEN]; enum { WPA_PTK_INITIALIZE, WPA_PTK_DISCONNECT, WPA_PTK_DISCONNECTED, WPA_PTK_AUTHENTICATION, WPA_PTK_AUTHENTICATION2, WPA_PTK_INITPMK, WPA_PTK_INITPSK, WPA_PTK_PTKSTART, WPA_PTK_PTKCALCNEGOTIATING, WPA_PTK_PTKCALCNEGOTIATING2, WPA_PTK_PTKINITNEGOTIATING, WPA_PTK_PTKINITDONE } wpa_ptk_state; enum { WPA_PTK_GROUP_IDLE = 0, WPA_PTK_GROUP_REKEYNEGOTIATING, WPA_PTK_GROUP_REKEYESTABLISHED, WPA_PTK_GROUP_KEYERROR } wpa_ptk_group_state; Boolean Init; Boolean DeauthenticationRequest; Boolean AuthenticationRequest; Boolean ReAuthenticationRequest; Boolean Disconnect; int TimeoutCtr; int GTimeoutCtr; Boolean TimeoutEvt; Boolean EAPOLKeyReceived; Boolean EAPOLKeyPairwise; Boolean EAPOLKeyRequest; Boolean MICVerified; Boolean GUpdateStationKeys; u8 ANonce[WPA_NONCE_LEN]; u8 SNonce[WPA_NONCE_LEN]; u8 PMK[PMK_LEN]; struct wpa_ptk PTK; Boolean PTK_valid; Boolean pairwise_set; int keycount; Boolean Pair; struct wpa_key_replay_counter { u8 counter[WPA_REPLAY_COUNTER_LEN]; Boolean valid; } key_replay[RSNA_MAX_EAPOL_RETRIES], prev_key_replay[RSNA_MAX_EAPOL_RETRIES]; Boolean PInitAKeys; /* WPA only, not in IEEE 802.11i */ Boolean PTKRequest; /* not in IEEE 802.11i state machine */ Boolean has_GTK; Boolean PtkGroupInit; /* init request for PTK Group state machine */ u8 *last_rx_eapol_key; /* starting from IEEE 802.1X header */ size_t last_rx_eapol_key_len; unsigned int changed:1; unsigned int in_step_loop:1; unsigned int pending_deinit:1; unsigned int started:1; unsigned int mgmt_frame_prot:1; unsigned int rx_eapol_key_secure:1; unsigned int update_snonce:1; #ifdef CONFIG_IEEE80211R unsigned int ft_completed:1; unsigned int pmk_r1_name_valid:1; #endif /* CONFIG_IEEE80211R */ unsigned int is_wnmsleep:1; u8 req_replay_counter[WPA_REPLAY_COUNTER_LEN]; int req_replay_counter_used; u8 *wpa_ie; size_t wpa_ie_len; enum { WPA_VERSION_NO_WPA = 0 /* WPA not used */, WPA_VERSION_WPA = 1 /* WPA / IEEE 802.11i/D3.0 */, WPA_VERSION_WPA2 = 2 /* WPA2 / IEEE 802.11i */ } wpa; int pairwise; /* Pairwise cipher suite, WPA_CIPHER_* */ int wpa_key_mgmt; /* the selected WPA_KEY_MGMT_* */ struct rsn_pmksa_cache_entry *pmksa; u32 dot11RSNAStatsTKIPLocalMICFailures; u32 dot11RSNAStatsTKIPRemoteMICFailures; #ifdef CONFIG_IEEE80211R u8 xxkey[PMK_LEN]; /* PSK or the second 256 bits of MSK */ size_t xxkey_len; u8 pmk_r1_name[WPA_PMK_NAME_LEN]; /* PMKR1Name derived from FT Auth * Request */ u8 r0kh_id[FT_R0KH_ID_MAX_LEN]; /* R0KH-ID from FT Auth Request */ size_t r0kh_id_len; u8 sup_pmk_r1_name[WPA_PMK_NAME_LEN]; /* PMKR1Name from EAPOL-Key * message 2/4 */ u8 *assoc_resp_ftie; #endif /* CONFIG_IEEE80211R */ int pending_1_of_4_timeout; #ifdef CONFIG_P2P u8 ip_addr[4]; #endif /* CONFIG_P2P */ }; /* per group key state machine data */ struct wpa_group { struct wpa_group *next; int vlan_id; Boolean GInit; int GKeyDoneStations; Boolean GTKReKey; int GTK_len; int GN, GM; Boolean GTKAuthenticator; u8 Counter[WPA_NONCE_LEN]; enum { WPA_GROUP_GTK_INIT = 0, WPA_GROUP_SETKEYS, WPA_GROUP_SETKEYSDONE, WPA_GROUP_FATAL_FAILURE } wpa_group_state; u8 GMK[WPA_GMK_LEN]; u8 GTK[2][WPA_GTK_MAX_LEN]; u8 GNonce[WPA_NONCE_LEN]; Boolean changed; Boolean first_sta_seen; Boolean reject_4way_hs_for_entropy; #ifdef CONFIG_IEEE80211W u8 IGTK[2][WPA_IGTK_LEN]; int GN_igtk, GM_igtk; #endif /* CONFIG_IEEE80211W */ }; struct wpa_ft_pmk_cache; /* per authenticator data */ struct wpa_authenticator { struct wpa_group *group; unsigned int dot11RSNAStatsTKIPRemoteMICFailures; u32 dot11RSNAAuthenticationSuiteSelected; u32 dot11RSNAPairwiseCipherSelected; u32 dot11RSNAGroupCipherSelected; u8 dot11RSNAPMKIDUsed[PMKID_LEN]; u32 dot11RSNAAuthenticationSuiteRequested; /* FIX: update */ u32 dot11RSNAPairwiseCipherRequested; /* FIX: update */ u32 dot11RSNAGroupCipherRequested; /* FIX: update */ unsigned int dot11RSNATKIPCounterMeasuresInvoked; unsigned int dot11RSNA4WayHandshakeFailures; struct wpa_stsl_negotiation *stsl_negotiations; struct wpa_auth_config conf; struct wpa_auth_callbacks cb; u8 *wpa_ie; size_t wpa_ie_len; u8 addr[ETH_ALEN]; struct rsn_pmksa_cache *pmksa; struct wpa_ft_pmk_cache *ft_pmk_cache; #ifdef CONFIG_P2P struct bitfield *ip_pool; #endif /* CONFIG_P2P */ }; int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len, const u8 *pmkid); void wpa_auth_logger(struct wpa_authenticator *wpa_auth, const u8 *addr, logger_level level, const char *txt); void wpa_auth_vlogger(struct wpa_authenticator *wpa_auth, const u8 *addr, logger_level level, const char *fmt, ...); void __wpa_send_eapol(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, int key_info, const u8 *key_rsc, const u8 *nonce, const u8 *kde, size_t kde_len, int keyidx, int encr, int force_version); int wpa_auth_for_each_sta(struct wpa_authenticator *wpa_auth, int (*cb)(struct wpa_state_machine *sm, void *ctx), void *cb_ctx); int wpa_auth_for_each_auth(struct wpa_authenticator *wpa_auth, int (*cb)(struct wpa_authenticator *a, void *ctx), void *cb_ctx); #ifdef CONFIG_PEERKEY int wpa_stsl_remove(struct wpa_authenticator *wpa_auth, struct wpa_stsl_negotiation *neg); void wpa_smk_error(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, struct wpa_eapol_key *key); void wpa_smk_m1(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, struct wpa_eapol_key *key); void wpa_smk_m3(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, struct wpa_eapol_key *key); #endif /* CONFIG_PEERKEY */ #ifdef CONFIG_IEEE80211R int wpa_write_mdie(struct wpa_auth_config *conf, u8 *buf, size_t len); int wpa_write_ftie(struct wpa_auth_config *conf, const u8 *r0kh_id, size_t r0kh_id_len, const u8 *anonce, const u8 *snonce, u8 *buf, size_t len, const u8 *subelem, size_t subelem_len); int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk, struct wpa_ptk *ptk, size_t ptk_len); struct wpa_ft_pmk_cache * wpa_ft_pmk_cache_init(void); void wpa_ft_pmk_cache_deinit(struct wpa_ft_pmk_cache *cache); void wpa_ft_install_ptk(struct wpa_state_machine *sm); #endif /* CONFIG_IEEE80211R */ #endif /* WPA_AUTH_I_H */ wpa-2.1/src/ap/wpa_auth_ie.c000066400000000000000000000511531227414666700160070ustar00rootroot00000000000000/* * hostapd - WPA/RSN IE and KDE definitions * Copyright (c) 2004-2008, 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 "common/ieee802_11_defs.h" #include "eapol_auth/eapol_auth_sm.h" #include "ap_config.h" #include "ieee802_11.h" #include "wpa_auth.h" #include "pmksa_cache_auth.h" #include "wpa_auth_ie.h" #include "wpa_auth_i.h" #ifdef CONFIG_RSN_TESTING int rsn_testing = 0; #endif /* CONFIG_RSN_TESTING */ static int wpa_write_wpa_ie(struct wpa_auth_config *conf, u8 *buf, size_t len) { struct wpa_ie_hdr *hdr; int num_suites; u8 *pos, *count; u32 suite; hdr = (struct wpa_ie_hdr *) buf; hdr->elem_id = WLAN_EID_VENDOR_SPECIFIC; RSN_SELECTOR_PUT(hdr->oui, WPA_OUI_TYPE); WPA_PUT_LE16(hdr->version, WPA_VERSION); pos = (u8 *) (hdr + 1); suite = wpa_cipher_to_suite(WPA_PROTO_WPA, conf->wpa_group); if (suite == 0) { wpa_printf(MSG_DEBUG, "Invalid group cipher (%d).", conf->wpa_group); return -1; } RSN_SELECTOR_PUT(pos, suite); pos += WPA_SELECTOR_LEN; count = pos; pos += 2; num_suites = wpa_cipher_put_suites(pos, conf->wpa_pairwise); if (num_suites == 0) { wpa_printf(MSG_DEBUG, "Invalid pairwise cipher (%d).", conf->wpa_pairwise); return -1; } pos += num_suites * WPA_SELECTOR_LEN; WPA_PUT_LE16(count, num_suites); num_suites = 0; count = pos; pos += 2; if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) { RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_UNSPEC_802_1X); pos += WPA_SELECTOR_LEN; num_suites++; } if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) { RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X); pos += WPA_SELECTOR_LEN; num_suites++; } if (num_suites == 0) { wpa_printf(MSG_DEBUG, "Invalid key management type (%d).", conf->wpa_key_mgmt); return -1; } WPA_PUT_LE16(count, num_suites); /* WPA Capabilities; use defaults, so no need to include it */ hdr->len = (pos - buf) - 2; return pos - buf; } int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len, const u8 *pmkid) { struct rsn_ie_hdr *hdr; int num_suites, res; u8 *pos, *count; u16 capab; u32 suite; hdr = (struct rsn_ie_hdr *) buf; hdr->elem_id = WLAN_EID_RSN; WPA_PUT_LE16(hdr->version, RSN_VERSION); pos = (u8 *) (hdr + 1); suite = wpa_cipher_to_suite(WPA_PROTO_RSN, conf->wpa_group); if (suite == 0) { wpa_printf(MSG_DEBUG, "Invalid group cipher (%d).", conf->wpa_group); return -1; } RSN_SELECTOR_PUT(pos, suite); pos += RSN_SELECTOR_LEN; num_suites = 0; count = pos; pos += 2; #ifdef CONFIG_RSN_TESTING if (rsn_testing) { RSN_SELECTOR_PUT(pos, RSN_SELECTOR(0x12, 0x34, 0x56, 1)); pos += RSN_SELECTOR_LEN; num_suites++; } #endif /* CONFIG_RSN_TESTING */ res = rsn_cipher_put_suites(pos, conf->rsn_pairwise); num_suites += res; pos += res * RSN_SELECTOR_LEN; #ifdef CONFIG_RSN_TESTING if (rsn_testing) { RSN_SELECTOR_PUT(pos, RSN_SELECTOR(0x12, 0x34, 0x56, 2)); pos += RSN_SELECTOR_LEN; num_suites++; } #endif /* CONFIG_RSN_TESTING */ if (num_suites == 0) { wpa_printf(MSG_DEBUG, "Invalid pairwise cipher (%d).", conf->rsn_pairwise); return -1; } WPA_PUT_LE16(count, num_suites); num_suites = 0; count = pos; pos += 2; #ifdef CONFIG_RSN_TESTING if (rsn_testing) { RSN_SELECTOR_PUT(pos, RSN_SELECTOR(0x12, 0x34, 0x56, 1)); pos += RSN_SELECTOR_LEN; num_suites++; } #endif /* CONFIG_RSN_TESTING */ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) { RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_UNSPEC_802_1X); pos += RSN_SELECTOR_LEN; num_suites++; } if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) { RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X); pos += RSN_SELECTOR_LEN; num_suites++; } #ifdef CONFIG_IEEE80211R if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) { RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X); pos += RSN_SELECTOR_LEN; num_suites++; } if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_PSK) { RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_PSK); pos += RSN_SELECTOR_LEN; num_suites++; } #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_IEEE80211W if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) { RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SHA256); pos += RSN_SELECTOR_LEN; num_suites++; } if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK_SHA256) { RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PSK_SHA256); pos += RSN_SELECTOR_LEN; num_suites++; } #endif /* CONFIG_IEEE80211W */ #ifdef CONFIG_SAE if (conf->wpa_key_mgmt & WPA_KEY_MGMT_SAE) { RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_SAE); pos += RSN_SELECTOR_LEN; num_suites++; } if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_SAE) { RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_SAE); pos += RSN_SELECTOR_LEN; num_suites++; } #endif /* CONFIG_SAE */ #ifdef CONFIG_RSN_TESTING if (rsn_testing) { RSN_SELECTOR_PUT(pos, RSN_SELECTOR(0x12, 0x34, 0x56, 2)); pos += RSN_SELECTOR_LEN; num_suites++; } #endif /* CONFIG_RSN_TESTING */ if (num_suites == 0) { wpa_printf(MSG_DEBUG, "Invalid key management type (%d).", conf->wpa_key_mgmt); return -1; } WPA_PUT_LE16(count, num_suites); /* RSN Capabilities */ capab = 0; if (conf->rsn_preauth) capab |= WPA_CAPABILITY_PREAUTH; if (conf->peerkey) capab |= WPA_CAPABILITY_PEERKEY_ENABLED; if (conf->wmm_enabled) { /* 4 PTKSA replay counters when using WMM */ capab |= (RSN_NUM_REPLAY_COUNTERS_16 << 2); } #ifdef CONFIG_IEEE80211W if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) { capab |= WPA_CAPABILITY_MFPC; if (conf->ieee80211w == MGMT_FRAME_PROTECTION_REQUIRED) capab |= WPA_CAPABILITY_MFPR; } #endif /* CONFIG_IEEE80211W */ #ifdef CONFIG_RSN_TESTING if (rsn_testing) capab |= BIT(8) | BIT(14) | BIT(15); #endif /* CONFIG_RSN_TESTING */ WPA_PUT_LE16(pos, capab); pos += 2; if (pmkid) { if (pos + 2 + PMKID_LEN > buf + len) return -1; /* PMKID Count */ WPA_PUT_LE16(pos, 1); pos += 2; os_memcpy(pos, pmkid, PMKID_LEN); pos += PMKID_LEN; } #ifdef CONFIG_IEEE80211W if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) { if (pos + 2 + 4 > buf + len) return -1; if (pmkid == NULL) { /* PMKID Count */ WPA_PUT_LE16(pos, 0); pos += 2; } /* Management Group Cipher Suite */ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_AES_128_CMAC); pos += RSN_SELECTOR_LEN; } #endif /* CONFIG_IEEE80211W */ #ifdef CONFIG_RSN_TESTING if (rsn_testing) { /* * Fill in any defined fields and add extra data to the end of * the element. */ int pmkid_count_set = pmkid != NULL; if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) pmkid_count_set = 1; /* PMKID Count */ WPA_PUT_LE16(pos, 0); pos += 2; if (conf->ieee80211w == NO_MGMT_FRAME_PROTECTION) { /* Management Group Cipher Suite */ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_AES_128_CMAC); pos += RSN_SELECTOR_LEN; } os_memset(pos, 0x12, 17); pos += 17; } #endif /* CONFIG_RSN_TESTING */ hdr->len = (pos - buf) - 2; return pos - buf; } int wpa_auth_gen_wpa_ie(struct wpa_authenticator *wpa_auth) { u8 *pos, buf[128]; int res; pos = buf; if (wpa_auth->conf.wpa & WPA_PROTO_RSN) { res = wpa_write_rsn_ie(&wpa_auth->conf, pos, buf + sizeof(buf) - pos, NULL); if (res < 0) return res; pos += res; } #ifdef CONFIG_IEEE80211R if (wpa_key_mgmt_ft(wpa_auth->conf.wpa_key_mgmt)) { res = wpa_write_mdie(&wpa_auth->conf, pos, buf + sizeof(buf) - pos); if (res < 0) return res; pos += res; } #endif /* CONFIG_IEEE80211R */ if (wpa_auth->conf.wpa & WPA_PROTO_WPA) { res = wpa_write_wpa_ie(&wpa_auth->conf, pos, buf + sizeof(buf) - pos); if (res < 0) return res; pos += res; } os_free(wpa_auth->wpa_ie); wpa_auth->wpa_ie = os_malloc(pos - buf); if (wpa_auth->wpa_ie == NULL) return -1; os_memcpy(wpa_auth->wpa_ie, buf, pos - buf); wpa_auth->wpa_ie_len = pos - buf; return 0; } u8 * wpa_add_kde(u8 *pos, u32 kde, const u8 *data, size_t data_len, const u8 *data2, size_t data2_len) { *pos++ = WLAN_EID_VENDOR_SPECIFIC; *pos++ = RSN_SELECTOR_LEN + data_len + data2_len; RSN_SELECTOR_PUT(pos, kde); pos += RSN_SELECTOR_LEN; os_memcpy(pos, data, data_len); pos += data_len; if (data2) { os_memcpy(pos, data2, data2_len); pos += data2_len; } return pos; } struct wpa_auth_okc_iter_data { struct rsn_pmksa_cache_entry *pmksa; const u8 *aa; const u8 *spa; const u8 *pmkid; }; static int wpa_auth_okc_iter(struct wpa_authenticator *a, void *ctx) { struct wpa_auth_okc_iter_data *data = ctx; data->pmksa = pmksa_cache_get_okc(a->pmksa, data->aa, data->spa, data->pmkid); if (data->pmksa) return 1; return 0; } int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, const u8 *wpa_ie, size_t wpa_ie_len, const u8 *mdie, size_t mdie_len) { struct wpa_ie_data data; int ciphers, key_mgmt, res, version; u32 selector; size_t i; const u8 *pmkid = NULL; if (wpa_auth == NULL || sm == NULL) return WPA_NOT_ENABLED; if (wpa_ie == NULL || wpa_ie_len < 1) return WPA_INVALID_IE; if (wpa_ie[0] == WLAN_EID_RSN) version = WPA_PROTO_RSN; else version = WPA_PROTO_WPA; if (!(wpa_auth->conf.wpa & version)) { wpa_printf(MSG_DEBUG, "Invalid WPA proto (%d) from " MACSTR, version, MAC2STR(sm->addr)); return WPA_INVALID_PROTO; } if (version == WPA_PROTO_RSN) { res = wpa_parse_wpa_ie_rsn(wpa_ie, wpa_ie_len, &data); selector = RSN_AUTH_KEY_MGMT_UNSPEC_802_1X; if (0) { } #ifdef CONFIG_IEEE80211R else if (data.key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) selector = RSN_AUTH_KEY_MGMT_FT_802_1X; else if (data.key_mgmt & WPA_KEY_MGMT_FT_PSK) selector = RSN_AUTH_KEY_MGMT_FT_PSK; #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_IEEE80211W else if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) selector = RSN_AUTH_KEY_MGMT_802_1X_SHA256; else if (data.key_mgmt & WPA_KEY_MGMT_PSK_SHA256) selector = RSN_AUTH_KEY_MGMT_PSK_SHA256; #endif /* CONFIG_IEEE80211W */ #ifdef CONFIG_SAE else if (data.key_mgmt & WPA_KEY_MGMT_SAE) selector = RSN_AUTH_KEY_MGMT_SAE; else if (data.key_mgmt & WPA_KEY_MGMT_FT_SAE) selector = RSN_AUTH_KEY_MGMT_FT_SAE; #endif /* CONFIG_SAE */ else if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X) selector = RSN_AUTH_KEY_MGMT_UNSPEC_802_1X; else if (data.key_mgmt & WPA_KEY_MGMT_PSK) selector = RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X; wpa_auth->dot11RSNAAuthenticationSuiteSelected = selector; selector = wpa_cipher_to_suite(WPA_PROTO_RSN, data.pairwise_cipher); if (!selector) selector = RSN_CIPHER_SUITE_CCMP; wpa_auth->dot11RSNAPairwiseCipherSelected = selector; selector = wpa_cipher_to_suite(WPA_PROTO_RSN, data.group_cipher); if (!selector) selector = RSN_CIPHER_SUITE_CCMP; wpa_auth->dot11RSNAGroupCipherSelected = selector; } else { res = wpa_parse_wpa_ie_wpa(wpa_ie, wpa_ie_len, &data); selector = WPA_AUTH_KEY_MGMT_UNSPEC_802_1X; if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X) selector = WPA_AUTH_KEY_MGMT_UNSPEC_802_1X; else if (data.key_mgmt & WPA_KEY_MGMT_PSK) selector = WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X; wpa_auth->dot11RSNAAuthenticationSuiteSelected = selector; selector = wpa_cipher_to_suite(WPA_PROTO_WPA, data.pairwise_cipher); if (!selector) selector = RSN_CIPHER_SUITE_TKIP; wpa_auth->dot11RSNAPairwiseCipherSelected = selector; selector = wpa_cipher_to_suite(WPA_PROTO_WPA, data.group_cipher); if (!selector) selector = WPA_CIPHER_SUITE_TKIP; wpa_auth->dot11RSNAGroupCipherSelected = selector; } if (res) { wpa_printf(MSG_DEBUG, "Failed to parse WPA/RSN IE from " MACSTR " (res=%d)", MAC2STR(sm->addr), res); wpa_hexdump(MSG_DEBUG, "WPA/RSN IE", wpa_ie, wpa_ie_len); return WPA_INVALID_IE; } if (data.group_cipher != wpa_auth->conf.wpa_group) { wpa_printf(MSG_DEBUG, "Invalid WPA group cipher (0x%x) from " MACSTR, data.group_cipher, MAC2STR(sm->addr)); return WPA_INVALID_GROUP; } key_mgmt = data.key_mgmt & wpa_auth->conf.wpa_key_mgmt; if (!key_mgmt) { wpa_printf(MSG_DEBUG, "Invalid WPA key mgmt (0x%x) from " MACSTR, data.key_mgmt, MAC2STR(sm->addr)); return WPA_INVALID_AKMP; } if (0) { } #ifdef CONFIG_IEEE80211R else if (key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X; else if (key_mgmt & WPA_KEY_MGMT_FT_PSK) sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_PSK; #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_IEEE80211W else if (key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X_SHA256; else if (key_mgmt & WPA_KEY_MGMT_PSK_SHA256) sm->wpa_key_mgmt = WPA_KEY_MGMT_PSK_SHA256; #endif /* CONFIG_IEEE80211W */ #ifdef CONFIG_SAE else if (key_mgmt & WPA_KEY_MGMT_SAE) sm->wpa_key_mgmt = WPA_KEY_MGMT_SAE; else if (key_mgmt & WPA_KEY_MGMT_FT_SAE) sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_SAE; #endif /* CONFIG_SAE */ else if (key_mgmt & WPA_KEY_MGMT_IEEE8021X) sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X; else sm->wpa_key_mgmt = WPA_KEY_MGMT_PSK; if (version == WPA_PROTO_RSN) ciphers = data.pairwise_cipher & wpa_auth->conf.rsn_pairwise; else ciphers = data.pairwise_cipher & wpa_auth->conf.wpa_pairwise; if (!ciphers) { wpa_printf(MSG_DEBUG, "Invalid %s pairwise cipher (0x%x) " "from " MACSTR, version == WPA_PROTO_RSN ? "RSN" : "WPA", data.pairwise_cipher, MAC2STR(sm->addr)); return WPA_INVALID_PAIRWISE; } #ifdef CONFIG_IEEE80211W if (wpa_auth->conf.ieee80211w == MGMT_FRAME_PROTECTION_REQUIRED) { if (!(data.capabilities & WPA_CAPABILITY_MFPC)) { wpa_printf(MSG_DEBUG, "Management frame protection " "required, but client did not enable it"); return WPA_MGMT_FRAME_PROTECTION_VIOLATION; } if (ciphers & WPA_CIPHER_TKIP) { wpa_printf(MSG_DEBUG, "Management frame protection " "cannot use TKIP"); return WPA_MGMT_FRAME_PROTECTION_VIOLATION; } if (data.mgmt_group_cipher != WPA_CIPHER_AES_128_CMAC) { wpa_printf(MSG_DEBUG, "Unsupported management group " "cipher %d", data.mgmt_group_cipher); return WPA_INVALID_MGMT_GROUP_CIPHER; } } if (wpa_auth->conf.ieee80211w == NO_MGMT_FRAME_PROTECTION || !(data.capabilities & WPA_CAPABILITY_MFPC)) sm->mgmt_frame_prot = 0; else sm->mgmt_frame_prot = 1; #endif /* CONFIG_IEEE80211W */ #ifdef CONFIG_IEEE80211R if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) { if (mdie == NULL || mdie_len < MOBILITY_DOMAIN_ID_LEN + 1) { wpa_printf(MSG_DEBUG, "RSN: Trying to use FT, but " "MDIE not included"); return WPA_INVALID_MDIE; } if (os_memcmp(mdie, wpa_auth->conf.mobility_domain, MOBILITY_DOMAIN_ID_LEN) != 0) { wpa_hexdump(MSG_DEBUG, "RSN: Attempted to use unknown " "MDIE", mdie, MOBILITY_DOMAIN_ID_LEN); return WPA_INVALID_MDIE; } } #endif /* CONFIG_IEEE80211R */ sm->pairwise = wpa_pick_pairwise_cipher(ciphers, 0); if (sm->pairwise < 0) return WPA_INVALID_PAIRWISE; /* TODO: clear WPA/WPA2 state if STA changes from one to another */ if (wpa_ie[0] == WLAN_EID_RSN) sm->wpa = WPA_VERSION_WPA2; else sm->wpa = WPA_VERSION_WPA; sm->pmksa = NULL; for (i = 0; i < data.num_pmkid; i++) { wpa_hexdump(MSG_DEBUG, "RSN IE: STA PMKID", &data.pmkid[i * PMKID_LEN], PMKID_LEN); sm->pmksa = pmksa_cache_auth_get(wpa_auth->pmksa, sm->addr, &data.pmkid[i * PMKID_LEN]); if (sm->pmksa) { pmkid = sm->pmksa->pmkid; break; } } for (i = 0; sm->pmksa == NULL && wpa_auth->conf.okc && i < data.num_pmkid; i++) { struct wpa_auth_okc_iter_data idata; idata.pmksa = NULL; idata.aa = wpa_auth->addr; idata.spa = sm->addr; idata.pmkid = &data.pmkid[i * PMKID_LEN]; wpa_auth_for_each_auth(wpa_auth, wpa_auth_okc_iter, &idata); if (idata.pmksa) { wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG, "OKC match for PMKID"); sm->pmksa = pmksa_cache_add_okc(wpa_auth->pmksa, idata.pmksa, wpa_auth->addr, idata.pmkid); pmkid = idata.pmkid; break; } } if (sm->pmksa) { wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG, "PMKID found from PMKSA cache " "eap_type=%d vlan_id=%d", sm->pmksa->eap_type_authsrv, sm->pmksa->vlan_id); os_memcpy(wpa_auth->dot11RSNAPMKIDUsed, pmkid, PMKID_LEN); } if (sm->wpa_ie == NULL || sm->wpa_ie_len < wpa_ie_len) { os_free(sm->wpa_ie); sm->wpa_ie = os_malloc(wpa_ie_len); if (sm->wpa_ie == NULL) return WPA_ALLOC_FAIL; } os_memcpy(sm->wpa_ie, wpa_ie, wpa_ie_len); sm->wpa_ie_len = wpa_ie_len; return WPA_IE_OK; } /** * wpa_parse_generic - Parse EAPOL-Key Key Data Generic IEs * @pos: Pointer to the IE header * @end: Pointer to the end of the Key Data buffer * @ie: Pointer to parsed IE data * Returns: 0 on success, 1 if end mark is found, -1 on failure */ static int wpa_parse_generic(const u8 *pos, const u8 *end, struct wpa_eapol_ie_parse *ie) { if (pos[1] == 0) return 1; if (pos[1] >= 6 && RSN_SELECTOR_GET(pos + 2) == WPA_OUI_TYPE && pos[2 + WPA_SELECTOR_LEN] == 1 && pos[2 + WPA_SELECTOR_LEN + 1] == 0) { ie->wpa_ie = pos; ie->wpa_ie_len = pos[1] + 2; return 0; } if (pos + 1 + RSN_SELECTOR_LEN < end && pos[1] >= RSN_SELECTOR_LEN + PMKID_LEN && RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_PMKID) { ie->pmkid = pos + 2 + RSN_SELECTOR_LEN; return 0; } if (pos[1] > RSN_SELECTOR_LEN + 2 && RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_GROUPKEY) { ie->gtk = pos + 2 + RSN_SELECTOR_LEN; ie->gtk_len = pos[1] - RSN_SELECTOR_LEN; return 0; } if (pos[1] > RSN_SELECTOR_LEN + 2 && RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_MAC_ADDR) { ie->mac_addr = pos + 2 + RSN_SELECTOR_LEN; ie->mac_addr_len = pos[1] - RSN_SELECTOR_LEN; return 0; } #ifdef CONFIG_PEERKEY if (pos[1] > RSN_SELECTOR_LEN + 2 && RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_SMK) { ie->smk = pos + 2 + RSN_SELECTOR_LEN; ie->smk_len = pos[1] - RSN_SELECTOR_LEN; return 0; } if (pos[1] > RSN_SELECTOR_LEN + 2 && RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_NONCE) { ie->nonce = pos + 2 + RSN_SELECTOR_LEN; ie->nonce_len = pos[1] - RSN_SELECTOR_LEN; return 0; } if (pos[1] > RSN_SELECTOR_LEN + 2 && RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_LIFETIME) { ie->lifetime = pos + 2 + RSN_SELECTOR_LEN; ie->lifetime_len = pos[1] - RSN_SELECTOR_LEN; return 0; } if (pos[1] > RSN_SELECTOR_LEN + 2 && RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_ERROR) { ie->error = pos + 2 + RSN_SELECTOR_LEN; ie->error_len = pos[1] - RSN_SELECTOR_LEN; return 0; } #endif /* CONFIG_PEERKEY */ #ifdef CONFIG_IEEE80211W if (pos[1] > RSN_SELECTOR_LEN + 2 && RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_IGTK) { ie->igtk = pos + 2 + RSN_SELECTOR_LEN; ie->igtk_len = pos[1] - RSN_SELECTOR_LEN; return 0; } #endif /* CONFIG_IEEE80211W */ #ifdef CONFIG_P2P if (pos[1] >= RSN_SELECTOR_LEN + 1 && RSN_SELECTOR_GET(pos + 2) == WFA_KEY_DATA_IP_ADDR_REQ) { ie->ip_addr_req = pos + 2 + RSN_SELECTOR_LEN; wpa_hexdump(MSG_DEBUG, "WPA: IP Address Request in EAPOL-Key", ie->ip_addr_req, pos[1] - RSN_SELECTOR_LEN); return 0; } if (pos[1] >= RSN_SELECTOR_LEN + 3 * 4 && RSN_SELECTOR_GET(pos + 2) == WFA_KEY_DATA_IP_ADDR_ALLOC) { ie->ip_addr_alloc = pos + 2 + RSN_SELECTOR_LEN; wpa_hexdump(MSG_DEBUG, "WPA: IP Address Allocation in EAPOL-Key", ie->ip_addr_alloc, pos[1] - RSN_SELECTOR_LEN); return 0; } #endif /* CONFIG_P2P */ return 0; } /** * wpa_parse_kde_ies - Parse EAPOL-Key Key Data IEs * @buf: Pointer to the Key Data buffer * @len: Key Data Length * @ie: Pointer to parsed IE data * Returns: 0 on success, -1 on failure */ int wpa_parse_kde_ies(const u8 *buf, size_t len, struct wpa_eapol_ie_parse *ie) { const u8 *pos, *end; int ret = 0; os_memset(ie, 0, sizeof(*ie)); for (pos = buf, end = pos + len; pos + 1 < end; pos += 2 + pos[1]) { if (pos[0] == 0xdd && ((pos == buf + len - 1) || pos[1] == 0)) { /* Ignore padding */ break; } if (pos + 2 + pos[1] > end) { wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key Key Data " "underflow (ie=%d len=%d pos=%d)", pos[0], pos[1], (int) (pos - buf)); wpa_hexdump_key(MSG_DEBUG, "WPA: Key Data", buf, len); ret = -1; break; } if (*pos == WLAN_EID_RSN) { ie->rsn_ie = pos; ie->rsn_ie_len = pos[1] + 2; #ifdef CONFIG_IEEE80211R } else if (*pos == WLAN_EID_MOBILITY_DOMAIN) { ie->mdie = pos; ie->mdie_len = pos[1] + 2; } else if (*pos == WLAN_EID_FAST_BSS_TRANSITION) { ie->ftie = pos; ie->ftie_len = pos[1] + 2; #endif /* CONFIG_IEEE80211R */ } else if (*pos == WLAN_EID_VENDOR_SPECIFIC) { ret = wpa_parse_generic(pos, end, ie); if (ret < 0) break; if (ret > 0) { ret = 0; break; } } else { wpa_hexdump(MSG_DEBUG, "WPA: Unrecognized EAPOL-Key " "Key Data IE", pos, 2 + pos[1]); } } return ret; } int wpa_auth_uses_mfp(struct wpa_state_machine *sm) { return sm ? sm->mgmt_frame_prot : 0; } wpa-2.1/src/ap/wpa_auth_ie.h000066400000000000000000000023461227414666700160140ustar00rootroot00000000000000/* * hostapd - WPA/RSN IE and KDE definitions * Copyright (c) 2004-2007, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef WPA_AUTH_IE_H #define WPA_AUTH_IE_H struct wpa_eapol_ie_parse { const u8 *wpa_ie; size_t wpa_ie_len; const u8 *rsn_ie; size_t rsn_ie_len; const u8 *pmkid; const u8 *gtk; size_t gtk_len; const u8 *mac_addr; size_t mac_addr_len; #ifdef CONFIG_PEERKEY const u8 *smk; size_t smk_len; const u8 *nonce; size_t nonce_len; const u8 *lifetime; size_t lifetime_len; const u8 *error; size_t error_len; #endif /* CONFIG_PEERKEY */ #ifdef CONFIG_IEEE80211W const u8 *igtk; size_t igtk_len; #endif /* CONFIG_IEEE80211W */ #ifdef CONFIG_IEEE80211R const u8 *mdie; size_t mdie_len; const u8 *ftie; size_t ftie_len; #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_P2P const u8 *ip_addr_req; const u8 *ip_addr_alloc; #endif /* CONFIG_P2P */ }; int wpa_parse_kde_ies(const u8 *buf, size_t len, struct wpa_eapol_ie_parse *ie); u8 * wpa_add_kde(u8 *pos, u32 kde, const u8 *data, size_t data_len, const u8 *data2, size_t data2_len); int wpa_auth_gen_wpa_ie(struct wpa_authenticator *wpa_auth); #endif /* WPA_AUTH_IE_H */ wpa-2.1/src/ap/wps_hostapd.c000066400000000000000000001510221227414666700160510ustar00rootroot00000000000000/* * hostapd / WPS integration * Copyright (c) 2008-2012, 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/eloop.h" #include "utils/uuid.h" #include "common/wpa_ctrl.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" #include "eapol_auth/eapol_auth_sm.h" #include "eapol_auth/eapol_auth_sm_i.h" #include "wps/wps.h" #include "wps/wps_defs.h" #include "wps/wps_dev_attr.h" #include "wps/wps_attr_parse.h" #include "hostapd.h" #include "ap_config.h" #include "ap_drv_ops.h" #include "beacon.h" #include "sta_info.h" #include "wps_hostapd.h" #ifdef CONFIG_WPS_UPNP #include "wps/wps_upnp.h" static int hostapd_wps_upnp_init(struct hostapd_data *hapd, struct wps_context *wps); static void hostapd_wps_upnp_deinit(struct hostapd_data *hapd); #endif /* CONFIG_WPS_UPNP */ static int hostapd_wps_probe_req_rx(void *ctx, const u8 *addr, const u8 *da, const u8 *bssid, const u8 *ie, size_t ie_len, int ssi_signal); static void hostapd_wps_ap_pin_timeout(void *eloop_data, void *user_ctx); struct wps_for_each_data { int (*func)(struct hostapd_data *h, void *ctx); void *ctx; struct hostapd_data *calling_hapd; }; static int wps_for_each(struct hostapd_iface *iface, void *ctx) { struct wps_for_each_data *data = ctx; size_t j; if (iface == NULL) return 0; for (j = 0; j < iface->num_bss; j++) { struct hostapd_data *hapd = iface->bss[j]; int ret; if (hapd != data->calling_hapd && (hapd->conf->wps_independent || data->calling_hapd->conf->wps_independent)) continue; ret = data->func(hapd, data->ctx); if (ret) return ret; } return 0; } static int hostapd_wps_for_each(struct hostapd_data *hapd, int (*func)(struct hostapd_data *h, void *ctx), void *ctx) { struct hostapd_iface *iface = hapd->iface; struct wps_for_each_data data; data.func = func; data.ctx = ctx; data.calling_hapd = hapd; if (iface->interfaces == NULL || iface->interfaces->for_each_interface == NULL) return wps_for_each(iface, &data); return iface->interfaces->for_each_interface(iface->interfaces, wps_for_each, &data); } static int hostapd_wps_new_psk_cb(void *ctx, const u8 *mac_addr, const u8 *p2p_dev_addr, const u8 *psk, size_t psk_len) { struct hostapd_data *hapd = ctx; struct hostapd_wpa_psk *p; struct hostapd_ssid *ssid = &hapd->conf->ssid; if (is_zero_ether_addr(p2p_dev_addr)) { wpa_printf(MSG_DEBUG, "Received new WPA/WPA2-PSK from WPS for STA " MACSTR, MAC2STR(mac_addr)); } else { wpa_printf(MSG_DEBUG, "Received new WPA/WPA2-PSK from WPS for STA " MACSTR " P2P Device Addr " MACSTR, MAC2STR(mac_addr), MAC2STR(p2p_dev_addr)); } wpa_hexdump_key(MSG_DEBUG, "Per-device PSK", psk, psk_len); if (psk_len != PMK_LEN) { wpa_printf(MSG_DEBUG, "Unexpected PSK length %lu", (unsigned long) psk_len); return -1; } /* Add the new PSK to runtime PSK list */ p = os_zalloc(sizeof(*p)); if (p == NULL) return -1; os_memcpy(p->addr, mac_addr, ETH_ALEN); os_memcpy(p->p2p_dev_addr, p2p_dev_addr, ETH_ALEN); os_memcpy(p->psk, psk, PMK_LEN); if (hapd->new_psk_cb) { hapd->new_psk_cb(hapd->new_psk_cb_ctx, mac_addr, p2p_dev_addr, psk, psk_len); } p->next = ssid->wpa_psk; ssid->wpa_psk = p; if (ssid->wpa_psk_file) { FILE *f; char hex[PMK_LEN * 2 + 1]; /* Add the new PSK to PSK list file */ f = fopen(ssid->wpa_psk_file, "a"); if (f == NULL) { wpa_printf(MSG_DEBUG, "Failed to add the PSK to " "'%s'", ssid->wpa_psk_file); return -1; } wpa_snprintf_hex(hex, sizeof(hex), psk, psk_len); fprintf(f, MACSTR " %s\n", MAC2STR(mac_addr), hex); fclose(f); } return 0; } static int hostapd_wps_set_ie_cb(void *ctx, struct wpabuf *beacon_ie, struct wpabuf *probe_resp_ie) { struct hostapd_data *hapd = ctx; wpabuf_free(hapd->wps_beacon_ie); hapd->wps_beacon_ie = beacon_ie; wpabuf_free(hapd->wps_probe_resp_ie); hapd->wps_probe_resp_ie = probe_resp_ie; if (hapd->beacon_set_done) ieee802_11_set_beacon(hapd); return hostapd_set_ap_wps_ie(hapd); } static void hostapd_wps_pin_needed_cb(void *ctx, const u8 *uuid_e, const struct wps_device_data *dev) { struct hostapd_data *hapd = ctx; char uuid[40], txt[400]; int len; char devtype[WPS_DEV_TYPE_BUFSIZE]; if (uuid_bin2str(uuid_e, uuid, sizeof(uuid))) return; wpa_printf(MSG_DEBUG, "WPS: PIN needed for E-UUID %s", uuid); len = os_snprintf(txt, sizeof(txt), WPS_EVENT_PIN_NEEDED "%s " MACSTR " [%s|%s|%s|%s|%s|%s]", uuid, MAC2STR(dev->mac_addr), dev->device_name, dev->manufacturer, dev->model_name, dev->model_number, dev->serial_number, wps_dev_type_bin2str(dev->pri_dev_type, devtype, sizeof(devtype))); if (len > 0 && len < (int) sizeof(txt)) wpa_msg(hapd->msg_ctx, MSG_INFO, "%s", txt); if (hapd->conf->wps_pin_requests) { FILE *f; struct os_time t; f = fopen(hapd->conf->wps_pin_requests, "a"); if (f == NULL) return; os_get_time(&t); fprintf(f, "%ld\t%s\t" MACSTR "\t%s\t%s\t%s\t%s\t%s" "\t%s\n", t.sec, uuid, MAC2STR(dev->mac_addr), dev->device_name, dev->manufacturer, dev->model_name, dev->model_number, dev->serial_number, wps_dev_type_bin2str(dev->pri_dev_type, devtype, sizeof(devtype))); fclose(f); } } struct wps_stop_reg_data { struct hostapd_data *current_hapd; const u8 *uuid_e; const u8 *dev_pw; size_t dev_pw_len; }; static int wps_stop_registrar(struct hostapd_data *hapd, void *ctx) { struct wps_stop_reg_data *data = ctx; if (hapd != data->current_hapd && hapd->wps != NULL) wps_registrar_complete(hapd->wps->registrar, data->uuid_e, data->dev_pw, data->dev_pw_len); return 0; } static void hostapd_wps_reg_success_cb(void *ctx, const u8 *mac_addr, const u8 *uuid_e, const u8 *dev_pw, size_t dev_pw_len) { struct hostapd_data *hapd = ctx; char uuid[40]; struct wps_stop_reg_data data; if (uuid_bin2str(uuid_e, uuid, sizeof(uuid))) return; wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_REG_SUCCESS MACSTR " %s", MAC2STR(mac_addr), uuid); if (hapd->wps_reg_success_cb) hapd->wps_reg_success_cb(hapd->wps_reg_success_cb_ctx, mac_addr, uuid_e); data.current_hapd = hapd; data.uuid_e = uuid_e; data.dev_pw = dev_pw; data.dev_pw_len = dev_pw_len; hostapd_wps_for_each(hapd, wps_stop_registrar, &data); } static void hostapd_wps_enrollee_seen_cb(void *ctx, const u8 *addr, const u8 *uuid_e, const u8 *pri_dev_type, u16 config_methods, u16 dev_password_id, u8 request_type, const char *dev_name) { struct hostapd_data *hapd = ctx; char uuid[40]; char devtype[WPS_DEV_TYPE_BUFSIZE]; if (uuid_bin2str(uuid_e, uuid, sizeof(uuid))) return; if (dev_name == NULL) dev_name = ""; wpa_msg_ctrl(hapd->msg_ctx, MSG_INFO, WPS_EVENT_ENROLLEE_SEEN MACSTR " %s %s 0x%x %u %u [%s]", MAC2STR(addr), uuid, wps_dev_type_bin2str(pri_dev_type, devtype, sizeof(devtype)), config_methods, dev_password_id, request_type, dev_name); } static int str_starts(const char *str, const char *start) { return os_strncmp(str, start, os_strlen(start)) == 0; } static void wps_reload_config(void *eloop_data, void *user_ctx) { struct hostapd_iface *iface = eloop_data; wpa_printf(MSG_DEBUG, "WPS: Reload configuration data"); if (iface->interfaces == NULL || iface->interfaces->reload_config(iface) < 0) { wpa_printf(MSG_WARNING, "WPS: Failed to reload the updated " "configuration"); } } void hostapd_wps_eap_completed(struct hostapd_data *hapd) { /* * Reduce race condition of the station trying to reconnect immediately * after AP reconfiguration through WPS by rescheduling the reload * timeout to happen after EAP completion rather than the originally * scheduled 100 ms after new configuration became known. */ if (eloop_deplete_timeout(0, 0, wps_reload_config, hapd->iface, NULL) == 1) wpa_printf(MSG_DEBUG, "WPS: Reschedule immediate configuration reload"); } static void hapd_new_ap_event(struct hostapd_data *hapd, const u8 *attr, size_t attr_len) { size_t blen = attr_len * 2 + 1; char *buf = os_malloc(blen); if (buf) { wpa_snprintf_hex(buf, blen, attr, attr_len); wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_NEW_AP_SETTINGS "%s", buf); os_free(buf); } } static int hapd_wps_reconfig_in_memory(struct hostapd_data *hapd, const struct wps_credential *cred) { struct hostapd_bss_config *bss = hapd->conf; wpa_printf(MSG_DEBUG, "WPS: Updating in-memory configuration"); bss->wps_state = 2; if (cred->ssid_len <= HOSTAPD_MAX_SSID_LEN) { os_memcpy(bss->ssid.ssid, cred->ssid, cred->ssid_len); bss->ssid.ssid_len = cred->ssid_len; bss->ssid.ssid_set = 1; } if ((cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK)) && (cred->auth_type & (WPS_AUTH_WPA | WPS_AUTH_WPAPSK))) bss->wpa = 3; else if (cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK)) bss->wpa = 2; else if (cred->auth_type & (WPS_AUTH_WPA | WPS_AUTH_WPAPSK)) bss->wpa = 1; else bss->wpa = 0; if (bss->wpa) { if (cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA)) bss->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X; if (cred->auth_type & (WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK)) bss->wpa_key_mgmt = WPA_KEY_MGMT_PSK; bss->wpa_pairwise = 0; if (cred->encr_type & WPS_ENCR_AES) bss->wpa_pairwise |= WPA_CIPHER_CCMP; if (cred->encr_type & WPS_ENCR_TKIP) bss->wpa_pairwise |= WPA_CIPHER_TKIP; bss->rsn_pairwise = bss->wpa_pairwise; bss->wpa_group = wpa_select_ap_group_cipher(bss->wpa, bss->wpa_pairwise, bss->rsn_pairwise); if (cred->key_len >= 8 && cred->key_len < 64) { os_free(bss->ssid.wpa_passphrase); bss->ssid.wpa_passphrase = os_zalloc(cred->key_len + 1); if (bss->ssid.wpa_passphrase) os_memcpy(bss->ssid.wpa_passphrase, cred->key, cred->key_len); os_free(bss->ssid.wpa_psk); bss->ssid.wpa_psk = NULL; } else if (cred->key_len == 64) { os_free(bss->ssid.wpa_psk); bss->ssid.wpa_psk = os_zalloc(sizeof(struct hostapd_wpa_psk)); if (bss->ssid.wpa_psk && hexstr2bin((const char *) cred->key, bss->ssid.wpa_psk->psk, PMK_LEN) == 0) { bss->ssid.wpa_psk->group = 1; os_free(bss->ssid.wpa_passphrase); bss->ssid.wpa_passphrase = NULL; } } bss->auth_algs = 1; } else { if ((cred->auth_type & WPS_AUTH_OPEN) && (cred->auth_type & WPS_AUTH_SHARED)) bss->auth_algs = 3; else if (cred->auth_type & WPS_AUTH_SHARED) bss->auth_algs = 2; else bss->auth_algs = 1; if (cred->encr_type & WPS_ENCR_WEP && cred->key_idx > 0 && cred->key_idx <= 4) { struct hostapd_wep_keys *wep = &bss->ssid.wep; int idx = cred->key_idx; if (idx) idx--; wep->idx = idx; if (cred->key_len == 10 || cred->key_len == 26) { os_free(wep->key[idx]); wep->key[idx] = os_malloc(cred->key_len / 2); if (wep->key[idx] == NULL || hexstr2bin((const char *) cred->key, wep->key[idx], cred->key_len / 2)) return -1; wep->len[idx] = cred->key_len / 2; } else { os_free(wep->key[idx]); wep->key[idx] = os_malloc(cred->key_len); if (wep->key[idx] == NULL) return -1; os_memcpy(wep->key[idx], cred->key, cred->key_len); wep->len[idx] = cred->key_len; } wep->keys_set = 1; } } /* Schedule configuration reload after short period of time to allow * EAP-WSC to be finished. */ eloop_register_timeout(0, 100000, wps_reload_config, hapd->iface, NULL); return 0; } static int hapd_wps_cred_cb(struct hostapd_data *hapd, void *ctx) { const struct wps_credential *cred = ctx; FILE *oconf, *nconf; size_t len, i; char *tmp_fname; char buf[1024]; int multi_bss; int wpa; if (hapd->wps == NULL) return 0; wpa_hexdump_key(MSG_DEBUG, "WPS: Received Credential attribute", cred->cred_attr, cred->cred_attr_len); wpa_printf(MSG_DEBUG, "WPS: Received new AP Settings"); wpa_hexdump_ascii(MSG_DEBUG, "WPS: SSID", cred->ssid, cred->ssid_len); wpa_printf(MSG_DEBUG, "WPS: Authentication Type 0x%x", cred->auth_type); wpa_printf(MSG_DEBUG, "WPS: Encryption Type 0x%x", cred->encr_type); wpa_printf(MSG_DEBUG, "WPS: Network Key Index %d", cred->key_idx); wpa_hexdump_key(MSG_DEBUG, "WPS: Network Key", cred->key, cred->key_len); wpa_printf(MSG_DEBUG, "WPS: MAC Address " MACSTR, MAC2STR(cred->mac_addr)); if ((hapd->conf->wps_cred_processing == 1 || hapd->conf->wps_cred_processing == 2) && cred->cred_attr) { hapd_new_ap_event(hapd, cred->cred_attr, cred->cred_attr_len); } else if (hapd->conf->wps_cred_processing == 1 || hapd->conf->wps_cred_processing == 2) { struct wpabuf *attr; attr = wpabuf_alloc(200); if (attr && wps_build_credential_wrap(attr, cred) == 0) hapd_new_ap_event(hapd, wpabuf_head_u8(attr), wpabuf_len(attr)); wpabuf_free(attr); } else wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_NEW_AP_SETTINGS); if (hapd->conf->wps_cred_processing == 1) return 0; os_memcpy(hapd->wps->ssid, cred->ssid, cred->ssid_len); hapd->wps->ssid_len = cred->ssid_len; hapd->wps->encr_types = cred->encr_type; hapd->wps->auth_types = cred->auth_type; if (cred->key_len == 0) { os_free(hapd->wps->network_key); hapd->wps->network_key = NULL; hapd->wps->network_key_len = 0; } else { if (hapd->wps->network_key == NULL || hapd->wps->network_key_len < cred->key_len) { hapd->wps->network_key_len = 0; os_free(hapd->wps->network_key); hapd->wps->network_key = os_malloc(cred->key_len); if (hapd->wps->network_key == NULL) return -1; } hapd->wps->network_key_len = cred->key_len; os_memcpy(hapd->wps->network_key, cred->key, cred->key_len); } hapd->wps->wps_state = WPS_STATE_CONFIGURED; if (hapd->iface->config_fname == NULL) return hapd_wps_reconfig_in_memory(hapd, cred); len = os_strlen(hapd->iface->config_fname) + 5; tmp_fname = os_malloc(len); if (tmp_fname == NULL) return -1; os_snprintf(tmp_fname, len, "%s-new", hapd->iface->config_fname); oconf = fopen(hapd->iface->config_fname, "r"); if (oconf == NULL) { wpa_printf(MSG_WARNING, "WPS: Could not open current " "configuration file"); os_free(tmp_fname); return -1; } nconf = fopen(tmp_fname, "w"); if (nconf == NULL) { wpa_printf(MSG_WARNING, "WPS: Could not write updated " "configuration file"); os_free(tmp_fname); fclose(oconf); return -1; } fprintf(nconf, "# WPS configuration - START\n"); fprintf(nconf, "wps_state=2\n"); if (is_hex(cred->ssid, cred->ssid_len)) { fprintf(nconf, "ssid2="); for (i = 0; i < cred->ssid_len; i++) fprintf(nconf, "%02x", cred->ssid[i]); fprintf(nconf, "\n"); } else { fprintf(nconf, "ssid="); for (i = 0; i < cred->ssid_len; i++) fputc(cred->ssid[i], nconf); fprintf(nconf, "\n"); } if ((cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK)) && (cred->auth_type & (WPS_AUTH_WPA | WPS_AUTH_WPAPSK))) wpa = 3; else if (cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK)) wpa = 2; else if (cred->auth_type & (WPS_AUTH_WPA | WPS_AUTH_WPAPSK)) wpa = 1; else wpa = 0; if (wpa) { char *prefix; fprintf(nconf, "wpa=%d\n", wpa); fprintf(nconf, "wpa_key_mgmt="); prefix = ""; if (cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA)) { fprintf(nconf, "WPA-EAP"); prefix = " "; } if (cred->auth_type & (WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK)) fprintf(nconf, "%sWPA-PSK", prefix); fprintf(nconf, "\n"); fprintf(nconf, "wpa_pairwise="); prefix = ""; if (cred->encr_type & WPS_ENCR_AES) { fprintf(nconf, "CCMP"); prefix = " "; } if (cred->encr_type & WPS_ENCR_TKIP) { fprintf(nconf, "%sTKIP", prefix); } fprintf(nconf, "\n"); if (cred->key_len >= 8 && cred->key_len < 64) { fprintf(nconf, "wpa_passphrase="); for (i = 0; i < cred->key_len; i++) fputc(cred->key[i], nconf); fprintf(nconf, "\n"); } else if (cred->key_len == 64) { fprintf(nconf, "wpa_psk="); for (i = 0; i < cred->key_len; i++) fputc(cred->key[i], nconf); fprintf(nconf, "\n"); } else { wpa_printf(MSG_WARNING, "WPS: Invalid key length %lu " "for WPA/WPA2", (unsigned long) cred->key_len); } fprintf(nconf, "auth_algs=1\n"); } else { if ((cred->auth_type & WPS_AUTH_OPEN) && (cred->auth_type & WPS_AUTH_SHARED)) fprintf(nconf, "auth_algs=3\n"); else if (cred->auth_type & WPS_AUTH_SHARED) fprintf(nconf, "auth_algs=2\n"); else fprintf(nconf, "auth_algs=1\n"); if (cred->encr_type & WPS_ENCR_WEP && cred->key_idx <= 4) { int key_idx = cred->key_idx; if (key_idx) key_idx--; fprintf(nconf, "wep_default_key=%d\n", key_idx); fprintf(nconf, "wep_key%d=", key_idx); if (cred->key_len == 10 || cred->key_len == 26) { /* WEP key as a hex string */ for (i = 0; i < cred->key_len; i++) fputc(cred->key[i], nconf); } else { /* Raw WEP key; convert to hex */ for (i = 0; i < cred->key_len; i++) fprintf(nconf, "%02x", cred->key[i]); } fprintf(nconf, "\n"); } } fprintf(nconf, "# WPS configuration - END\n"); multi_bss = 0; while (fgets(buf, sizeof(buf), oconf)) { if (os_strncmp(buf, "bss=", 4) == 0) multi_bss = 1; if (!multi_bss && (str_starts(buf, "ssid=") || str_starts(buf, "ssid2=") || str_starts(buf, "auth_algs=") || str_starts(buf, "wep_default_key=") || str_starts(buf, "wep_key") || str_starts(buf, "wps_state=") || str_starts(buf, "wpa=") || str_starts(buf, "wpa_psk=") || str_starts(buf, "wpa_pairwise=") || str_starts(buf, "rsn_pairwise=") || str_starts(buf, "wpa_key_mgmt=") || str_starts(buf, "wpa_passphrase="))) { fprintf(nconf, "#WPS# %s", buf); } else fprintf(nconf, "%s", buf); } fclose(nconf); fclose(oconf); if (rename(tmp_fname, hapd->iface->config_fname) < 0) { wpa_printf(MSG_WARNING, "WPS: Failed to rename the updated " "configuration file: %s", strerror(errno)); os_free(tmp_fname); return -1; } os_free(tmp_fname); /* Schedule configuration reload after short period of time to allow * EAP-WSC to be finished. */ eloop_register_timeout(0, 100000, wps_reload_config, hapd->iface, NULL); wpa_printf(MSG_DEBUG, "WPS: AP configuration updated"); return 0; } static int hostapd_wps_cred_cb(void *ctx, const struct wps_credential *cred) { struct hostapd_data *hapd = ctx; return hostapd_wps_for_each(hapd, hapd_wps_cred_cb, (void *) cred); } static void hostapd_wps_reenable_ap_pin(void *eloop_data, void *user_ctx) { struct hostapd_data *hapd = eloop_data; if (hapd->conf->ap_setup_locked) return; if (hapd->ap_pin_failures_consecutive >= 10) return; wpa_printf(MSG_DEBUG, "WPS: Re-enable AP PIN"); wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_AP_SETUP_UNLOCKED); hapd->wps->ap_setup_locked = 0; wps_registrar_update_ie(hapd->wps->registrar); } static int wps_pwd_auth_fail(struct hostapd_data *hapd, void *ctx) { struct wps_event_pwd_auth_fail *data = ctx; if (!data->enrollee || hapd->conf->ap_pin == NULL || hapd->wps == NULL) return 0; /* * Registrar failed to prove its knowledge of the AP PIN. Lock AP setup * for some time if this happens multiple times to slow down brute * force attacks. */ hapd->ap_pin_failures++; hapd->ap_pin_failures_consecutive++; wpa_printf(MSG_DEBUG, "WPS: AP PIN authentication failure number %u " "(%u consecutive)", hapd->ap_pin_failures, hapd->ap_pin_failures_consecutive); if (hapd->ap_pin_failures < 3) return 0; wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_AP_SETUP_LOCKED); hapd->wps->ap_setup_locked = 1; wps_registrar_update_ie(hapd->wps->registrar); if (!hapd->conf->ap_setup_locked && hapd->ap_pin_failures_consecutive >= 10) { /* * In indefinite lockdown - disable automatic AP PIN * reenablement. */ eloop_cancel_timeout(hostapd_wps_reenable_ap_pin, hapd, NULL); wpa_printf(MSG_DEBUG, "WPS: AP PIN disabled indefinitely"); } else if (!hapd->conf->ap_setup_locked) { if (hapd->ap_pin_lockout_time == 0) hapd->ap_pin_lockout_time = 60; else if (hapd->ap_pin_lockout_time < 365 * 24 * 60 * 60 && (hapd->ap_pin_failures % 3) == 0) hapd->ap_pin_lockout_time *= 2; wpa_printf(MSG_DEBUG, "WPS: Disable AP PIN for %u seconds", hapd->ap_pin_lockout_time); eloop_cancel_timeout(hostapd_wps_reenable_ap_pin, hapd, NULL); eloop_register_timeout(hapd->ap_pin_lockout_time, 0, hostapd_wps_reenable_ap_pin, hapd, NULL); } return 0; } static void hostapd_pwd_auth_fail(struct hostapd_data *hapd, struct wps_event_pwd_auth_fail *data) { /* Update WPS Status - Authentication Failure */ wpa_printf(MSG_DEBUG, "WPS: Authentication failure update"); hapd->wps_stats.status = WPS_STATUS_FAILURE; hapd->wps_stats.failure_reason = WPS_EI_AUTH_FAILURE; os_memcpy(hapd->wps_stats.peer_addr, data->peer_macaddr, ETH_ALEN); hostapd_wps_for_each(hapd, wps_pwd_auth_fail, data); } static int wps_ap_pin_success(struct hostapd_data *hapd, void *ctx) { if (hapd->conf->ap_pin == NULL || hapd->wps == NULL) return 0; if (hapd->ap_pin_failures_consecutive == 0) return 0; wpa_printf(MSG_DEBUG, "WPS: Clear consecutive AP PIN failure counter " "- total validation failures %u (%u consecutive)", hapd->ap_pin_failures, hapd->ap_pin_failures_consecutive); hapd->ap_pin_failures_consecutive = 0; return 0; } static void hostapd_wps_ap_pin_success(struct hostapd_data *hapd) { hostapd_wps_for_each(hapd, wps_ap_pin_success, NULL); } static void hostapd_wps_event_pbc_overlap(struct hostapd_data *hapd) { /* Update WPS Status - PBC Overlap */ hapd->wps_stats.pbc_status = WPS_PBC_STATUS_OVERLAP; } static void hostapd_wps_event_pbc_timeout(struct hostapd_data *hapd) { /* Update WPS PBC Status:PBC Timeout */ hapd->wps_stats.pbc_status = WPS_PBC_STATUS_TIMEOUT; } static void hostapd_wps_event_pbc_active(struct hostapd_data *hapd) { /* Update WPS PBC status - Active */ hapd->wps_stats.pbc_status = WPS_PBC_STATUS_ACTIVE; } static void hostapd_wps_event_pbc_disable(struct hostapd_data *hapd) { /* Update WPS PBC status - Active */ hapd->wps_stats.pbc_status = WPS_PBC_STATUS_DISABLE; } static void hostapd_wps_event_success(struct hostapd_data *hapd, struct wps_event_success *success) { /* Update WPS status - Success */ hapd->wps_stats.pbc_status = WPS_PBC_STATUS_DISABLE; hapd->wps_stats.status = WPS_STATUS_SUCCESS; os_memcpy(hapd->wps_stats.peer_addr, success->peer_macaddr, ETH_ALEN); } static void hostapd_wps_event_fail(struct hostapd_data *hapd, struct wps_event_fail *fail) { /* Update WPS status - Failure */ hapd->wps_stats.status = WPS_STATUS_FAILURE; os_memcpy(hapd->wps_stats.peer_addr, fail->peer_macaddr, ETH_ALEN); hapd->wps_stats.failure_reason = fail->error_indication; if (fail->error_indication > 0 && fail->error_indication < NUM_WPS_EI_VALUES) { wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_FAIL "msg=%d config_error=%d reason=%d (%s)", fail->msg, fail->config_error, fail->error_indication, wps_ei_str(fail->error_indication)); } else { wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_FAIL "msg=%d config_error=%d", fail->msg, fail->config_error); } } static void hostapd_wps_event_cb(void *ctx, enum wps_event event, union wps_event_data *data) { struct hostapd_data *hapd = ctx; switch (event) { case WPS_EV_M2D: wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_M2D); break; case WPS_EV_FAIL: hostapd_wps_event_fail(hapd, &data->fail); break; case WPS_EV_SUCCESS: hostapd_wps_event_success(hapd, &data->success); wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_SUCCESS); break; case WPS_EV_PWD_AUTH_FAIL: hostapd_pwd_auth_fail(hapd, &data->pwd_auth_fail); break; case WPS_EV_PBC_OVERLAP: hostapd_wps_event_pbc_overlap(hapd); wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_OVERLAP); break; case WPS_EV_PBC_TIMEOUT: hostapd_wps_event_pbc_timeout(hapd); wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_TIMEOUT); break; case WPS_EV_PBC_ACTIVE: hostapd_wps_event_pbc_active(hapd); wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_ACTIVE); break; case WPS_EV_PBC_DISABLE: hostapd_wps_event_pbc_disable(hapd); wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_DISABLE); break; case WPS_EV_ER_AP_ADD: break; case WPS_EV_ER_AP_REMOVE: break; case WPS_EV_ER_ENROLLEE_ADD: break; case WPS_EV_ER_ENROLLEE_REMOVE: break; case WPS_EV_ER_AP_SETTINGS: break; case WPS_EV_ER_SET_SELECTED_REGISTRAR: break; case WPS_EV_AP_PIN_SUCCESS: hostapd_wps_ap_pin_success(hapd); break; } if (hapd->wps_event_cb) hapd->wps_event_cb(hapd->wps_event_cb_ctx, event, data); } static int hostapd_wps_rf_band_cb(void *ctx) { struct hostapd_data *hapd = ctx; return hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211A ? WPS_RF_50GHZ : WPS_RF_24GHZ; /* FIX: dualband AP */ } static void hostapd_wps_clear_ies(struct hostapd_data *hapd) { wpabuf_free(hapd->wps_beacon_ie); hapd->wps_beacon_ie = NULL; wpabuf_free(hapd->wps_probe_resp_ie); hapd->wps_probe_resp_ie = NULL; hostapd_set_ap_wps_ie(hapd); } static int get_uuid_cb(struct hostapd_iface *iface, void *ctx) { const u8 **uuid = ctx; size_t j; if (iface == NULL) return 0; for (j = 0; j < iface->num_bss; j++) { struct hostapd_data *hapd = iface->bss[j]; if (hapd->wps && !hapd->conf->wps_independent && !is_nil_uuid(hapd->wps->uuid)) { *uuid = hapd->wps->uuid; return 1; } } return 0; } static const u8 * get_own_uuid(struct hostapd_iface *iface) { const u8 *uuid; if (iface->interfaces == NULL || iface->interfaces->for_each_interface == NULL) return NULL; uuid = NULL; iface->interfaces->for_each_interface(iface->interfaces, get_uuid_cb, &uuid); return uuid; } static int count_interface_cb(struct hostapd_iface *iface, void *ctx) { int *count= ctx; (*count)++; return 0; } static int interface_count(struct hostapd_iface *iface) { int count = 0; if (iface->interfaces == NULL || iface->interfaces->for_each_interface == NULL) return 0; iface->interfaces->for_each_interface(iface->interfaces, count_interface_cb, &count); return count; } static int hostapd_wps_set_vendor_ext(struct hostapd_data *hapd, struct wps_context *wps) { int i; for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++) { wpabuf_free(wps->dev.vendor_ext[i]); wps->dev.vendor_ext[i] = NULL; if (hapd->conf->wps_vendor_ext[i] == NULL) continue; wps->dev.vendor_ext[i] = wpabuf_dup(hapd->conf->wps_vendor_ext[i]); if (wps->dev.vendor_ext[i] == NULL) { while (--i >= 0) wpabuf_free(wps->dev.vendor_ext[i]); return -1; } } return 0; } int hostapd_init_wps(struct hostapd_data *hapd, struct hostapd_bss_config *conf) { struct wps_context *wps; struct wps_registrar_config cfg; if (conf->wps_state == 0) { hostapd_wps_clear_ies(hapd); return 0; } wps = os_zalloc(sizeof(*wps)); if (wps == NULL) return -1; wps->cred_cb = hostapd_wps_cred_cb; wps->event_cb = hostapd_wps_event_cb; wps->rf_band_cb = hostapd_wps_rf_band_cb; wps->cb_ctx = hapd; os_memset(&cfg, 0, sizeof(cfg)); wps->wps_state = hapd->conf->wps_state; wps->ap_setup_locked = hapd->conf->ap_setup_locked; if (is_nil_uuid(hapd->conf->uuid)) { const u8 *uuid; uuid = get_own_uuid(hapd->iface); if (uuid && !conf->wps_independent) { os_memcpy(wps->uuid, uuid, UUID_LEN); wpa_hexdump(MSG_DEBUG, "WPS: Clone UUID from another " "interface", wps->uuid, UUID_LEN); } else { uuid_gen_mac_addr(hapd->own_addr, wps->uuid); wpa_hexdump(MSG_DEBUG, "WPS: UUID based on MAC " "address", wps->uuid, UUID_LEN); } } else { os_memcpy(wps->uuid, hapd->conf->uuid, UUID_LEN); wpa_hexdump(MSG_DEBUG, "WPS: Use configured UUID", wps->uuid, UUID_LEN); } wps->ssid_len = hapd->conf->ssid.ssid_len; os_memcpy(wps->ssid, hapd->conf->ssid.ssid, wps->ssid_len); wps->ap = 1; os_memcpy(wps->dev.mac_addr, hapd->own_addr, ETH_ALEN); wps->dev.device_name = hapd->conf->device_name ? os_strdup(hapd->conf->device_name) : NULL; wps->dev.manufacturer = hapd->conf->manufacturer ? os_strdup(hapd->conf->manufacturer) : NULL; wps->dev.model_name = hapd->conf->model_name ? os_strdup(hapd->conf->model_name) : NULL; wps->dev.model_number = hapd->conf->model_number ? os_strdup(hapd->conf->model_number) : NULL; wps->dev.serial_number = hapd->conf->serial_number ? os_strdup(hapd->conf->serial_number) : NULL; wps->config_methods = wps_config_methods_str2bin(hapd->conf->config_methods); #ifdef CONFIG_WPS2 if ((wps->config_methods & (WPS_CONFIG_DISPLAY | WPS_CONFIG_VIRT_DISPLAY | WPS_CONFIG_PHY_DISPLAY)) == WPS_CONFIG_DISPLAY) { wpa_printf(MSG_INFO, "WPS: Converting display to " "virtual_display for WPS 2.0 compliance"); wps->config_methods |= WPS_CONFIG_VIRT_DISPLAY; } if ((wps->config_methods & (WPS_CONFIG_PUSHBUTTON | WPS_CONFIG_VIRT_PUSHBUTTON | WPS_CONFIG_PHY_PUSHBUTTON)) == WPS_CONFIG_PUSHBUTTON) { wpa_printf(MSG_INFO, "WPS: Converting push_button to " "virtual_push_button for WPS 2.0 compliance"); wps->config_methods |= WPS_CONFIG_VIRT_PUSHBUTTON; } #endif /* CONFIG_WPS2 */ os_memcpy(wps->dev.pri_dev_type, hapd->conf->device_type, WPS_DEV_TYPE_LEN); if (hostapd_wps_set_vendor_ext(hapd, wps) < 0) { os_free(wps); return -1; } wps->dev.os_version = WPA_GET_BE32(hapd->conf->os_version); if (conf->wps_rf_bands) { wps->dev.rf_bands = conf->wps_rf_bands; } else { wps->dev.rf_bands = hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211A ? WPS_RF_50GHZ : WPS_RF_24GHZ; /* FIX: dualband AP */ } if (conf->wpa & WPA_PROTO_RSN) { if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) wps->auth_types |= WPS_AUTH_WPA2PSK; if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) wps->auth_types |= WPS_AUTH_WPA2; if (conf->rsn_pairwise & WPA_CIPHER_CCMP) wps->encr_types |= WPS_ENCR_AES; if (conf->rsn_pairwise & WPA_CIPHER_TKIP) wps->encr_types |= WPS_ENCR_TKIP; } if (conf->wpa & WPA_PROTO_WPA) { if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) wps->auth_types |= WPS_AUTH_WPAPSK; if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) wps->auth_types |= WPS_AUTH_WPA; if (conf->wpa_pairwise & WPA_CIPHER_CCMP) wps->encr_types |= WPS_ENCR_AES; if (conf->wpa_pairwise & WPA_CIPHER_TKIP) wps->encr_types |= WPS_ENCR_TKIP; } if (conf->ssid.security_policy == SECURITY_PLAINTEXT) { wps->encr_types |= WPS_ENCR_NONE; wps->auth_types |= WPS_AUTH_OPEN; } else if (conf->ssid.security_policy == SECURITY_STATIC_WEP) { wps->encr_types |= WPS_ENCR_WEP; if (conf->auth_algs & WPA_AUTH_ALG_OPEN) wps->auth_types |= WPS_AUTH_OPEN; if (conf->auth_algs & WPA_AUTH_ALG_SHARED) wps->auth_types |= WPS_AUTH_SHARED; } else if (conf->ssid.security_policy == SECURITY_IEEE_802_1X) { wps->auth_types |= WPS_AUTH_OPEN; if (conf->default_wep_key_len) wps->encr_types |= WPS_ENCR_WEP; else wps->encr_types |= WPS_ENCR_NONE; } if (conf->ssid.wpa_psk_file) { /* Use per-device PSKs */ } else if (conf->ssid.wpa_passphrase) { wps->network_key = (u8 *) os_strdup(conf->ssid.wpa_passphrase); wps->network_key_len = os_strlen(conf->ssid.wpa_passphrase); } else if (conf->ssid.wpa_psk) { wps->network_key = os_malloc(2 * PMK_LEN + 1); if (wps->network_key == NULL) { os_free(wps); return -1; } wpa_snprintf_hex((char *) wps->network_key, 2 * PMK_LEN + 1, conf->ssid.wpa_psk->psk, PMK_LEN); wps->network_key_len = 2 * PMK_LEN; } else if (conf->ssid.wep.keys_set && conf->ssid.wep.key[0]) { wps->network_key = os_malloc(conf->ssid.wep.len[0]); if (wps->network_key == NULL) { os_free(wps); return -1; } os_memcpy(wps->network_key, conf->ssid.wep.key[0], conf->ssid.wep.len[0]); wps->network_key_len = conf->ssid.wep.len[0]; } if (conf->ssid.wpa_psk) { os_memcpy(wps->psk, conf->ssid.wpa_psk->psk, PMK_LEN); wps->psk_set = 1; } if (conf->wps_state == WPS_STATE_NOT_CONFIGURED) { /* Override parameters to enable security by default */ wps->auth_types = WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK; wps->encr_types = WPS_ENCR_AES | WPS_ENCR_TKIP; } wps->ap_settings = conf->ap_settings; wps->ap_settings_len = conf->ap_settings_len; cfg.new_psk_cb = hostapd_wps_new_psk_cb; cfg.set_ie_cb = hostapd_wps_set_ie_cb; cfg.pin_needed_cb = hostapd_wps_pin_needed_cb; cfg.reg_success_cb = hostapd_wps_reg_success_cb; cfg.enrollee_seen_cb = hostapd_wps_enrollee_seen_cb; cfg.cb_ctx = hapd; cfg.skip_cred_build = conf->skip_cred_build; cfg.extra_cred = conf->extra_cred; cfg.extra_cred_len = conf->extra_cred_len; cfg.disable_auto_conf = (hapd->conf->wps_cred_processing == 1) && conf->skip_cred_build; if (conf->ssid.security_policy == SECURITY_STATIC_WEP) cfg.static_wep_only = 1; cfg.dualband = interface_count(hapd->iface) > 1; if ((wps->dev.rf_bands & (WPS_RF_50GHZ | WPS_RF_24GHZ)) == (WPS_RF_50GHZ | WPS_RF_24GHZ)) cfg.dualband = 1; if (cfg.dualband) wpa_printf(MSG_DEBUG, "WPS: Dualband AP"); cfg.force_per_enrollee_psk = conf->force_per_enrollee_psk; wps->registrar = wps_registrar_init(wps, &cfg); if (wps->registrar == NULL) { wpa_printf(MSG_ERROR, "Failed to initialize WPS Registrar"); os_free(wps->network_key); os_free(wps); return -1; } #ifdef CONFIG_WPS_UPNP wps->friendly_name = hapd->conf->friendly_name; wps->manufacturer_url = hapd->conf->manufacturer_url; wps->model_description = hapd->conf->model_description; wps->model_url = hapd->conf->model_url; wps->upc = hapd->conf->upc; #endif /* CONFIG_WPS_UPNP */ hostapd_register_probereq_cb(hapd, hostapd_wps_probe_req_rx, hapd); hapd->wps = wps; return 0; } int hostapd_init_wps_complete(struct hostapd_data *hapd) { struct wps_context *wps = hapd->wps; if (wps == NULL) return 0; #ifdef CONFIG_WPS_UPNP if (hostapd_wps_upnp_init(hapd, wps) < 0) { wpa_printf(MSG_ERROR, "Failed to initialize WPS UPnP"); wps_registrar_deinit(wps->registrar); os_free(wps->network_key); os_free(wps); hapd->wps = NULL; return -1; } #endif /* CONFIG_WPS_UPNP */ return 0; } static void hostapd_wps_nfc_clear(struct wps_context *wps) { #ifdef CONFIG_WPS_NFC wpa_printf(MSG_DEBUG, "WPS: Clear NFC Tag context %p", wps); wps->ap_nfc_dev_pw_id = 0; wpabuf_free(wps->ap_nfc_dh_pubkey); wps->ap_nfc_dh_pubkey = NULL; wpabuf_free(wps->ap_nfc_dh_privkey); wps->ap_nfc_dh_privkey = NULL; wpabuf_free(wps->ap_nfc_dev_pw); wps->ap_nfc_dev_pw = NULL; #endif /* CONFIG_WPS_NFC */ } void hostapd_deinit_wps(struct hostapd_data *hapd) { eloop_cancel_timeout(hostapd_wps_reenable_ap_pin, hapd, NULL); eloop_cancel_timeout(hostapd_wps_ap_pin_timeout, hapd, NULL); eloop_cancel_timeout(wps_reload_config, hapd->iface, NULL); if (hapd->wps == NULL) return; #ifdef CONFIG_WPS_UPNP hostapd_wps_upnp_deinit(hapd); #endif /* CONFIG_WPS_UPNP */ wps_registrar_deinit(hapd->wps->registrar); os_free(hapd->wps->network_key); wps_device_data_free(&hapd->wps->dev); wpabuf_free(hapd->wps->dh_pubkey); wpabuf_free(hapd->wps->dh_privkey); wps_free_pending_msgs(hapd->wps->upnp_msgs); hostapd_wps_nfc_clear(hapd->wps); os_free(hapd->wps); hapd->wps = NULL; hostapd_wps_clear_ies(hapd); } void hostapd_update_wps(struct hostapd_data *hapd) { if (hapd->wps == NULL) return; #ifdef CONFIG_WPS_UPNP hapd->wps->friendly_name = hapd->conf->friendly_name; hapd->wps->manufacturer_url = hapd->conf->manufacturer_url; hapd->wps->model_description = hapd->conf->model_description; hapd->wps->model_url = hapd->conf->model_url; hapd->wps->upc = hapd->conf->upc; #endif /* CONFIG_WPS_UPNP */ hostapd_wps_set_vendor_ext(hapd, hapd->wps); if (hapd->conf->wps_state) wps_registrar_update_ie(hapd->wps->registrar); else hostapd_deinit_wps(hapd); } struct wps_add_pin_data { const u8 *addr; const u8 *uuid; const u8 *pin; size_t pin_len; int timeout; int added; }; static int wps_add_pin(struct hostapd_data *hapd, void *ctx) { struct wps_add_pin_data *data = ctx; int ret; if (hapd->wps == NULL) return 0; ret = wps_registrar_add_pin(hapd->wps->registrar, data->addr, data->uuid, data->pin, data->pin_len, data->timeout); if (ret == 0) data->added++; return ret; } int hostapd_wps_add_pin(struct hostapd_data *hapd, const u8 *addr, const char *uuid, const char *pin, int timeout) { u8 u[UUID_LEN]; struct wps_add_pin_data data; data.addr = addr; data.uuid = u; data.pin = (const u8 *) pin; data.pin_len = os_strlen(pin); data.timeout = timeout; data.added = 0; if (os_strcmp(uuid, "any") == 0) data.uuid = NULL; else { if (uuid_str2bin(uuid, u)) return -1; data.uuid = u; } if (hostapd_wps_for_each(hapd, wps_add_pin, &data) < 0) return -1; return data.added ? 0 : -1; } static int wps_button_pushed(struct hostapd_data *hapd, void *ctx) { const u8 *p2p_dev_addr = ctx; if (hapd->wps == NULL) return 0; return wps_registrar_button_pushed(hapd->wps->registrar, p2p_dev_addr); } int hostapd_wps_button_pushed(struct hostapd_data *hapd, const u8 *p2p_dev_addr) { return hostapd_wps_for_each(hapd, wps_button_pushed, (void *) p2p_dev_addr); } static int wps_cancel(struct hostapd_data *hapd, void *ctx) { if (hapd->wps == NULL) return 0; wps_registrar_wps_cancel(hapd->wps->registrar); ap_for_each_sta(hapd, ap_sta_wps_cancel, NULL); return 0; } int hostapd_wps_cancel(struct hostapd_data *hapd) { return hostapd_wps_for_each(hapd, wps_cancel, NULL); } static int hostapd_wps_probe_req_rx(void *ctx, const u8 *addr, const u8 *da, const u8 *bssid, const u8 *ie, size_t ie_len, int ssi_signal) { struct hostapd_data *hapd = ctx; struct wpabuf *wps_ie; struct ieee802_11_elems elems; if (hapd->wps == NULL) return 0; if (ieee802_11_parse_elems(ie, ie_len, &elems, 0) == ParseFailed) { wpa_printf(MSG_DEBUG, "WPS: Could not parse ProbeReq from " MACSTR, MAC2STR(addr)); return 0; } if (elems.ssid && elems.ssid_len > 0 && (elems.ssid_len != hapd->conf->ssid.ssid_len || os_memcmp(elems.ssid, hapd->conf->ssid.ssid, elems.ssid_len) != 0)) return 0; /* Not for us */ wps_ie = ieee802_11_vendor_ie_concat(ie, ie_len, WPS_DEV_OUI_WFA); if (wps_ie == NULL) return 0; if (wps_validate_probe_req(wps_ie, addr) < 0) { wpabuf_free(wps_ie); return 0; } if (wpabuf_len(wps_ie) > 0) { int p2p_wildcard = 0; #ifdef CONFIG_P2P if (elems.ssid && elems.ssid_len == P2P_WILDCARD_SSID_LEN && os_memcmp(elems.ssid, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN) == 0) p2p_wildcard = 1; #endif /* CONFIG_P2P */ wps_registrar_probe_req_rx(hapd->wps->registrar, addr, wps_ie, p2p_wildcard); #ifdef CONFIG_WPS_UPNP /* FIX: what exactly should be included in the WLANEvent? * WPS attributes? Full ProbeReq frame? */ if (!p2p_wildcard) upnp_wps_device_send_wlan_event( hapd->wps_upnp, addr, UPNP_WPS_WLANEVENT_TYPE_PROBE, wps_ie); #endif /* CONFIG_WPS_UPNP */ } wpabuf_free(wps_ie); return 0; } #ifdef CONFIG_WPS_UPNP static int hostapd_rx_req_put_wlan_response( void *priv, enum upnp_wps_wlanevent_type ev_type, const u8 *mac_addr, const struct wpabuf *msg, enum wps_msg_type msg_type) { struct hostapd_data *hapd = priv; struct sta_info *sta; struct upnp_pending_message *p; wpa_printf(MSG_DEBUG, "WPS UPnP: PutWLANResponse ev_type=%d mac_addr=" MACSTR, ev_type, MAC2STR(mac_addr)); wpa_hexdump(MSG_MSGDUMP, "WPS UPnP: PutWLANResponse NewMessage", wpabuf_head(msg), wpabuf_len(msg)); if (ev_type != UPNP_WPS_WLANEVENT_TYPE_EAP) { wpa_printf(MSG_DEBUG, "WPS UPnP: Ignored unexpected " "PutWLANResponse WLANEventType %d", ev_type); return -1; } /* * EAP response to ongoing to WPS Registration. Send it to EAP-WSC * server implementation for delivery to the peer. */ sta = ap_get_sta(hapd, mac_addr); #ifndef CONFIG_WPS_STRICT if (!sta) { /* * Workaround - Intel wsccmd uses bogus NewWLANEventMAC: * Pick STA that is in an ongoing WPS registration without * checking the MAC address. */ wpa_printf(MSG_DEBUG, "WPS UPnP: No matching STA found based " "on NewWLANEventMAC; try wildcard match"); for (sta = hapd->sta_list; sta; sta = sta->next) { if (sta->eapol_sm && (sta->flags & WLAN_STA_WPS)) break; } } #endif /* CONFIG_WPS_STRICT */ if (!sta || !(sta->flags & WLAN_STA_WPS)) { wpa_printf(MSG_DEBUG, "WPS UPnP: No matching STA found"); return 0; } p = os_zalloc(sizeof(*p)); if (p == NULL) return -1; os_memcpy(p->addr, sta->addr, ETH_ALEN); p->msg = wpabuf_dup(msg); p->type = msg_type; p->next = hapd->wps->upnp_msgs; hapd->wps->upnp_msgs = p; return eapol_auth_eap_pending_cb(sta->eapol_sm, sta->eapol_sm->eap); } static int hostapd_wps_upnp_init(struct hostapd_data *hapd, struct wps_context *wps) { struct upnp_wps_device_ctx *ctx; if (!hapd->conf->upnp_iface) return 0; ctx = os_zalloc(sizeof(*ctx)); if (ctx == NULL) return -1; ctx->rx_req_put_wlan_response = hostapd_rx_req_put_wlan_response; if (hapd->conf->ap_pin) ctx->ap_pin = os_strdup(hapd->conf->ap_pin); hapd->wps_upnp = upnp_wps_device_init(ctx, wps, hapd, hapd->conf->upnp_iface); if (hapd->wps_upnp == NULL) return -1; wps->wps_upnp = hapd->wps_upnp; return 0; } static void hostapd_wps_upnp_deinit(struct hostapd_data *hapd) { upnp_wps_device_deinit(hapd->wps_upnp, hapd); } #endif /* CONFIG_WPS_UPNP */ int hostapd_wps_get_mib_sta(struct hostapd_data *hapd, const u8 *addr, char *buf, size_t buflen) { if (hapd->wps == NULL) return 0; return wps_registrar_get_info(hapd->wps->registrar, addr, buf, buflen); } static void hostapd_wps_ap_pin_timeout(void *eloop_data, void *user_ctx) { struct hostapd_data *hapd = eloop_data; wpa_printf(MSG_DEBUG, "WPS: AP PIN timed out"); hostapd_wps_ap_pin_disable(hapd); wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_AP_PIN_DISABLED); } static void hostapd_wps_ap_pin_enable(struct hostapd_data *hapd, int timeout) { wpa_printf(MSG_DEBUG, "WPS: Enabling AP PIN (timeout=%d)", timeout); hapd->ap_pin_failures = 0; hapd->ap_pin_failures_consecutive = 0; hapd->conf->ap_setup_locked = 0; if (hapd->wps->ap_setup_locked) { wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_AP_SETUP_UNLOCKED); hapd->wps->ap_setup_locked = 0; wps_registrar_update_ie(hapd->wps->registrar); } eloop_cancel_timeout(hostapd_wps_ap_pin_timeout, hapd, NULL); if (timeout > 0) eloop_register_timeout(timeout, 0, hostapd_wps_ap_pin_timeout, hapd, NULL); } static int wps_ap_pin_disable(struct hostapd_data *hapd, void *ctx) { os_free(hapd->conf->ap_pin); hapd->conf->ap_pin = NULL; #ifdef CONFIG_WPS_UPNP upnp_wps_set_ap_pin(hapd->wps_upnp, NULL); #endif /* CONFIG_WPS_UPNP */ eloop_cancel_timeout(hostapd_wps_ap_pin_timeout, hapd, NULL); return 0; } void hostapd_wps_ap_pin_disable(struct hostapd_data *hapd) { wpa_printf(MSG_DEBUG, "WPS: Disabling AP PIN"); hostapd_wps_for_each(hapd, wps_ap_pin_disable, NULL); } struct wps_ap_pin_data { char pin_txt[9]; int timeout; }; static int wps_ap_pin_set(struct hostapd_data *hapd, void *ctx) { struct wps_ap_pin_data *data = ctx; os_free(hapd->conf->ap_pin); hapd->conf->ap_pin = os_strdup(data->pin_txt); #ifdef CONFIG_WPS_UPNP upnp_wps_set_ap_pin(hapd->wps_upnp, data->pin_txt); #endif /* CONFIG_WPS_UPNP */ hostapd_wps_ap_pin_enable(hapd, data->timeout); return 0; } const char * hostapd_wps_ap_pin_random(struct hostapd_data *hapd, int timeout) { unsigned int pin; struct wps_ap_pin_data data; pin = wps_generate_pin(); os_snprintf(data.pin_txt, sizeof(data.pin_txt), "%08u", pin); data.timeout = timeout; hostapd_wps_for_each(hapd, wps_ap_pin_set, &data); return hapd->conf->ap_pin; } const char * hostapd_wps_ap_pin_get(struct hostapd_data *hapd) { return hapd->conf->ap_pin; } int hostapd_wps_ap_pin_set(struct hostapd_data *hapd, const char *pin, int timeout) { struct wps_ap_pin_data data; int ret; ret = os_snprintf(data.pin_txt, sizeof(data.pin_txt), "%s", pin); if (ret < 0 || ret >= (int) sizeof(data.pin_txt)) return -1; data.timeout = timeout; return hostapd_wps_for_each(hapd, wps_ap_pin_set, &data); } static int wps_update_ie(struct hostapd_data *hapd, void *ctx) { if (hapd->wps) wps_registrar_update_ie(hapd->wps->registrar); return 0; } void hostapd_wps_update_ie(struct hostapd_data *hapd) { hostapd_wps_for_each(hapd, wps_update_ie, NULL); } int hostapd_wps_config_ap(struct hostapd_data *hapd, const char *ssid, const char *auth, const char *encr, const char *key) { struct wps_credential cred; size_t len; os_memset(&cred, 0, sizeof(cred)); len = os_strlen(ssid); if ((len & 1) || len > 2 * sizeof(cred.ssid) || hexstr2bin(ssid, cred.ssid, len / 2)) return -1; cred.ssid_len = len / 2; if (os_strncmp(auth, "OPEN", 4) == 0) cred.auth_type = WPS_AUTH_OPEN; else if (os_strncmp(auth, "WPAPSK", 6) == 0) cred.auth_type = WPS_AUTH_WPAPSK; else if (os_strncmp(auth, "WPA2PSK", 7) == 0) cred.auth_type = WPS_AUTH_WPA2PSK; else return -1; if (encr) { if (os_strncmp(encr, "NONE", 4) == 0) cred.encr_type = WPS_ENCR_NONE; else if (os_strncmp(encr, "WEP", 3) == 0) cred.encr_type = WPS_ENCR_WEP; else if (os_strncmp(encr, "TKIP", 4) == 0) cred.encr_type = WPS_ENCR_TKIP; else if (os_strncmp(encr, "CCMP", 4) == 0) cred.encr_type = WPS_ENCR_AES; else return -1; } else cred.encr_type = WPS_ENCR_NONE; if (key) { len = os_strlen(key); if ((len & 1) || len > 2 * sizeof(cred.key) || hexstr2bin(key, cred.key, len / 2)) return -1; cred.key_len = len / 2; } return wps_registrar_config_ap(hapd->wps->registrar, &cred); } #ifdef CONFIG_WPS_NFC struct wps_nfc_password_token_data { const u8 *oob_dev_pw; size_t oob_dev_pw_len; int added; }; static int wps_add_nfc_password_token(struct hostapd_data *hapd, void *ctx) { struct wps_nfc_password_token_data *data = ctx; int ret; if (hapd->wps == NULL) return 0; ret = wps_registrar_add_nfc_password_token(hapd->wps->registrar, data->oob_dev_pw, data->oob_dev_pw_len); if (ret == 0) data->added++; return ret; } static int hostapd_wps_add_nfc_password_token(struct hostapd_data *hapd, struct wps_parse_attr *attr) { struct wps_nfc_password_token_data data; data.oob_dev_pw = attr->oob_dev_password; data.oob_dev_pw_len = attr->oob_dev_password_len; data.added = 0; if (hostapd_wps_for_each(hapd, wps_add_nfc_password_token, &data) < 0) return -1; return data.added ? 0 : -1; } static int hostapd_wps_nfc_tag_process(struct hostapd_data *hapd, const struct wpabuf *wps) { struct wps_parse_attr attr; wpa_hexdump_buf(MSG_DEBUG, "WPS: Received NFC tag payload", wps); if (wps_parse_msg(wps, &attr)) { wpa_printf(MSG_DEBUG, "WPS: Ignore invalid data from NFC tag"); return -1; } if (attr.oob_dev_password) return hostapd_wps_add_nfc_password_token(hapd, &attr); wpa_printf(MSG_DEBUG, "WPS: Ignore unrecognized NFC tag"); return -1; } int hostapd_wps_nfc_tag_read(struct hostapd_data *hapd, const struct wpabuf *data) { const struct wpabuf *wps = data; struct wpabuf *tmp = NULL; int ret; if (wpabuf_len(data) < 4) return -1; if (*wpabuf_head_u8(data) != 0x10) { /* Assume this contains full NDEF record */ tmp = ndef_parse_wifi(data); if (tmp == NULL) { wpa_printf(MSG_DEBUG, "WPS: Could not parse NDEF"); return -1; } wps = tmp; } ret = hostapd_wps_nfc_tag_process(hapd, wps); wpabuf_free(tmp); return ret; } struct wpabuf * hostapd_wps_nfc_config_token(struct hostapd_data *hapd, int ndef) { struct wpabuf *ret; if (hapd->wps == NULL) return NULL; ret = wps_get_oob_cred(hapd->wps, hostapd_wps_rf_band_cb(hapd), hapd->iconf->channel); if (ndef && ret) { struct wpabuf *tmp; tmp = ndef_build_wifi(ret); wpabuf_free(ret); if (tmp == NULL) return NULL; ret = tmp; } return ret; } struct wpabuf * hostapd_wps_nfc_hs_cr(struct hostapd_data *hapd, int ndef) { struct wpabuf *ret; if (hapd->wps == NULL) return NULL; if (hapd->conf->wps_nfc_dh_pubkey == NULL) { struct wps_context *wps = hapd->wps; if (wps_nfc_gen_dh(&hapd->conf->wps_nfc_dh_pubkey, &hapd->conf->wps_nfc_dh_privkey) < 0) return NULL; hostapd_wps_nfc_clear(wps); wps->ap_nfc_dev_pw_id = DEV_PW_NFC_CONNECTION_HANDOVER; wps->ap_nfc_dh_pubkey = wpabuf_dup(hapd->conf->wps_nfc_dh_pubkey); wps->ap_nfc_dh_privkey = wpabuf_dup(hapd->conf->wps_nfc_dh_privkey); if (!wps->ap_nfc_dh_pubkey || !wps->ap_nfc_dh_privkey) { hostapd_wps_nfc_clear(wps); return NULL; } } ret = wps_build_nfc_handover_sel(hapd->wps, hapd->conf->wps_nfc_dh_pubkey, hapd->own_addr, hapd->iface->freq); if (ndef && ret) { struct wpabuf *tmp; tmp = ndef_build_wifi(ret); wpabuf_free(ret); if (tmp == NULL) return NULL; ret = tmp; } return ret; } int hostapd_wps_nfc_report_handover(struct hostapd_data *hapd, const struct wpabuf *req, const struct wpabuf *sel) { struct wpabuf *wps; int ret = -1; u16 wsc_len; const u8 *pos; struct wpabuf msg; struct wps_parse_attr attr; u16 dev_pw_id; /* * Enrollee/station is always initiator of the NFC connection handover, * so use the request message here to find Enrollee public key hash. */ wps = ndef_parse_wifi(req); if (wps == NULL) return -1; wpa_printf(MSG_DEBUG, "WPS: Received application/vnd.wfa.wsc " "payload from NFC connection handover"); wpa_hexdump_buf(MSG_DEBUG, "WPS: NFC payload", wps); if (wpabuf_len(wps) < 2) { wpa_printf(MSG_DEBUG, "WPS: Too short Wi-Fi Handover Request " "Message"); goto out; } pos = wpabuf_head(wps); wsc_len = WPA_GET_BE16(pos); if (wsc_len > wpabuf_len(wps) - 2) { wpa_printf(MSG_DEBUG, "WPS: Invalid WSC attribute length (%u) " "in rt Wi-Fi Handover Request Message", wsc_len); goto out; } pos += 2; wpa_hexdump(MSG_DEBUG, "WPS: WSC attributes in Wi-Fi Handover Request Message", pos, wsc_len); if (wsc_len < wpabuf_len(wps) - 2) { wpa_hexdump(MSG_DEBUG, "WPS: Ignore extra data after WSC attributes", pos + wsc_len, wpabuf_len(wps) - 2 - wsc_len); } wpabuf_set(&msg, pos, wsc_len); ret = wps_parse_msg(&msg, &attr); if (ret < 0) { wpa_printf(MSG_DEBUG, "WPS: Could not parse WSC attributes in " "Wi-Fi Handover Request Message"); goto out; } if (attr.oob_dev_password == NULL || attr.oob_dev_password_len < WPS_OOB_PUBKEY_HASH_LEN + 2) { wpa_printf(MSG_DEBUG, "WPS: No Out-of-Band Device Password " "included in Wi-Fi Handover Request Message"); ret = -1; goto out; } if (attr.uuid_e == NULL) { wpa_printf(MSG_DEBUG, "WPS: No UUID-E included in Wi-Fi " "Handover Request Message"); ret = -1; goto out; } wpa_hexdump(MSG_DEBUG, "WPS: UUID-E", attr.uuid_e, WPS_UUID_LEN); wpa_hexdump(MSG_DEBUG, "WPS: Out-of-Band Device Password", attr.oob_dev_password, attr.oob_dev_password_len); dev_pw_id = WPA_GET_BE16(attr.oob_dev_password + WPS_OOB_PUBKEY_HASH_LEN); if (dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER) { wpa_printf(MSG_DEBUG, "WPS: Unexpected OOB Device Password ID " "%u in Wi-Fi Handover Request Message", dev_pw_id); ret = -1; goto out; } wpa_hexdump(MSG_DEBUG, "WPS: Enrollee Public Key hash", attr.oob_dev_password, WPS_OOB_PUBKEY_HASH_LEN); ret = wps_registrar_add_nfc_pw_token(hapd->wps->registrar, attr.oob_dev_password, DEV_PW_NFC_CONNECTION_HANDOVER, NULL, 0, 1); out: wpabuf_free(wps); return ret; } struct wpabuf * hostapd_wps_nfc_token_gen(struct hostapd_data *hapd, int ndef) { if (hapd->conf->wps_nfc_pw_from_config) { return wps_nfc_token_build(ndef, hapd->conf->wps_nfc_dev_pw_id, hapd->conf->wps_nfc_dh_pubkey, hapd->conf->wps_nfc_dev_pw); } return wps_nfc_token_gen(ndef, &hapd->conf->wps_nfc_dev_pw_id, &hapd->conf->wps_nfc_dh_pubkey, &hapd->conf->wps_nfc_dh_privkey, &hapd->conf->wps_nfc_dev_pw); } int hostapd_wps_nfc_token_enable(struct hostapd_data *hapd) { struct wps_context *wps = hapd->wps; struct wpabuf *pw; if (wps == NULL) return -1; if (!hapd->conf->wps_nfc_dh_pubkey || !hapd->conf->wps_nfc_dh_privkey || !hapd->conf->wps_nfc_dev_pw || !hapd->conf->wps_nfc_dev_pw_id) return -1; hostapd_wps_nfc_clear(wps); wpa_printf(MSG_DEBUG, "WPS: Enable NFC Tag (Dev Pw Id %u) for AP interface %s (context %p)", hapd->conf->wps_nfc_dev_pw_id, hapd->conf->iface, wps); wps->ap_nfc_dev_pw_id = hapd->conf->wps_nfc_dev_pw_id; wps->ap_nfc_dh_pubkey = wpabuf_dup(hapd->conf->wps_nfc_dh_pubkey); wps->ap_nfc_dh_privkey = wpabuf_dup(hapd->conf->wps_nfc_dh_privkey); pw = hapd->conf->wps_nfc_dev_pw; wps->ap_nfc_dev_pw = wpabuf_alloc( wpabuf_len(pw) * 2 + 1); if (wps->ap_nfc_dev_pw) { wpa_snprintf_hex_uppercase( (char *) wpabuf_put(wps->ap_nfc_dev_pw, wpabuf_len(pw) * 2), wpabuf_len(pw) * 2 + 1, wpabuf_head(pw), wpabuf_len(pw)); } if (!wps->ap_nfc_dh_pubkey || !wps->ap_nfc_dh_privkey || !wps->ap_nfc_dev_pw) { hostapd_wps_nfc_clear(wps); return -1; } return 0; } void hostapd_wps_nfc_token_disable(struct hostapd_data *hapd) { wpa_printf(MSG_DEBUG, "WPS: Disable NFC token for AP interface %s", hapd->conf->iface); hostapd_wps_nfc_clear(hapd->wps); } #endif /* CONFIG_WPS_NFC */ wpa-2.1/src/ap/wps_hostapd.h000066400000000000000000000054501227414666700160610ustar00rootroot00000000000000/* * hostapd / WPS integration * Copyright (c) 2008-2012, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef WPS_HOSTAPD_H #define WPS_HOSTAPD_H #ifdef CONFIG_WPS int hostapd_init_wps(struct hostapd_data *hapd, struct hostapd_bss_config *conf); int hostapd_init_wps_complete(struct hostapd_data *hapd); void hostapd_deinit_wps(struct hostapd_data *hapd); void hostapd_update_wps(struct hostapd_data *hapd); void hostapd_wps_eap_completed(struct hostapd_data *hapd); int hostapd_wps_add_pin(struct hostapd_data *hapd, const u8 *addr, const char *uuid, const char *pin, int timeout); int hostapd_wps_button_pushed(struct hostapd_data *hapd, const u8 *p2p_dev_addr); int hostapd_wps_cancel(struct hostapd_data *hapd); int hostapd_wps_get_mib_sta(struct hostapd_data *hapd, const u8 *addr, char *buf, size_t buflen); void hostapd_wps_ap_pin_disable(struct hostapd_data *hapd); const char * hostapd_wps_ap_pin_random(struct hostapd_data *hapd, int timeout); const char * hostapd_wps_ap_pin_get(struct hostapd_data *hapd); int hostapd_wps_ap_pin_set(struct hostapd_data *hapd, const char *pin, int timeout); void hostapd_wps_update_ie(struct hostapd_data *hapd); int hostapd_wps_config_ap(struct hostapd_data *hapd, const char *ssid, const char *auth, const char *encr, const char *key); int hostapd_wps_nfc_tag_read(struct hostapd_data *hapd, const struct wpabuf *data); struct wpabuf * hostapd_wps_nfc_config_token(struct hostapd_data *hapd, int ndef); struct wpabuf * hostapd_wps_nfc_hs_cr(struct hostapd_data *hapd, int ndef); int hostapd_wps_nfc_report_handover(struct hostapd_data *hapd, const struct wpabuf *req, const struct wpabuf *sel); struct wpabuf * hostapd_wps_nfc_token_gen(struct hostapd_data *hapd, int ndef); int hostapd_wps_nfc_token_enable(struct hostapd_data *hapd); void hostapd_wps_nfc_token_disable(struct hostapd_data *hapd); #else /* CONFIG_WPS */ static inline int hostapd_init_wps(struct hostapd_data *hapd, struct hostapd_bss_config *conf) { return 0; } static inline void hostapd_deinit_wps(struct hostapd_data *hapd) { } static inline int hostapd_init_wps_complete(struct hostapd_data *hapd) { return 0; } static inline void hostapd_update_wps(struct hostapd_data *hapd) { } static inline void hostapd_wps_eap_completed(struct hostapd_data *hapd) { } static inline int hostapd_wps_get_mib_sta(struct hostapd_data *hapd, const u8 *addr, char *buf, size_t buflen) { return 0; } static inline int hostapd_wps_button_pushed(struct hostapd_data *hapd, const u8 *p2p_dev_addr) { return 0; } static inline int hostapd_wps_cancel(struct hostapd_data *hapd) { return 0; } #endif /* CONFIG_WPS */ #endif /* WPS_HOSTAPD_H */ wpa-2.1/src/common/000077500000000000000000000000001227414666700142415ustar00rootroot00000000000000wpa-2.1/src/common/Makefile000066400000000000000000000001641227414666700157020ustar00rootroot00000000000000all: @echo Nothing to be made. clean: rm -f *~ *.o *.d *.gcno *.gcda *.gcov install: @echo Nothing to be made. wpa-2.1/src/common/defs.h000066400000000000000000000201731227414666700153360ustar00rootroot00000000000000/* * WPA Supplicant - Common definitions * Copyright (c) 2004-2008, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef DEFS_H #define DEFS_H #ifdef FALSE #undef FALSE #endif #ifdef TRUE #undef TRUE #endif typedef enum { FALSE = 0, TRUE = 1 } Boolean; #define WPA_CIPHER_NONE BIT(0) #define WPA_CIPHER_WEP40 BIT(1) #define WPA_CIPHER_WEP104 BIT(2) #define WPA_CIPHER_TKIP BIT(3) #define WPA_CIPHER_CCMP BIT(4) #define WPA_CIPHER_AES_128_CMAC BIT(5) #define WPA_CIPHER_GCMP BIT(6) #define WPA_CIPHER_SMS4 BIT(7) #define WPA_CIPHER_GCMP_256 BIT(8) #define WPA_CIPHER_CCMP_256 BIT(9) #define WPA_CIPHER_BIP_GMAC_128 BIT(11) #define WPA_CIPHER_BIP_GMAC_256 BIT(12) #define WPA_CIPHER_BIP_CMAC_256 BIT(13) #define WPA_CIPHER_GTK_NOT_USED BIT(14) #define WPA_KEY_MGMT_IEEE8021X BIT(0) #define WPA_KEY_MGMT_PSK BIT(1) #define WPA_KEY_MGMT_NONE BIT(2) #define WPA_KEY_MGMT_IEEE8021X_NO_WPA BIT(3) #define WPA_KEY_MGMT_WPA_NONE BIT(4) #define WPA_KEY_MGMT_FT_IEEE8021X BIT(5) #define WPA_KEY_MGMT_FT_PSK BIT(6) #define WPA_KEY_MGMT_IEEE8021X_SHA256 BIT(7) #define WPA_KEY_MGMT_PSK_SHA256 BIT(8) #define WPA_KEY_MGMT_WPS BIT(9) #define WPA_KEY_MGMT_SAE BIT(10) #define WPA_KEY_MGMT_FT_SAE BIT(11) #define WPA_KEY_MGMT_WAPI_PSK BIT(12) #define WPA_KEY_MGMT_WAPI_CERT BIT(13) #define WPA_KEY_MGMT_CCKM BIT(14) static inline int wpa_key_mgmt_wpa_ieee8021x(int akm) { return !!(akm & (WPA_KEY_MGMT_IEEE8021X | WPA_KEY_MGMT_FT_IEEE8021X | WPA_KEY_MGMT_CCKM | WPA_KEY_MGMT_IEEE8021X_SHA256)); } static inline int wpa_key_mgmt_wpa_psk(int akm) { return !!(akm & (WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_FT_PSK | WPA_KEY_MGMT_PSK_SHA256 | WPA_KEY_MGMT_SAE | WPA_KEY_MGMT_FT_SAE)); } static inline int wpa_key_mgmt_ft(int akm) { return !!(akm & (WPA_KEY_MGMT_FT_PSK | WPA_KEY_MGMT_FT_IEEE8021X | WPA_KEY_MGMT_FT_SAE)); } static inline int wpa_key_mgmt_sae(int akm) { return !!(akm & (WPA_KEY_MGMT_SAE | WPA_KEY_MGMT_FT_SAE)); } static inline int wpa_key_mgmt_sha256(int akm) { return !!(akm & (WPA_KEY_MGMT_PSK_SHA256 | WPA_KEY_MGMT_IEEE8021X_SHA256)); } static inline int wpa_key_mgmt_wpa(int akm) { return wpa_key_mgmt_wpa_ieee8021x(akm) || wpa_key_mgmt_wpa_psk(akm) || wpa_key_mgmt_sae(akm); } static inline int wpa_key_mgmt_wpa_any(int akm) { return wpa_key_mgmt_wpa(akm) || (akm & WPA_KEY_MGMT_WPA_NONE); } static inline int wpa_key_mgmt_cckm(int akm) { return akm == WPA_KEY_MGMT_CCKM; } #define WPA_PROTO_WPA BIT(0) #define WPA_PROTO_RSN BIT(1) #define WPA_PROTO_WAPI BIT(2) #define WPA_AUTH_ALG_OPEN BIT(0) #define WPA_AUTH_ALG_SHARED BIT(1) #define WPA_AUTH_ALG_LEAP BIT(2) #define WPA_AUTH_ALG_FT BIT(3) #define WPA_AUTH_ALG_SAE BIT(4) enum wpa_alg { WPA_ALG_NONE, WPA_ALG_WEP, WPA_ALG_TKIP, WPA_ALG_CCMP, WPA_ALG_IGTK, WPA_ALG_PMK, WPA_ALG_GCMP, WPA_ALG_SMS4, WPA_ALG_KRK, WPA_ALG_GCMP_256, WPA_ALG_CCMP_256, WPA_ALG_BIP_GMAC_128, WPA_ALG_BIP_GMAC_256, WPA_ALG_BIP_CMAC_256 }; /** * enum wpa_states - wpa_supplicant state * * These enumeration values are used to indicate the current wpa_supplicant * state (wpa_s->wpa_state). The current state can be retrieved with * wpa_supplicant_get_state() function and the state can be changed by calling * wpa_supplicant_set_state(). In WPA state machine (wpa.c and preauth.c), the * wrapper functions wpa_sm_get_state() and wpa_sm_set_state() should be used * to access the state variable. */ enum wpa_states { /** * WPA_DISCONNECTED - Disconnected state * * This state indicates that client is not associated, but is likely to * start looking for an access point. This state is entered when a * connection is lost. */ WPA_DISCONNECTED, /** * WPA_INTERFACE_DISABLED - Interface disabled * * This stat eis entered if the network interface is disabled, e.g., * due to rfkill. wpa_supplicant refuses any new operations that would * use the radio until the interface has been enabled. */ WPA_INTERFACE_DISABLED, /** * WPA_INACTIVE - Inactive state (wpa_supplicant disabled) * * This state is entered if there are no enabled networks in the * configuration. wpa_supplicant is not trying to associate with a new * network and external interaction (e.g., ctrl_iface call to add or * enable a network) is needed to start association. */ WPA_INACTIVE, /** * WPA_SCANNING - Scanning for a network * * This state is entered when wpa_supplicant starts scanning for a * network. */ WPA_SCANNING, /** * WPA_AUTHENTICATING - Trying to authenticate with a BSS/SSID * * This state is entered when wpa_supplicant has found a suitable BSS * to authenticate with and the driver is configured to try to * authenticate with this BSS. This state is used only with drivers * that use wpa_supplicant as the SME. */ WPA_AUTHENTICATING, /** * WPA_ASSOCIATING - Trying to associate with a BSS/SSID * * This state is entered when wpa_supplicant has found a suitable BSS * to associate with and the driver is configured to try to associate * with this BSS in ap_scan=1 mode. When using ap_scan=2 mode, this * state is entered when the driver is configured to try to associate * with a network using the configured SSID and security policy. */ WPA_ASSOCIATING, /** * WPA_ASSOCIATED - Association completed * * This state is entered when the driver reports that association has * been successfully completed with an AP. If IEEE 802.1X is used * (with or without WPA/WPA2), wpa_supplicant remains in this state * until the IEEE 802.1X/EAPOL authentication has been completed. */ WPA_ASSOCIATED, /** * WPA_4WAY_HANDSHAKE - WPA 4-Way Key Handshake in progress * * This state is entered when WPA/WPA2 4-Way Handshake is started. In * case of WPA-PSK, this happens when receiving the first EAPOL-Key * frame after association. In case of WPA-EAP, this state is entered * when the IEEE 802.1X/EAPOL authentication has been completed. */ WPA_4WAY_HANDSHAKE, /** * WPA_GROUP_HANDSHAKE - WPA Group Key Handshake in progress * * This state is entered when 4-Way Key Handshake has been completed * (i.e., when the supplicant sends out message 4/4) and when Group * Key rekeying is started by the AP (i.e., when supplicant receives * message 1/2). */ WPA_GROUP_HANDSHAKE, /** * WPA_COMPLETED - All authentication completed * * This state is entered when the full authentication process is * completed. In case of WPA2, this happens when the 4-Way Handshake is * successfully completed. With WPA, this state is entered after the * Group Key Handshake; with IEEE 802.1X (non-WPA) connection is * completed after dynamic keys are received (or if not used, after * the EAP authentication has been completed). With static WEP keys and * plaintext connections, this state is entered when an association * has been completed. * * This state indicates that the supplicant has completed its * processing for the association phase and that data connection is * fully configured. */ WPA_COMPLETED }; #define MLME_SETPROTECTION_PROTECT_TYPE_NONE 0 #define MLME_SETPROTECTION_PROTECT_TYPE_RX 1 #define MLME_SETPROTECTION_PROTECT_TYPE_TX 2 #define MLME_SETPROTECTION_PROTECT_TYPE_RX_TX 3 #define MLME_SETPROTECTION_KEY_TYPE_GROUP 0 #define MLME_SETPROTECTION_KEY_TYPE_PAIRWISE 1 /** * enum mfp_options - Management frame protection (IEEE 802.11w) options */ enum mfp_options { NO_MGMT_FRAME_PROTECTION = 0, MGMT_FRAME_PROTECTION_OPTIONAL = 1, MGMT_FRAME_PROTECTION_REQUIRED = 2, }; #define MGMT_FRAME_PROTECTION_DEFAULT 3 /** * enum hostapd_hw_mode - Hardware mode */ enum hostapd_hw_mode { HOSTAPD_MODE_IEEE80211B, HOSTAPD_MODE_IEEE80211G, HOSTAPD_MODE_IEEE80211A, HOSTAPD_MODE_IEEE80211AD, NUM_HOSTAPD_MODES }; /** * enum wpa_ctrl_req_type - Control interface request types */ enum wpa_ctrl_req_type { WPA_CTRL_REQ_UNKNOWN, WPA_CTRL_REQ_EAP_IDENTITY, WPA_CTRL_REQ_EAP_PASSWORD, WPA_CTRL_REQ_EAP_NEW_PASSWORD, WPA_CTRL_REQ_EAP_PIN, WPA_CTRL_REQ_EAP_OTP, WPA_CTRL_REQ_EAP_PASSPHRASE, WPA_CTRL_REQ_SIM, NUM_WPA_CTRL_REQS }; /* Maximum number of EAP methods to store for EAP server user information */ #define EAP_MAX_METHODS 8 #endif /* DEFS_H */ wpa-2.1/src/common/eapol_common.h000066400000000000000000000043761227414666700170740ustar00rootroot00000000000000/* * EAPOL definitions shared between hostapd and wpa_supplicant * Copyright (c) 2002-2007, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef EAPOL_COMMON_H #define EAPOL_COMMON_H /* IEEE Std 802.1X-2004 */ #ifdef _MSC_VER #pragma pack(push, 1) #endif /* _MSC_VER */ struct ieee802_1x_hdr { u8 version; u8 type; be16 length; /* followed by length octets of data */ } STRUCT_PACKED; #ifdef _MSC_VER #pragma pack(pop) #endif /* _MSC_VER */ #define EAPOL_VERSION 2 enum { IEEE802_1X_TYPE_EAP_PACKET = 0, IEEE802_1X_TYPE_EAPOL_START = 1, IEEE802_1X_TYPE_EAPOL_LOGOFF = 2, IEEE802_1X_TYPE_EAPOL_KEY = 3, IEEE802_1X_TYPE_EAPOL_ENCAPSULATED_ASF_ALERT = 4 }; enum { EAPOL_KEY_TYPE_RC4 = 1, EAPOL_KEY_TYPE_RSN = 2, EAPOL_KEY_TYPE_WPA = 254 }; #define IEEE8021X_REPLAY_COUNTER_LEN 8 #define IEEE8021X_KEY_SIGN_LEN 16 #define IEEE8021X_KEY_IV_LEN 16 #define IEEE8021X_KEY_INDEX_FLAG 0x80 #define IEEE8021X_KEY_INDEX_MASK 0x03 #ifdef _MSC_VER #pragma pack(push, 1) #endif /* _MSC_VER */ struct ieee802_1x_eapol_key { u8 type; /* Note: key_length is unaligned */ u8 key_length[2]; /* does not repeat within the life of the keying material used to * encrypt the Key field; 64-bit NTP timestamp MAY be used here */ u8 replay_counter[IEEE8021X_REPLAY_COUNTER_LEN]; u8 key_iv[IEEE8021X_KEY_IV_LEN]; /* cryptographically random number */ u8 key_index; /* key flag in the most significant bit: * 0 = broadcast (default key), * 1 = unicast (key mapping key); key index is in the * 7 least significant bits */ /* HMAC-MD5 message integrity check computed with MS-MPPE-Send-Key as * the key */ u8 key_signature[IEEE8021X_KEY_SIGN_LEN]; /* followed by key: if packet body length = 44 + key length, then the * key field (of key_length bytes) contains the key in encrypted form; * if packet body length = 44, key field is absent and key_length * represents the number of least significant octets from * MS-MPPE-Send-Key attribute to be used as the keying material; * RC4 key used in encryption = Key-IV + MS-MPPE-Recv-Key */ } STRUCT_PACKED; #ifdef _MSC_VER #pragma pack(pop) #endif /* _MSC_VER */ #endif /* EAPOL_COMMON_H */ wpa-2.1/src/common/gas.c000066400000000000000000000142161227414666700151630ustar00rootroot00000000000000/* * Generic advertisement service (GAS) (IEEE 802.11u) * Copyright (c) 2009, Atheros Communications * Copyright (c) 2011-2012, Qualcomm Atheros * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "includes.h" #include "common.h" #include "ieee802_11_defs.h" #include "gas.h" static struct wpabuf * gas_build_req(u8 action, u8 dialog_token, size_t size) { struct wpabuf *buf; buf = wpabuf_alloc(100 + size); if (buf == NULL) return NULL; wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC); wpabuf_put_u8(buf, action); wpabuf_put_u8(buf, dialog_token); return buf; } struct wpabuf * gas_build_initial_req(u8 dialog_token, size_t size) { return gas_build_req(WLAN_PA_GAS_INITIAL_REQ, dialog_token, size); } struct wpabuf * gas_build_comeback_req(u8 dialog_token) { return gas_build_req(WLAN_PA_GAS_COMEBACK_REQ, dialog_token, 0); } static struct wpabuf * gas_build_resp(u8 action, u8 dialog_token, u16 status_code, u8 frag_id, u8 more, u16 comeback_delay, size_t size) { struct wpabuf *buf; buf = wpabuf_alloc(100 + size); if (buf == NULL) return NULL; wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC); wpabuf_put_u8(buf, action); wpabuf_put_u8(buf, dialog_token); wpabuf_put_le16(buf, status_code); if (action == WLAN_PA_GAS_COMEBACK_RESP) wpabuf_put_u8(buf, frag_id | (more ? 0x80 : 0)); wpabuf_put_le16(buf, comeback_delay); return buf; } struct wpabuf * gas_build_initial_resp(u8 dialog_token, u16 status_code, u16 comeback_delay, size_t size) { return gas_build_resp(WLAN_PA_GAS_INITIAL_RESP, dialog_token, status_code, 0, 0, comeback_delay, size); } static struct wpabuf * gas_build_comeback_resp(u8 dialog_token, u16 status_code, u8 frag_id, u8 more, u16 comeback_delay, size_t size) { return gas_build_resp(WLAN_PA_GAS_COMEBACK_RESP, dialog_token, status_code, frag_id, more, comeback_delay, size); } /** * gas_add_adv_proto_anqp - Add an Advertisement Protocol element * @buf: Buffer to which the element is added * @query_resp_len_limit: Query Response Length Limit in units of 256 octets * @pame_bi: Pre-Association Message Exchange BSSID Independent (0/1) * * * @query_resp_len_limit is 0 for request and 1-0x7f for response. 0x7f means * that the maximum limit is determined by the maximum allowable number of * fragments in the GAS Query Response Fragment ID. */ static void gas_add_adv_proto_anqp(struct wpabuf *buf, u8 query_resp_len_limit, u8 pame_bi) { /* Advertisement Protocol IE */ wpabuf_put_u8(buf, WLAN_EID_ADV_PROTO); wpabuf_put_u8(buf, 2); /* Length */ wpabuf_put_u8(buf, (query_resp_len_limit & 0x7f) | (pame_bi ? 0x80 : 0)); /* Advertisement Protocol */ wpabuf_put_u8(buf, ACCESS_NETWORK_QUERY_PROTOCOL); } struct wpabuf * gas_anqp_build_initial_req(u8 dialog_token, size_t size) { struct wpabuf *buf; buf = gas_build_initial_req(dialog_token, 4 + size); if (buf == NULL) return NULL; gas_add_adv_proto_anqp(buf, 0, 0); wpabuf_put(buf, 2); /* Query Request Length to be filled */ return buf; } struct wpabuf * gas_anqp_build_initial_resp(u8 dialog_token, u16 status_code, u16 comeback_delay, size_t size) { struct wpabuf *buf; buf = gas_build_initial_resp(dialog_token, status_code, comeback_delay, 4 + size); if (buf == NULL) return NULL; gas_add_adv_proto_anqp(buf, 0x7f, 0); wpabuf_put(buf, 2); /* Query Response Length to be filled */ return buf; } struct wpabuf * gas_anqp_build_initial_resp_buf(u8 dialog_token, u16 status_code, u16 comeback_delay, struct wpabuf *payload) { struct wpabuf *buf; buf = gas_anqp_build_initial_resp(dialog_token, status_code, comeback_delay, payload ? wpabuf_len(payload) : 0); if (buf == NULL) return NULL; if (payload) wpabuf_put_buf(buf, payload); gas_anqp_set_len(buf); return buf; } struct wpabuf * gas_anqp_build_comeback_resp(u8 dialog_token, u16 status_code, u8 frag_id, u8 more, u16 comeback_delay, size_t size) { struct wpabuf *buf; buf = gas_build_comeback_resp(dialog_token, status_code, frag_id, more, comeback_delay, 4 + size); if (buf == NULL) return NULL; gas_add_adv_proto_anqp(buf, 0x7f, 0); wpabuf_put(buf, 2); /* Query Response Length to be filled */ return buf; } struct wpabuf * gas_anqp_build_comeback_resp_buf(u8 dialog_token, u16 status_code, u8 frag_id, u8 more, u16 comeback_delay, struct wpabuf *payload) { struct wpabuf *buf; buf = gas_anqp_build_comeback_resp(dialog_token, status_code, frag_id, more, comeback_delay, payload ? wpabuf_len(payload) : 0); if (buf == NULL) return NULL; if (payload) wpabuf_put_buf(buf, payload); gas_anqp_set_len(buf); return buf; } /** * gas_anqp_set_len - Set Query Request/Response Length * @buf: GAS message * * This function is used to update the Query Request/Response Length field once * the payload has been filled. */ void gas_anqp_set_len(struct wpabuf *buf) { u8 action; size_t offset; u8 *len; if (buf == NULL || wpabuf_len(buf) < 2) return; action = *(wpabuf_head_u8(buf) + 1); switch (action) { case WLAN_PA_GAS_INITIAL_REQ: offset = 3 + 4; break; case WLAN_PA_GAS_INITIAL_RESP: offset = 7 + 4; break; case WLAN_PA_GAS_COMEBACK_RESP: offset = 8 + 4; break; default: return; } if (wpabuf_len(buf) < offset + 2) return; len = wpabuf_mhead_u8(buf) + offset; WPA_PUT_LE16(len, (u8 *) wpabuf_put(buf, 0) - len - 2); } /** * gas_anqp_add_element - Add ANQP element header * @buf: GAS message * @info_id: ANQP Info ID * Returns: Pointer to the Length field for gas_anqp_set_element_len() */ u8 * gas_anqp_add_element(struct wpabuf *buf, u16 info_id) { wpabuf_put_le16(buf, info_id); return wpabuf_put(buf, 2); /* Length to be filled */ } /** * gas_anqp_set_element_len - Update ANQP element Length field * @buf: GAS message * @len_pos: Length field position from gas_anqp_add_element() * * This function is called after the ANQP element payload has been added to the * buffer. */ void gas_anqp_set_element_len(struct wpabuf *buf, u8 *len_pos) { WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(buf, 0) - len_pos - 2); } wpa-2.1/src/common/gas.h000066400000000000000000000025601227414666700151670ustar00rootroot00000000000000/* * Generic advertisement service (GAS) (IEEE 802.11u) * Copyright (c) 2009, Atheros Communications * Copyright (c) 2011-2012, Qualcomm Atheros * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef GAS_H #define GAS_H struct wpabuf * gas_build_initial_req(u8 dialog_token, size_t size); struct wpabuf * gas_build_comeback_req(u8 dialog_token); struct wpabuf * gas_build_initial_resp(u8 dialog_token, u16 status_code, u16 comeback_delay, size_t size); struct wpabuf * gas_anqp_build_initial_req(u8 dialog_token, size_t size); struct wpabuf * gas_anqp_build_initial_resp(u8 dialog_token, u16 status_code, u16 comeback_delay, size_t size); struct wpabuf * gas_anqp_build_initial_resp_buf(u8 dialog_token, u16 status_code, u16 comeback_delay, struct wpabuf *payload); struct wpabuf * gas_anqp_build_comeback_resp(u8 dialog_token, u16 status_code, u8 frag_id, u8 more, u16 comeback_delay, size_t size); struct wpabuf * gas_anqp_build_comeback_resp_buf(u8 dialog_token, u16 status_code, u8 frag_id, u8 more, u16 comeback_delay, struct wpabuf *payload); void gas_anqp_set_len(struct wpabuf *buf); u8 * gas_anqp_add_element(struct wpabuf *buf, u16 info_id); void gas_anqp_set_element_len(struct wpabuf *buf, u8 *len_pos); #endif /* GAS_H */ wpa-2.1/src/common/ieee802_11_common.c000066400000000000000000000272541227414666700174310ustar00rootroot00000000000000/* * IEEE 802.11 Common routines * Copyright (c) 2002-2013, 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 "defs.h" #include "ieee802_11_defs.h" #include "ieee802_11_common.h" static int ieee802_11_parse_vendor_specific(const u8 *pos, size_t elen, struct ieee802_11_elems *elems, int show_errors) { unsigned int oui; /* first 3 bytes in vendor specific information element are the IEEE * OUI of the vendor. The following byte is used a vendor specific * sub-type. */ if (elen < 4) { if (show_errors) { wpa_printf(MSG_MSGDUMP, "short vendor specific " "information element ignored (len=%lu)", (unsigned long) elen); } return -1; } oui = WPA_GET_BE24(pos); switch (oui) { case OUI_MICROSOFT: /* Microsoft/Wi-Fi information elements are further typed and * subtyped */ switch (pos[3]) { case 1: /* Microsoft OUI (00:50:F2) with OUI Type 1: * real WPA information element */ elems->wpa_ie = pos; elems->wpa_ie_len = elen; break; case WMM_OUI_TYPE: /* WMM information element */ if (elen < 5) { wpa_printf(MSG_MSGDUMP, "short WMM " "information element ignored " "(len=%lu)", (unsigned long) elen); return -1; } switch (pos[4]) { case WMM_OUI_SUBTYPE_INFORMATION_ELEMENT: case WMM_OUI_SUBTYPE_PARAMETER_ELEMENT: /* * Share same pointer since only one of these * is used and they start with same data. * Length field can be used to distinguish the * IEs. */ elems->wmm = pos; elems->wmm_len = elen; break; case WMM_OUI_SUBTYPE_TSPEC_ELEMENT: elems->wmm_tspec = pos; elems->wmm_tspec_len = elen; break; default: wpa_printf(MSG_EXCESSIVE, "unknown WMM " "information element ignored " "(subtype=%d len=%lu)", pos[4], (unsigned long) elen); return -1; } break; case 4: /* Wi-Fi Protected Setup (WPS) IE */ elems->wps_ie = pos; elems->wps_ie_len = elen; break; default: wpa_printf(MSG_EXCESSIVE, "Unknown Microsoft " "information element ignored " "(type=%d len=%lu)", pos[3], (unsigned long) elen); return -1; } break; case OUI_WFA: switch (pos[3]) { case P2P_OUI_TYPE: /* Wi-Fi Alliance - P2P IE */ elems->p2p = pos; elems->p2p_len = elen; break; case WFD_OUI_TYPE: /* Wi-Fi Alliance - WFD IE */ elems->wfd = pos; elems->wfd_len = elen; break; case HS20_INDICATION_OUI_TYPE: /* Hotspot 2.0 */ elems->hs20 = pos; elems->hs20_len = elen; break; default: wpa_printf(MSG_MSGDUMP, "Unknown WFA " "information element ignored " "(type=%d len=%lu)\n", pos[3], (unsigned long) elen); return -1; } break; case OUI_BROADCOM: switch (pos[3]) { case VENDOR_HT_CAPAB_OUI_TYPE: elems->vendor_ht_cap = pos; elems->vendor_ht_cap_len = elen; break; default: wpa_printf(MSG_EXCESSIVE, "Unknown Broadcom " "information element ignored " "(type=%d len=%lu)", pos[3], (unsigned long) elen); return -1; } break; default: wpa_printf(MSG_EXCESSIVE, "unknown vendor specific " "information element ignored (vendor OUI " "%02x:%02x:%02x len=%lu)", pos[0], pos[1], pos[2], (unsigned long) elen); return -1; } return 0; } /** * ieee802_11_parse_elems - Parse information elements in management frames * @start: Pointer to the start of IEs * @len: Length of IE buffer in octets * @elems: Data structure for parsed elements * @show_errors: Whether to show parsing errors in debug log * Returns: Parsing result */ ParseRes ieee802_11_parse_elems(const u8 *start, size_t len, struct ieee802_11_elems *elems, int show_errors) { size_t left = len; const u8 *pos = start; int unknown = 0; os_memset(elems, 0, sizeof(*elems)); while (left >= 2) { u8 id, elen; id = *pos++; elen = *pos++; left -= 2; if (elen > left) { if (show_errors) { wpa_printf(MSG_DEBUG, "IEEE 802.11 element " "parse failed (id=%d elen=%d " "left=%lu)", id, elen, (unsigned long) left); wpa_hexdump(MSG_MSGDUMP, "IEs", start, len); } return ParseFailed; } switch (id) { case WLAN_EID_SSID: elems->ssid = pos; elems->ssid_len = elen; break; case WLAN_EID_SUPP_RATES: elems->supp_rates = pos; elems->supp_rates_len = elen; break; case WLAN_EID_DS_PARAMS: elems->ds_params = pos; elems->ds_params_len = elen; break; case WLAN_EID_CF_PARAMS: case WLAN_EID_TIM: break; case WLAN_EID_CHALLENGE: elems->challenge = pos; elems->challenge_len = elen; break; case WLAN_EID_ERP_INFO: elems->erp_info = pos; elems->erp_info_len = elen; break; case WLAN_EID_EXT_SUPP_RATES: elems->ext_supp_rates = pos; elems->ext_supp_rates_len = elen; break; case WLAN_EID_VENDOR_SPECIFIC: if (ieee802_11_parse_vendor_specific(pos, elen, elems, show_errors)) unknown++; break; case WLAN_EID_RSN: elems->rsn_ie = pos; elems->rsn_ie_len = elen; break; case WLAN_EID_PWR_CAPABILITY: break; case WLAN_EID_SUPPORTED_CHANNELS: elems->supp_channels = pos; elems->supp_channels_len = elen; break; case WLAN_EID_MOBILITY_DOMAIN: elems->mdie = pos; elems->mdie_len = elen; break; case WLAN_EID_FAST_BSS_TRANSITION: elems->ftie = pos; elems->ftie_len = elen; break; case WLAN_EID_TIMEOUT_INTERVAL: elems->timeout_int = pos; elems->timeout_int_len = elen; break; case WLAN_EID_HT_CAP: elems->ht_capabilities = pos; elems->ht_capabilities_len = elen; break; case WLAN_EID_HT_OPERATION: elems->ht_operation = pos; elems->ht_operation_len = elen; break; case WLAN_EID_VHT_CAP: elems->vht_capabilities = pos; elems->vht_capabilities_len = elen; break; case WLAN_EID_VHT_OPERATION: elems->vht_operation = pos; elems->vht_operation_len = elen; break; case WLAN_EID_LINK_ID: if (elen < 18) break; elems->link_id = pos; break; case WLAN_EID_INTERWORKING: elems->interworking = pos; elems->interworking_len = elen; break; case WLAN_EID_QOS_MAP_SET: if (elen < 16) break; elems->qos_map_set = pos; elems->qos_map_set_len = elen; break; case WLAN_EID_EXT_CAPAB: elems->ext_capab = pos; elems->ext_capab_len = elen; break; case WLAN_EID_BSS_MAX_IDLE_PERIOD: if (elen < 3) break; elems->bss_max_idle_period = pos; break; case WLAN_EID_SSID_LIST: elems->ssid_list = pos; elems->ssid_list_len = elen; break; default: unknown++; if (!show_errors) break; wpa_printf(MSG_MSGDUMP, "IEEE 802.11 element parse " "ignored unknown element (id=%d elen=%d)", id, elen); break; } left -= elen; pos += elen; } if (left) return ParseFailed; return unknown ? ParseUnknown : ParseOK; } int ieee802_11_ie_count(const u8 *ies, size_t ies_len) { int count = 0; const u8 *pos, *end; if (ies == NULL) return 0; pos = ies; end = ies + ies_len; while (pos + 2 <= end) { if (pos + 2 + pos[1] > end) break; count++; pos += 2 + pos[1]; } return count; } struct wpabuf * ieee802_11_vendor_ie_concat(const u8 *ies, size_t ies_len, u32 oui_type) { struct wpabuf *buf; const u8 *end, *pos, *ie; pos = ies; end = ies + ies_len; ie = NULL; while (pos + 1 < end) { if (pos + 2 + pos[1] > end) return NULL; if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 && WPA_GET_BE32(&pos[2]) == oui_type) { ie = pos; break; } pos += 2 + pos[1]; } if (ie == NULL) return NULL; /* No specified vendor IE found */ buf = wpabuf_alloc(ies_len); if (buf == NULL) return NULL; /* * There may be multiple vendor IEs in the message, so need to * concatenate their data fields. */ while (pos + 1 < end) { if (pos + 2 + pos[1] > end) break; if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 && WPA_GET_BE32(&pos[2]) == oui_type) wpabuf_put_data(buf, pos + 6, pos[1] - 4); pos += 2 + pos[1]; } return buf; } const u8 * get_hdr_bssid(const struct ieee80211_hdr *hdr, size_t len) { u16 fc, type, stype; /* * PS-Poll frames are 16 bytes. All other frames are * 24 bytes or longer. */ if (len < 16) return NULL; fc = le_to_host16(hdr->frame_control); type = WLAN_FC_GET_TYPE(fc); stype = WLAN_FC_GET_STYPE(fc); switch (type) { case WLAN_FC_TYPE_DATA: if (len < 24) return NULL; switch (fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) { case WLAN_FC_FROMDS | WLAN_FC_TODS: case WLAN_FC_TODS: return hdr->addr1; case WLAN_FC_FROMDS: return hdr->addr2; default: return NULL; } case WLAN_FC_TYPE_CTRL: if (stype != WLAN_FC_STYPE_PSPOLL) return NULL; return hdr->addr1; case WLAN_FC_TYPE_MGMT: return hdr->addr3; default: return NULL; } } int hostapd_config_wmm_ac(struct hostapd_wmm_ac_params wmm_ac_params[], const char *name, const char *val) { int num, v; const char *pos; struct hostapd_wmm_ac_params *ac; /* skip 'wme_ac_' or 'wmm_ac_' prefix */ pos = name + 7; if (os_strncmp(pos, "be_", 3) == 0) { num = 0; pos += 3; } else if (os_strncmp(pos, "bk_", 3) == 0) { num = 1; pos += 3; } else if (os_strncmp(pos, "vi_", 3) == 0) { num = 2; pos += 3; } else if (os_strncmp(pos, "vo_", 3) == 0) { num = 3; pos += 3; } else { wpa_printf(MSG_ERROR, "Unknown WMM name '%s'", pos); return -1; } ac = &wmm_ac_params[num]; if (os_strcmp(pos, "aifs") == 0) { v = atoi(val); if (v < 1 || v > 255) { wpa_printf(MSG_ERROR, "Invalid AIFS value %d", v); return -1; } ac->aifs = v; } else if (os_strcmp(pos, "cwmin") == 0) { v = atoi(val); if (v < 0 || v > 12) { wpa_printf(MSG_ERROR, "Invalid cwMin value %d", v); return -1; } ac->cwmin = v; } else if (os_strcmp(pos, "cwmax") == 0) { v = atoi(val); if (v < 0 || v > 12) { wpa_printf(MSG_ERROR, "Invalid cwMax value %d", v); return -1; } ac->cwmax = v; } else if (os_strcmp(pos, "txop_limit") == 0) { v = atoi(val); if (v < 0 || v > 0xffff) { wpa_printf(MSG_ERROR, "Invalid txop value %d", v); return -1; } ac->txop_limit = v; } else if (os_strcmp(pos, "acm") == 0) { v = atoi(val); if (v < 0 || v > 1) { wpa_printf(MSG_ERROR, "Invalid acm value %d", v); return -1; } ac->admission_control_mandatory = v; } else { wpa_printf(MSG_ERROR, "Unknown wmm_ac_ field '%s'", pos); return -1; } return 0; } enum hostapd_hw_mode ieee80211_freq_to_chan(int freq, u8 *channel) { enum hostapd_hw_mode mode = NUM_HOSTAPD_MODES; if (freq >= 2412 && freq <= 2472) { mode = HOSTAPD_MODE_IEEE80211G; *channel = (freq - 2407) / 5; } else if (freq == 2484) { mode = HOSTAPD_MODE_IEEE80211B; *channel = 14; } else if (freq >= 4900 && freq < 5000) { mode = HOSTAPD_MODE_IEEE80211A; *channel = (freq - 4000) / 5; } else if (freq >= 5000 && freq < 5900) { mode = HOSTAPD_MODE_IEEE80211A; *channel = (freq - 5000) / 5; } else if (freq >= 56160 + 2160 * 1 && freq <= 56160 + 2160 * 4) { mode = HOSTAPD_MODE_IEEE80211AD; *channel = (freq - 56160) / 2160; } return mode; } static int is_11b(u8 rate) { return rate == 0x02 || rate == 0x04 || rate == 0x0b || rate == 0x16; } int supp_rates_11b_only(struct ieee802_11_elems *elems) { int num_11b = 0, num_others = 0; int i; if (elems->supp_rates == NULL && elems->ext_supp_rates == NULL) return 0; for (i = 0; elems->supp_rates && i < elems->supp_rates_len; i++) { if (is_11b(elems->supp_rates[i])) num_11b++; else num_others++; } for (i = 0; elems->ext_supp_rates && i < elems->ext_supp_rates_len; i++) { if (is_11b(elems->ext_supp_rates[i])) num_11b++; else num_others++; } return num_11b > 0 && num_others == 0; } wpa-2.1/src/common/ieee802_11_common.h000066400000000000000000000045571227414666700174370ustar00rootroot00000000000000/* * IEEE 802.11 Common routines * Copyright (c) 2002-2012, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef IEEE802_11_COMMON_H #define IEEE802_11_COMMON_H /* Parsed Information Elements */ struct ieee802_11_elems { const u8 *ssid; const u8 *supp_rates; const u8 *ds_params; const u8 *challenge; const u8 *erp_info; const u8 *ext_supp_rates; const u8 *wpa_ie; const u8 *rsn_ie; const u8 *wmm; /* WMM Information or Parameter Element */ const u8 *wmm_tspec; const u8 *wps_ie; const u8 *supp_channels; const u8 *mdie; const u8 *ftie; const u8 *timeout_int; const u8 *ht_capabilities; const u8 *ht_operation; const u8 *vht_capabilities; const u8 *vht_operation; const u8 *vendor_ht_cap; const u8 *p2p; const u8 *wfd; const u8 *link_id; const u8 *interworking; const u8 *qos_map_set; const u8 *hs20; const u8 *ext_capab; const u8 *bss_max_idle_period; const u8 *ssid_list; u8 ssid_len; u8 supp_rates_len; u8 ds_params_len; u8 challenge_len; u8 erp_info_len; u8 ext_supp_rates_len; u8 wpa_ie_len; u8 rsn_ie_len; u8 wmm_len; /* 7 = WMM Information; 24 = WMM Parameter */ u8 wmm_tspec_len; u8 wps_ie_len; u8 supp_channels_len; u8 mdie_len; u8 ftie_len; u8 timeout_int_len; u8 ht_capabilities_len; u8 ht_operation_len; u8 vht_capabilities_len; u8 vht_operation_len; u8 vendor_ht_cap_len; u8 p2p_len; u8 wfd_len; u8 interworking_len; u8 qos_map_set_len; u8 hs20_len; u8 ext_capab_len; u8 ssid_list_len; }; typedef enum { ParseOK = 0, ParseUnknown = 1, ParseFailed = -1 } ParseRes; ParseRes ieee802_11_parse_elems(const u8 *start, size_t len, struct ieee802_11_elems *elems, int show_errors); int ieee802_11_ie_count(const u8 *ies, size_t ies_len); struct wpabuf * ieee802_11_vendor_ie_concat(const u8 *ies, size_t ies_len, u32 oui_type); struct ieee80211_hdr; const u8 * get_hdr_bssid(const struct ieee80211_hdr *hdr, size_t len); struct hostapd_wmm_ac_params { int cwmin; int cwmax; int aifs; int txop_limit; /* in units of 32us */ int admission_control_mandatory; }; int hostapd_config_wmm_ac(struct hostapd_wmm_ac_params wmm_ac_params[], const char *name, const char *val); enum hostapd_hw_mode ieee80211_freq_to_chan(int freq, u8 *channel); int supp_rates_11b_only(struct ieee802_11_elems *elems); #endif /* IEEE802_11_COMMON_H */ wpa-2.1/src/common/ieee802_11_defs.h000066400000000000000000001112721227414666700170610ustar00rootroot00000000000000/* * IEEE 802.11 Frame type definitions * Copyright (c) 2002-2009, Jouni Malinen * Copyright (c) 2007-2008 Intel Corporation * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef IEEE802_11_DEFS_H #define IEEE802_11_DEFS_H /* IEEE 802.11 defines */ #define WLAN_FC_PVER 0x0003 #define WLAN_FC_TODS 0x0100 #define WLAN_FC_FROMDS 0x0200 #define WLAN_FC_MOREFRAG 0x0400 #define WLAN_FC_RETRY 0x0800 #define WLAN_FC_PWRMGT 0x1000 #define WLAN_FC_MOREDATA 0x2000 #define WLAN_FC_ISWEP 0x4000 #define WLAN_FC_ORDER 0x8000 #define WLAN_FC_GET_TYPE(fc) (((fc) & 0x000c) >> 2) #define WLAN_FC_GET_STYPE(fc) (((fc) & 0x00f0) >> 4) #define WLAN_GET_SEQ_FRAG(seq) ((seq) & (BIT(3) | BIT(2) | BIT(1) | BIT(0))) #define WLAN_GET_SEQ_SEQ(seq) \ (((seq) & (~(BIT(3) | BIT(2) | BIT(1) | BIT(0)))) >> 4) #define WLAN_FC_TYPE_MGMT 0 #define WLAN_FC_TYPE_CTRL 1 #define WLAN_FC_TYPE_DATA 2 /* management */ #define WLAN_FC_STYPE_ASSOC_REQ 0 #define WLAN_FC_STYPE_ASSOC_RESP 1 #define WLAN_FC_STYPE_REASSOC_REQ 2 #define WLAN_FC_STYPE_REASSOC_RESP 3 #define WLAN_FC_STYPE_PROBE_REQ 4 #define WLAN_FC_STYPE_PROBE_RESP 5 #define WLAN_FC_STYPE_BEACON 8 #define WLAN_FC_STYPE_ATIM 9 #define WLAN_FC_STYPE_DISASSOC 10 #define WLAN_FC_STYPE_AUTH 11 #define WLAN_FC_STYPE_DEAUTH 12 #define WLAN_FC_STYPE_ACTION 13 /* control */ #define WLAN_FC_STYPE_PSPOLL 10 #define WLAN_FC_STYPE_RTS 11 #define WLAN_FC_STYPE_CTS 12 #define WLAN_FC_STYPE_ACK 13 #define WLAN_FC_STYPE_CFEND 14 #define WLAN_FC_STYPE_CFENDACK 15 /* data */ #define WLAN_FC_STYPE_DATA 0 #define WLAN_FC_STYPE_DATA_CFACK 1 #define WLAN_FC_STYPE_DATA_CFPOLL 2 #define WLAN_FC_STYPE_DATA_CFACKPOLL 3 #define WLAN_FC_STYPE_NULLFUNC 4 #define WLAN_FC_STYPE_CFACK 5 #define WLAN_FC_STYPE_CFPOLL 6 #define WLAN_FC_STYPE_CFACKPOLL 7 #define WLAN_FC_STYPE_QOS_DATA 8 #define WLAN_FC_STYPE_QOS_DATA_CFACK 9 #define WLAN_FC_STYPE_QOS_DATA_CFPOLL 10 #define WLAN_FC_STYPE_QOS_DATA_CFACKPOLL 11 #define WLAN_FC_STYPE_QOS_NULL 12 #define WLAN_FC_STYPE_QOS_CFPOLL 14 #define WLAN_FC_STYPE_QOS_CFACKPOLL 15 /* Authentication algorithms */ #define WLAN_AUTH_OPEN 0 #define WLAN_AUTH_SHARED_KEY 1 #define WLAN_AUTH_FT 2 #define WLAN_AUTH_SAE 3 #define WLAN_AUTH_LEAP 128 #define WLAN_AUTH_CHALLENGE_LEN 128 #define WLAN_CAPABILITY_ESS BIT(0) #define WLAN_CAPABILITY_IBSS BIT(1) #define WLAN_CAPABILITY_CF_POLLABLE BIT(2) #define WLAN_CAPABILITY_CF_POLL_REQUEST BIT(3) #define WLAN_CAPABILITY_PRIVACY BIT(4) #define WLAN_CAPABILITY_SHORT_PREAMBLE BIT(5) #define WLAN_CAPABILITY_PBCC BIT(6) #define WLAN_CAPABILITY_CHANNEL_AGILITY BIT(7) #define WLAN_CAPABILITY_SPECTRUM_MGMT BIT(8) #define WLAN_CAPABILITY_SHORT_SLOT_TIME BIT(10) #define WLAN_CAPABILITY_DSSS_OFDM BIT(13) /* Status codes (IEEE 802.11-2007, 7.3.1.9, Table 7-23) */ #define WLAN_STATUS_SUCCESS 0 #define WLAN_STATUS_UNSPECIFIED_FAILURE 1 #define WLAN_STATUS_TDLS_WAKEUP_ALTERNATE 2 #define WLAN_STATUS_TDLS_WAKEUP_REJECT 3 #define WLAN_STATUS_SECURITY_DISABLED 5 #define WLAN_STATUS_UNACCEPTABLE_LIFETIME 6 #define WLAN_STATUS_NOT_IN_SAME_BSS 7 #define WLAN_STATUS_CAPS_UNSUPPORTED 10 #define WLAN_STATUS_REASSOC_NO_ASSOC 11 #define WLAN_STATUS_ASSOC_DENIED_UNSPEC 12 #define WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG 13 #define WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION 14 #define WLAN_STATUS_CHALLENGE_FAIL 15 #define WLAN_STATUS_AUTH_TIMEOUT 16 #define WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA 17 #define WLAN_STATUS_ASSOC_DENIED_RATES 18 /* IEEE 802.11b */ #define WLAN_STATUS_ASSOC_DENIED_NOSHORT 19 #define WLAN_STATUS_ASSOC_DENIED_NOPBCC 20 #define WLAN_STATUS_ASSOC_DENIED_NOAGILITY 21 /* IEEE 802.11h */ #define WLAN_STATUS_SPEC_MGMT_REQUIRED 22 #define WLAN_STATUS_PWR_CAPABILITY_NOT_VALID 23 #define WLAN_STATUS_SUPPORTED_CHANNEL_NOT_VALID 24 /* IEEE 802.11g */ #define WLAN_STATUS_ASSOC_DENIED_NO_SHORT_SLOT_TIME 25 #define WLAN_STATUS_ASSOC_DENIED_NO_DSSS_OFDM 26 #define WLAN_STATUS_ASSOC_DENIED_NO_HT 27 #define WLAN_STATUS_R0KH_UNREACHABLE 28 #define WLAN_STATUS_ASSOC_DENIED_NO_PCO 29 /* IEEE 802.11w */ #define WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY 30 #define WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION 31 #define WLAN_STATUS_UNSPECIFIED_QOS_FAILURE 32 #define WLAN_STATUS_REQUEST_DECLINED 37 #define WLAN_STATUS_INVALID_PARAMETERS 38 /* IEEE 802.11i */ #define WLAN_STATUS_INVALID_IE 40 #define WLAN_STATUS_GROUP_CIPHER_NOT_VALID 41 #define WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID 42 #define WLAN_STATUS_AKMP_NOT_VALID 43 #define WLAN_STATUS_UNSUPPORTED_RSN_IE_VERSION 44 #define WLAN_STATUS_INVALID_RSN_IE_CAPAB 45 #define WLAN_STATUS_CIPHER_REJECTED_PER_POLICY 46 #define WLAN_STATUS_TS_NOT_CREATED 47 #define WLAN_STATUS_DIRECT_LINK_NOT_ALLOWED 48 #define WLAN_STATUS_DEST_STA_NOT_PRESENT 49 #define WLAN_STATUS_DEST_STA_NOT_QOS_STA 50 #define WLAN_STATUS_ASSOC_DENIED_LISTEN_INT_TOO_LARGE 51 /* IEEE 802.11r */ #define WLAN_STATUS_INVALID_FT_ACTION_FRAME_COUNT 52 #define WLAN_STATUS_INVALID_PMKID 53 #define WLAN_STATUS_INVALID_MDIE 54 #define WLAN_STATUS_INVALID_FTIE 55 #define WLAN_STATUS_GAS_ADV_PROTO_NOT_SUPPORTED 59 #define WLAN_STATUS_NO_OUTSTANDING_GAS_REQ 60 #define WLAN_STATUS_GAS_RESP_NOT_RECEIVED 61 #define WLAN_STATUS_STA_TIMED_OUT_WAITING_FOR_GAS_RESP 62 #define WLAN_STATUS_GAS_RESP_LARGER_THAN_LIMIT 63 #define WLAN_STATUS_REQ_REFUSED_HOME 64 #define WLAN_STATUS_ADV_SRV_UNREACHABLE 65 #define WLAN_STATUS_REQ_REFUSED_SSPN 67 #define WLAN_STATUS_REQ_REFUSED_UNAUTH_ACCESS 68 #define WLAN_STATUS_INVALID_RSNIE 72 #define WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ 76 #define WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED 77 #define WLAN_STATUS_TRANSMISSION_FAILURE 79 #define WLAN_STATUS_ASSOC_DENIED_NO_VHT 104 /* Reason codes (IEEE 802.11-2007, 7.3.1.7, Table 7-22) */ #define WLAN_REASON_UNSPECIFIED 1 #define WLAN_REASON_PREV_AUTH_NOT_VALID 2 #define WLAN_REASON_DEAUTH_LEAVING 3 #define WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY 4 #define WLAN_REASON_DISASSOC_AP_BUSY 5 #define WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA 6 #define WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA 7 #define WLAN_REASON_DISASSOC_STA_HAS_LEFT 8 #define WLAN_REASON_STA_REQ_ASSOC_WITHOUT_AUTH 9 /* IEEE 802.11h */ #define WLAN_REASON_PWR_CAPABILITY_NOT_VALID 10 #define WLAN_REASON_SUPPORTED_CHANNEL_NOT_VALID 11 /* IEEE 802.11i */ #define WLAN_REASON_INVALID_IE 13 #define WLAN_REASON_MICHAEL_MIC_FAILURE 14 #define WLAN_REASON_4WAY_HANDSHAKE_TIMEOUT 15 #define WLAN_REASON_GROUP_KEY_UPDATE_TIMEOUT 16 #define WLAN_REASON_IE_IN_4WAY_DIFFERS 17 #define WLAN_REASON_GROUP_CIPHER_NOT_VALID 18 #define WLAN_REASON_PAIRWISE_CIPHER_NOT_VALID 19 #define WLAN_REASON_AKMP_NOT_VALID 20 #define WLAN_REASON_UNSUPPORTED_RSN_IE_VERSION 21 #define WLAN_REASON_INVALID_RSN_IE_CAPAB 22 #define WLAN_REASON_IEEE_802_1X_AUTH_FAILED 23 #define WLAN_REASON_CIPHER_SUITE_REJECTED 24 #define WLAN_REASON_TDLS_TEARDOWN_UNREACHABLE 25 #define WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED 26 /* IEEE 802.11e */ #define WLAN_REASON_DISASSOC_LOW_ACK 34 /* Information Element IDs */ #define WLAN_EID_SSID 0 #define WLAN_EID_SUPP_RATES 1 #define WLAN_EID_FH_PARAMS 2 #define WLAN_EID_DS_PARAMS 3 #define WLAN_EID_CF_PARAMS 4 #define WLAN_EID_TIM 5 #define WLAN_EID_IBSS_PARAMS 6 #define WLAN_EID_COUNTRY 7 #define WLAN_EID_BSS_LOAD 11 #define WLAN_EID_CHALLENGE 16 /* EIDs defined by IEEE 802.11h - START */ #define WLAN_EID_PWR_CONSTRAINT 32 #define WLAN_EID_PWR_CAPABILITY 33 #define WLAN_EID_TPC_REQUEST 34 #define WLAN_EID_TPC_REPORT 35 #define WLAN_EID_SUPPORTED_CHANNELS 36 #define WLAN_EID_CHANNEL_SWITCH 37 #define WLAN_EID_MEASURE_REQUEST 38 #define WLAN_EID_MEASURE_REPORT 39 #define WLAN_EID_QUITE 40 #define WLAN_EID_IBSS_DFS 41 /* EIDs defined by IEEE 802.11h - END */ #define WLAN_EID_ERP_INFO 42 #define WLAN_EID_HT_CAP 45 #define WLAN_EID_QOS 46 #define WLAN_EID_RSN 48 #define WLAN_EID_EXT_SUPP_RATES 50 #define WLAN_EID_MOBILITY_DOMAIN 54 #define WLAN_EID_FAST_BSS_TRANSITION 55 #define WLAN_EID_TIMEOUT_INTERVAL 56 #define WLAN_EID_RIC_DATA 57 #define WLAN_EID_SUPPORTED_OPERATING_CLASSES 59 #define WLAN_EID_HT_OPERATION 61 #define WLAN_EID_SECONDARY_CHANNEL_OFFSET 62 #define WLAN_EID_WAPI 68 #define WLAN_EID_TIME_ADVERTISEMENT 69 #define WLAN_EID_20_40_BSS_COEXISTENCE 72 #define WLAN_EID_20_40_BSS_INTOLERANT 73 #define WLAN_EID_OVERLAPPING_BSS_SCAN_PARAMS 74 #define WLAN_EID_MMIE 76 #define WLAN_EID_SSID_LIST 84 #define WLAN_EID_BSS_MAX_IDLE_PERIOD 90 #define WLAN_EID_TFS_REQ 91 #define WLAN_EID_TFS_RESP 92 #define WLAN_EID_WNMSLEEP 93 #define WLAN_EID_TIME_ZONE 98 #define WLAN_EID_LINK_ID 101 #define WLAN_EID_INTERWORKING 107 #define WLAN_EID_ADV_PROTO 108 #define WLAN_EID_QOS_MAP_SET 110 #define WLAN_EID_ROAMING_CONSORTIUM 111 #define WLAN_EID_EXT_CAPAB 127 #define WLAN_EID_CCKM 156 #define WLAN_EID_VHT_CAP 191 #define WLAN_EID_VHT_OPERATION 192 #define WLAN_EID_VHT_EXTENDED_BSS_LOAD 193 #define WLAN_EID_VHT_WIDE_BW_CHSWITCH 194 #define WLAN_EID_VHT_TRANSMIT_POWER_ENVELOPE 195 #define WLAN_EID_VHT_CHANNEL_SWITCH_WRAPPER 196 #define WLAN_EID_VHT_AID 197 #define WLAN_EID_VHT_QUIET_CHANNEL 198 #define WLAN_EID_VHT_OPERATING_MODE_NOTIFICATION 199 #define WLAN_EID_VENDOR_SPECIFIC 221 /* Action frame categories (IEEE 802.11-2007, 7.3.1.11, Table 7-24) */ #define WLAN_ACTION_SPECTRUM_MGMT 0 #define WLAN_ACTION_QOS 1 #define WLAN_ACTION_DLS 2 #define WLAN_ACTION_BLOCK_ACK 3 #define WLAN_ACTION_PUBLIC 4 #define WLAN_ACTION_RADIO_MEASUREMENT 5 #define WLAN_ACTION_FT 6 #define WLAN_ACTION_HT 7 #define WLAN_ACTION_SA_QUERY 8 #define WLAN_ACTION_PROTECTED_DUAL 9 #define WLAN_ACTION_WNM 10 #define WLAN_ACTION_UNPROTECTED_WNM 11 #define WLAN_ACTION_TDLS 12 #define WLAN_ACTION_WMM 17 /* WMM Specification 1.1 */ #define WLAN_ACTION_VENDOR_SPECIFIC 127 /* Public action codes */ #define WLAN_PA_20_40_BSS_COEX 0 #define WLAN_PA_VENDOR_SPECIFIC 9 #define WLAN_PA_GAS_INITIAL_REQ 10 #define WLAN_PA_GAS_INITIAL_RESP 11 #define WLAN_PA_GAS_COMEBACK_REQ 12 #define WLAN_PA_GAS_COMEBACK_RESP 13 #define WLAN_TDLS_DISCOVERY_RESPONSE 14 /* Protected Dual of Public Action frames */ #define WLAN_PROT_DSE_ENABLEMENT 1 #define WLAN_PROT_DSE_DEENABLEMENT 2 #define WLAN_PROT_EXT_CSA 4 #define WLAN_PROT_MEASUREMENT_REQ 5 #define WLAN_PROT_MEASUREMENT_REPORT 6 #define WLAN_PROT_DSE_POWER_CONSTRAINT 8 #define WLAN_PROT_VENDOR_SPECIFIC 9 #define WLAN_PROT_GAS_INITIAL_REQ 10 #define WLAN_PROT_GAS_INITIAL_RESP 11 #define WLAN_PROT_GAS_COMEBACK_REQ 12 #define WLAN_PROT_GAS_COMEBACK_RESP 13 /* SA Query Action frame (IEEE 802.11w/D8.0, 7.4.9) */ #define WLAN_SA_QUERY_REQUEST 0 #define WLAN_SA_QUERY_RESPONSE 1 #define WLAN_SA_QUERY_TR_ID_LEN 2 /* TDLS action codes */ #define WLAN_TDLS_SETUP_REQUEST 0 #define WLAN_TDLS_SETUP_RESPONSE 1 #define WLAN_TDLS_SETUP_CONFIRM 2 #define WLAN_TDLS_TEARDOWN 3 #define WLAN_TDLS_PEER_TRAFFIC_INDICATION 4 #define WLAN_TDLS_CHANNEL_SWITCH_REQUEST 5 #define WLAN_TDLS_CHANNEL_SWITCH_RESPONSE 6 #define WLAN_TDLS_PEER_PSM_REQUEST 7 #define WLAN_TDLS_PEER_PSM_RESPONSE 8 #define WLAN_TDLS_PEER_TRAFFIC_RESPONSE 9 #define WLAN_TDLS_DISCOVERY_REQUEST 10 /* Timeout Interval Type */ #define WLAN_TIMEOUT_REASSOC_DEADLINE 1 #define WLAN_TIMEOUT_KEY_LIFETIME 2 #define WLAN_TIMEOUT_ASSOC_COMEBACK 3 /* Interworking element (IEEE 802.11u) - Access Network Options */ #define INTERWORKING_ANO_ACCESS_NETWORK_MASK 0x0f #define INTERWORKING_ANO_INTERNET 0x10 #define INTERWORKING_ANO_ASRA 0x20 #define INTERWORKING_ANO_ESR 0x40 #define INTERWORKING_ANO_UESA 0x80 #define INTERWORKING_ANT_PRIVATE 0 #define INTERWORKING_ANT_PRIVATE_WITH_GUEST 1 #define INTERWORKING_ANT_CHARGEABLE_PUBLIC 2 #define INTERWORKING_ANT_FREE_PUBLIC 3 #define INTERWORKING_ANT_PERSONAL_DEVICE 4 #define INTERWORKING_ANT_EMERGENCY_SERVICES 5 #define INTERWORKING_ANT_TEST 6 #define INTERWORKING_ANT_WILDCARD 15 /* Advertisement Protocol ID definitions (IEEE Std 802.11u-2011) */ enum adv_proto_id { ACCESS_NETWORK_QUERY_PROTOCOL = 0, MIH_INFO_SERVICE = 1, MIH_CMD_AND_EVENT_DISCOVERY = 2, EMERGENCY_ALERT_SYSTEM = 3, ADV_PROTO_VENDOR_SPECIFIC = 221 }; /* Access Network Query Protocol info ID definitions (IEEE Std 802.11u-2011) */ enum anqp_info_id { ANQP_QUERY_LIST = 256, ANQP_CAPABILITY_LIST = 257, ANQP_VENUE_NAME = 258, ANQP_EMERGENCY_CALL_NUMBER = 259, ANQP_NETWORK_AUTH_TYPE = 260, ANQP_ROAMING_CONSORTIUM = 261, ANQP_IP_ADDR_TYPE_AVAILABILITY = 262, ANQP_NAI_REALM = 263, ANQP_3GPP_CELLULAR_NETWORK = 264, ANQP_AP_GEOSPATIAL_LOCATION = 265, ANQP_AP_CIVIC_LOCATION = 266, ANQP_AP_LOCATION_PUBLIC_URI = 267, ANQP_DOMAIN_NAME = 268, ANQP_EMERGENCY_ALERT_URI = 269, ANQP_EMERGENCY_NAI = 271, ANQP_VENDOR_SPECIFIC = 56797 }; /* NAI Realm list - EAP Method subfield - Authentication Parameter ID */ enum nai_realm_eap_auth_param { NAI_REALM_EAP_AUTH_EXPANDED_EAP_METHOD = 1, NAI_REALM_EAP_AUTH_NON_EAP_INNER_AUTH = 2, NAI_REALM_EAP_AUTH_INNER_AUTH_EAP_METHOD = 3, NAI_REALM_EAP_AUTH_EXPANDED_INNER_EAP_METHOD = 4, NAI_REALM_EAP_AUTH_CRED_TYPE = 5, NAI_REALM_EAP_AUTH_TUNNELED_CRED_TYPE = 6, NAI_REALM_EAP_AUTH_VENDOR_SPECIFIC = 221 }; enum nai_realm_eap_auth_inner_non_eap { NAI_REALM_INNER_NON_EAP_PAP = 1, NAI_REALM_INNER_NON_EAP_CHAP = 2, NAI_REALM_INNER_NON_EAP_MSCHAP = 3, NAI_REALM_INNER_NON_EAP_MSCHAPV2 = 4 }; enum nai_realm_eap_cred_type { NAI_REALM_CRED_TYPE_SIM = 1, NAI_REALM_CRED_TYPE_USIM = 2, NAI_REALM_CRED_TYPE_NFC_SECURE_ELEMENT = 3, NAI_REALM_CRED_TYPE_HARDWARE_TOKEN = 4, NAI_REALM_CRED_TYPE_SOFTOKEN = 5, NAI_REALM_CRED_TYPE_CERTIFICATE = 6, NAI_REALM_CRED_TYPE_USERNAME_PASSWORD = 7, NAI_REALM_CRED_TYPE_NONE = 8, NAI_REALM_CRED_TYPE_ANONYMOUS = 9, NAI_REALM_CRED_TYPE_VENDOR_SPECIFIC = 10 }; #ifdef _MSC_VER #pragma pack(push, 1) #endif /* _MSC_VER */ struct ieee80211_hdr { le16 frame_control; le16 duration_id; u8 addr1[6]; u8 addr2[6]; u8 addr3[6]; le16 seq_ctrl; /* followed by 'u8 addr4[6];' if ToDS and FromDS is set in data frame */ } STRUCT_PACKED; #define IEEE80211_DA_FROMDS addr1 #define IEEE80211_BSSID_FROMDS addr2 #define IEEE80211_SA_FROMDS addr3 #define IEEE80211_HDRLEN (sizeof(struct ieee80211_hdr)) #define IEEE80211_FC(type, stype) host_to_le16((type << 2) | (stype << 4)) struct ieee80211_mgmt { le16 frame_control; le16 duration; u8 da[6]; u8 sa[6]; u8 bssid[6]; le16 seq_ctrl; union { struct { le16 auth_alg; le16 auth_transaction; le16 status_code; /* possibly followed by Challenge text */ u8 variable[0]; } STRUCT_PACKED auth; struct { le16 reason_code; u8 variable[0]; } STRUCT_PACKED deauth; struct { le16 capab_info; le16 listen_interval; /* followed by SSID and Supported rates */ u8 variable[0]; } STRUCT_PACKED assoc_req; struct { le16 capab_info; le16 status_code; le16 aid; /* followed by Supported rates */ u8 variable[0]; } STRUCT_PACKED assoc_resp, reassoc_resp; struct { le16 capab_info; le16 listen_interval; u8 current_ap[6]; /* followed by SSID and Supported rates */ u8 variable[0]; } STRUCT_PACKED reassoc_req; struct { le16 reason_code; u8 variable[0]; } STRUCT_PACKED disassoc; struct { u8 timestamp[8]; le16 beacon_int; le16 capab_info; /* followed by some of SSID, Supported rates, * FH Params, DS Params, CF Params, IBSS Params, TIM */ u8 variable[0]; } STRUCT_PACKED beacon; struct { /* only variable items: SSID, Supported rates */ u8 variable[0]; } STRUCT_PACKED probe_req; struct { u8 timestamp[8]; le16 beacon_int; le16 capab_info; /* followed by some of SSID, Supported rates, * FH Params, DS Params, CF Params, IBSS Params */ u8 variable[0]; } STRUCT_PACKED probe_resp; struct { u8 category; union { struct { u8 action_code; u8 dialog_token; u8 status_code; u8 variable[0]; } STRUCT_PACKED wmm_action; struct{ u8 action_code; u8 element_id; u8 length; u8 switch_mode; u8 new_chan; u8 switch_count; } STRUCT_PACKED chan_switch; struct { u8 action; u8 sta_addr[ETH_ALEN]; u8 target_ap_addr[ETH_ALEN]; u8 variable[0]; /* FT Request */ } STRUCT_PACKED ft_action_req; struct { u8 action; u8 sta_addr[ETH_ALEN]; u8 target_ap_addr[ETH_ALEN]; le16 status_code; u8 variable[0]; /* FT Request */ } STRUCT_PACKED ft_action_resp; struct { u8 action; u8 trans_id[WLAN_SA_QUERY_TR_ID_LEN]; } STRUCT_PACKED sa_query_req; struct { u8 action; /* */ u8 trans_id[WLAN_SA_QUERY_TR_ID_LEN]; } STRUCT_PACKED sa_query_resp; struct { u8 action; u8 dialogtoken; u8 variable[0]; } STRUCT_PACKED wnm_sleep_req; struct { u8 action; u8 dialogtoken; le16 keydata_len; u8 variable[0]; } STRUCT_PACKED wnm_sleep_resp; struct { u8 action; u8 variable[0]; } STRUCT_PACKED public_action; struct { u8 action; /* 9 */ u8 oui[3]; /* Vendor-specific content */ u8 variable[0]; } STRUCT_PACKED vs_public_action; struct { u8 action; /* 7 */ u8 dialog_token; u8 req_mode; le16 disassoc_timer; u8 validity_interval; /* BSS Termination Duration (optional), * Session Information URL (optional), * BSS Transition Candidate List * Entries */ u8 variable[0]; } STRUCT_PACKED bss_tm_req; struct { u8 action; /* 8 */ u8 dialog_token; u8 status_code; u8 bss_termination_delay; /* Target BSSID (optional), * BSS Transition Candidate List * Entries (optional) */ u8 variable[0]; } STRUCT_PACKED bss_tm_resp; struct { u8 action; /* 6 */ u8 dialog_token; u8 query_reason; /* BSS Transition Candidate List * Entries (optional) */ u8 variable[0]; } STRUCT_PACKED bss_tm_query; } u; } STRUCT_PACKED action; } u; } STRUCT_PACKED; /* Rx MCS bitmask is in the first 77 bits of supported_mcs_set */ #define IEEE80211_HT_MCS_MASK_LEN 10 struct ieee80211_ht_capabilities { le16 ht_capabilities_info; u8 a_mpdu_params; u8 supported_mcs_set[16]; le16 ht_extended_capabilities; le32 tx_bf_capability_info; u8 asel_capabilities; } STRUCT_PACKED; struct ieee80211_ht_operation { u8 control_chan; u8 ht_param; le16 operation_mode; le16 stbc_param; u8 basic_set[16]; } STRUCT_PACKED; struct ieee80211_obss_scan_parameters { le16 scan_passive_dwell; le16 scan_active_dwell; le16 width_trigger_scan_interval; le16 scan_passive_total_per_channel; le16 scan_active_total_per_channel; le16 channel_transition_delay_factor; le16 scan_activity_threshold; } STRUCT_PACKED; struct ieee80211_vht_capabilities { le32 vht_capabilities_info; struct { le16 rx_map; le16 rx_highest; le16 tx_map; le16 tx_highest; } vht_supported_mcs_set; } STRUCT_PACKED; struct ieee80211_vht_operation { u8 vht_op_info_chwidth; u8 vht_op_info_chan_center_freq_seg0_idx; u8 vht_op_info_chan_center_freq_seg1_idx; le16 vht_basic_mcs_set; } STRUCT_PACKED; #ifdef _MSC_VER #pragma pack(pop) #endif /* _MSC_VER */ #define ERP_INFO_NON_ERP_PRESENT BIT(0) #define ERP_INFO_USE_PROTECTION BIT(1) #define ERP_INFO_BARKER_PREAMBLE_MODE BIT(2) #define HT_CAP_INFO_LDPC_CODING_CAP ((u16) BIT(0)) #define HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET ((u16) BIT(1)) #define HT_CAP_INFO_SMPS_MASK ((u16) (BIT(2) | BIT(3))) #define HT_CAP_INFO_SMPS_STATIC ((u16) 0) #define HT_CAP_INFO_SMPS_DYNAMIC ((u16) BIT(2)) #define HT_CAP_INFO_SMPS_DISABLED ((u16) (BIT(2) | BIT(3))) #define HT_CAP_INFO_GREEN_FIELD ((u16) BIT(4)) #define HT_CAP_INFO_SHORT_GI20MHZ ((u16) BIT(5)) #define HT_CAP_INFO_SHORT_GI40MHZ ((u16) BIT(6)) #define HT_CAP_INFO_TX_STBC ((u16) BIT(7)) #define HT_CAP_INFO_RX_STBC_MASK ((u16) (BIT(8) | BIT(9))) #define HT_CAP_INFO_RX_STBC_1 ((u16) BIT(8)) #define HT_CAP_INFO_RX_STBC_12 ((u16) BIT(9)) #define HT_CAP_INFO_RX_STBC_123 ((u16) (BIT(8) | BIT(9))) #define HT_CAP_INFO_DELAYED_BA ((u16) BIT(10)) #define HT_CAP_INFO_MAX_AMSDU_SIZE ((u16) BIT(11)) #define HT_CAP_INFO_DSSS_CCK40MHZ ((u16) BIT(12)) #define HT_CAP_INFO_PSMP_SUPP ((u16) BIT(13)) #define HT_CAP_INFO_40MHZ_INTOLERANT ((u16) BIT(14)) #define HT_CAP_INFO_LSIG_TXOP_PROTECT_SUPPORT ((u16) BIT(15)) #define EXT_HT_CAP_INFO_PCO ((u16) BIT(0)) #define EXT_HT_CAP_INFO_TRANS_TIME_OFFSET 1 #define EXT_HT_CAP_INFO_MCS_FEEDBACK_OFFSET 8 #define EXT_HT_CAP_INFO_HTC_SUPPORTED ((u16) BIT(10)) #define EXT_HT_CAP_INFO_RD_RESPONDER ((u16) BIT(11)) #define TX_BEAMFORM_CAP_TXBF_CAP ((u32) BIT(0)) #define TX_BEAMFORM_CAP_RX_STAGGERED_SOUNDING_CAP ((u32) BIT(1)) #define TX_BEAMFORM_CAP_TX_STAGGERED_SOUNDING_CAP ((u32) BIT(2)) #define TX_BEAMFORM_CAP_RX_ZLF_CAP ((u32) BIT(3)) #define TX_BEAMFORM_CAP_TX_ZLF_CAP ((u32) BIT(4)) #define TX_BEAMFORM_CAP_IMPLICIT_ZLF_CAP ((u32) BIT(5)) #define TX_BEAMFORM_CAP_CALIB_OFFSET 6 #define TX_BEAMFORM_CAP_EXPLICIT_CSI_TXBF_CAP ((u32) BIT(8)) #define TX_BEAMFORM_CAP_EXPLICIT_UNCOMPR_STEERING_MATRIX_CAP ((u32) BIT(9)) #define TX_BEAMFORM_CAP_EXPLICIT_BF_CSI_FEEDBACK_CAP ((u32) BIT(10)) #define TX_BEAMFORM_CAP_EXPLICIT_BF_CSI_FEEDBACK_OFFSET 11 #define TX_BEAMFORM_CAP_EXPLICIT_UNCOMPR_STEERING_MATRIX_FEEDBACK_OFFSET 13 #define TX_BEAMFORM_CAP_EXPLICIT_COMPRESSED_STEERING_MATRIX_FEEDBACK_OFFSET 15 #define TX_BEAMFORM_CAP_MINIMAL_GROUPING_OFFSET 17 #define TX_BEAMFORM_CAP_CSI_NUM_BEAMFORMER_ANT_OFFSET 19 #define TX_BEAMFORM_CAP_UNCOMPRESSED_STEERING_MATRIX_BEAMFORMER_ANT_OFFSET 21 #define TX_BEAMFORM_CAP_COMPRESSED_STEERING_MATRIX_BEAMFORMER_ANT_OFFSET 23 #define TX_BEAMFORM_CAP_SCI_MAX_OF_ROWS_BEANFORMER_SUPPORTED_OFFSET 25 #define ASEL_CAPABILITY_ASEL_CAPABLE ((u8) BIT(0)) #define ASEL_CAPABILITY_EXPLICIT_CSI_FEEDBACK_BASED_TX_AS_CAP ((u8) BIT(1)) #define ASEL_CAPABILITY_ANT_INDICES_FEEDBACK_BASED_TX_AS_CAP ((u8) BIT(2)) #define ASEL_CAPABILITY_EXPLICIT_CSI_FEEDBACK_CAP ((u8) BIT(3)) #define ASEL_CAPABILITY_ANT_INDICES_FEEDBACK_CAP ((u8) BIT(4)) #define ASEL_CAPABILITY_RX_AS_CAP ((u8) BIT(5)) #define ASEL_CAPABILITY_TX_SOUND_PPDUS_CAP ((u8) BIT(6)) #define HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK ((u8) BIT(0) | BIT(1)) #define HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE ((u8) BIT(0)) #define HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW ((u8) BIT(0) | BIT(1)) #define HT_INFO_HT_PARAM_REC_TRANS_CHNL_WIDTH ((u8) BIT(2)) #define HT_INFO_HT_PARAM_RIFS_MODE ((u8) BIT(3)) #define HT_INFO_HT_PARAM_CTRL_ACCESS_ONLY ((u8) BIT(4)) #define HT_INFO_HT_PARAM_SRV_INTERVAL_GRANULARITY ((u8) BIT(5)) #define OP_MODE_PURE 0 #define OP_MODE_MAY_BE_LEGACY_STAS 1 #define OP_MODE_20MHZ_HT_STA_ASSOCED 2 #define OP_MODE_MIXED 3 #define HT_INFO_OPERATION_MODE_OP_MODE_MASK \ (0x0001 | 0x0002) #define HT_INFO_OPERATION_MODE_OP_MODE_OFFSET 0 #define HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT ((u8) BIT(2)) #define HT_INFO_OPERATION_MODE_TRANSMIT_BURST_LIMIT ((u8) BIT(3)) #define HT_INFO_OPERATION_MODE_NON_HT_STA_PRESENT ((u8) BIT(4)) #define HT_INFO_STBC_PARAM_DUAL_BEACON ((u16) BIT(6)) #define HT_INFO_STBC_PARAM_DUAL_STBC_PROTECT ((u16) BIT(7)) #define HT_INFO_STBC_PARAM_SECONDARY_BCN ((u16) BIT(8)) #define HT_INFO_STBC_PARAM_LSIG_TXOP_PROTECT_ALLOWED ((u16) BIT(9)) #define HT_INFO_STBC_PARAM_PCO_ACTIVE ((u16) BIT(10)) #define HT_INFO_STBC_PARAM_PCO_PHASE ((u16) BIT(11)) #define BSS_MEMBERSHIP_SELECTOR_VHT_PHY 126 #define BSS_MEMBERSHIP_SELECTOR_HT_PHY 127 /* VHT Defines */ #define VHT_CAP_MAX_MPDU_LENGTH_7991 ((u32) BIT(0)) #define VHT_CAP_MAX_MPDU_LENGTH_11454 ((u32) BIT(1)) #define VHT_CAP_MAX_MPDU_LENGTH_MASK ((u32) BIT(0) | BIT(1)) #define VHT_CAP_SUPP_CHAN_WIDTH_160MHZ ((u32) BIT(2)) #define VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ ((u32) BIT(3)) #define VHT_CAP_SUPP_CHAN_WIDTH_MASK ((u32) BIT(2) | BIT(3)) #define VHT_CAP_RXLDPC ((u32) BIT(4)) #define VHT_CAP_SHORT_GI_80 ((u32) BIT(5)) #define VHT_CAP_SHORT_GI_160 ((u32) BIT(6)) #define VHT_CAP_TXSTBC ((u32) BIT(7)) #define VHT_CAP_RXSTBC_1 ((u32) BIT(8)) #define VHT_CAP_RXSTBC_2 ((u32) BIT(9)) #define VHT_CAP_RXSTBC_3 ((u32) BIT(8) | BIT(9)) #define VHT_CAP_RXSTBC_4 ((u32) BIT(10)) #define VHT_CAP_RXSTBC_MASK ((u32) BIT(8) | BIT(9) | \ BIT(10)) #define VHT_CAP_SU_BEAMFORMER_CAPABLE ((u32) BIT(11)) #define VHT_CAP_SU_BEAMFORMEE_CAPABLE ((u32) BIT(12)) #define VHT_CAP_BEAMFORMEE_STS_MAX ((u32) BIT(13) | \ BIT(14) | BIT(15)) #define VHT_CAP_BEAMFORMEE_STS_OFFSET 13 #define VHT_CAP_SOUNDING_DIMENSION_MAX ((u32) BIT(16) | \ BIT(17) | BIT(18)) #define VHT_CAP_SOUNDING_DIMENSION_OFFSET 16 #define VHT_CAP_MU_BEAMFORMER_CAPABLE ((u32) BIT(19)) #define VHT_CAP_MU_BEAMFORMEE_CAPABLE ((u32) BIT(20)) #define VHT_CAP_VHT_TXOP_PS ((u32) BIT(21)) #define VHT_CAP_HTC_VHT ((u32) BIT(22)) #define VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT ((u32) BIT(23) | \ BIT(24) | BIT(25)) #define VHT_CAP_VHT_LINK_ADAPTATION_VHT_UNSOL_MFB ((u32) BIT(27)) #define VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB ((u32) BIT(26) | BIT(27)) #define VHT_CAP_RX_ANTENNA_PATTERN ((u32) BIT(28)) #define VHT_CAP_TX_ANTENNA_PATTERN ((u32) BIT(29)) /* VHT channel widths */ #define VHT_CHANWIDTH_USE_HT 0 #define VHT_CHANWIDTH_80MHZ 1 #define VHT_CHANWIDTH_160MHZ 2 #define VHT_CHANWIDTH_80P80MHZ 3 #define OUI_MICROSOFT 0x0050f2 /* Microsoft (also used in Wi-Fi specs) * 00:50:F2 */ #define WPA_IE_VENDOR_TYPE 0x0050f201 #define WPS_IE_VENDOR_TYPE 0x0050f204 #define OUI_WFA 0x506f9a #define P2P_IE_VENDOR_TYPE 0x506f9a09 #define WFD_IE_VENDOR_TYPE 0x506f9a0a #define WFD_OUI_TYPE 10 #define HS20_IE_VENDOR_TYPE 0x506f9a10 #define WMM_OUI_TYPE 2 #define WMM_OUI_SUBTYPE_INFORMATION_ELEMENT 0 #define WMM_OUI_SUBTYPE_PARAMETER_ELEMENT 1 #define WMM_OUI_SUBTYPE_TSPEC_ELEMENT 2 #define WMM_VERSION 1 #define WMM_ACTION_CODE_ADDTS_REQ 0 #define WMM_ACTION_CODE_ADDTS_RESP 1 #define WMM_ACTION_CODE_DELTS 2 #define WMM_ADDTS_STATUS_ADMISSION_ACCEPTED 0 #define WMM_ADDTS_STATUS_INVALID_PARAMETERS 1 /* 2 - Reserved */ #define WMM_ADDTS_STATUS_REFUSED 3 /* 4-255 - Reserved */ /* WMM TSPEC Direction Field Values */ #define WMM_TSPEC_DIRECTION_UPLINK 0 #define WMM_TSPEC_DIRECTION_DOWNLINK 1 /* 2 - Reserved */ #define WMM_TSPEC_DIRECTION_BI_DIRECTIONAL 3 /* * WMM Information Element (used in (Re)Association Request frames; may also be * used in Beacon frames) */ struct wmm_information_element { /* Element ID: 221 (0xdd); Length: 7 */ /* required fields for WMM version 1 */ u8 oui[3]; /* 00:50:f2 */ u8 oui_type; /* 2 */ u8 oui_subtype; /* 0 */ u8 version; /* 1 for WMM version 1.0 */ u8 qos_info; /* AP/STA specific QoS info */ } STRUCT_PACKED; #define WMM_QOSINFO_STA_AC_MASK 0x0f #define WMM_QOSINFO_STA_SP_MASK 0x03 #define WMM_QOSINFO_STA_SP_SHIFT 5 #define WMM_AC_AIFSN_MASK 0x0f #define WMM_AC_AIFNS_SHIFT 0 #define WMM_AC_ACM 0x10 #define WMM_AC_ACI_MASK 0x60 #define WMM_AC_ACI_SHIFT 5 #define WMM_AC_ECWMIN_MASK 0x0f #define WMM_AC_ECWMIN_SHIFT 0 #define WMM_AC_ECWMAX_MASK 0xf0 #define WMM_AC_ECWMAX_SHIFT 4 struct wmm_ac_parameter { u8 aci_aifsn; /* AIFSN, ACM, ACI */ u8 cw; /* ECWmin, ECWmax (CW = 2^ECW - 1) */ le16 txop_limit; } STRUCT_PACKED; /* * WMM Parameter Element (used in Beacon, Probe Response, and (Re)Association * Response frmaes) */ struct wmm_parameter_element { /* Element ID: 221 (0xdd); Length: 24 */ /* required fields for WMM version 1 */ u8 oui[3]; /* 00:50:f2 */ u8 oui_type; /* 2 */ u8 oui_subtype; /* 1 */ u8 version; /* 1 for WMM version 1.0 */ u8 qos_info; /* AP/STA specific QoS info */ u8 reserved; /* 0 */ struct wmm_ac_parameter ac[4]; /* AC_BE, AC_BK, AC_VI, AC_VO */ } STRUCT_PACKED; /* WMM TSPEC Element */ struct wmm_tspec_element { u8 eid; /* 221 = 0xdd */ u8 length; /* 6 + 55 = 61 */ u8 oui[3]; /* 00:50:f2 */ u8 oui_type; /* 2 */ u8 oui_subtype; /* 2 */ u8 version; /* 1 */ /* WMM TSPEC body (55 octets): */ u8 ts_info[3]; le16 nominal_msdu_size; le16 maximum_msdu_size; le32 minimum_service_interval; le32 maximum_service_interval; le32 inactivity_interval; le32 suspension_interval; le32 service_start_time; le32 minimum_data_rate; le32 mean_data_rate; le32 peak_data_rate; le32 maximum_burst_size; le32 delay_bound; le32 minimum_phy_rate; le16 surplus_bandwidth_allowance; le16 medium_time; } STRUCT_PACKED; /* Access Categories / ACI to AC coding */ enum { WMM_AC_BE = 0 /* Best Effort */, WMM_AC_BK = 1 /* Background */, WMM_AC_VI = 2 /* Video */, WMM_AC_VO = 3 /* Voice */ }; #define HS20_INDICATION_OUI_TYPE 16 #define HS20_ANQP_OUI_TYPE 17 #define HS20_STYPE_QUERY_LIST 1 #define HS20_STYPE_CAPABILITY_LIST 2 #define HS20_STYPE_OPERATOR_FRIENDLY_NAME 3 #define HS20_STYPE_WAN_METRICS 4 #define HS20_STYPE_CONNECTION_CAPABILITY 5 #define HS20_STYPE_NAI_HOME_REALM_QUERY 6 #define HS20_STYPE_OPERATING_CLASS 7 /* Wi-Fi Direct (P2P) */ #define P2P_OUI_TYPE 9 enum p2p_attr_id { P2P_ATTR_STATUS = 0, P2P_ATTR_MINOR_REASON_CODE = 1, P2P_ATTR_CAPABILITY = 2, P2P_ATTR_DEVICE_ID = 3, P2P_ATTR_GROUP_OWNER_INTENT = 4, P2P_ATTR_CONFIGURATION_TIMEOUT = 5, P2P_ATTR_LISTEN_CHANNEL = 6, P2P_ATTR_GROUP_BSSID = 7, P2P_ATTR_EXT_LISTEN_TIMING = 8, P2P_ATTR_INTENDED_INTERFACE_ADDR = 9, P2P_ATTR_MANAGEABILITY = 10, P2P_ATTR_CHANNEL_LIST = 11, P2P_ATTR_NOTICE_OF_ABSENCE = 12, P2P_ATTR_DEVICE_INFO = 13, P2P_ATTR_GROUP_INFO = 14, P2P_ATTR_GROUP_ID = 15, P2P_ATTR_INTERFACE = 16, P2P_ATTR_OPERATING_CHANNEL = 17, P2P_ATTR_INVITATION_FLAGS = 18, P2P_ATTR_OOB_GO_NEG_CHANNEL = 19, P2P_ATTR_VENDOR_SPECIFIC = 221 }; #define P2P_MAX_GO_INTENT 15 /* P2P Capability - Device Capability bitmap */ #define P2P_DEV_CAPAB_SERVICE_DISCOVERY BIT(0) #define P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY BIT(1) #define P2P_DEV_CAPAB_CONCURRENT_OPER BIT(2) #define P2P_DEV_CAPAB_INFRA_MANAGED BIT(3) #define P2P_DEV_CAPAB_DEVICE_LIMIT BIT(4) #define P2P_DEV_CAPAB_INVITATION_PROCEDURE BIT(5) /* P2P Capability - Group Capability bitmap */ #define P2P_GROUP_CAPAB_GROUP_OWNER BIT(0) #define P2P_GROUP_CAPAB_PERSISTENT_GROUP BIT(1) #define P2P_GROUP_CAPAB_GROUP_LIMIT BIT(2) #define P2P_GROUP_CAPAB_INTRA_BSS_DIST BIT(3) #define P2P_GROUP_CAPAB_CROSS_CONN BIT(4) #define P2P_GROUP_CAPAB_PERSISTENT_RECONN BIT(5) #define P2P_GROUP_CAPAB_GROUP_FORMATION BIT(6) #define P2P_GROUP_CAPAB_IP_ADDR_ALLOCATION BIT(7) /* Invitation Flags */ #define P2P_INVITATION_FLAGS_TYPE BIT(0) /* P2P Manageability */ #define P2P_MAN_DEVICE_MANAGEMENT BIT(0) #define P2P_MAN_CROSS_CONNECTION_PERMITTED BIT(1) #define P2P_MAN_COEXISTENCE_OPTIONAL BIT(2) enum p2p_status_code { P2P_SC_SUCCESS = 0, P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE = 1, P2P_SC_FAIL_INCOMPATIBLE_PARAMS = 2, P2P_SC_FAIL_LIMIT_REACHED = 3, P2P_SC_FAIL_INVALID_PARAMS = 4, P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE = 5, P2P_SC_FAIL_PREV_PROTOCOL_ERROR = 6, P2P_SC_FAIL_NO_COMMON_CHANNELS = 7, P2P_SC_FAIL_UNKNOWN_GROUP = 8, P2P_SC_FAIL_BOTH_GO_INTENT_15 = 9, P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD = 10, P2P_SC_FAIL_REJECTED_BY_USER = 11, }; enum p2p_role_indication { P2P_DEVICE_NOT_IN_GROUP = 0x00, P2P_CLIENT_IN_A_GROUP = 0x01, P2P_GO_IN_A_GROUP = 0x02, }; #define P2P_WILDCARD_SSID "DIRECT-" #define P2P_WILDCARD_SSID_LEN 7 /* P2P action frames */ enum p2p_act_frame_type { P2P_NOA = 0, P2P_PRESENCE_REQ = 1, P2P_PRESENCE_RESP = 2, P2P_GO_DISC_REQ = 3 }; /* P2P public action frames */ enum p2p_action_frame_type { P2P_GO_NEG_REQ = 0, P2P_GO_NEG_RESP = 1, P2P_GO_NEG_CONF = 2, P2P_INVITATION_REQ = 3, P2P_INVITATION_RESP = 4, P2P_DEV_DISC_REQ = 5, P2P_DEV_DISC_RESP = 6, P2P_PROV_DISC_REQ = 7, P2P_PROV_DISC_RESP = 8 }; enum p2p_service_protocol_type { P2P_SERV_ALL_SERVICES = 0, P2P_SERV_BONJOUR = 1, P2P_SERV_UPNP = 2, P2P_SERV_WS_DISCOVERY = 3, P2P_SERV_WIFI_DISPLAY = 4, P2P_SERV_VENDOR_SPECIFIC = 255 }; enum p2p_sd_status { P2P_SD_SUCCESS = 0, P2P_SD_PROTO_NOT_AVAILABLE = 1, P2P_SD_REQUESTED_INFO_NOT_AVAILABLE = 2, P2P_SD_BAD_REQUEST = 3 }; enum wifi_display_subelem { WFD_SUBELEM_DEVICE_INFO = 0, WFD_SUBELEM_ASSOCIATED_BSSID = 1, WFD_SUBELEM_AUDIO_FORMATS = 2, WFD_SUBELEM_VIDEO_FORMATS = 3, WFD_SUBELEM_3D_VIDEO_FORMATS = 4, WFD_SUBELEM_CONTENT_PROTECTION = 5, WFD_SUBELEM_COUPLED_SINK = 6, WFD_SUBELEM_EXT_CAPAB = 7, WFD_SUBELEM_LOCAL_IP_ADDRESS = 8, WFD_SUBELEM_SESSION_INFO = 9 }; #define OUI_BROADCOM 0x00904c /* Broadcom (Epigram) */ #define VENDOR_HT_CAPAB_OUI_TYPE 0x33 /* 00-90-4c:0x33 */ /* cipher suite selectors */ #define WLAN_CIPHER_SUITE_USE_GROUP 0x000FAC00 #define WLAN_CIPHER_SUITE_WEP40 0x000FAC01 #define WLAN_CIPHER_SUITE_TKIP 0x000FAC02 /* reserved: 0x000FAC03 */ #define WLAN_CIPHER_SUITE_CCMP 0x000FAC04 #define WLAN_CIPHER_SUITE_WEP104 0x000FAC05 #define WLAN_CIPHER_SUITE_AES_CMAC 0x000FAC06 #define WLAN_CIPHER_SUITE_NO_GROUP_ADDR 0x000FAC07 #define WLAN_CIPHER_SUITE_GCMP 0x000FAC08 #define WLAN_CIPHER_SUITE_GCMP_256 0x000FAC09 #define WLAN_CIPHER_SUITE_CCMP_256 0x000FAC0A #define WLAN_CIPHER_SUITE_BIP_GMAC_128 0x000FAC0B #define WLAN_CIPHER_SUITE_BIP_GMAC_256 0x000FAC0C #define WLAN_CIPHER_SUITE_BIP_CMAC_256 0x000FAC0D #define WLAN_CIPHER_SUITE_SMS4 0x00147201 #define WLAN_CIPHER_SUITE_CKIP 0x00409600 #define WLAN_CIPHER_SUITE_CKIP_CMIC 0x00409601 #define WLAN_CIPHER_SUITE_CMIC 0x00409602 #define WLAN_CIPHER_SUITE_KRK 0x004096FF /* for nl80211 use only */ /* AKM suite selectors */ #define WLAN_AKM_SUITE_8021X 0x000FAC01 #define WLAN_AKM_SUITE_PSK 0x000FAC02 #define WLAN_AKM_SUITE_FT_8021X 0x000FAC03 #define WLAN_AKM_SUITE_FT_PSK 0x000FAC04 #define WLAN_AKM_SUITE_CCKM 0x00409600 /* IEEE 802.11v - WNM Action field values */ enum wnm_action { WNM_EVENT_REQ = 0, WNM_EVENT_REPORT = 1, WNM_DIAGNOSTIC_REQ = 2, WNM_DIAGNOSTIC_REPORT = 3, WNM_LOCATION_CFG_REQ = 4, WNM_LOCATION_CFG_RESP = 5, WNM_BSS_TRANS_MGMT_QUERY = 6, WNM_BSS_TRANS_MGMT_REQ = 7, WNM_BSS_TRANS_MGMT_RESP = 8, WNM_FMS_REQ = 9, WNM_FMS_RESP = 10, WNM_COLLOCATED_INTERFERENCE_REQ = 11, WNM_COLLOCATED_INTERFERENCE_REPORT = 12, WNM_TFS_REQ = 13, WNM_TFS_RESP = 14, WNM_TFS_NOTIFY = 15, WNM_SLEEP_MODE_REQ = 16, WNM_SLEEP_MODE_RESP = 17, WNM_TIM_BROADCAST_REQ = 18, WNM_TIM_BROADCAST_RESP = 19, WNM_QOS_TRAFFIC_CAPAB_UPDATE = 20, WNM_CHANNEL_USAGE_REQ = 21, WNM_CHANNEL_USAGE_RESP = 22, WNM_DMS_REQ = 23, WNM_DMS_RESP = 24, WNM_TIMING_MEASUREMENT_REQ = 25, WNM_NOTIFICATION_REQ = 26, WNM_NOTIFICATION_RESP = 27 }; /* IEEE 802.11v - BSS Transition Management Request - Request Mode */ #define WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED BIT(0) #define WNM_BSS_TM_REQ_ABRIDGED BIT(1) #define WNM_BSS_TM_REQ_DISASSOC_IMMINENT BIT(2) #define WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED BIT(3) #define WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT BIT(4) /* IEEE Std 802.11-2012 - Table 8-253 */ enum bss_trans_mgmt_status_code { WNM_BSS_TM_ACCEPT = 0, WNM_BSS_TM_REJECT_UNSPECIFIED = 1, WNM_BSS_TM_REJECT_INSUFFICIENT_BEACON = 2, WNM_BSS_TM_REJECT_INSUFFICIENT_CAPABITY = 3, WNM_BSS_TM_REJECT_UNDESIRED = 4, WNM_BSS_TM_REJECT_DELAY_REQUEST = 5, WNM_BSS_TM_REJECT_STA_CANDIDATE_LIST_PROVIDED = 6, WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES = 7, WNM_BSS_TM_REJECT_LEAVING_ESS = 8 }; #define WNM_NEIGHBOR_TSF 1 #define WNM_NEIGHBOR_CONDENSED_COUNTRY_STRING 2 #define WNM_NEIGHBOR_BSS_TRANSITION_CANDIDATE 3 #define WNM_NEIGHBOR_BSS_TERMINATION_DURATION 4 #define WNM_NEIGHBOR_BEARING 5 #define WNM_NEIGHBOR_MEASUREMENT_PILOT 66 #define WNM_NEIGHBOR_RRM_ENABLED_CAPABILITIES 70 #define WNM_NEIGHBOR_MULTIPLE_BSSID 71 /* QoS action */ enum qos_action { QOS_ADDTS_REQ = 0, QOS_ADDTS_RESP = 1, QOS_DELTS = 2, QOS_SCHEDULE = 3, QOS_QOS_MAP_CONFIG = 4, }; /* IEEE Std 802.11-2012, 8.4.2.62 20/40 BSS Coexistence element */ #define WLAN_20_40_BSS_COEX_INFO_REQ BIT(0) #define WLAN_20_40_BSS_COEX_40MHZ_INTOL BIT(1) #define WLAN_20_40_BSS_COEX_20MHZ_WIDTH_REQ BIT(2) #define WLAN_20_40_BSS_COEX_OBSS_EXEMPT_REQ BIT(3) #define WLAN_20_40_BSS_COEX_OBSS_EXEMPT_GRNT BIT(4) struct ieee80211_2040_bss_coex_ie { u8 element_id; u8 length; u8 coex_param; } STRUCT_PACKED; struct ieee80211_2040_intol_chan_report { u8 element_id; u8 length; u8 op_class; u8 variable[0]; /* Channel List */ } STRUCT_PACKED; /* IEEE 802.11v - WNM-Sleep Mode element */ struct wnm_sleep_element { u8 eid; /* WLAN_EID_WNMSLEEP */ u8 len; u8 action_type; /* WNM_SLEEP_ENTER/WNM_SLEEP_MODE_EXIT */ u8 status; le16 intval; } STRUCT_PACKED; #define WNM_SLEEP_MODE_ENTER 0 #define WNM_SLEEP_MODE_EXIT 1 enum wnm_sleep_mode_response_status { WNM_STATUS_SLEEP_ACCEPT = 0, WNM_STATUS_SLEEP_EXIT_ACCEPT_GTK_UPDATE = 1, WNM_STATUS_DENIED_ACTION = 2, WNM_STATUS_DENIED_TMP = 3, WNM_STATUS_DENIED_KEY = 4, WNM_STATUS_DENIED_OTHER_WNM_SERVICE = 5 }; /* WNM-Sleep Mode subelement IDs */ enum wnm_sleep_mode_subelement_id { WNM_SLEEP_SUBELEM_GTK = 0, WNM_SLEEP_SUBELEM_IGTK = 1 }; /* Channel Switch modes (802.11h) */ #define CHAN_SWITCH_MODE_ALLOW_TX 0 #define CHAN_SWITCH_MODE_BLOCK_TX 1 #endif /* IEEE802_11_DEFS_H */ wpa-2.1/src/common/privsep_commands.h000066400000000000000000000025501227414666700177650ustar00rootroot00000000000000/* * WPA Supplicant - privilege separation commands * Copyright (c) 2007-2009, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef PRIVSEP_COMMANDS_H #define PRIVSEP_COMMANDS_H enum privsep_cmd { PRIVSEP_CMD_REGISTER, PRIVSEP_CMD_UNREGISTER, PRIVSEP_CMD_SCAN, PRIVSEP_CMD_GET_SCAN_RESULTS, PRIVSEP_CMD_ASSOCIATE, PRIVSEP_CMD_GET_BSSID, PRIVSEP_CMD_GET_SSID, PRIVSEP_CMD_SET_KEY, PRIVSEP_CMD_GET_CAPA, PRIVSEP_CMD_L2_REGISTER, PRIVSEP_CMD_L2_UNREGISTER, PRIVSEP_CMD_L2_NOTIFY_AUTH_START, PRIVSEP_CMD_L2_SEND, PRIVSEP_CMD_SET_COUNTRY, }; struct privsep_cmd_associate { u8 bssid[ETH_ALEN]; u8 ssid[32]; size_t ssid_len; int freq; int pairwise_suite; int group_suite; int key_mgmt_suite; int auth_alg; int mode; size_t wpa_ie_len; /* followed by wpa_ie_len bytes of wpa_ie */ }; struct privsep_cmd_set_key { int alg; u8 addr[ETH_ALEN]; int key_idx; int set_tx; u8 seq[8]; size_t seq_len; u8 key[32]; size_t key_len; }; enum privsep_event { PRIVSEP_EVENT_SCAN_RESULTS, PRIVSEP_EVENT_ASSOC, PRIVSEP_EVENT_DISASSOC, PRIVSEP_EVENT_ASSOCINFO, PRIVSEP_EVENT_MICHAEL_MIC_FAILURE, PRIVSEP_EVENT_INTERFACE_STATUS, PRIVSEP_EVENT_PMKID_CANDIDATE, PRIVSEP_EVENT_STKSTART, PRIVSEP_EVENT_FT_RESPONSE, PRIVSEP_EVENT_RX_EAPOL, }; #endif /* PRIVSEP_COMMANDS_H */ wpa-2.1/src/common/qca-vendor.h000066400000000000000000000026151227414666700164550ustar00rootroot00000000000000/* * Qualcomm Atheros OUI and vendor specific assignments * Copyright (c) 2014, Qualcomm Atheros, Inc. * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef QCA_VENDOR_H #define QCA_VENDOR_H /* * This file is a registry of identifier assignments from the Qualcomm Atheros * OUI 00:13:74 for purposes other than MAC address assignment. New identifiers * can be assigned through normal review process for changes to the upstream * hostap.git repository. */ #define OUI_QCA 0x001374 /** * enum qca_nl80211_vendor_subcmds - QCA nl80211 vendor command identifiers * * @QCA_NL80211_VENDOR_SUBCMD_UNSPEC: Reserved value 0 * * @QCA_NL80211_VENDOR_SUBCMD_TEST: Test command/event * * @QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY: Recommendation of frequency * ranges to avoid to reduce issues due to interference or internal * co-existence information in the driver. The event data structure is * defined in struct qca_avoid_freq_list. */ enum qca_nl80211_vendor_subcmds { QCA_NL80211_VENDOR_SUBCMD_UNSPEC = 0, QCA_NL80211_VENDOR_SUBCMD_TEST = 1, /* subcmds 2..9 not yet allocated */ QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY = 10, }; struct qca_avoid_freq_range { u32 start_freq; u32 end_freq; } STRUCT_PACKED; struct qca_avoid_freq_list { u32 count; struct qca_avoid_freq_range range[0]; } STRUCT_PACKED; #endif /* QCA_VENDOR_H */ wpa-2.1/src/common/sae.c000066400000000000000000000702371227414666700151660ustar00rootroot00000000000000/* * Simultaneous authentication of equals * Copyright (c) 2012-2013, 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/crypto.h" #include "crypto/sha256.h" #include "crypto/random.h" #include "crypto/dh_groups.h" #include "ieee802_11_defs.h" #include "sae.h" int sae_set_group(struct sae_data *sae, int group) { struct sae_temporary_data *tmp; sae_clear_data(sae); tmp = sae->tmp = os_zalloc(sizeof(*tmp)); if (tmp == NULL) return -1; /* First, check if this is an ECC group */ tmp->ec = crypto_ec_init(group); if (tmp->ec) { sae->group = group; tmp->prime_len = crypto_ec_prime_len(tmp->ec); tmp->prime = crypto_ec_get_prime(tmp->ec); tmp->order = crypto_ec_get_order(tmp->ec); return 0; } /* Not an ECC group, check FFC */ tmp->dh = dh_groups_get(group); if (tmp->dh) { sae->group = group; tmp->prime_len = tmp->dh->prime_len; if (tmp->prime_len > SAE_MAX_PRIME_LEN) { sae_clear_data(sae); return -1; } tmp->prime_buf = crypto_bignum_init_set(tmp->dh->prime, tmp->prime_len); if (tmp->prime_buf == NULL) { sae_clear_data(sae); return -1; } tmp->prime = tmp->prime_buf; tmp->order_buf = crypto_bignum_init_set(tmp->dh->order, tmp->dh->order_len); if (tmp->order_buf == NULL) { sae_clear_data(sae); return -1; } tmp->order = tmp->order_buf; return 0; } /* Unsupported group */ return -1; } void sae_clear_temp_data(struct sae_data *sae) { struct sae_temporary_data *tmp; if (sae == NULL || sae->tmp == NULL) return; tmp = sae->tmp; crypto_ec_deinit(tmp->ec); crypto_bignum_deinit(tmp->prime_buf, 0); crypto_bignum_deinit(tmp->order_buf, 0); crypto_bignum_deinit(tmp->sae_rand, 1); crypto_bignum_deinit(tmp->pwe_ffc, 1); crypto_bignum_deinit(tmp->own_commit_scalar, 0); crypto_bignum_deinit(tmp->own_commit_element_ffc, 0); crypto_bignum_deinit(tmp->peer_commit_element_ffc, 0); crypto_ec_point_deinit(tmp->pwe_ecc, 1); crypto_ec_point_deinit(tmp->own_commit_element_ecc, 0); crypto_ec_point_deinit(tmp->peer_commit_element_ecc, 0); os_free(sae->tmp); sae->tmp = NULL; } void sae_clear_data(struct sae_data *sae) { if (sae == NULL) return; sae_clear_temp_data(sae); crypto_bignum_deinit(sae->peer_commit_scalar, 0); os_memset(sae, 0, sizeof(*sae)); } static void buf_shift_right(u8 *buf, size_t len, size_t bits) { size_t i; for (i = len - 1; i > 0; i--) buf[i] = (buf[i - 1] << (8 - bits)) | (buf[i] >> bits); buf[0] >>= bits; } static struct crypto_bignum * sae_get_rand(struct sae_data *sae) { u8 val[SAE_MAX_PRIME_LEN]; int iter = 0; struct crypto_bignum *bn = NULL; int order_len_bits = crypto_bignum_bits(sae->tmp->order); size_t order_len = (order_len_bits + 7) / 8; if (order_len > sizeof(val)) return NULL; for (;;) { if (iter++ > 100) return NULL; if (random_get_bytes(val, order_len) < 0) return NULL; if (order_len_bits % 8) buf_shift_right(val, order_len, 8 - order_len_bits % 8); bn = crypto_bignum_init_set(val, order_len); if (bn == NULL) return NULL; if (crypto_bignum_is_zero(bn) || crypto_bignum_is_one(bn) || crypto_bignum_cmp(bn, sae->tmp->order) >= 0) continue; break; } os_memset(val, 0, order_len); return bn; } static struct crypto_bignum * sae_get_rand_and_mask(struct sae_data *sae) { crypto_bignum_deinit(sae->tmp->sae_rand, 1); sae->tmp->sae_rand = sae_get_rand(sae); if (sae->tmp->sae_rand == NULL) return NULL; return sae_get_rand(sae); } static void sae_pwd_seed_key(const u8 *addr1, const u8 *addr2, u8 *key) { wpa_printf(MSG_DEBUG, "SAE: PWE derivation - addr1=" MACSTR " addr2=" MACSTR, MAC2STR(addr1), MAC2STR(addr2)); if (os_memcmp(addr1, addr2, ETH_ALEN) > 0) { os_memcpy(key, addr1, ETH_ALEN); os_memcpy(key + ETH_ALEN, addr2, ETH_ALEN); } else { os_memcpy(key, addr2, ETH_ALEN); os_memcpy(key + ETH_ALEN, addr1, ETH_ALEN); } } static int sae_test_pwd_seed_ecc(struct sae_data *sae, const u8 *pwd_seed, struct crypto_ec_point *pwe) { u8 pwd_value[SAE_MAX_ECC_PRIME_LEN], prime[SAE_MAX_ECC_PRIME_LEN]; struct crypto_bignum *x; int y_bit; size_t bits; if (crypto_bignum_to_bin(sae->tmp->prime, prime, sizeof(prime), sae->tmp->prime_len) < 0) return -1; wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, SHA256_MAC_LEN); /* pwd-value = KDF-z(pwd-seed, "SAE Hunting and Pecking", p) */ bits = crypto_ec_prime_len_bits(sae->tmp->ec); sha256_prf_bits(pwd_seed, SHA256_MAC_LEN, "SAE Hunting and Pecking", prime, sae->tmp->prime_len, pwd_value, bits); if (bits % 8) buf_shift_right(pwd_value, sizeof(pwd_value), 8 - bits % 8); wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value", pwd_value, sae->tmp->prime_len); if (os_memcmp(pwd_value, prime, sae->tmp->prime_len) >= 0) return 0; y_bit = pwd_seed[SHA256_MAC_LEN - 1] & 0x01; x = crypto_bignum_init_set(pwd_value, sae->tmp->prime_len); if (x == NULL) return -1; if (crypto_ec_point_solve_y_coord(sae->tmp->ec, pwe, x, y_bit) < 0) { crypto_bignum_deinit(x, 0); wpa_printf(MSG_DEBUG, "SAE: No solution found"); return 0; } crypto_bignum_deinit(x, 0); wpa_printf(MSG_DEBUG, "SAE: PWE found"); return 1; } static int sae_test_pwd_seed_ffc(struct sae_data *sae, const u8 *pwd_seed, struct crypto_bignum *pwe) { u8 pwd_value[SAE_MAX_PRIME_LEN]; size_t bits = sae->tmp->prime_len * 8; u8 exp[1]; struct crypto_bignum *a, *b; int res; wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, SHA256_MAC_LEN); /* pwd-value = KDF-z(pwd-seed, "SAE Hunting and Pecking", p) */ sha256_prf_bits(pwd_seed, SHA256_MAC_LEN, "SAE Hunting and Pecking", sae->tmp->dh->prime, sae->tmp->prime_len, pwd_value, bits); if (bits % 8) buf_shift_right(pwd_value, sizeof(pwd_value), 8 - bits % 8); wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value", pwd_value, sae->tmp->prime_len); if (os_memcmp(pwd_value, sae->tmp->dh->prime, sae->tmp->prime_len) >= 0) { wpa_printf(MSG_DEBUG, "SAE: pwd-value >= p"); return 0; } /* PWE = pwd-value^((p-1)/r) modulo p */ a = crypto_bignum_init_set(pwd_value, sae->tmp->prime_len); if (sae->tmp->dh->safe_prime) { /* * r = (p-1)/2 for the group used here, so this becomes: * PWE = pwd-value^2 modulo p */ exp[0] = 2; b = crypto_bignum_init_set(exp, sizeof(exp)); } else { /* Calculate exponent: (p-1)/r */ exp[0] = 1; b = crypto_bignum_init_set(exp, sizeof(exp)); if (b == NULL || crypto_bignum_sub(sae->tmp->prime, b, b) < 0 || crypto_bignum_div(b, sae->tmp->order, b) < 0) { crypto_bignum_deinit(b, 0); b = NULL; } } if (a == NULL || b == NULL) res = -1; else res = crypto_bignum_exptmod(a, b, sae->tmp->prime, pwe); crypto_bignum_deinit(a, 0); crypto_bignum_deinit(b, 0); if (res < 0) { wpa_printf(MSG_DEBUG, "SAE: Failed to calculate PWE"); return -1; } /* if (PWE > 1) --> found */ if (crypto_bignum_is_zero(pwe) || crypto_bignum_is_one(pwe)) { wpa_printf(MSG_DEBUG, "SAE: PWE <= 1"); return 0; } wpa_printf(MSG_DEBUG, "SAE: PWE found"); return 1; } static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1, const u8 *addr2, const u8 *password, size_t password_len) { u8 counter, k = 4; u8 addrs[2 * ETH_ALEN]; const u8 *addr[2]; size_t len[2]; int found = 0; struct crypto_ec_point *pwe_tmp; if (sae->tmp->pwe_ecc == NULL) { sae->tmp->pwe_ecc = crypto_ec_point_init(sae->tmp->ec); if (sae->tmp->pwe_ecc == NULL) return -1; } pwe_tmp = crypto_ec_point_init(sae->tmp->ec); if (pwe_tmp == NULL) return -1; wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password", password, password_len); /* * H(salt, ikm) = HMAC-SHA256(salt, ikm) * pwd-seed = H(MAX(STA-A-MAC, STA-B-MAC) || MIN(STA-A-MAC, STA-B-MAC), * password || counter) */ sae_pwd_seed_key(addr1, addr2, addrs); addr[0] = password; len[0] = password_len; addr[1] = &counter; len[1] = sizeof(counter); /* * Continue for at least k iterations to protect against side-channel * attacks that attempt to determine the number of iterations required * in the loop. */ for (counter = 1; counter < k || !found; counter++) { u8 pwd_seed[SHA256_MAC_LEN]; int res; if (counter > 200) { /* This should not happen in practice */ wpa_printf(MSG_DEBUG, "SAE: Failed to derive PWE"); break; } wpa_printf(MSG_DEBUG, "SAE: counter = %u", counter); if (hmac_sha256_vector(addrs, sizeof(addrs), 2, addr, len, pwd_seed) < 0) break; res = sae_test_pwd_seed_ecc(sae, pwd_seed, found ? pwe_tmp : sae->tmp->pwe_ecc); if (res < 0) break; if (res == 0) continue; if (found) { wpa_printf(MSG_DEBUG, "SAE: Ignore this PWE (one was " "already selected)"); } else { wpa_printf(MSG_DEBUG, "SAE: Use this PWE"); found = 1; } } crypto_ec_point_deinit(pwe_tmp, 1); return found ? 0 : -1; } static int sae_derive_pwe_ffc(struct sae_data *sae, const u8 *addr1, const u8 *addr2, const u8 *password, size_t password_len) { u8 counter; u8 addrs[2 * ETH_ALEN]; const u8 *addr[2]; size_t len[2]; int found = 0; if (sae->tmp->pwe_ffc == NULL) { sae->tmp->pwe_ffc = crypto_bignum_init(); if (sae->tmp->pwe_ffc == NULL) return -1; } wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password", password, password_len); /* * H(salt, ikm) = HMAC-SHA256(salt, ikm) * pwd-seed = H(MAX(STA-A-MAC, STA-B-MAC) || MIN(STA-A-MAC, STA-B-MAC), * password || counter) */ sae_pwd_seed_key(addr1, addr2, addrs); addr[0] = password; len[0] = password_len; addr[1] = &counter; len[1] = sizeof(counter); for (counter = 1; !found; counter++) { u8 pwd_seed[SHA256_MAC_LEN]; int res; if (counter > 200) { /* This should not happen in practice */ wpa_printf(MSG_DEBUG, "SAE: Failed to derive PWE"); break; } wpa_printf(MSG_DEBUG, "SAE: counter = %u", counter); if (hmac_sha256_vector(addrs, sizeof(addrs), 2, addr, len, pwd_seed) < 0) break; res = sae_test_pwd_seed_ffc(sae, pwd_seed, sae->tmp->pwe_ffc); if (res < 0) break; if (res > 0) { wpa_printf(MSG_DEBUG, "SAE: Use this PWE"); found = 1; } } return found ? 0 : -1; } static int sae_derive_commit_element_ecc(struct sae_data *sae, struct crypto_bignum *mask) { /* COMMIT-ELEMENT = inverse(scalar-op(mask, PWE)) */ if (!sae->tmp->own_commit_element_ecc) { sae->tmp->own_commit_element_ecc = crypto_ec_point_init(sae->tmp->ec); if (!sae->tmp->own_commit_element_ecc) return -1; } if (crypto_ec_point_mul(sae->tmp->ec, sae->tmp->pwe_ecc, mask, sae->tmp->own_commit_element_ecc) < 0 || crypto_ec_point_invert(sae->tmp->ec, sae->tmp->own_commit_element_ecc) < 0) { wpa_printf(MSG_DEBUG, "SAE: Could not compute commit-element"); return -1; } return 0; } static int sae_derive_commit_element_ffc(struct sae_data *sae, struct crypto_bignum *mask) { /* COMMIT-ELEMENT = inverse(scalar-op(mask, PWE)) */ if (!sae->tmp->own_commit_element_ffc) { sae->tmp->own_commit_element_ffc = crypto_bignum_init(); if (!sae->tmp->own_commit_element_ffc) return -1; } if (crypto_bignum_exptmod(sae->tmp->pwe_ffc, mask, sae->tmp->prime, sae->tmp->own_commit_element_ffc) < 0 || crypto_bignum_inverse(sae->tmp->own_commit_element_ffc, sae->tmp->prime, sae->tmp->own_commit_element_ffc) < 0) { wpa_printf(MSG_DEBUG, "SAE: Could not compute commit-element"); return -1; } return 0; } static int sae_derive_commit(struct sae_data *sae) { struct crypto_bignum *mask; int ret = -1; mask = sae_get_rand_and_mask(sae); if (mask == NULL) { wpa_printf(MSG_DEBUG, "SAE: Could not get rand/mask"); return -1; } /* commit-scalar = (rand + mask) modulo r */ if (!sae->tmp->own_commit_scalar) { sae->tmp->own_commit_scalar = crypto_bignum_init(); if (!sae->tmp->own_commit_scalar) goto fail; } crypto_bignum_add(sae->tmp->sae_rand, mask, sae->tmp->own_commit_scalar); crypto_bignum_mod(sae->tmp->own_commit_scalar, sae->tmp->order, sae->tmp->own_commit_scalar); if (sae->tmp->ec && sae_derive_commit_element_ecc(sae, mask) < 0) goto fail; if (sae->tmp->dh && sae_derive_commit_element_ffc(sae, mask) < 0) goto fail; ret = 0; fail: crypto_bignum_deinit(mask, 1); return ret; } int sae_prepare_commit(const u8 *addr1, const u8 *addr2, const u8 *password, size_t password_len, struct sae_data *sae) { if (sae->tmp->ec && sae_derive_pwe_ecc(sae, addr1, addr2, password, password_len) < 0) return -1; if (sae->tmp->dh && sae_derive_pwe_ffc(sae, addr1, addr2, password, password_len) < 0) return -1; if (sae_derive_commit(sae) < 0) return -1; return 0; } static int sae_derive_k_ecc(struct sae_data *sae, u8 *k) { struct crypto_ec_point *K; int ret = -1; K = crypto_ec_point_init(sae->tmp->ec); if (K == NULL) goto fail; /* * K = scalar-op(rand, (elem-op(scalar-op(peer-commit-scalar, PWE), * PEER-COMMIT-ELEMENT))) * If K is identity element (point-at-infinity), reject * k = F(K) (= x coordinate) */ if (crypto_ec_point_mul(sae->tmp->ec, sae->tmp->pwe_ecc, sae->peer_commit_scalar, K) < 0 || crypto_ec_point_add(sae->tmp->ec, K, sae->tmp->peer_commit_element_ecc, K) < 0 || crypto_ec_point_mul(sae->tmp->ec, K, sae->tmp->sae_rand, K) < 0 || crypto_ec_point_is_at_infinity(sae->tmp->ec, K) || crypto_ec_point_to_bin(sae->tmp->ec, K, k, NULL) < 0) { wpa_printf(MSG_DEBUG, "SAE: Failed to calculate K and k"); goto fail; } wpa_hexdump_key(MSG_DEBUG, "SAE: k", k, sae->tmp->prime_len); ret = 0; fail: crypto_ec_point_deinit(K, 1); return ret; } static int sae_derive_k_ffc(struct sae_data *sae, u8 *k) { struct crypto_bignum *K; int ret = -1; K = crypto_bignum_init(); if (K == NULL) goto fail; /* * K = scalar-op(rand, (elem-op(scalar-op(peer-commit-scalar, PWE), * PEER-COMMIT-ELEMENT))) * If K is identity element (one), reject. * k = F(K) (= x coordinate) */ if (crypto_bignum_exptmod(sae->tmp->pwe_ffc, sae->peer_commit_scalar, sae->tmp->prime, K) < 0 || crypto_bignum_mulmod(K, sae->tmp->peer_commit_element_ffc, sae->tmp->prime, K) < 0 || crypto_bignum_exptmod(K, sae->tmp->sae_rand, sae->tmp->prime, K) < 0 || crypto_bignum_is_one(K) || crypto_bignum_to_bin(K, k, SAE_MAX_PRIME_LEN, sae->tmp->prime_len) < 0) { wpa_printf(MSG_DEBUG, "SAE: Failed to calculate K and k"); goto fail; } wpa_hexdump_key(MSG_DEBUG, "SAE: k", k, sae->tmp->prime_len); ret = 0; fail: crypto_bignum_deinit(K, 1); return ret; } static int sae_derive_keys(struct sae_data *sae, const u8 *k) { u8 null_key[SAE_KEYSEED_KEY_LEN], val[SAE_MAX_PRIME_LEN]; u8 keyseed[SHA256_MAC_LEN]; u8 keys[SAE_KCK_LEN + SAE_PMK_LEN]; struct crypto_bignum *tmp; int ret = -1; tmp = crypto_bignum_init(); if (tmp == NULL) goto fail; /* keyseed = H(<0>32, k) * KCK || PMK = KDF-512(keyseed, "SAE KCK and PMK", * (commit-scalar + peer-commit-scalar) modulo r) * PMKID = L((commit-scalar + peer-commit-scalar) modulo r, 0, 128) */ os_memset(null_key, 0, sizeof(null_key)); hmac_sha256(null_key, sizeof(null_key), k, sae->tmp->prime_len, keyseed); wpa_hexdump_key(MSG_DEBUG, "SAE: keyseed", keyseed, sizeof(keyseed)); crypto_bignum_add(sae->tmp->own_commit_scalar, sae->peer_commit_scalar, tmp); crypto_bignum_mod(tmp, sae->tmp->order, tmp); crypto_bignum_to_bin(tmp, val, sizeof(val), sae->tmp->prime_len); wpa_hexdump(MSG_DEBUG, "SAE: PMKID", val, SAE_PMKID_LEN); sha256_prf(keyseed, sizeof(keyseed), "SAE KCK and PMK", val, sae->tmp->prime_len, keys, sizeof(keys)); os_memcpy(sae->tmp->kck, keys, SAE_KCK_LEN); os_memcpy(sae->pmk, keys + SAE_KCK_LEN, SAE_PMK_LEN); wpa_hexdump_key(MSG_DEBUG, "SAE: KCK", sae->tmp->kck, SAE_KCK_LEN); wpa_hexdump_key(MSG_DEBUG, "SAE: PMK", sae->pmk, SAE_PMK_LEN); ret = 0; fail: crypto_bignum_deinit(tmp, 0); return ret; } int sae_process_commit(struct sae_data *sae) { u8 k[SAE_MAX_PRIME_LEN]; if ((sae->tmp->ec && sae_derive_k_ecc(sae, k) < 0) || (sae->tmp->dh && sae_derive_k_ffc(sae, k) < 0) || sae_derive_keys(sae, k) < 0) return -1; return 0; } void sae_write_commit(struct sae_data *sae, struct wpabuf *buf, const struct wpabuf *token) { u8 *pos; wpabuf_put_le16(buf, sae->group); /* Finite Cyclic Group */ if (token) wpabuf_put_buf(buf, token); pos = wpabuf_put(buf, sae->tmp->prime_len); crypto_bignum_to_bin(sae->tmp->own_commit_scalar, pos, sae->tmp->prime_len, sae->tmp->prime_len); wpa_hexdump(MSG_DEBUG, "SAE: own commit-scalar", pos, sae->tmp->prime_len); if (sae->tmp->ec) { pos = wpabuf_put(buf, 2 * sae->tmp->prime_len); crypto_ec_point_to_bin(sae->tmp->ec, sae->tmp->own_commit_element_ecc, pos, pos + sae->tmp->prime_len); wpa_hexdump(MSG_DEBUG, "SAE: own commit-element(x)", pos, sae->tmp->prime_len); wpa_hexdump(MSG_DEBUG, "SAE: own commit-element(y)", pos + sae->tmp->prime_len, sae->tmp->prime_len); } else { pos = wpabuf_put(buf, sae->tmp->prime_len); crypto_bignum_to_bin(sae->tmp->own_commit_element_ffc, pos, sae->tmp->prime_len, sae->tmp->prime_len); wpa_hexdump(MSG_DEBUG, "SAE: own commit-element", pos, sae->tmp->prime_len); } } static u16 sae_group_allowed(struct sae_data *sae, int *allowed_groups, u16 group) { if (allowed_groups) { int i; for (i = 0; allowed_groups[i] > 0; i++) { if (allowed_groups[i] == group) break; } if (allowed_groups[i] != group) { wpa_printf(MSG_DEBUG, "SAE: Proposed group %u not " "enabled in the current configuration", group); return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; } } if (sae->state == SAE_COMMITTED && group != sae->group) { wpa_printf(MSG_DEBUG, "SAE: Do not allow group to be changed"); return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; } if (group != sae->group && sae_set_group(sae, group) < 0) { wpa_printf(MSG_DEBUG, "SAE: Unsupported Finite Cyclic Group %u", group); return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; } if (sae->tmp == NULL) { wpa_printf(MSG_DEBUG, "SAE: Group information not yet initialized"); return WLAN_STATUS_UNSPECIFIED_FAILURE; } if (sae->tmp->dh && !allowed_groups) { wpa_printf(MSG_DEBUG, "SAE: Do not allow FFC group %u without " "explicit configuration enabling it", group); return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; } return WLAN_STATUS_SUCCESS; } static void sae_parse_commit_token(struct sae_data *sae, const u8 **pos, const u8 *end, const u8 **token, size_t *token_len) { if (*pos + (sae->tmp->ec ? 3 : 2) * sae->tmp->prime_len < end) { size_t tlen = end - (*pos + (sae->tmp->ec ? 3 : 2) * sae->tmp->prime_len); wpa_hexdump(MSG_DEBUG, "SAE: Anti-Clogging Token", *pos, tlen); if (token) *token = *pos; if (token_len) *token_len = tlen; *pos += tlen; } else { if (token) *token = NULL; if (token_len) *token_len = 0; } } static u16 sae_parse_commit_scalar(struct sae_data *sae, const u8 **pos, const u8 *end) { struct crypto_bignum *peer_scalar; if (*pos + sae->tmp->prime_len > end) { wpa_printf(MSG_DEBUG, "SAE: Not enough data for scalar"); return WLAN_STATUS_UNSPECIFIED_FAILURE; } peer_scalar = crypto_bignum_init_set(*pos, sae->tmp->prime_len); if (peer_scalar == NULL) return WLAN_STATUS_UNSPECIFIED_FAILURE; /* * IEEE Std 802.11-2012, 11.3.8.6.1: If there is a protocol instance for * the peer and it is in Authenticated state, the new Commit Message * shall be dropped if the peer-scalar is identical to the one used in * the existing protocol instance. */ if (sae->state == SAE_ACCEPTED && sae->peer_commit_scalar && crypto_bignum_cmp(sae->peer_commit_scalar, peer_scalar) == 0) { wpa_printf(MSG_DEBUG, "SAE: Do not accept re-use of previous " "peer-commit-scalar"); crypto_bignum_deinit(peer_scalar, 0); return WLAN_STATUS_UNSPECIFIED_FAILURE; } /* 0 < scalar < r */ if (crypto_bignum_is_zero(peer_scalar) || crypto_bignum_cmp(peer_scalar, sae->tmp->order) >= 0) { wpa_printf(MSG_DEBUG, "SAE: Invalid peer scalar"); crypto_bignum_deinit(peer_scalar, 0); return WLAN_STATUS_UNSPECIFIED_FAILURE; } crypto_bignum_deinit(sae->peer_commit_scalar, 0); sae->peer_commit_scalar = peer_scalar; wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-scalar", *pos, sae->tmp->prime_len); *pos += sae->tmp->prime_len; return WLAN_STATUS_SUCCESS; } static u16 sae_parse_commit_element_ecc(struct sae_data *sae, const u8 *pos, const u8 *end) { u8 prime[SAE_MAX_ECC_PRIME_LEN]; if (pos + 2 * sae->tmp->prime_len > end) { wpa_printf(MSG_DEBUG, "SAE: Not enough data for " "commit-element"); return WLAN_STATUS_UNSPECIFIED_FAILURE; } if (crypto_bignum_to_bin(sae->tmp->prime, prime, sizeof(prime), sae->tmp->prime_len) < 0) return WLAN_STATUS_UNSPECIFIED_FAILURE; /* element x and y coordinates < p */ if (os_memcmp(pos, prime, sae->tmp->prime_len) >= 0 || os_memcmp(pos + sae->tmp->prime_len, prime, sae->tmp->prime_len) >= 0) { wpa_printf(MSG_DEBUG, "SAE: Invalid coordinates in peer " "element"); return WLAN_STATUS_UNSPECIFIED_FAILURE; } wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element(x)", pos, sae->tmp->prime_len); wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element(y)", pos + sae->tmp->prime_len, sae->tmp->prime_len); crypto_ec_point_deinit(sae->tmp->peer_commit_element_ecc, 0); sae->tmp->peer_commit_element_ecc = crypto_ec_point_from_bin(sae->tmp->ec, pos); if (sae->tmp->peer_commit_element_ecc == NULL) return WLAN_STATUS_UNSPECIFIED_FAILURE; if (!crypto_ec_point_is_on_curve(sae->tmp->ec, sae->tmp->peer_commit_element_ecc)) { wpa_printf(MSG_DEBUG, "SAE: Peer element is not on curve"); return WLAN_STATUS_UNSPECIFIED_FAILURE; } return WLAN_STATUS_SUCCESS; } static u16 sae_parse_commit_element_ffc(struct sae_data *sae, const u8 *pos, const u8 *end) { struct crypto_bignum *res; if (pos + sae->tmp->prime_len > end) { wpa_printf(MSG_DEBUG, "SAE: Not enough data for " "commit-element"); return WLAN_STATUS_UNSPECIFIED_FAILURE; } wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element", pos, sae->tmp->prime_len); crypto_bignum_deinit(sae->tmp->peer_commit_element_ffc, 0); sae->tmp->peer_commit_element_ffc = crypto_bignum_init_set(pos, sae->tmp->prime_len); if (sae->tmp->peer_commit_element_ffc == NULL) return WLAN_STATUS_UNSPECIFIED_FAILURE; if (crypto_bignum_is_zero(sae->tmp->peer_commit_element_ffc) || crypto_bignum_is_one(sae->tmp->peer_commit_element_ffc) || crypto_bignum_cmp(sae->tmp->peer_commit_element_ffc, sae->tmp->prime) >= 0) { wpa_printf(MSG_DEBUG, "SAE: Invalid peer element"); return WLAN_STATUS_UNSPECIFIED_FAILURE; } /* scalar-op(r, ELEMENT) = 1 modulo p */ res = crypto_bignum_init(); if (res == NULL || crypto_bignum_exptmod(sae->tmp->peer_commit_element_ffc, sae->tmp->order, sae->tmp->prime, res) < 0 || !crypto_bignum_is_one(res)) { wpa_printf(MSG_DEBUG, "SAE: Invalid peer element (scalar-op)"); crypto_bignum_deinit(res, 0); return WLAN_STATUS_UNSPECIFIED_FAILURE; } crypto_bignum_deinit(res, 0); return WLAN_STATUS_SUCCESS; } static u16 sae_parse_commit_element(struct sae_data *sae, const u8 *pos, const u8 *end) { if (sae->tmp->dh) return sae_parse_commit_element_ffc(sae, pos, end); return sae_parse_commit_element_ecc(sae, pos, end); } u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len, const u8 **token, size_t *token_len, int *allowed_groups) { const u8 *pos = data, *end = data + len; u16 res; /* Check Finite Cyclic Group */ if (pos + 2 > end) return WLAN_STATUS_UNSPECIFIED_FAILURE; res = sae_group_allowed(sae, allowed_groups, WPA_GET_LE16(pos)); if (res != WLAN_STATUS_SUCCESS) return res; pos += 2; /* Optional Anti-Clogging Token */ sae_parse_commit_token(sae, &pos, end, token, token_len); /* commit-scalar */ res = sae_parse_commit_scalar(sae, &pos, end); if (res != WLAN_STATUS_SUCCESS) return res; /* commit-element */ return sae_parse_commit_element(sae, pos, end); } static void sae_cn_confirm(struct sae_data *sae, const u8 *sc, const struct crypto_bignum *scalar1, const u8 *element1, size_t element1_len, const struct crypto_bignum *scalar2, const u8 *element2, size_t element2_len, u8 *confirm) { const u8 *addr[5]; size_t len[5]; u8 scalar_b1[SAE_MAX_PRIME_LEN], scalar_b2[SAE_MAX_PRIME_LEN]; /* Confirm * CN(key, X, Y, Z, ...) = * HMAC-SHA256(key, D2OS(X) || D2OS(Y) || D2OS(Z) | ...) * confirm = CN(KCK, send-confirm, commit-scalar, COMMIT-ELEMENT, * peer-commit-scalar, PEER-COMMIT-ELEMENT) * verifier = CN(KCK, peer-send-confirm, peer-commit-scalar, * PEER-COMMIT-ELEMENT, commit-scalar, COMMIT-ELEMENT) */ addr[0] = sc; len[0] = 2; crypto_bignum_to_bin(scalar1, scalar_b1, sizeof(scalar_b1), sae->tmp->prime_len); addr[1] = scalar_b1; len[1] = sae->tmp->prime_len; addr[2] = element1; len[2] = element1_len; crypto_bignum_to_bin(scalar2, scalar_b2, sizeof(scalar_b2), sae->tmp->prime_len); addr[3] = scalar_b2; len[3] = sae->tmp->prime_len; addr[4] = element2; len[4] = element2_len; hmac_sha256_vector(sae->tmp->kck, sizeof(sae->tmp->kck), 5, addr, len, confirm); } static void sae_cn_confirm_ecc(struct sae_data *sae, const u8 *sc, const struct crypto_bignum *scalar1, const struct crypto_ec_point *element1, const struct crypto_bignum *scalar2, const struct crypto_ec_point *element2, u8 *confirm) { u8 element_b1[2 * SAE_MAX_ECC_PRIME_LEN]; u8 element_b2[2 * SAE_MAX_ECC_PRIME_LEN]; crypto_ec_point_to_bin(sae->tmp->ec, element1, element_b1, element_b1 + sae->tmp->prime_len); crypto_ec_point_to_bin(sae->tmp->ec, element2, element_b2, element_b2 + sae->tmp->prime_len); sae_cn_confirm(sae, sc, scalar1, element_b1, 2 * sae->tmp->prime_len, scalar2, element_b2, 2 * sae->tmp->prime_len, confirm); } static void sae_cn_confirm_ffc(struct sae_data *sae, const u8 *sc, const struct crypto_bignum *scalar1, const struct crypto_bignum *element1, const struct crypto_bignum *scalar2, const struct crypto_bignum *element2, u8 *confirm) { u8 element_b1[SAE_MAX_PRIME_LEN]; u8 element_b2[SAE_MAX_PRIME_LEN]; crypto_bignum_to_bin(element1, element_b1, sizeof(element_b1), sae->tmp->prime_len); crypto_bignum_to_bin(element2, element_b2, sizeof(element_b2), sae->tmp->prime_len); sae_cn_confirm(sae, sc, scalar1, element_b1, sae->tmp->prime_len, scalar2, element_b2, sae->tmp->prime_len, confirm); } void sae_write_confirm(struct sae_data *sae, struct wpabuf *buf) { const u8 *sc; /* Send-Confirm */ sc = wpabuf_put(buf, 0); wpabuf_put_le16(buf, sae->send_confirm); sae->send_confirm++; if (sae->tmp->ec) sae_cn_confirm_ecc(sae, sc, sae->tmp->own_commit_scalar, sae->tmp->own_commit_element_ecc, sae->peer_commit_scalar, sae->tmp->peer_commit_element_ecc, wpabuf_put(buf, SHA256_MAC_LEN)); else sae_cn_confirm_ffc(sae, sc, sae->tmp->own_commit_scalar, sae->tmp->own_commit_element_ffc, sae->peer_commit_scalar, sae->tmp->peer_commit_element_ffc, wpabuf_put(buf, SHA256_MAC_LEN)); } int sae_check_confirm(struct sae_data *sae, const u8 *data, size_t len) { u8 verifier[SHA256_MAC_LEN]; if (len < 2 + SHA256_MAC_LEN) { wpa_printf(MSG_DEBUG, "SAE: Too short confirm message"); return -1; } wpa_printf(MSG_DEBUG, "SAE: peer-send-confirm %u", WPA_GET_LE16(data)); if (sae->tmp->ec) sae_cn_confirm_ecc(sae, data, sae->peer_commit_scalar, sae->tmp->peer_commit_element_ecc, sae->tmp->own_commit_scalar, sae->tmp->own_commit_element_ecc, verifier); else sae_cn_confirm_ffc(sae, data, sae->peer_commit_scalar, sae->tmp->peer_commit_element_ffc, sae->tmp->own_commit_scalar, sae->tmp->own_commit_element_ffc, verifier); if (os_memcmp(verifier, data + 2, SHA256_MAC_LEN) != 0) { wpa_printf(MSG_DEBUG, "SAE: Confirm mismatch"); wpa_hexdump(MSG_DEBUG, "SAE: Received confirm", data + 2, SHA256_MAC_LEN); wpa_hexdump(MSG_DEBUG, "SAE: Calculated verifier", verifier, SHA256_MAC_LEN); return -1; } return 0; } wpa-2.1/src/common/sae.h000066400000000000000000000040101227414666700151550ustar00rootroot00000000000000/* * Simultaneous authentication of equals * Copyright (c) 2012-2013, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef SAE_H #define SAE_H #define SAE_KCK_LEN 32 #define SAE_PMK_LEN 32 #define SAE_PMKID_LEN 16 #define SAE_KEYSEED_KEY_LEN 32 #define SAE_MAX_PRIME_LEN 512 #define SAE_MAX_ECC_PRIME_LEN 66 #define SAE_COMMIT_MAX_LEN (2 + 3 * SAE_MAX_PRIME_LEN) #define SAE_CONFIRM_MAX_LEN (2 + SAE_MAX_PRIME_LEN) struct sae_temporary_data { u8 kck[SAE_KCK_LEN]; struct crypto_bignum *own_commit_scalar; struct crypto_bignum *own_commit_element_ffc; struct crypto_ec_point *own_commit_element_ecc; struct crypto_bignum *peer_commit_element_ffc; struct crypto_ec_point *peer_commit_element_ecc; struct crypto_ec_point *pwe_ecc; struct crypto_bignum *pwe_ffc; struct crypto_bignum *sae_rand; struct crypto_ec *ec; int prime_len; const struct dh_group *dh; const struct crypto_bignum *prime; const struct crypto_bignum *order; struct crypto_bignum *prime_buf; struct crypto_bignum *order_buf; }; struct sae_data { enum { SAE_NOTHING, SAE_COMMITTED, SAE_CONFIRMED, SAE_ACCEPTED } state; u16 send_confirm; u8 pmk[SAE_PMK_LEN]; struct crypto_bignum *peer_commit_scalar; int group; struct sae_temporary_data *tmp; }; int sae_set_group(struct sae_data *sae, int group); void sae_clear_temp_data(struct sae_data *sae); void sae_clear_data(struct sae_data *sae); int sae_prepare_commit(const u8 *addr1, const u8 *addr2, const u8 *password, size_t password_len, struct sae_data *sae); int sae_process_commit(struct sae_data *sae); void sae_write_commit(struct sae_data *sae, struct wpabuf *buf, const struct wpabuf *token); u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len, const u8 **token, size_t *token_len, int *allowed_groups); void sae_write_confirm(struct sae_data *sae, struct wpabuf *buf); int sae_check_confirm(struct sae_data *sae, const u8 *data, size_t len); #endif /* SAE_H */ wpa-2.1/src/common/version.h000066400000000000000000000003101227414666700160710ustar00rootroot00000000000000#ifndef VERSION_H #define VERSION_H #ifndef VERSION_STR_POSTFIX #define VERSION_STR_POSTFIX "" #endif /* VERSION_STR_POSTFIX */ #define VERSION_STR "2.1" VERSION_STR_POSTFIX #endif /* VERSION_H */ wpa-2.1/src/common/wpa_common.c000066400000000000000000001073401227414666700165510ustar00rootroot00000000000000/* * WPA/RSN - Shared functions for supplicant and authenticator * Copyright (c) 2002-2013, 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/md5.h" #include "crypto/sha1.h" #include "crypto/sha256.h" #include "crypto/aes_wrap.h" #include "crypto/crypto.h" #include "ieee802_11_defs.h" #include "defs.h" #include "wpa_common.h" /** * wpa_eapol_key_mic - Calculate EAPOL-Key MIC * @key: EAPOL-Key Key Confirmation Key (KCK) * @ver: Key descriptor version (WPA_KEY_INFO_TYPE_*) * @buf: Pointer to the beginning of the EAPOL header (version field) * @len: Length of the EAPOL frame (from EAPOL header to the end of the frame) * @mic: Pointer to the buffer to which the EAPOL-Key MIC is written * Returns: 0 on success, -1 on failure * * Calculate EAPOL-Key MIC for an EAPOL-Key packet. The EAPOL-Key MIC field has * to be cleared (all zeroes) when calling this function. * * Note: 'IEEE Std 802.11i-2004 - 8.5.2 EAPOL-Key frames' has an error in the * description of the Key MIC calculation. It includes packet data from the * beginning of the EAPOL-Key header, not EAPOL header. This incorrect change * happened during final editing of the standard and the correct behavior is * defined in the last draft (IEEE 802.11i/D10). */ int wpa_eapol_key_mic(const u8 *key, int ver, const u8 *buf, size_t len, u8 *mic) { u8 hash[SHA1_MAC_LEN]; switch (ver) { #ifndef CONFIG_FIPS case WPA_KEY_INFO_TYPE_HMAC_MD5_RC4: return hmac_md5(key, 16, buf, len, mic); #endif /* CONFIG_FIPS */ case WPA_KEY_INFO_TYPE_HMAC_SHA1_AES: if (hmac_sha1(key, 16, buf, len, hash)) return -1; os_memcpy(mic, hash, MD5_MAC_LEN); break; #if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W) case WPA_KEY_INFO_TYPE_AES_128_CMAC: return omac1_aes_128(key, buf, len, mic); #endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */ default: return -1; } return 0; } /** * wpa_pmk_to_ptk - Calculate PTK from PMK, addresses, and nonces * @pmk: Pairwise master key * @pmk_len: Length of PMK * @label: Label to use in derivation * @addr1: AA or SA * @addr2: SA or AA * @nonce1: ANonce or SNonce * @nonce2: SNonce or ANonce * @ptk: Buffer for pairwise transient key * @ptk_len: Length of PTK * @use_sha256: Whether to use SHA256-based KDF * * IEEE Std 802.11i-2004 - 8.5.1.2 Pairwise key hierarchy * PTK = PRF-X(PMK, "Pairwise key expansion", * Min(AA, SA) || Max(AA, SA) || * Min(ANonce, SNonce) || Max(ANonce, SNonce)) * * STK = PRF-X(SMK, "Peer key expansion", * Min(MAC_I, MAC_P) || Max(MAC_I, MAC_P) || * Min(INonce, PNonce) || Max(INonce, PNonce)) */ void wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label, const u8 *addr1, const u8 *addr2, const u8 *nonce1, const u8 *nonce2, u8 *ptk, size_t ptk_len, int use_sha256) { u8 data[2 * ETH_ALEN + 2 * WPA_NONCE_LEN]; if (os_memcmp(addr1, addr2, ETH_ALEN) < 0) { os_memcpy(data, addr1, ETH_ALEN); os_memcpy(data + ETH_ALEN, addr2, ETH_ALEN); } else { os_memcpy(data, addr2, ETH_ALEN); os_memcpy(data + ETH_ALEN, addr1, ETH_ALEN); } if (os_memcmp(nonce1, nonce2, WPA_NONCE_LEN) < 0) { os_memcpy(data + 2 * ETH_ALEN, nonce1, WPA_NONCE_LEN); os_memcpy(data + 2 * ETH_ALEN + WPA_NONCE_LEN, nonce2, WPA_NONCE_LEN); } else { os_memcpy(data + 2 * ETH_ALEN, nonce2, WPA_NONCE_LEN); os_memcpy(data + 2 * ETH_ALEN + WPA_NONCE_LEN, nonce1, WPA_NONCE_LEN); } #ifdef CONFIG_IEEE80211W if (use_sha256) sha256_prf(pmk, pmk_len, label, data, sizeof(data), ptk, ptk_len); else #endif /* CONFIG_IEEE80211W */ sha1_prf(pmk, pmk_len, label, data, sizeof(data), ptk, ptk_len); wpa_printf(MSG_DEBUG, "WPA: PTK derivation - A1=" MACSTR " A2=" MACSTR, MAC2STR(addr1), MAC2STR(addr2)); wpa_hexdump(MSG_DEBUG, "WPA: Nonce1", nonce1, WPA_NONCE_LEN); wpa_hexdump(MSG_DEBUG, "WPA: Nonce2", nonce2, WPA_NONCE_LEN); wpa_hexdump_key(MSG_DEBUG, "WPA: PMK", pmk, pmk_len); wpa_hexdump_key(MSG_DEBUG, "WPA: PTK", ptk, ptk_len); } #ifdef CONFIG_IEEE80211R int wpa_ft_mic(const u8 *kck, const u8 *sta_addr, const u8 *ap_addr, u8 transaction_seqnum, const u8 *mdie, size_t mdie_len, const u8 *ftie, size_t ftie_len, const u8 *rsnie, size_t rsnie_len, const u8 *ric, size_t ric_len, u8 *mic) { u8 *buf, *pos; size_t buf_len; buf_len = 2 * ETH_ALEN + 1 + mdie_len + ftie_len + rsnie_len + ric_len; buf = os_malloc(buf_len); if (buf == NULL) return -1; pos = buf; os_memcpy(pos, sta_addr, ETH_ALEN); pos += ETH_ALEN; os_memcpy(pos, ap_addr, ETH_ALEN); pos += ETH_ALEN; *pos++ = transaction_seqnum; if (rsnie) { os_memcpy(pos, rsnie, rsnie_len); pos += rsnie_len; } if (mdie) { os_memcpy(pos, mdie, mdie_len); pos += mdie_len; } if (ftie) { struct rsn_ftie *_ftie; os_memcpy(pos, ftie, ftie_len); if (ftie_len < 2 + sizeof(*_ftie)) { os_free(buf); return -1; } _ftie = (struct rsn_ftie *) (pos + 2); os_memset(_ftie->mic, 0, sizeof(_ftie->mic)); pos += ftie_len; } if (ric) { os_memcpy(pos, ric, ric_len); pos += ric_len; } wpa_hexdump(MSG_MSGDUMP, "FT: MIC data", buf, pos - buf); if (omac1_aes_128(kck, buf, pos - buf, mic)) { os_free(buf); return -1; } os_free(buf); return 0; } static int wpa_ft_parse_ftie(const u8 *ie, size_t ie_len, struct wpa_ft_ies *parse) { const u8 *end, *pos; parse->ftie = ie; parse->ftie_len = ie_len; pos = ie + sizeof(struct rsn_ftie); end = ie + ie_len; while (pos + 2 <= end && pos + 2 + pos[1] <= end) { switch (pos[0]) { case FTIE_SUBELEM_R1KH_ID: if (pos[1] != FT_R1KH_ID_LEN) { wpa_printf(MSG_DEBUG, "FT: Invalid R1KH-ID " "length in FTIE: %d", pos[1]); return -1; } parse->r1kh_id = pos + 2; break; case FTIE_SUBELEM_GTK: parse->gtk = pos + 2; parse->gtk_len = pos[1]; break; case FTIE_SUBELEM_R0KH_ID: if (pos[1] < 1 || pos[1] > FT_R0KH_ID_MAX_LEN) { wpa_printf(MSG_DEBUG, "FT: Invalid R0KH-ID " "length in FTIE: %d", pos[1]); return -1; } parse->r0kh_id = pos + 2; parse->r0kh_id_len = pos[1]; break; #ifdef CONFIG_IEEE80211W case FTIE_SUBELEM_IGTK: parse->igtk = pos + 2; parse->igtk_len = pos[1]; break; #endif /* CONFIG_IEEE80211W */ } pos += 2 + pos[1]; } return 0; } int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, struct wpa_ft_ies *parse) { const u8 *end, *pos; struct wpa_ie_data data; int ret; const struct rsn_ftie *ftie; int prot_ie_count = 0; os_memset(parse, 0, sizeof(*parse)); if (ies == NULL) return 0; pos = ies; end = ies + ies_len; while (pos + 2 <= end && pos + 2 + pos[1] <= end) { switch (pos[0]) { case WLAN_EID_RSN: parse->rsn = pos + 2; parse->rsn_len = pos[1]; ret = wpa_parse_wpa_ie_rsn(parse->rsn - 2, parse->rsn_len + 2, &data); if (ret < 0) { wpa_printf(MSG_DEBUG, "FT: Failed to parse " "RSN IE: %d", ret); return -1; } if (data.num_pmkid == 1 && data.pmkid) parse->rsn_pmkid = data.pmkid; break; case WLAN_EID_MOBILITY_DOMAIN: parse->mdie = pos + 2; parse->mdie_len = pos[1]; break; case WLAN_EID_FAST_BSS_TRANSITION: if (pos[1] < sizeof(*ftie)) return -1; ftie = (const struct rsn_ftie *) (pos + 2); prot_ie_count = ftie->mic_control[1]; if (wpa_ft_parse_ftie(pos + 2, pos[1], parse) < 0) return -1; break; case WLAN_EID_TIMEOUT_INTERVAL: parse->tie = pos + 2; parse->tie_len = pos[1]; break; case WLAN_EID_RIC_DATA: if (parse->ric == NULL) parse->ric = pos; break; } pos += 2 + pos[1]; } if (prot_ie_count == 0) return 0; /* no MIC */ /* * Check that the protected IE count matches with IEs included in the * frame. */ if (parse->rsn) prot_ie_count--; if (parse->mdie) prot_ie_count--; if (parse->ftie) prot_ie_count--; if (prot_ie_count < 0) { wpa_printf(MSG_DEBUG, "FT: Some required IEs not included in " "the protected IE count"); return -1; } if (prot_ie_count == 0 && parse->ric) { wpa_printf(MSG_DEBUG, "FT: RIC IE(s) in the frame, but not " "included in protected IE count"); return -1; } /* Determine the end of the RIC IE(s) */ pos = parse->ric; while (pos && pos + 2 <= end && pos + 2 + pos[1] <= end && prot_ie_count) { prot_ie_count--; pos += 2 + pos[1]; } parse->ric_len = pos - parse->ric; if (prot_ie_count) { wpa_printf(MSG_DEBUG, "FT: %d protected IEs missing from " "frame", (int) prot_ie_count); return -1; } return 0; } #endif /* CONFIG_IEEE80211R */ static int rsn_selector_to_bitfield(const u8 *s) { if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_NONE) return WPA_CIPHER_NONE; if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_WEP40) return WPA_CIPHER_WEP40; if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_TKIP) return WPA_CIPHER_TKIP; if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_CCMP) return WPA_CIPHER_CCMP; if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_WEP104) return WPA_CIPHER_WEP104; #ifdef CONFIG_IEEE80211W if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_AES_128_CMAC) return WPA_CIPHER_AES_128_CMAC; #endif /* CONFIG_IEEE80211W */ if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_GCMP) return WPA_CIPHER_GCMP; if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_CCMP_256) return WPA_CIPHER_CCMP_256; if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_GCMP_256) return WPA_CIPHER_GCMP_256; if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_BIP_GMAC_128) return WPA_CIPHER_BIP_GMAC_128; if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_BIP_GMAC_256) return WPA_CIPHER_BIP_GMAC_256; if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_BIP_CMAC_256) return WPA_CIPHER_BIP_CMAC_256; return 0; } static int rsn_key_mgmt_to_bitfield(const u8 *s) { if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_UNSPEC_802_1X) return WPA_KEY_MGMT_IEEE8021X; if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X) return WPA_KEY_MGMT_PSK; #ifdef CONFIG_IEEE80211R if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_802_1X) return WPA_KEY_MGMT_FT_IEEE8021X; if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_PSK) return WPA_KEY_MGMT_FT_PSK; #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_IEEE80211W if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_802_1X_SHA256) return WPA_KEY_MGMT_IEEE8021X_SHA256; if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_PSK_SHA256) return WPA_KEY_MGMT_PSK_SHA256; #endif /* CONFIG_IEEE80211W */ #ifdef CONFIG_SAE if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_SAE) return WPA_KEY_MGMT_SAE; if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_SAE) return WPA_KEY_MGMT_FT_SAE; #endif /* CONFIG_SAE */ return 0; } /** * wpa_parse_wpa_ie_rsn - Parse RSN IE * @rsn_ie: Buffer containing RSN IE * @rsn_ie_len: RSN IE buffer length (including IE number and length octets) * @data: Pointer to structure that will be filled in with parsed data * Returns: 0 on success, <0 on failure */ int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len, struct wpa_ie_data *data) { const struct rsn_ie_hdr *hdr; const u8 *pos; int left; int i, count; os_memset(data, 0, sizeof(*data)); data->proto = WPA_PROTO_RSN; data->pairwise_cipher = WPA_CIPHER_CCMP; data->group_cipher = WPA_CIPHER_CCMP; data->key_mgmt = WPA_KEY_MGMT_IEEE8021X; data->capabilities = 0; data->pmkid = NULL; data->num_pmkid = 0; #ifdef CONFIG_IEEE80211W data->mgmt_group_cipher = WPA_CIPHER_AES_128_CMAC; #else /* CONFIG_IEEE80211W */ data->mgmt_group_cipher = 0; #endif /* CONFIG_IEEE80211W */ if (rsn_ie_len == 0) { /* No RSN IE - fail silently */ return -1; } if (rsn_ie_len < sizeof(struct rsn_ie_hdr)) { wpa_printf(MSG_DEBUG, "%s: ie len too short %lu", __func__, (unsigned long) rsn_ie_len); return -1; } hdr = (const struct rsn_ie_hdr *) rsn_ie; if (hdr->elem_id != WLAN_EID_RSN || hdr->len != rsn_ie_len - 2 || WPA_GET_LE16(hdr->version) != RSN_VERSION) { wpa_printf(MSG_DEBUG, "%s: malformed ie or unknown version", __func__); return -2; } pos = (const u8 *) (hdr + 1); left = rsn_ie_len - sizeof(*hdr); if (left >= RSN_SELECTOR_LEN) { data->group_cipher = rsn_selector_to_bitfield(pos); #ifdef CONFIG_IEEE80211W if (data->group_cipher == WPA_CIPHER_AES_128_CMAC) { wpa_printf(MSG_DEBUG, "%s: AES-128-CMAC used as group " "cipher", __func__); return -1; } #endif /* CONFIG_IEEE80211W */ pos += RSN_SELECTOR_LEN; left -= RSN_SELECTOR_LEN; } else if (left > 0) { wpa_printf(MSG_DEBUG, "%s: ie length mismatch, %u too much", __func__, left); return -3; } if (left >= 2) { data->pairwise_cipher = 0; count = WPA_GET_LE16(pos); pos += 2; left -= 2; if (count == 0 || left < count * RSN_SELECTOR_LEN) { wpa_printf(MSG_DEBUG, "%s: ie count botch (pairwise), " "count %u left %u", __func__, count, left); return -4; } for (i = 0; i < count; i++) { data->pairwise_cipher |= rsn_selector_to_bitfield(pos); pos += RSN_SELECTOR_LEN; left -= RSN_SELECTOR_LEN; } #ifdef CONFIG_IEEE80211W if (data->pairwise_cipher & WPA_CIPHER_AES_128_CMAC) { wpa_printf(MSG_DEBUG, "%s: AES-128-CMAC used as " "pairwise cipher", __func__); return -1; } #endif /* CONFIG_IEEE80211W */ } else if (left == 1) { wpa_printf(MSG_DEBUG, "%s: ie too short (for key mgmt)", __func__); return -5; } if (left >= 2) { data->key_mgmt = 0; count = WPA_GET_LE16(pos); pos += 2; left -= 2; if (count == 0 || left < count * RSN_SELECTOR_LEN) { wpa_printf(MSG_DEBUG, "%s: ie count botch (key mgmt), " "count %u left %u", __func__, count, left); return -6; } for (i = 0; i < count; i++) { data->key_mgmt |= rsn_key_mgmt_to_bitfield(pos); pos += RSN_SELECTOR_LEN; left -= RSN_SELECTOR_LEN; } } else if (left == 1) { wpa_printf(MSG_DEBUG, "%s: ie too short (for capabilities)", __func__); return -7; } if (left >= 2) { data->capabilities = WPA_GET_LE16(pos); pos += 2; left -= 2; } if (left >= 2) { data->num_pmkid = WPA_GET_LE16(pos); pos += 2; left -= 2; if (left < (int) data->num_pmkid * PMKID_LEN) { wpa_printf(MSG_DEBUG, "%s: PMKID underflow " "(num_pmkid=%lu left=%d)", __func__, (unsigned long) data->num_pmkid, left); data->num_pmkid = 0; return -9; } else { data->pmkid = pos; pos += data->num_pmkid * PMKID_LEN; left -= data->num_pmkid * PMKID_LEN; } } #ifdef CONFIG_IEEE80211W if (left >= 4) { data->mgmt_group_cipher = rsn_selector_to_bitfield(pos); if (data->mgmt_group_cipher != WPA_CIPHER_AES_128_CMAC) { wpa_printf(MSG_DEBUG, "%s: Unsupported management " "group cipher 0x%x", __func__, data->mgmt_group_cipher); return -10; } pos += RSN_SELECTOR_LEN; left -= RSN_SELECTOR_LEN; } #endif /* CONFIG_IEEE80211W */ if (left > 0) { wpa_printf(MSG_DEBUG, "%s: ie has %u trailing bytes - ignored", __func__, left); } return 0; } static int wpa_selector_to_bitfield(const u8 *s) { if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_NONE) return WPA_CIPHER_NONE; if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_WEP40) return WPA_CIPHER_WEP40; if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_TKIP) return WPA_CIPHER_TKIP; if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_CCMP) return WPA_CIPHER_CCMP; if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_WEP104) return WPA_CIPHER_WEP104; return 0; } static int wpa_key_mgmt_to_bitfield(const u8 *s) { if (RSN_SELECTOR_GET(s) == WPA_AUTH_KEY_MGMT_UNSPEC_802_1X) return WPA_KEY_MGMT_IEEE8021X; if (RSN_SELECTOR_GET(s) == WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X) return WPA_KEY_MGMT_PSK; if (RSN_SELECTOR_GET(s) == WPA_AUTH_KEY_MGMT_NONE) return WPA_KEY_MGMT_WPA_NONE; return 0; } int wpa_parse_wpa_ie_wpa(const u8 *wpa_ie, size_t wpa_ie_len, struct wpa_ie_data *data) { const struct wpa_ie_hdr *hdr; const u8 *pos; int left; int i, count; os_memset(data, 0, sizeof(*data)); data->proto = WPA_PROTO_WPA; data->pairwise_cipher = WPA_CIPHER_TKIP; data->group_cipher = WPA_CIPHER_TKIP; data->key_mgmt = WPA_KEY_MGMT_IEEE8021X; data->capabilities = 0; data->pmkid = NULL; data->num_pmkid = 0; data->mgmt_group_cipher = 0; if (wpa_ie_len == 0) { /* No WPA IE - fail silently */ return -1; } if (wpa_ie_len < sizeof(struct wpa_ie_hdr)) { wpa_printf(MSG_DEBUG, "%s: ie len too short %lu", __func__, (unsigned long) wpa_ie_len); return -1; } hdr = (const struct wpa_ie_hdr *) wpa_ie; if (hdr->elem_id != WLAN_EID_VENDOR_SPECIFIC || hdr->len != wpa_ie_len - 2 || RSN_SELECTOR_GET(hdr->oui) != WPA_OUI_TYPE || WPA_GET_LE16(hdr->version) != WPA_VERSION) { wpa_printf(MSG_DEBUG, "%s: malformed ie or unknown version", __func__); return -2; } pos = (const u8 *) (hdr + 1); left = wpa_ie_len - sizeof(*hdr); if (left >= WPA_SELECTOR_LEN) { data->group_cipher = wpa_selector_to_bitfield(pos); pos += WPA_SELECTOR_LEN; left -= WPA_SELECTOR_LEN; } else if (left > 0) { wpa_printf(MSG_DEBUG, "%s: ie length mismatch, %u too much", __func__, left); return -3; } if (left >= 2) { data->pairwise_cipher = 0; count = WPA_GET_LE16(pos); pos += 2; left -= 2; if (count == 0 || left < count * WPA_SELECTOR_LEN) { wpa_printf(MSG_DEBUG, "%s: ie count botch (pairwise), " "count %u left %u", __func__, count, left); return -4; } for (i = 0; i < count; i++) { data->pairwise_cipher |= wpa_selector_to_bitfield(pos); pos += WPA_SELECTOR_LEN; left -= WPA_SELECTOR_LEN; } } else if (left == 1) { wpa_printf(MSG_DEBUG, "%s: ie too short (for key mgmt)", __func__); return -5; } if (left >= 2) { data->key_mgmt = 0; count = WPA_GET_LE16(pos); pos += 2; left -= 2; if (count == 0 || left < count * WPA_SELECTOR_LEN) { wpa_printf(MSG_DEBUG, "%s: ie count botch (key mgmt), " "count %u left %u", __func__, count, left); return -6; } for (i = 0; i < count; i++) { data->key_mgmt |= wpa_key_mgmt_to_bitfield(pos); pos += WPA_SELECTOR_LEN; left -= WPA_SELECTOR_LEN; } } else if (left == 1) { wpa_printf(MSG_DEBUG, "%s: ie too short (for capabilities)", __func__); return -7; } if (left >= 2) { data->capabilities = WPA_GET_LE16(pos); pos += 2; left -= 2; } if (left > 0) { wpa_printf(MSG_DEBUG, "%s: ie has %u trailing bytes - ignored", __func__, left); } return 0; } #ifdef CONFIG_IEEE80211R /** * wpa_derive_pmk_r0 - Derive PMK-R0 and PMKR0Name * * IEEE Std 802.11r-2008 - 8.5.1.5.3 */ void wpa_derive_pmk_r0(const u8 *xxkey, size_t xxkey_len, const u8 *ssid, size_t ssid_len, const u8 *mdid, const u8 *r0kh_id, size_t r0kh_id_len, const u8 *s0kh_id, u8 *pmk_r0, u8 *pmk_r0_name) { u8 buf[1 + WPA_MAX_SSID_LEN + MOBILITY_DOMAIN_ID_LEN + 1 + FT_R0KH_ID_MAX_LEN + ETH_ALEN]; u8 *pos, r0_key_data[48], hash[32]; const u8 *addr[2]; size_t len[2]; /* * R0-Key-Data = KDF-384(XXKey, "FT-R0", * SSIDlength || SSID || MDID || R0KHlength || * R0KH-ID || S0KH-ID) * XXKey is either the second 256 bits of MSK or PSK. * PMK-R0 = L(R0-Key-Data, 0, 256) * PMK-R0Name-Salt = L(R0-Key-Data, 256, 128) */ if (ssid_len > WPA_MAX_SSID_LEN || r0kh_id_len > FT_R0KH_ID_MAX_LEN) return; pos = buf; *pos++ = ssid_len; os_memcpy(pos, ssid, ssid_len); pos += ssid_len; os_memcpy(pos, mdid, MOBILITY_DOMAIN_ID_LEN); pos += MOBILITY_DOMAIN_ID_LEN; *pos++ = r0kh_id_len; os_memcpy(pos, r0kh_id, r0kh_id_len); pos += r0kh_id_len; os_memcpy(pos, s0kh_id, ETH_ALEN); pos += ETH_ALEN; sha256_prf(xxkey, xxkey_len, "FT-R0", buf, pos - buf, r0_key_data, sizeof(r0_key_data)); os_memcpy(pmk_r0, r0_key_data, PMK_LEN); /* * PMKR0Name = Truncate-128(SHA-256("FT-R0N" || PMK-R0Name-Salt) */ addr[0] = (const u8 *) "FT-R0N"; len[0] = 6; addr[1] = r0_key_data + PMK_LEN; len[1] = 16; sha256_vector(2, addr, len, hash); os_memcpy(pmk_r0_name, hash, WPA_PMK_NAME_LEN); } /** * wpa_derive_pmk_r1_name - Derive PMKR1Name * * IEEE Std 802.11r-2008 - 8.5.1.5.4 */ void wpa_derive_pmk_r1_name(const u8 *pmk_r0_name, const u8 *r1kh_id, const u8 *s1kh_id, u8 *pmk_r1_name) { u8 hash[32]; const u8 *addr[4]; size_t len[4]; /* * PMKR1Name = Truncate-128(SHA-256("FT-R1N" || PMKR0Name || * R1KH-ID || S1KH-ID)) */ addr[0] = (const u8 *) "FT-R1N"; len[0] = 6; addr[1] = pmk_r0_name; len[1] = WPA_PMK_NAME_LEN; addr[2] = r1kh_id; len[2] = FT_R1KH_ID_LEN; addr[3] = s1kh_id; len[3] = ETH_ALEN; sha256_vector(4, addr, len, hash); os_memcpy(pmk_r1_name, hash, WPA_PMK_NAME_LEN); } /** * wpa_derive_pmk_r1 - Derive PMK-R1 and PMKR1Name from PMK-R0 * * IEEE Std 802.11r-2008 - 8.5.1.5.4 */ void wpa_derive_pmk_r1(const u8 *pmk_r0, const u8 *pmk_r0_name, const u8 *r1kh_id, const u8 *s1kh_id, u8 *pmk_r1, u8 *pmk_r1_name) { u8 buf[FT_R1KH_ID_LEN + ETH_ALEN]; u8 *pos; /* PMK-R1 = KDF-256(PMK-R0, "FT-R1", R1KH-ID || S1KH-ID) */ pos = buf; os_memcpy(pos, r1kh_id, FT_R1KH_ID_LEN); pos += FT_R1KH_ID_LEN; os_memcpy(pos, s1kh_id, ETH_ALEN); pos += ETH_ALEN; sha256_prf(pmk_r0, PMK_LEN, "FT-R1", buf, pos - buf, pmk_r1, PMK_LEN); wpa_derive_pmk_r1_name(pmk_r0_name, r1kh_id, s1kh_id, pmk_r1_name); } /** * wpa_pmk_r1_to_ptk - Derive PTK and PTKName from PMK-R1 * * IEEE Std 802.11r-2008 - 8.5.1.5.5 */ void wpa_pmk_r1_to_ptk(const u8 *pmk_r1, const u8 *snonce, const u8 *anonce, const u8 *sta_addr, const u8 *bssid, const u8 *pmk_r1_name, u8 *ptk, size_t ptk_len, u8 *ptk_name) { u8 buf[2 * WPA_NONCE_LEN + 2 * ETH_ALEN]; u8 *pos, hash[32]; const u8 *addr[6]; size_t len[6]; /* * PTK = KDF-PTKLen(PMK-R1, "FT-PTK", SNonce || ANonce || * BSSID || STA-ADDR) */ pos = buf; os_memcpy(pos, snonce, WPA_NONCE_LEN); pos += WPA_NONCE_LEN; os_memcpy(pos, anonce, WPA_NONCE_LEN); pos += WPA_NONCE_LEN; os_memcpy(pos, bssid, ETH_ALEN); pos += ETH_ALEN; os_memcpy(pos, sta_addr, ETH_ALEN); pos += ETH_ALEN; sha256_prf(pmk_r1, PMK_LEN, "FT-PTK", buf, pos - buf, ptk, ptk_len); /* * PTKName = Truncate-128(SHA-256(PMKR1Name || "FT-PTKN" || SNonce || * ANonce || BSSID || STA-ADDR)) */ addr[0] = pmk_r1_name; len[0] = WPA_PMK_NAME_LEN; addr[1] = (const u8 *) "FT-PTKN"; len[1] = 7; addr[2] = snonce; len[2] = WPA_NONCE_LEN; addr[3] = anonce; len[3] = WPA_NONCE_LEN; addr[4] = bssid; len[4] = ETH_ALEN; addr[5] = sta_addr; len[5] = ETH_ALEN; sha256_vector(6, addr, len, hash); os_memcpy(ptk_name, hash, WPA_PMK_NAME_LEN); } #endif /* CONFIG_IEEE80211R */ /** * rsn_pmkid - Calculate PMK identifier * @pmk: Pairwise master key * @pmk_len: Length of pmk in bytes * @aa: Authenticator address * @spa: Supplicant address * @pmkid: Buffer for PMKID * @use_sha256: Whether to use SHA256-based KDF * * IEEE Std 802.11i-2004 - 8.5.1.2 Pairwise key hierarchy * PMKID = HMAC-SHA1-128(PMK, "PMK Name" || AA || SPA) */ void rsn_pmkid(const u8 *pmk, size_t pmk_len, const u8 *aa, const u8 *spa, u8 *pmkid, int use_sha256) { char *title = "PMK Name"; const u8 *addr[3]; const size_t len[3] = { 8, ETH_ALEN, ETH_ALEN }; unsigned char hash[SHA256_MAC_LEN]; addr[0] = (u8 *) title; addr[1] = aa; addr[2] = spa; #ifdef CONFIG_IEEE80211W if (use_sha256) hmac_sha256_vector(pmk, pmk_len, 3, addr, len, hash); else #endif /* CONFIG_IEEE80211W */ hmac_sha1_vector(pmk, pmk_len, 3, addr, len, hash); os_memcpy(pmkid, hash, PMKID_LEN); } /** * wpa_cipher_txt - Convert cipher suite to a text string * @cipher: Cipher suite (WPA_CIPHER_* enum) * Returns: Pointer to a text string of the cipher suite name */ const char * wpa_cipher_txt(int cipher) { switch (cipher) { case WPA_CIPHER_NONE: return "NONE"; case WPA_CIPHER_WEP40: return "WEP-40"; case WPA_CIPHER_WEP104: return "WEP-104"; case WPA_CIPHER_TKIP: return "TKIP"; case WPA_CIPHER_CCMP: return "CCMP"; case WPA_CIPHER_CCMP | WPA_CIPHER_TKIP: return "CCMP+TKIP"; case WPA_CIPHER_GCMP: return "GCMP"; case WPA_CIPHER_GCMP_256: return "GCMP-256"; case WPA_CIPHER_CCMP_256: return "CCMP-256"; case WPA_CIPHER_GTK_NOT_USED: return "GTK_NOT_USED"; default: return "UNKNOWN"; } } /** * wpa_key_mgmt_txt - Convert key management suite to a text string * @key_mgmt: Key management suite (WPA_KEY_MGMT_* enum) * @proto: WPA/WPA2 version (WPA_PROTO_*) * Returns: Pointer to a text string of the key management suite name */ const char * wpa_key_mgmt_txt(int key_mgmt, int proto) { switch (key_mgmt) { case WPA_KEY_MGMT_IEEE8021X: if (proto == (WPA_PROTO_RSN | WPA_PROTO_WPA)) return "WPA2+WPA/IEEE 802.1X/EAP"; return proto == WPA_PROTO_RSN ? "WPA2/IEEE 802.1X/EAP" : "WPA/IEEE 802.1X/EAP"; case WPA_KEY_MGMT_PSK: if (proto == (WPA_PROTO_RSN | WPA_PROTO_WPA)) return "WPA2-PSK+WPA-PSK"; return proto == WPA_PROTO_RSN ? "WPA2-PSK" : "WPA-PSK"; case WPA_KEY_MGMT_NONE: return "NONE"; case WPA_KEY_MGMT_IEEE8021X_NO_WPA: return "IEEE 802.1X (no WPA)"; #ifdef CONFIG_IEEE80211R case WPA_KEY_MGMT_FT_IEEE8021X: return "FT-EAP"; case WPA_KEY_MGMT_FT_PSK: return "FT-PSK"; #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_IEEE80211W case WPA_KEY_MGMT_IEEE8021X_SHA256: return "WPA2-EAP-SHA256"; case WPA_KEY_MGMT_PSK_SHA256: return "WPA2-PSK-SHA256"; #endif /* CONFIG_IEEE80211W */ default: return "UNKNOWN"; } } int wpa_compare_rsn_ie(int ft_initial_assoc, const u8 *ie1, size_t ie1len, const u8 *ie2, size_t ie2len) { if (ie1 == NULL || ie2 == NULL) return -1; if (ie1len == ie2len && os_memcmp(ie1, ie2, ie1len) == 0) return 0; /* identical IEs */ #ifdef CONFIG_IEEE80211R if (ft_initial_assoc) { struct wpa_ie_data ie1d, ie2d; /* * The PMKID-List in RSN IE is different between Beacon/Probe * Response/(Re)Association Request frames and EAPOL-Key * messages in FT initial mobility domain association. Allow * for this, but verify that other parts of the RSN IEs are * identical. */ if (wpa_parse_wpa_ie_rsn(ie1, ie1len, &ie1d) < 0 || wpa_parse_wpa_ie_rsn(ie2, ie2len, &ie2d) < 0) return -1; if (ie1d.proto == ie2d.proto && ie1d.pairwise_cipher == ie2d.pairwise_cipher && ie1d.group_cipher == ie2d.group_cipher && ie1d.key_mgmt == ie2d.key_mgmt && ie1d.capabilities == ie2d.capabilities && ie1d.mgmt_group_cipher == ie2d.mgmt_group_cipher) return 0; } #endif /* CONFIG_IEEE80211R */ return -1; } #ifdef CONFIG_IEEE80211R int wpa_insert_pmkid(u8 *ies, size_t ies_len, const u8 *pmkid) { u8 *start, *end, *rpos, *rend; int added = 0; start = ies; end = ies + ies_len; while (start < end) { if (*start == WLAN_EID_RSN) break; start += 2 + start[1]; } if (start >= end) { wpa_printf(MSG_ERROR, "FT: Could not find RSN IE in " "IEs data"); return -1; } wpa_hexdump(MSG_DEBUG, "FT: RSN IE before modification", start, 2 + start[1]); /* Find start of PMKID-Count */ rpos = start + 2; rend = rpos + start[1]; /* Skip Version and Group Data Cipher Suite */ rpos += 2 + 4; /* Skip Pairwise Cipher Suite Count and List */ rpos += 2 + WPA_GET_LE16(rpos) * RSN_SELECTOR_LEN; /* Skip AKM Suite Count and List */ rpos += 2 + WPA_GET_LE16(rpos) * RSN_SELECTOR_LEN; if (rpos == rend) { /* Add RSN Capabilities */ os_memmove(rpos + 2, rpos, end - rpos); *rpos++ = 0; *rpos++ = 0; } else { /* Skip RSN Capabilities */ rpos += 2; if (rpos > rend) { wpa_printf(MSG_ERROR, "FT: Could not parse RSN IE in " "IEs data"); return -1; } } if (rpos == rend) { /* No PMKID-Count field included; add it */ os_memmove(rpos + 2 + PMKID_LEN, rpos, end - rpos); WPA_PUT_LE16(rpos, 1); rpos += 2; os_memcpy(rpos, pmkid, PMKID_LEN); added += 2 + PMKID_LEN; start[1] += 2 + PMKID_LEN; } else { /* PMKID-Count was included; use it */ if (WPA_GET_LE16(rpos) != 0) { wpa_printf(MSG_ERROR, "FT: Unexpected PMKID " "in RSN IE in EAPOL-Key data"); return -1; } WPA_PUT_LE16(rpos, 1); rpos += 2; os_memmove(rpos + PMKID_LEN, rpos, end - rpos); os_memcpy(rpos, pmkid, PMKID_LEN); added += PMKID_LEN; start[1] += PMKID_LEN; } wpa_hexdump(MSG_DEBUG, "FT: RSN IE after modification " "(PMKID inserted)", start, 2 + start[1]); return added; } #endif /* CONFIG_IEEE80211R */ int wpa_cipher_key_len(int cipher) { switch (cipher) { case WPA_CIPHER_CCMP_256: case WPA_CIPHER_GCMP_256: return 32; case WPA_CIPHER_CCMP: case WPA_CIPHER_GCMP: return 16; case WPA_CIPHER_TKIP: return 32; case WPA_CIPHER_WEP104: return 13; case WPA_CIPHER_WEP40: return 5; } return 0; } int wpa_cipher_rsc_len(int cipher) { switch (cipher) { case WPA_CIPHER_CCMP_256: case WPA_CIPHER_GCMP_256: case WPA_CIPHER_CCMP: case WPA_CIPHER_GCMP: case WPA_CIPHER_TKIP: return 6; case WPA_CIPHER_WEP104: case WPA_CIPHER_WEP40: return 0; } return 0; } int wpa_cipher_to_alg(int cipher) { switch (cipher) { case WPA_CIPHER_CCMP_256: return WPA_ALG_CCMP_256; case WPA_CIPHER_GCMP_256: return WPA_ALG_GCMP_256; case WPA_CIPHER_CCMP: return WPA_ALG_CCMP; case WPA_CIPHER_GCMP: return WPA_ALG_GCMP; case WPA_CIPHER_TKIP: return WPA_ALG_TKIP; case WPA_CIPHER_WEP104: case WPA_CIPHER_WEP40: return WPA_ALG_WEP; } return WPA_ALG_NONE; } int wpa_cipher_valid_pairwise(int cipher) { return cipher == WPA_CIPHER_CCMP_256 || cipher == WPA_CIPHER_GCMP_256 || cipher == WPA_CIPHER_CCMP || cipher == WPA_CIPHER_GCMP || cipher == WPA_CIPHER_TKIP; } u32 wpa_cipher_to_suite(int proto, int cipher) { if (cipher & WPA_CIPHER_CCMP_256) return RSN_CIPHER_SUITE_CCMP_256; if (cipher & WPA_CIPHER_GCMP_256) return RSN_CIPHER_SUITE_GCMP_256; if (cipher & WPA_CIPHER_CCMP) return (proto == WPA_PROTO_RSN ? RSN_CIPHER_SUITE_CCMP : WPA_CIPHER_SUITE_CCMP); if (cipher & WPA_CIPHER_GCMP) return RSN_CIPHER_SUITE_GCMP; if (cipher & WPA_CIPHER_TKIP) return (proto == WPA_PROTO_RSN ? RSN_CIPHER_SUITE_TKIP : WPA_CIPHER_SUITE_TKIP); if (cipher & WPA_CIPHER_WEP104) return (proto == WPA_PROTO_RSN ? RSN_CIPHER_SUITE_WEP104 : WPA_CIPHER_SUITE_WEP104); if (cipher & WPA_CIPHER_WEP40) return (proto == WPA_PROTO_RSN ? RSN_CIPHER_SUITE_WEP40 : WPA_CIPHER_SUITE_WEP40); if (cipher & WPA_CIPHER_NONE) return (proto == WPA_PROTO_RSN ? RSN_CIPHER_SUITE_NONE : WPA_CIPHER_SUITE_NONE); if (cipher & WPA_CIPHER_GTK_NOT_USED) return RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED; return 0; } int rsn_cipher_put_suites(u8 *pos, int ciphers) { int num_suites = 0; if (ciphers & WPA_CIPHER_CCMP_256) { RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP_256); pos += RSN_SELECTOR_LEN; num_suites++; } if (ciphers & WPA_CIPHER_GCMP_256) { RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_GCMP_256); pos += RSN_SELECTOR_LEN; num_suites++; } if (ciphers & WPA_CIPHER_CCMP) { RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP); pos += RSN_SELECTOR_LEN; num_suites++; } if (ciphers & WPA_CIPHER_GCMP) { RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_GCMP); pos += RSN_SELECTOR_LEN; num_suites++; } if (ciphers & WPA_CIPHER_TKIP) { RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP); pos += RSN_SELECTOR_LEN; num_suites++; } if (ciphers & WPA_CIPHER_NONE) { RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NONE); pos += RSN_SELECTOR_LEN; num_suites++; } return num_suites; } int wpa_cipher_put_suites(u8 *pos, int ciphers) { int num_suites = 0; if (ciphers & WPA_CIPHER_CCMP) { RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_CCMP); pos += WPA_SELECTOR_LEN; num_suites++; } if (ciphers & WPA_CIPHER_TKIP) { RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_TKIP); pos += WPA_SELECTOR_LEN; num_suites++; } if (ciphers & WPA_CIPHER_NONE) { RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_NONE); pos += WPA_SELECTOR_LEN; num_suites++; } return num_suites; } int wpa_pick_pairwise_cipher(int ciphers, int none_allowed) { if (ciphers & WPA_CIPHER_CCMP_256) return WPA_CIPHER_CCMP_256; if (ciphers & WPA_CIPHER_GCMP_256) return WPA_CIPHER_GCMP_256; if (ciphers & WPA_CIPHER_CCMP) return WPA_CIPHER_CCMP; if (ciphers & WPA_CIPHER_GCMP) return WPA_CIPHER_GCMP; if (ciphers & WPA_CIPHER_TKIP) return WPA_CIPHER_TKIP; if (none_allowed && (ciphers & WPA_CIPHER_NONE)) return WPA_CIPHER_NONE; return -1; } int wpa_pick_group_cipher(int ciphers) { if (ciphers & WPA_CIPHER_CCMP_256) return WPA_CIPHER_CCMP_256; if (ciphers & WPA_CIPHER_GCMP_256) return WPA_CIPHER_GCMP_256; if (ciphers & WPA_CIPHER_CCMP) return WPA_CIPHER_CCMP; if (ciphers & WPA_CIPHER_GCMP) return WPA_CIPHER_GCMP; if (ciphers & WPA_CIPHER_GTK_NOT_USED) return WPA_CIPHER_GTK_NOT_USED; if (ciphers & WPA_CIPHER_TKIP) return WPA_CIPHER_TKIP; if (ciphers & WPA_CIPHER_WEP104) return WPA_CIPHER_WEP104; if (ciphers & WPA_CIPHER_WEP40) return WPA_CIPHER_WEP40; return -1; } int wpa_parse_cipher(const char *value) { int val = 0, last; char *start, *end, *buf; buf = os_strdup(value); if (buf == NULL) return -1; start = buf; while (*start != '\0') { while (*start == ' ' || *start == '\t') start++; if (*start == '\0') break; end = start; while (*end != ' ' && *end != '\t' && *end != '\0') end++; last = *end == '\0'; *end = '\0'; if (os_strcmp(start, "CCMP-256") == 0) val |= WPA_CIPHER_CCMP_256; else if (os_strcmp(start, "GCMP-256") == 0) val |= WPA_CIPHER_GCMP_256; else if (os_strcmp(start, "CCMP") == 0) val |= WPA_CIPHER_CCMP; else if (os_strcmp(start, "GCMP") == 0) val |= WPA_CIPHER_GCMP; else if (os_strcmp(start, "TKIP") == 0) val |= WPA_CIPHER_TKIP; else if (os_strcmp(start, "WEP104") == 0) val |= WPA_CIPHER_WEP104; else if (os_strcmp(start, "WEP40") == 0) val |= WPA_CIPHER_WEP40; else if (os_strcmp(start, "NONE") == 0) val |= WPA_CIPHER_NONE; else if (os_strcmp(start, "GTK_NOT_USED") == 0) val |= WPA_CIPHER_GTK_NOT_USED; else { os_free(buf); return -1; } if (last) break; start = end + 1; } os_free(buf); return val; } int wpa_write_ciphers(char *start, char *end, int ciphers, const char *delim) { char *pos = start; int ret; if (ciphers & WPA_CIPHER_CCMP_256) { ret = os_snprintf(pos, end - pos, "%sCCMP-256", pos == start ? "" : delim); if (ret < 0 || ret >= end - pos) return -1; pos += ret; } if (ciphers & WPA_CIPHER_GCMP_256) { ret = os_snprintf(pos, end - pos, "%sGCMP-256", pos == start ? "" : delim); if (ret < 0 || ret >= end - pos) return -1; pos += ret; } if (ciphers & WPA_CIPHER_CCMP) { ret = os_snprintf(pos, end - pos, "%sCCMP", pos == start ? "" : delim); if (ret < 0 || ret >= end - pos) return -1; pos += ret; } if (ciphers & WPA_CIPHER_GCMP) { ret = os_snprintf(pos, end - pos, "%sGCMP", pos == start ? "" : delim); if (ret < 0 || ret >= end - pos) return -1; pos += ret; } if (ciphers & WPA_CIPHER_TKIP) { ret = os_snprintf(pos, end - pos, "%sTKIP", pos == start ? "" : delim); if (ret < 0 || ret >= end - pos) return -1; pos += ret; } if (ciphers & WPA_CIPHER_WEP104) { ret = os_snprintf(pos, end - pos, "%sWEP104", pos == start ? "" : delim); if (ret < 0 || ret >= end - pos) return -1; pos += ret; } if (ciphers & WPA_CIPHER_WEP40) { ret = os_snprintf(pos, end - pos, "%sWEP40", pos == start ? "" : delim); if (ret < 0 || ret >= end - pos) return -1; pos += ret; } if (ciphers & WPA_CIPHER_NONE) { ret = os_snprintf(pos, end - pos, "%sNONE", pos == start ? "" : delim); if (ret < 0 || ret >= end - pos) return -1; pos += ret; } return pos - start; } int wpa_select_ap_group_cipher(int wpa, int wpa_pairwise, int rsn_pairwise) { int pairwise = 0; /* Select group cipher based on the enabled pairwise cipher suites */ if (wpa & 1) pairwise |= wpa_pairwise; if (wpa & 2) pairwise |= rsn_pairwise; if (pairwise & WPA_CIPHER_TKIP) return WPA_CIPHER_TKIP; if ((pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP)) == WPA_CIPHER_GCMP) return WPA_CIPHER_GCMP; if ((pairwise & (WPA_CIPHER_GCMP_256 | WPA_CIPHER_CCMP | WPA_CIPHER_GCMP)) == WPA_CIPHER_GCMP_256) return WPA_CIPHER_GCMP_256; if ((pairwise & (WPA_CIPHER_CCMP_256 | WPA_CIPHER_CCMP | WPA_CIPHER_GCMP)) == WPA_CIPHER_CCMP_256) return WPA_CIPHER_CCMP_256; return WPA_CIPHER_CCMP; } wpa-2.1/src/common/wpa_common.h000066400000000000000000000325041227414666700165550ustar00rootroot00000000000000/* * WPA definitions shared between hostapd and wpa_supplicant * Copyright (c) 2002-2013, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef WPA_COMMON_H #define WPA_COMMON_H #define WPA_MAX_SSID_LEN 32 /* IEEE 802.11i */ #define PMKID_LEN 16 #define PMK_LEN 32 #define WPA_REPLAY_COUNTER_LEN 8 #define WPA_NONCE_LEN 32 #define WPA_KEY_RSC_LEN 8 #define WPA_GMK_LEN 32 #define WPA_GTK_MAX_LEN 32 #define WPA_ALLOWED_PAIRWISE_CIPHERS \ (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | WPA_CIPHER_TKIP | WPA_CIPHER_NONE | \ WPA_CIPHER_GCMP_256 | WPA_CIPHER_CCMP_256) #define WPA_ALLOWED_GROUP_CIPHERS \ (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | WPA_CIPHER_TKIP | WPA_CIPHER_WEP104 | \ WPA_CIPHER_WEP40 | WPA_CIPHER_GCMP_256 | WPA_CIPHER_CCMP_256 | \ WPA_CIPHER_GTK_NOT_USED) #define WPA_SELECTOR_LEN 4 #define WPA_VERSION 1 #define RSN_SELECTOR_LEN 4 #define RSN_VERSION 1 #define RSN_SELECTOR(a, b, c, d) \ ((((u32) (a)) << 24) | (((u32) (b)) << 16) | (((u32) (c)) << 8) | \ (u32) (d)) #define WPA_AUTH_KEY_MGMT_NONE RSN_SELECTOR(0x00, 0x50, 0xf2, 0) #define WPA_AUTH_KEY_MGMT_UNSPEC_802_1X RSN_SELECTOR(0x00, 0x50, 0xf2, 1) #define WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X RSN_SELECTOR(0x00, 0x50, 0xf2, 2) #define WPA_AUTH_KEY_MGMT_CCKM RSN_SELECTOR(0x00, 0x40, 0x96, 0) #define WPA_CIPHER_SUITE_NONE RSN_SELECTOR(0x00, 0x50, 0xf2, 0) #define WPA_CIPHER_SUITE_WEP40 RSN_SELECTOR(0x00, 0x50, 0xf2, 1) #define WPA_CIPHER_SUITE_TKIP RSN_SELECTOR(0x00, 0x50, 0xf2, 2) #if 0 #define WPA_CIPHER_SUITE_WRAP RSN_SELECTOR(0x00, 0x50, 0xf2, 3) #endif #define WPA_CIPHER_SUITE_CCMP RSN_SELECTOR(0x00, 0x50, 0xf2, 4) #define WPA_CIPHER_SUITE_WEP104 RSN_SELECTOR(0x00, 0x50, 0xf2, 5) #define RSN_AUTH_KEY_MGMT_UNSPEC_802_1X RSN_SELECTOR(0x00, 0x0f, 0xac, 1) #define RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X RSN_SELECTOR(0x00, 0x0f, 0xac, 2) #ifdef CONFIG_IEEE80211R #define RSN_AUTH_KEY_MGMT_FT_802_1X RSN_SELECTOR(0x00, 0x0f, 0xac, 3) #define RSN_AUTH_KEY_MGMT_FT_PSK RSN_SELECTOR(0x00, 0x0f, 0xac, 4) #endif /* CONFIG_IEEE80211R */ #define RSN_AUTH_KEY_MGMT_802_1X_SHA256 RSN_SELECTOR(0x00, 0x0f, 0xac, 5) #define RSN_AUTH_KEY_MGMT_PSK_SHA256 RSN_SELECTOR(0x00, 0x0f, 0xac, 6) #define RSN_AUTH_KEY_MGMT_TPK_HANDSHAKE RSN_SELECTOR(0x00, 0x0f, 0xac, 7) #define RSN_AUTH_KEY_MGMT_SAE RSN_SELECTOR(0x00, 0x0f, 0xac, 8) #define RSN_AUTH_KEY_MGMT_FT_SAE RSN_SELECTOR(0x00, 0x0f, 0xac, 9) #define RSN_AUTH_KEY_MGMT_802_1X_SUITE_B RSN_SELECTOR(0x00, 0x0f, 0xac, 11) #define RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_384 RSN_SELECTOR(0x00, 0x0f, 0xac, 12) #define RSN_AUTH_KEY_MGMT_FT_802_1X_SUITE_B_384 \ RSN_SELECTOR(0x00, 0x0f, 0xac, 13) #define RSN_AUTH_KEY_MGMT_CCKM RSN_SELECTOR(0x00, 0x40, 0x96, 0x00) #define RSN_CIPHER_SUITE_NONE RSN_SELECTOR(0x00, 0x0f, 0xac, 0) #define RSN_CIPHER_SUITE_WEP40 RSN_SELECTOR(0x00, 0x0f, 0xac, 1) #define RSN_CIPHER_SUITE_TKIP RSN_SELECTOR(0x00, 0x0f, 0xac, 2) #if 0 #define RSN_CIPHER_SUITE_WRAP RSN_SELECTOR(0x00, 0x0f, 0xac, 3) #endif #define RSN_CIPHER_SUITE_CCMP RSN_SELECTOR(0x00, 0x0f, 0xac, 4) #define RSN_CIPHER_SUITE_WEP104 RSN_SELECTOR(0x00, 0x0f, 0xac, 5) #ifdef CONFIG_IEEE80211W #define RSN_CIPHER_SUITE_AES_128_CMAC RSN_SELECTOR(0x00, 0x0f, 0xac, 6) #endif /* CONFIG_IEEE80211W */ #define RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED RSN_SELECTOR(0x00, 0x0f, 0xac, 7) #define RSN_CIPHER_SUITE_GCMP RSN_SELECTOR(0x00, 0x0f, 0xac, 8) #define RSN_CIPHER_SUITE_GCMP_256 RSN_SELECTOR(0x00, 0x0f, 0xac, 9) #define RSN_CIPHER_SUITE_CCMP_256 RSN_SELECTOR(0x00, 0x0f, 0xac, 10) #define RSN_CIPHER_SUITE_BIP_GMAC_128 RSN_SELECTOR(0x00, 0x0f, 0xac, 11) #define RSN_CIPHER_SUITE_BIP_GMAC_256 RSN_SELECTOR(0x00, 0x0f, 0xac, 12) #define RSN_CIPHER_SUITE_BIP_CMAC_256 RSN_SELECTOR(0x00, 0x0f, 0xac, 13) /* EAPOL-Key Key Data Encapsulation * GroupKey and PeerKey require encryption, otherwise, encryption is optional. */ #define RSN_KEY_DATA_GROUPKEY RSN_SELECTOR(0x00, 0x0f, 0xac, 1) #if 0 #define RSN_KEY_DATA_STAKEY RSN_SELECTOR(0x00, 0x0f, 0xac, 2) #endif #define RSN_KEY_DATA_MAC_ADDR RSN_SELECTOR(0x00, 0x0f, 0xac, 3) #define RSN_KEY_DATA_PMKID RSN_SELECTOR(0x00, 0x0f, 0xac, 4) #ifdef CONFIG_PEERKEY #define RSN_KEY_DATA_SMK RSN_SELECTOR(0x00, 0x0f, 0xac, 5) #define RSN_KEY_DATA_NONCE RSN_SELECTOR(0x00, 0x0f, 0xac, 6) #define RSN_KEY_DATA_LIFETIME RSN_SELECTOR(0x00, 0x0f, 0xac, 7) #define RSN_KEY_DATA_ERROR RSN_SELECTOR(0x00, 0x0f, 0xac, 8) #endif /* CONFIG_PEERKEY */ #ifdef CONFIG_IEEE80211W #define RSN_KEY_DATA_IGTK RSN_SELECTOR(0x00, 0x0f, 0xac, 9) #endif /* CONFIG_IEEE80211W */ #define RSN_KEY_DATA_KEYID RSN_SELECTOR(0x00, 0x0f, 0xac, 10) #define RSN_KEY_DATA_MULTIBAND_GTK RSN_SELECTOR(0x00, 0x0f, 0xac, 11) #define RSN_KEY_DATA_MULTIBAND_KEYID RSN_SELECTOR(0x00, 0x0f, 0xac, 12) #define WFA_KEY_DATA_IP_ADDR_REQ RSN_SELECTOR(0x50, 0x6f, 0x9a, 4) #define WFA_KEY_DATA_IP_ADDR_ALLOC RSN_SELECTOR(0x50, 0x6f, 0x9a, 5) #define WPA_OUI_TYPE RSN_SELECTOR(0x00, 0x50, 0xf2, 1) #define RSN_SELECTOR_PUT(a, val) WPA_PUT_BE32((u8 *) (a), (val)) #define RSN_SELECTOR_GET(a) WPA_GET_BE32((const u8 *) (a)) #define RSN_NUM_REPLAY_COUNTERS_1 0 #define RSN_NUM_REPLAY_COUNTERS_2 1 #define RSN_NUM_REPLAY_COUNTERS_4 2 #define RSN_NUM_REPLAY_COUNTERS_16 3 #ifdef _MSC_VER #pragma pack(push, 1) #endif /* _MSC_VER */ #ifdef CONFIG_IEEE80211W #define WPA_IGTK_LEN 16 #endif /* CONFIG_IEEE80211W */ /* IEEE 802.11, 7.3.2.25.3 RSN Capabilities */ #define WPA_CAPABILITY_PREAUTH BIT(0) #define WPA_CAPABILITY_NO_PAIRWISE BIT(1) /* B2-B3: PTKSA Replay Counter */ /* B4-B5: GTKSA Replay Counter */ #define WPA_CAPABILITY_MFPR BIT(6) #define WPA_CAPABILITY_MFPC BIT(7) /* B8: Reserved */ #define WPA_CAPABILITY_PEERKEY_ENABLED BIT(9) #define WPA_CAPABILITY_SPP_A_MSDU_CAPABLE BIT(10) #define WPA_CAPABILITY_SPP_A_MSDU_REQUIRED BIT(11) #define WPA_CAPABILITY_PBAC BIT(12) #define WPA_CAPABILITY_EXT_KEY_ID_FOR_UNICAST BIT(13) /* B14-B15: Reserved */ /* IEEE 802.11r */ #define MOBILITY_DOMAIN_ID_LEN 2 #define FT_R0KH_ID_MAX_LEN 48 #define FT_R1KH_ID_LEN 6 #define WPA_PMK_NAME_LEN 16 /* IEEE 802.11, 8.5.2 EAPOL-Key frames */ #define WPA_KEY_INFO_TYPE_MASK ((u16) (BIT(0) | BIT(1) | BIT(2))) #define WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 BIT(0) #define WPA_KEY_INFO_TYPE_HMAC_SHA1_AES BIT(1) #define WPA_KEY_INFO_TYPE_AES_128_CMAC 3 #define WPA_KEY_INFO_KEY_TYPE BIT(3) /* 1 = Pairwise, 0 = Group key */ /* bit4..5 is used in WPA, but is reserved in IEEE 802.11i/RSN */ #define WPA_KEY_INFO_KEY_INDEX_MASK (BIT(4) | BIT(5)) #define WPA_KEY_INFO_KEY_INDEX_SHIFT 4 #define WPA_KEY_INFO_INSTALL BIT(6) /* pairwise */ #define WPA_KEY_INFO_TXRX BIT(6) /* group */ #define WPA_KEY_INFO_ACK BIT(7) #define WPA_KEY_INFO_MIC BIT(8) #define WPA_KEY_INFO_SECURE BIT(9) #define WPA_KEY_INFO_ERROR BIT(10) #define WPA_KEY_INFO_REQUEST BIT(11) #define WPA_KEY_INFO_ENCR_KEY_DATA BIT(12) /* IEEE 802.11i/RSN only */ #define WPA_KEY_INFO_SMK_MESSAGE BIT(13) struct wpa_eapol_key { u8 type; /* Note: key_info, key_length, and key_data_length are unaligned */ u8 key_info[2]; /* big endian */ u8 key_length[2]; /* big endian */ u8 replay_counter[WPA_REPLAY_COUNTER_LEN]; u8 key_nonce[WPA_NONCE_LEN]; u8 key_iv[16]; u8 key_rsc[WPA_KEY_RSC_LEN]; u8 key_id[8]; /* Reserved in IEEE 802.11i/RSN */ u8 key_mic[16]; u8 key_data_length[2]; /* big endian */ /* followed by key_data_length bytes of key_data */ } STRUCT_PACKED; /** * struct wpa_ptk - WPA Pairwise Transient Key * IEEE Std 802.11i-2004 - 8.5.1.2 Pairwise key hierarchy */ struct wpa_ptk { u8 kck[16]; /* EAPOL-Key Key Confirmation Key (KCK) */ u8 kek[16]; /* EAPOL-Key Key Encryption Key (KEK) */ u8 tk1[16]; /* Temporal Key 1 (TK1) */ union { u8 tk2[16]; /* Temporal Key 2 (TK2) */ struct { u8 tx_mic_key[8]; u8 rx_mic_key[8]; } auth; } u; } STRUCT_PACKED; /* WPA IE version 1 * 00-50-f2:1 (OUI:OUI type) * 0x01 0x00 (version; little endian) * (all following fields are optional:) * Group Suite Selector (4 octets) (default: TKIP) * Pairwise Suite Count (2 octets, little endian) (default: 1) * Pairwise Suite List (4 * n octets) (default: TKIP) * Authenticated Key Management Suite Count (2 octets, little endian) * (default: 1) * Authenticated Key Management Suite List (4 * n octets) * (default: unspec 802.1X) * WPA Capabilities (2 octets, little endian) (default: 0) */ struct wpa_ie_hdr { u8 elem_id; u8 len; u8 oui[4]; /* 24-bit OUI followed by 8-bit OUI type */ u8 version[2]; /* little endian */ } STRUCT_PACKED; /* 1/4: PMKID * 2/4: RSN IE * 3/4: one or two RSN IEs + GTK IE (encrypted) * 4/4: empty * 1/2: GTK IE (encrypted) * 2/2: empty */ /* RSN IE version 1 * 0x01 0x00 (version; little endian) * (all following fields are optional:) * Group Suite Selector (4 octets) (default: CCMP) * Pairwise Suite Count (2 octets, little endian) (default: 1) * Pairwise Suite List (4 * n octets) (default: CCMP) * Authenticated Key Management Suite Count (2 octets, little endian) * (default: 1) * Authenticated Key Management Suite List (4 * n octets) * (default: unspec 802.1X) * RSN Capabilities (2 octets, little endian) (default: 0) * PMKID Count (2 octets) (default: 0) * PMKID List (16 * n octets) * Management Group Cipher Suite (4 octets) (default: AES-128-CMAC) */ struct rsn_ie_hdr { u8 elem_id; /* WLAN_EID_RSN */ u8 len; u8 version[2]; /* little endian */ } STRUCT_PACKED; #ifdef CONFIG_PEERKEY enum { STK_MUI_4WAY_STA_AP = 1, STK_MUI_4WAY_STAT_STA = 2, STK_MUI_GTK = 3, STK_MUI_SMK = 4 }; enum { STK_ERR_STA_NR = 1, STK_ERR_STA_NRSN = 2, STK_ERR_CPHR_NS = 3, STK_ERR_NO_STSL = 4 }; #endif /* CONFIG_PEERKEY */ struct rsn_error_kde { be16 mui; be16 error_type; } STRUCT_PACKED; #ifdef CONFIG_IEEE80211W struct wpa_igtk_kde { u8 keyid[2]; u8 pn[6]; u8 igtk[WPA_IGTK_LEN]; } STRUCT_PACKED; #endif /* CONFIG_IEEE80211W */ #ifdef CONFIG_IEEE80211R struct rsn_mdie { u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN]; u8 ft_capab; } STRUCT_PACKED; #define RSN_FT_CAPAB_FT_OVER_DS BIT(0) #define RSN_FT_CAPAB_FT_RESOURCE_REQ_SUPP BIT(1) struct rsn_ftie { u8 mic_control[2]; u8 mic[16]; u8 anonce[WPA_NONCE_LEN]; u8 snonce[WPA_NONCE_LEN]; /* followed by optional parameters */ } STRUCT_PACKED; #define FTIE_SUBELEM_R1KH_ID 1 #define FTIE_SUBELEM_GTK 2 #define FTIE_SUBELEM_R0KH_ID 3 #define FTIE_SUBELEM_IGTK 4 struct rsn_rdie { u8 id; u8 descr_count; le16 status_code; } STRUCT_PACKED; #endif /* CONFIG_IEEE80211R */ #ifdef _MSC_VER #pragma pack(pop) #endif /* _MSC_VER */ int wpa_eapol_key_mic(const u8 *key, int ver, const u8 *buf, size_t len, u8 *mic); void wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label, const u8 *addr1, const u8 *addr2, const u8 *nonce1, const u8 *nonce2, u8 *ptk, size_t ptk_len, int use_sha256); #ifdef CONFIG_IEEE80211R int wpa_ft_mic(const u8 *kck, const u8 *sta_addr, const u8 *ap_addr, u8 transaction_seqnum, const u8 *mdie, size_t mdie_len, const u8 *ftie, size_t ftie_len, const u8 *rsnie, size_t rsnie_len, const u8 *ric, size_t ric_len, u8 *mic); void wpa_derive_pmk_r0(const u8 *xxkey, size_t xxkey_len, const u8 *ssid, size_t ssid_len, const u8 *mdid, const u8 *r0kh_id, size_t r0kh_id_len, const u8 *s0kh_id, u8 *pmk_r0, u8 *pmk_r0_name); void wpa_derive_pmk_r1_name(const u8 *pmk_r0_name, const u8 *r1kh_id, const u8 *s1kh_id, u8 *pmk_r1_name); void wpa_derive_pmk_r1(const u8 *pmk_r0, const u8 *pmk_r0_name, const u8 *r1kh_id, const u8 *s1kh_id, u8 *pmk_r1, u8 *pmk_r1_name); void wpa_pmk_r1_to_ptk(const u8 *pmk_r1, const u8 *snonce, const u8 *anonce, const u8 *sta_addr, const u8 *bssid, const u8 *pmk_r1_name, u8 *ptk, size_t ptk_len, u8 *ptk_name); #endif /* CONFIG_IEEE80211R */ struct wpa_ie_data { int proto; int pairwise_cipher; int group_cipher; int key_mgmt; int capabilities; size_t num_pmkid; const u8 *pmkid; int mgmt_group_cipher; }; int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len, struct wpa_ie_data *data); int wpa_parse_wpa_ie_wpa(const u8 *wpa_ie, size_t wpa_ie_len, struct wpa_ie_data *data); void rsn_pmkid(const u8 *pmk, size_t pmk_len, const u8 *aa, const u8 *spa, u8 *pmkid, int use_sha256); const char * wpa_cipher_txt(int cipher); const char * wpa_key_mgmt_txt(int key_mgmt, int proto); int wpa_compare_rsn_ie(int ft_initial_assoc, const u8 *ie1, size_t ie1len, const u8 *ie2, size_t ie2len); int wpa_insert_pmkid(u8 *ies, size_t ies_len, const u8 *pmkid); struct wpa_ft_ies { const u8 *mdie; size_t mdie_len; const u8 *ftie; size_t ftie_len; const u8 *r1kh_id; const u8 *gtk; size_t gtk_len; const u8 *r0kh_id; size_t r0kh_id_len; const u8 *rsn; size_t rsn_len; const u8 *rsn_pmkid; const u8 *tie; size_t tie_len; const u8 *igtk; size_t igtk_len; const u8 *ric; size_t ric_len; }; int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, struct wpa_ft_ies *parse); int wpa_cipher_key_len(int cipher); int wpa_cipher_rsc_len(int cipher); int wpa_cipher_to_alg(int cipher); int wpa_cipher_valid_pairwise(int cipher); u32 wpa_cipher_to_suite(int proto, int cipher); int rsn_cipher_put_suites(u8 *pos, int ciphers); int wpa_cipher_put_suites(u8 *pos, int ciphers); int wpa_pick_pairwise_cipher(int ciphers, int none_allowed); int wpa_pick_group_cipher(int ciphers); int wpa_parse_cipher(const char *value); int wpa_write_ciphers(char *start, char *end, int ciphers, const char *delim); int wpa_select_ap_group_cipher(int wpa, int wpa_pairwise, int rsn_pairwise); #endif /* WPA_COMMON_H */ wpa-2.1/src/common/wpa_ctrl.c000066400000000000000000000353061227414666700162270ustar00rootroot00000000000000/* * wpa_supplicant/hostapd control interface library * Copyright (c) 2004-2007, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "includes.h" #ifdef CONFIG_CTRL_IFACE #ifdef CONFIG_CTRL_IFACE_UNIX #include #include #include #endif /* CONFIG_CTRL_IFACE_UNIX */ #ifdef CONFIG_CTRL_IFACE_UDP_REMOTE #include #endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ #ifdef ANDROID #include #include #include "private/android_filesystem_config.h" #endif /* ANDROID */ #include "wpa_ctrl.h" #include "common.h" #if defined(CONFIG_CTRL_IFACE_UNIX) || defined(CONFIG_CTRL_IFACE_UDP) #define CTRL_IFACE_SOCKET #endif /* CONFIG_CTRL_IFACE_UNIX || CONFIG_CTRL_IFACE_UDP */ /** * struct wpa_ctrl - Internal structure for control interface library * * This structure is used by the wpa_supplicant/hostapd control interface * library to store internal data. Programs using the library should not touch * this data directly. They can only use the pointer to the data structure as * an identifier for the control interface connection and use this as one of * the arguments for most of the control interface library functions. */ struct wpa_ctrl { #ifdef CONFIG_CTRL_IFACE_UDP int s; struct sockaddr_in local; struct sockaddr_in dest; char *cookie; char *remote_ifname; char *remote_ip; #endif /* CONFIG_CTRL_IFACE_UDP */ #ifdef CONFIG_CTRL_IFACE_UNIX int s; struct sockaddr_un local; struct sockaddr_un dest; #endif /* CONFIG_CTRL_IFACE_UNIX */ #ifdef CONFIG_CTRL_IFACE_NAMED_PIPE HANDLE pipe; #endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */ }; #ifdef CONFIG_CTRL_IFACE_UNIX #ifndef CONFIG_CTRL_IFACE_CLIENT_DIR #define CONFIG_CTRL_IFACE_CLIENT_DIR "/tmp" #endif /* CONFIG_CTRL_IFACE_CLIENT_DIR */ #ifndef CONFIG_CTRL_IFACE_CLIENT_PREFIX #define CONFIG_CTRL_IFACE_CLIENT_PREFIX "wpa_ctrl_" #endif /* CONFIG_CTRL_IFACE_CLIENT_PREFIX */ struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path) { struct wpa_ctrl *ctrl; static int counter = 0; int ret; size_t res; int tries = 0; int flags; if (ctrl_path == NULL) return NULL; ctrl = os_malloc(sizeof(*ctrl)); if (ctrl == NULL) return NULL; os_memset(ctrl, 0, sizeof(*ctrl)); ctrl->s = socket(PF_UNIX, SOCK_DGRAM, 0); if (ctrl->s < 0) { os_free(ctrl); return NULL; } ctrl->local.sun_family = AF_UNIX; counter++; try_again: ret = os_snprintf(ctrl->local.sun_path, sizeof(ctrl->local.sun_path), CONFIG_CTRL_IFACE_CLIENT_DIR "/" CONFIG_CTRL_IFACE_CLIENT_PREFIX "%d-%d", (int) getpid(), counter); if (ret < 0 || (size_t) ret >= sizeof(ctrl->local.sun_path)) { close(ctrl->s); os_free(ctrl); return NULL; } tries++; if (bind(ctrl->s, (struct sockaddr *) &ctrl->local, sizeof(ctrl->local)) < 0) { if (errno == EADDRINUSE && tries < 2) { /* * getpid() returns unique identifier for this instance * of wpa_ctrl, so the existing socket file must have * been left by unclean termination of an earlier run. * Remove the file and try again. */ unlink(ctrl->local.sun_path); goto try_again; } close(ctrl->s); os_free(ctrl); return NULL; } #ifdef ANDROID chmod(ctrl->local.sun_path, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); chown(ctrl->local.sun_path, AID_SYSTEM, AID_WIFI); if (os_strncmp(ctrl_path, "@android:", 9) == 0) { if (socket_local_client_connect( ctrl->s, ctrl_path + 9, ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_DGRAM) < 0) { close(ctrl->s); unlink(ctrl->local.sun_path); os_free(ctrl); return NULL; } return ctrl; } /* * If the ctrl_path isn't an absolute pathname, assume that * it's the name of a socket in the Android reserved namespace. * Otherwise, it's a normal UNIX domain socket appearing in the * filesystem. */ if (*ctrl_path != '/') { char buf[21]; os_snprintf(buf, sizeof(buf), "wpa_%s", ctrl_path); if (socket_local_client_connect( ctrl->s, buf, ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_DGRAM) < 0) { close(ctrl->s); unlink(ctrl->local.sun_path); os_free(ctrl); return NULL; } return ctrl; } #endif /* ANDROID */ ctrl->dest.sun_family = AF_UNIX; if (os_strncmp(ctrl_path, "@abstract:", 10) == 0) { ctrl->dest.sun_path[0] = '\0'; os_strlcpy(ctrl->dest.sun_path + 1, ctrl_path + 10, sizeof(ctrl->dest.sun_path) - 1); } else { res = os_strlcpy(ctrl->dest.sun_path, ctrl_path, sizeof(ctrl->dest.sun_path)); if (res >= sizeof(ctrl->dest.sun_path)) { close(ctrl->s); os_free(ctrl); return NULL; } } if (connect(ctrl->s, (struct sockaddr *) &ctrl->dest, sizeof(ctrl->dest)) < 0) { close(ctrl->s); unlink(ctrl->local.sun_path); os_free(ctrl); return NULL; } /* * Make socket non-blocking so that we don't hang forever if * target dies unexpectedly. */ flags = fcntl(ctrl->s, F_GETFL); if (flags >= 0) { flags |= O_NONBLOCK; if (fcntl(ctrl->s, F_SETFL, flags) < 0) { perror("fcntl(ctrl->s, O_NONBLOCK)"); /* Not fatal, continue on.*/ } } return ctrl; } void wpa_ctrl_close(struct wpa_ctrl *ctrl) { if (ctrl == NULL) return; unlink(ctrl->local.sun_path); if (ctrl->s >= 0) close(ctrl->s); os_free(ctrl); } #ifdef ANDROID /** * wpa_ctrl_cleanup() - Delete any local UNIX domain socket files that * may be left over from clients that were previously connected to * wpa_supplicant. This keeps these files from being orphaned in the * event of crashes that prevented them from being removed as part * of the normal orderly shutdown. */ void wpa_ctrl_cleanup(void) { DIR *dir; struct dirent entry; struct dirent *result; size_t dirnamelen; size_t maxcopy; char pathname[PATH_MAX]; char *namep; if ((dir = opendir(CONFIG_CTRL_IFACE_CLIENT_DIR)) == NULL) return; dirnamelen = (size_t) os_snprintf(pathname, sizeof(pathname), "%s/", CONFIG_CTRL_IFACE_CLIENT_DIR); if (dirnamelen >= sizeof(pathname)) { closedir(dir); return; } namep = pathname + dirnamelen; maxcopy = PATH_MAX - dirnamelen; while (readdir_r(dir, &entry, &result) == 0 && result != NULL) { if (os_strlcpy(namep, entry.d_name, maxcopy) < maxcopy) unlink(pathname); } closedir(dir); } #endif /* ANDROID */ #else /* CONFIG_CTRL_IFACE_UNIX */ #ifdef ANDROID void wpa_ctrl_cleanup(void) { } #endif /* ANDROID */ #endif /* CONFIG_CTRL_IFACE_UNIX */ #ifdef CONFIG_CTRL_IFACE_UDP struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path) { struct wpa_ctrl *ctrl; char buf[128]; size_t len; #ifdef CONFIG_CTRL_IFACE_UDP_REMOTE struct hostent *h; #endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ ctrl = os_malloc(sizeof(*ctrl)); if (ctrl == NULL) return NULL; os_memset(ctrl, 0, sizeof(*ctrl)); ctrl->s = socket(PF_INET, SOCK_DGRAM, 0); if (ctrl->s < 0) { perror("socket"); os_free(ctrl); return NULL; } ctrl->local.sin_family = AF_INET; #ifdef CONFIG_CTRL_IFACE_UDP_REMOTE ctrl->local.sin_addr.s_addr = INADDR_ANY; #else /* CONFIG_CTRL_IFACE_UDP_REMOTE */ ctrl->local.sin_addr.s_addr = htonl((127 << 24) | 1); #endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ if (bind(ctrl->s, (struct sockaddr *) &ctrl->local, sizeof(ctrl->local)) < 0) { close(ctrl->s); os_free(ctrl); return NULL; } ctrl->dest.sin_family = AF_INET; ctrl->dest.sin_addr.s_addr = htonl((127 << 24) | 1); ctrl->dest.sin_port = htons(WPA_CTRL_IFACE_PORT); #ifdef CONFIG_CTRL_IFACE_UDP_REMOTE if (ctrl_path) { char *port, *name; int port_id; name = os_strdup(ctrl_path); if (name == NULL) { close(ctrl->s); os_free(ctrl); return NULL; } port = os_strchr(name, ':'); if (port) { port_id = atoi(&port[1]); port[0] = '\0'; } else port_id = WPA_CTRL_IFACE_PORT; h = gethostbyname(name); ctrl->remote_ip = os_strdup(name); os_free(name); if (h == NULL) { perror("gethostbyname"); close(ctrl->s); os_free(ctrl->remote_ip); os_free(ctrl); return NULL; } ctrl->dest.sin_port = htons(port_id); os_memcpy(h->h_addr, (char *) &ctrl->dest.sin_addr.s_addr, h->h_length); } else ctrl->remote_ip = os_strdup("localhost"); #endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ if (connect(ctrl->s, (struct sockaddr *) &ctrl->dest, sizeof(ctrl->dest)) < 0) { perror("connect"); close(ctrl->s); os_free(ctrl->remote_ip); os_free(ctrl); return NULL; } len = sizeof(buf) - 1; if (wpa_ctrl_request(ctrl, "GET_COOKIE", 10, buf, &len, NULL) == 0) { buf[len] = '\0'; ctrl->cookie = os_strdup(buf); } if (wpa_ctrl_request(ctrl, "IFNAME", 6, buf, &len, NULL) == 0) { buf[len] = '\0'; ctrl->remote_ifname = os_strdup(buf); } return ctrl; } char * wpa_ctrl_get_remote_ifname(struct wpa_ctrl *ctrl) { #define WPA_CTRL_MAX_PS_NAME 100 static char ps[WPA_CTRL_MAX_PS_NAME] = {}; os_snprintf(ps, WPA_CTRL_MAX_PS_NAME, "%s/%s", ctrl->remote_ip, ctrl->remote_ifname); return ps; } void wpa_ctrl_close(struct wpa_ctrl *ctrl) { close(ctrl->s); os_free(ctrl->cookie); os_free(ctrl->remote_ifname); os_free(ctrl->remote_ip); os_free(ctrl); } #endif /* CONFIG_CTRL_IFACE_UDP */ #ifdef CTRL_IFACE_SOCKET int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len, char *reply, size_t *reply_len, void (*msg_cb)(char *msg, size_t len)) { struct timeval tv; struct os_reltime started_at; int res; fd_set rfds; const char *_cmd; char *cmd_buf = NULL; size_t _cmd_len; #ifdef CONFIG_CTRL_IFACE_UDP if (ctrl->cookie) { char *pos; _cmd_len = os_strlen(ctrl->cookie) + 1 + cmd_len; cmd_buf = os_malloc(_cmd_len); if (cmd_buf == NULL) return -1; _cmd = cmd_buf; pos = cmd_buf; os_strlcpy(pos, ctrl->cookie, _cmd_len); pos += os_strlen(ctrl->cookie); *pos++ = ' '; os_memcpy(pos, cmd, cmd_len); } else #endif /* CONFIG_CTRL_IFACE_UDP */ { _cmd = cmd; _cmd_len = cmd_len; } errno = 0; started_at.sec = 0; started_at.usec = 0; retry_send: if (send(ctrl->s, _cmd, _cmd_len, 0) < 0) { if (errno == EAGAIN || errno == EBUSY || errno == EWOULDBLOCK) { /* * Must be a non-blocking socket... Try for a bit * longer before giving up. */ if (started_at.sec == 0) os_get_reltime(&started_at); else { struct os_reltime n; os_get_reltime(&n); /* Try for a few seconds. */ if (os_reltime_expired(&n, &started_at, 5)) goto send_err; } os_sleep(1, 0); goto retry_send; } send_err: os_free(cmd_buf); return -1; } os_free(cmd_buf); for (;;) { tv.tv_sec = 10; tv.tv_usec = 0; FD_ZERO(&rfds); FD_SET(ctrl->s, &rfds); res = select(ctrl->s + 1, &rfds, NULL, NULL, &tv); if (res < 0) return res; if (FD_ISSET(ctrl->s, &rfds)) { res = recv(ctrl->s, reply, *reply_len, 0); if (res < 0) return res; if (res > 0 && reply[0] == '<') { /* This is an unsolicited message from * wpa_supplicant, not the reply to the * request. Use msg_cb to report this to the * caller. */ if (msg_cb) { /* Make sure the message is nul * terminated. */ if ((size_t) res == *reply_len) res = (*reply_len) - 1; reply[res] = '\0'; msg_cb(reply, res); } continue; } *reply_len = res; break; } else { return -2; } } return 0; } #endif /* CTRL_IFACE_SOCKET */ static int wpa_ctrl_attach_helper(struct wpa_ctrl *ctrl, int attach) { char buf[10]; int ret; size_t len = 10; ret = wpa_ctrl_request(ctrl, attach ? "ATTACH" : "DETACH", 6, buf, &len, NULL); if (ret < 0) return ret; if (len == 3 && os_memcmp(buf, "OK\n", 3) == 0) return 0; return -1; } int wpa_ctrl_attach(struct wpa_ctrl *ctrl) { return wpa_ctrl_attach_helper(ctrl, 1); } int wpa_ctrl_detach(struct wpa_ctrl *ctrl) { return wpa_ctrl_attach_helper(ctrl, 0); } #ifdef CTRL_IFACE_SOCKET int wpa_ctrl_recv(struct wpa_ctrl *ctrl, char *reply, size_t *reply_len) { int res; res = recv(ctrl->s, reply, *reply_len, 0); if (res < 0) return res; *reply_len = res; return 0; } int wpa_ctrl_pending(struct wpa_ctrl *ctrl) { struct timeval tv; fd_set rfds; tv.tv_sec = 0; tv.tv_usec = 0; FD_ZERO(&rfds); FD_SET(ctrl->s, &rfds); select(ctrl->s + 1, &rfds, NULL, NULL, &tv); return FD_ISSET(ctrl->s, &rfds); } int wpa_ctrl_get_fd(struct wpa_ctrl *ctrl) { return ctrl->s; } #endif /* CTRL_IFACE_SOCKET */ #ifdef CONFIG_CTRL_IFACE_NAMED_PIPE #ifndef WPA_SUPPLICANT_NAMED_PIPE #define WPA_SUPPLICANT_NAMED_PIPE "WpaSupplicant" #endif #define NAMED_PIPE_PREFIX TEXT("\\\\.\\pipe\\") TEXT(WPA_SUPPLICANT_NAMED_PIPE) struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path) { struct wpa_ctrl *ctrl; DWORD mode; TCHAR name[256]; int i, ret; ctrl = os_malloc(sizeof(*ctrl)); if (ctrl == NULL) return NULL; os_memset(ctrl, 0, sizeof(*ctrl)); #ifdef UNICODE if (ctrl_path == NULL) ret = _snwprintf(name, 256, NAMED_PIPE_PREFIX); else ret = _snwprintf(name, 256, NAMED_PIPE_PREFIX TEXT("-%S"), ctrl_path); #else /* UNICODE */ if (ctrl_path == NULL) ret = os_snprintf(name, 256, NAMED_PIPE_PREFIX); else ret = os_snprintf(name, 256, NAMED_PIPE_PREFIX "-%s", ctrl_path); #endif /* UNICODE */ if (ret < 0 || ret >= 256) { os_free(ctrl); return NULL; } for (i = 0; i < 10; i++) { ctrl->pipe = CreateFile(name, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); /* * Current named pipe server side in wpa_supplicant is * re-opening the pipe for new clients only after the previous * one is taken into use. This leaves a small window for race * conditions when two connections are being opened at almost * the same time. Retry if that was the case. */ if (ctrl->pipe != INVALID_HANDLE_VALUE || GetLastError() != ERROR_PIPE_BUSY) break; WaitNamedPipe(name, 1000); } if (ctrl->pipe == INVALID_HANDLE_VALUE) { os_free(ctrl); return NULL; } mode = PIPE_READMODE_MESSAGE; if (!SetNamedPipeHandleState(ctrl->pipe, &mode, NULL, NULL)) { CloseHandle(ctrl->pipe); os_free(ctrl); return NULL; } return ctrl; } void wpa_ctrl_close(struct wpa_ctrl *ctrl) { CloseHandle(ctrl->pipe); os_free(ctrl); } int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len, char *reply, size_t *reply_len, void (*msg_cb)(char *msg, size_t len)) { DWORD written; DWORD readlen = *reply_len; if (!WriteFile(ctrl->pipe, cmd, cmd_len, &written, NULL)) return -1; if (!ReadFile(ctrl->pipe, reply, *reply_len, &readlen, NULL)) return -1; *reply_len = readlen; return 0; } int wpa_ctrl_recv(struct wpa_ctrl *ctrl, char *reply, size_t *reply_len) { DWORD len = *reply_len; if (!ReadFile(ctrl->pipe, reply, *reply_len, &len, NULL)) return -1; *reply_len = len; return 0; } int wpa_ctrl_pending(struct wpa_ctrl *ctrl) { DWORD left; if (!PeekNamedPipe(ctrl->pipe, NULL, 0, NULL, &left, NULL)) return -1; return left ? 1 : 0; } int wpa_ctrl_get_fd(struct wpa_ctrl *ctrl) { return -1; } #endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */ #endif /* CONFIG_CTRL_IFACE */ wpa-2.1/src/common/wpa_ctrl.h000066400000000000000000000364511227414666700162360ustar00rootroot00000000000000/* * wpa_supplicant/hostapd control interface library * Copyright (c) 2004-2006, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef WPA_CTRL_H #define WPA_CTRL_H #ifdef __cplusplus extern "C" { #endif /* wpa_supplicant control interface - fixed message prefixes */ /** Interactive request for identity/password/pin */ #define WPA_CTRL_REQ "CTRL-REQ-" /** Response to identity/password/pin request */ #define WPA_CTRL_RSP "CTRL-RSP-" /* Event messages with fixed prefix */ /** Authentication completed successfully and data connection enabled */ #define WPA_EVENT_CONNECTED "CTRL-EVENT-CONNECTED " /** Disconnected, data connection is not available */ #define WPA_EVENT_DISCONNECTED "CTRL-EVENT-DISCONNECTED " /** Association rejected during connection attempt */ #define WPA_EVENT_ASSOC_REJECT "CTRL-EVENT-ASSOC-REJECT " /** wpa_supplicant is exiting */ #define WPA_EVENT_TERMINATING "CTRL-EVENT-TERMINATING " /** Password change was completed successfully */ #define WPA_EVENT_PASSWORD_CHANGED "CTRL-EVENT-PASSWORD-CHANGED " /** EAP-Request/Notification received */ #define WPA_EVENT_EAP_NOTIFICATION "CTRL-EVENT-EAP-NOTIFICATION " /** EAP authentication started (EAP-Request/Identity received) */ #define WPA_EVENT_EAP_STARTED "CTRL-EVENT-EAP-STARTED " /** EAP method proposed by the server */ #define WPA_EVENT_EAP_PROPOSED_METHOD "CTRL-EVENT-EAP-PROPOSED-METHOD " /** EAP method selected */ #define WPA_EVENT_EAP_METHOD "CTRL-EVENT-EAP-METHOD " /** EAP peer certificate from TLS */ #define WPA_EVENT_EAP_PEER_CERT "CTRL-EVENT-EAP-PEER-CERT " /** EAP TLS certificate chain validation error */ #define WPA_EVENT_EAP_TLS_CERT_ERROR "CTRL-EVENT-EAP-TLS-CERT-ERROR " /** EAP status */ #define WPA_EVENT_EAP_STATUS "CTRL-EVENT-EAP-STATUS " /** EAP authentication completed successfully */ #define WPA_EVENT_EAP_SUCCESS "CTRL-EVENT-EAP-SUCCESS " /** EAP authentication failed (EAP-Failure received) */ #define WPA_EVENT_EAP_FAILURE "CTRL-EVENT-EAP-FAILURE " /** Network block temporarily disabled (e.g., due to authentication failure) */ #define WPA_EVENT_TEMP_DISABLED "CTRL-EVENT-SSID-TEMP-DISABLED " /** Temporarily disabled network block re-enabled */ #define WPA_EVENT_REENABLED "CTRL-EVENT-SSID-REENABLED " /** New scan started */ #define WPA_EVENT_SCAN_STARTED "CTRL-EVENT-SCAN-STARTED " /** New scan results available */ #define WPA_EVENT_SCAN_RESULTS "CTRL-EVENT-SCAN-RESULTS " /** wpa_supplicant state change */ #define WPA_EVENT_STATE_CHANGE "CTRL-EVENT-STATE-CHANGE " /** A new BSS entry was added (followed by BSS entry id and BSSID) */ #define WPA_EVENT_BSS_ADDED "CTRL-EVENT-BSS-ADDED " /** A BSS entry was removed (followed by BSS entry id and BSSID) */ #define WPA_EVENT_BSS_REMOVED "CTRL-EVENT-BSS-REMOVED " /** RSN IBSS 4-way handshakes completed with specified peer */ #define IBSS_RSN_COMPLETED "IBSS-RSN-COMPLETED " /** Notification of frequency conflict due to a concurrent operation. * * The indicated network is disabled and needs to be re-enabled before it can * be used again. */ #define WPA_EVENT_FREQ_CONFLICT "CTRL-EVENT-FREQ-CONFLICT " /** Frequency ranges that the driver recommends to avoid */ #define WPA_EVENT_AVOID_FREQ "CTRL-EVENT-AVOID-FREQ " /** WPS overlap detected in PBC mode */ #define WPS_EVENT_OVERLAP "WPS-OVERLAP-DETECTED " /** Available WPS AP with active PBC found in scan results */ #define WPS_EVENT_AP_AVAILABLE_PBC "WPS-AP-AVAILABLE-PBC " /** Available WPS AP with our address as authorized in scan results */ #define WPS_EVENT_AP_AVAILABLE_AUTH "WPS-AP-AVAILABLE-AUTH " /** Available WPS AP with recently selected PIN registrar found in scan results */ #define WPS_EVENT_AP_AVAILABLE_PIN "WPS-AP-AVAILABLE-PIN " /** Available WPS AP found in scan results */ #define WPS_EVENT_AP_AVAILABLE "WPS-AP-AVAILABLE " /** A new credential received */ #define WPS_EVENT_CRED_RECEIVED "WPS-CRED-RECEIVED " /** M2D received */ #define WPS_EVENT_M2D "WPS-M2D " /** WPS registration failed after M2/M2D */ #define WPS_EVENT_FAIL "WPS-FAIL " /** WPS registration completed successfully */ #define WPS_EVENT_SUCCESS "WPS-SUCCESS " /** WPS enrollment attempt timed out and was terminated */ #define WPS_EVENT_TIMEOUT "WPS-TIMEOUT " /* PBC mode was activated */ #define WPS_EVENT_ACTIVE "WPS-PBC-ACTIVE " /* PBC mode was disabled */ #define WPS_EVENT_DISABLE "WPS-PBC-DISABLE " #define WPS_EVENT_ENROLLEE_SEEN "WPS-ENROLLEE-SEEN " #define WPS_EVENT_OPEN_NETWORK "WPS-OPEN-NETWORK " /* WPS ER events */ #define WPS_EVENT_ER_AP_ADD "WPS-ER-AP-ADD " #define WPS_EVENT_ER_AP_REMOVE "WPS-ER-AP-REMOVE " #define WPS_EVENT_ER_ENROLLEE_ADD "WPS-ER-ENROLLEE-ADD " #define WPS_EVENT_ER_ENROLLEE_REMOVE "WPS-ER-ENROLLEE-REMOVE " #define WPS_EVENT_ER_AP_SETTINGS "WPS-ER-AP-SETTINGS " #define WPS_EVENT_ER_SET_SEL_REG "WPS-ER-AP-SET-SEL-REG " /** P2P device found */ #define P2P_EVENT_DEVICE_FOUND "P2P-DEVICE-FOUND " /** P2P device lost */ #define P2P_EVENT_DEVICE_LOST "P2P-DEVICE-LOST " /** A P2P device requested GO negotiation, but we were not ready to start the * negotiation */ #define P2P_EVENT_GO_NEG_REQUEST "P2P-GO-NEG-REQUEST " #define P2P_EVENT_GO_NEG_SUCCESS "P2P-GO-NEG-SUCCESS " #define P2P_EVENT_GO_NEG_FAILURE "P2P-GO-NEG-FAILURE " #define P2P_EVENT_GROUP_FORMATION_SUCCESS "P2P-GROUP-FORMATION-SUCCESS " #define P2P_EVENT_GROUP_FORMATION_FAILURE "P2P-GROUP-FORMATION-FAILURE " #define P2P_EVENT_GROUP_STARTED "P2P-GROUP-STARTED " #define P2P_EVENT_GROUP_REMOVED "P2P-GROUP-REMOVED " #define P2P_EVENT_CROSS_CONNECT_ENABLE "P2P-CROSS-CONNECT-ENABLE " #define P2P_EVENT_CROSS_CONNECT_DISABLE "P2P-CROSS-CONNECT-DISABLE " /* parameters: */ #define P2P_EVENT_PROV_DISC_SHOW_PIN "P2P-PROV-DISC-SHOW-PIN " /* parameters: */ #define P2P_EVENT_PROV_DISC_ENTER_PIN "P2P-PROV-DISC-ENTER-PIN " /* parameters: */ #define P2P_EVENT_PROV_DISC_PBC_REQ "P2P-PROV-DISC-PBC-REQ " /* parameters: */ #define P2P_EVENT_PROV_DISC_PBC_RESP "P2P-PROV-DISC-PBC-RESP " /* parameters: */ #define P2P_EVENT_PROV_DISC_FAILURE "P2P-PROV-DISC-FAILURE" /* parameters: */ #define P2P_EVENT_SERV_DISC_REQ "P2P-SERV-DISC-REQ " /* parameters: */ #define P2P_EVENT_SERV_DISC_RESP "P2P-SERV-DISC-RESP " #define P2P_EVENT_INVITATION_RECEIVED "P2P-INVITATION-RECEIVED " #define P2P_EVENT_INVITATION_RESULT "P2P-INVITATION-RESULT " #define P2P_EVENT_FIND_STOPPED "P2P-FIND-STOPPED " #define P2P_EVENT_PERSISTENT_PSK_FAIL "P2P-PERSISTENT-PSK-FAIL id=" #define P2P_EVENT_PRESENCE_RESPONSE "P2P-PRESENCE-RESPONSE " #define P2P_EVENT_NFC_BOTH_GO "P2P-NFC-BOTH-GO " #define P2P_EVENT_NFC_PEER_CLIENT "P2P-NFC-PEER-CLIENT " #define P2P_EVENT_NFC_WHILE_CLIENT "P2P-NFC-WHILE-CLIENT " /* parameters: */ #define ESS_DISASSOC_IMMINENT "ESS-DISASSOC-IMMINENT " #define P2P_EVENT_REMOVE_AND_REFORM_GROUP "P2P-REMOVE-AND-REFORM-GROUP " #define INTERWORKING_AP "INTERWORKING-AP " #define INTERWORKING_NO_MATCH "INTERWORKING-NO-MATCH " #define INTERWORKING_ALREADY_CONNECTED "INTERWORKING-ALREADY-CONNECTED " #define GAS_RESPONSE_INFO "GAS-RESPONSE-INFO " /* parameters: */ #define GAS_QUERY_START "GAS-QUERY-START " /* parameters: */ #define GAS_QUERY_DONE "GAS-QUERY-DONE " #define EXT_RADIO_WORK_START "EXT-RADIO-WORK-START " #define EXT_RADIO_WORK_TIMEOUT "EXT-RADIO-WORK-TIMEOUT " /* hostapd control interface - fixed message prefixes */ #define WPS_EVENT_PIN_NEEDED "WPS-PIN-NEEDED " #define WPS_EVENT_NEW_AP_SETTINGS "WPS-NEW-AP-SETTINGS " #define WPS_EVENT_REG_SUCCESS "WPS-REG-SUCCESS " #define WPS_EVENT_AP_SETUP_LOCKED "WPS-AP-SETUP-LOCKED " #define WPS_EVENT_AP_SETUP_UNLOCKED "WPS-AP-SETUP-UNLOCKED " #define WPS_EVENT_AP_PIN_ENABLED "WPS-AP-PIN-ENABLED " #define WPS_EVENT_AP_PIN_DISABLED "WPS-AP-PIN-DISABLED " #define AP_STA_CONNECTED "AP-STA-CONNECTED " #define AP_STA_DISCONNECTED "AP-STA-DISCONNECTED " #define AP_REJECTED_MAX_STA "AP-REJECTED-MAX-STA " #define AP_REJECTED_BLOCKED_STA "AP-REJECTED-BLOCKED-STA " #define AP_EVENT_ENABLED "AP-ENABLED " #define AP_EVENT_DISABLED "AP-DISABLED " #define ACS_EVENT_STARTED "ACS-STARTED " #define ACS_EVENT_COMPLETED "ACS-COMPLETED " #define ACS_EVENT_FAILED "ACS-FAILED " #define DFS_EVENT_RADAR_DETECTED "DFS-RADAR-DETECTED " #define DFS_EVENT_NEW_CHANNEL "DFS-NEW-CHANNEL " #define DFS_EVENT_CAC_START "DFS-CAC-START " #define DFS_EVENT_CAC_COMPLETED "DFS-CAC-COMPLETED " #define DFS_EVENT_NOP_FINISHED "DFS-NOP-FINISHED " #define AP_CSA_FINISHED "AP-CSA-FINISHED " /* BSS command information masks */ #define WPA_BSS_MASK_ALL 0xFFFDFFFF #define WPA_BSS_MASK_ID BIT(0) #define WPA_BSS_MASK_BSSID BIT(1) #define WPA_BSS_MASK_FREQ BIT(2) #define WPA_BSS_MASK_BEACON_INT BIT(3) #define WPA_BSS_MASK_CAPABILITIES BIT(4) #define WPA_BSS_MASK_QUAL BIT(5) #define WPA_BSS_MASK_NOISE BIT(6) #define WPA_BSS_MASK_LEVEL BIT(7) #define WPA_BSS_MASK_TSF BIT(8) #define WPA_BSS_MASK_AGE BIT(9) #define WPA_BSS_MASK_IE BIT(10) #define WPA_BSS_MASK_FLAGS BIT(11) #define WPA_BSS_MASK_SSID BIT(12) #define WPA_BSS_MASK_WPS_SCAN BIT(13) #define WPA_BSS_MASK_P2P_SCAN BIT(14) #define WPA_BSS_MASK_INTERNETW BIT(15) #define WPA_BSS_MASK_WIFI_DISPLAY BIT(16) #define WPA_BSS_MASK_DELIM BIT(17) /* wpa_supplicant/hostapd control interface access */ /** * wpa_ctrl_open - Open a control interface to wpa_supplicant/hostapd * @ctrl_path: Path for UNIX domain sockets; ignored if UDP sockets are used. * Returns: Pointer to abstract control interface data or %NULL on failure * * This function is used to open a control interface to wpa_supplicant/hostapd. * ctrl_path is usually /var/run/wpa_supplicant or /var/run/hostapd. This path * is configured in wpa_supplicant/hostapd and other programs using the control * interface need to use matching path configuration. */ struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path); /** * wpa_ctrl_close - Close a control interface to wpa_supplicant/hostapd * @ctrl: Control interface data from wpa_ctrl_open() * * This function is used to close a control interface. */ void wpa_ctrl_close(struct wpa_ctrl *ctrl); /** * wpa_ctrl_request - Send a command to wpa_supplicant/hostapd * @ctrl: Control interface data from wpa_ctrl_open() * @cmd: Command; usually, ASCII text, e.g., "PING" * @cmd_len: Length of the cmd in bytes * @reply: Buffer for the response * @reply_len: Reply buffer length * @msg_cb: Callback function for unsolicited messages or %NULL if not used * Returns: 0 on success, -1 on error (send or receive failed), -2 on timeout * * This function is used to send commands to wpa_supplicant/hostapd. Received * response will be written to reply and reply_len is set to the actual length * of the reply. This function will block for up to two seconds while waiting * for the reply. If unsolicited messages are received, the blocking time may * be longer. * * msg_cb can be used to register a callback function that will be called for * unsolicited messages received while waiting for the command response. These * messages may be received if wpa_ctrl_request() is called at the same time as * wpa_supplicant/hostapd is sending such a message. This can happen only if * the program has used wpa_ctrl_attach() to register itself as a monitor for * event messages. Alternatively to msg_cb, programs can register two control * interface connections and use one of them for commands and the other one for * receiving event messages, in other words, call wpa_ctrl_attach() only for * the control interface connection that will be used for event messages. */ int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len, char *reply, size_t *reply_len, void (*msg_cb)(char *msg, size_t len)); /** * wpa_ctrl_attach - Register as an event monitor for the control interface * @ctrl: Control interface data from wpa_ctrl_open() * Returns: 0 on success, -1 on failure, -2 on timeout * * This function registers the control interface connection as a monitor for * wpa_supplicant/hostapd events. After a success wpa_ctrl_attach() call, the * control interface connection starts receiving event messages that can be * read with wpa_ctrl_recv(). */ int wpa_ctrl_attach(struct wpa_ctrl *ctrl); /** * wpa_ctrl_detach - Unregister event monitor from the control interface * @ctrl: Control interface data from wpa_ctrl_open() * Returns: 0 on success, -1 on failure, -2 on timeout * * This function unregisters the control interface connection as a monitor for * wpa_supplicant/hostapd events, i.e., cancels the registration done with * wpa_ctrl_attach(). */ int wpa_ctrl_detach(struct wpa_ctrl *ctrl); /** * wpa_ctrl_recv - Receive a pending control interface message * @ctrl: Control interface data from wpa_ctrl_open() * @reply: Buffer for the message data * @reply_len: Length of the reply buffer * Returns: 0 on success, -1 on failure * * This function will receive a pending control interface message. This * function will block if no messages are available. The received response will * be written to reply and reply_len is set to the actual length of the reply. * wpa_ctrl_recv() is only used for event messages, i.e., wpa_ctrl_attach() * must have been used to register the control interface as an event monitor. */ int wpa_ctrl_recv(struct wpa_ctrl *ctrl, char *reply, size_t *reply_len); /** * wpa_ctrl_pending - Check whether there are pending event messages * @ctrl: Control interface data from wpa_ctrl_open() * Returns: 1 if there are pending messages, 0 if no, or -1 on error * * This function will check whether there are any pending control interface * message available to be received with wpa_ctrl_recv(). wpa_ctrl_pending() is * only used for event messages, i.e., wpa_ctrl_attach() must have been used to * register the control interface as an event monitor. */ int wpa_ctrl_pending(struct wpa_ctrl *ctrl); /** * wpa_ctrl_get_fd - Get file descriptor used by the control interface * @ctrl: Control interface data from wpa_ctrl_open() * Returns: File descriptor used for the connection * * This function can be used to get the file descriptor that is used for the * control interface connection. The returned value can be used, e.g., with * select() while waiting for multiple events. * * The returned file descriptor must not be used directly for sending or * receiving packets; instead, the library functions wpa_ctrl_request() and * wpa_ctrl_recv() must be used for this. */ int wpa_ctrl_get_fd(struct wpa_ctrl *ctrl); char * wpa_ctrl_get_remote_ifname(struct wpa_ctrl *ctrl); #ifdef ANDROID /** * wpa_ctrl_cleanup() - Delete any local UNIX domain socket files that * may be left over from clients that were previously connected to * wpa_supplicant. This keeps these files from being orphaned in the * event of crashes that prevented them from being removed as part * of the normal orderly shutdown. */ void wpa_ctrl_cleanup(void); #endif /* ANDROID */ #ifdef CONFIG_CTRL_IFACE_UDP /* Port range for multiple wpa_supplicant instances and multiple VIFs */ #define WPA_CTRL_IFACE_PORT 9877 #define WPA_CTRL_IFACE_PORT_LIMIT 50 /* decremented from start */ #define WPA_GLOBAL_CTRL_IFACE_PORT 9878 #define WPA_GLOBAL_CTRL_IFACE_PORT_LIMIT 20 /* incremented from start */ #endif /* CONFIG_CTRL_IFACE_UDP */ #ifdef __cplusplus } #endif #endif /* WPA_CTRL_H */ wpa-2.1/src/crypto/000077500000000000000000000000001227414666700142715ustar00rootroot00000000000000wpa-2.1/src/crypto/.gitignore000066400000000000000000000000141227414666700162540ustar00rootroot00000000000000libcrypto.a wpa-2.1/src/crypto/Makefile000066400000000000000000000020301227414666700157240ustar00rootroot00000000000000all: libcrypto.a clean: rm -f *~ *.o *.d *.gcno *.gcda *.gcov libcrypto.a install: @echo Nothing to be made. include ../lib.rules CFLAGS += -DCONFIG_TLS_INTERNAL_CLIENT CFLAGS += -DCONFIG_TLS_INTERNAL_SERVER #CFLAGS += -DALL_DH_GROUPS CFLAGS += -DCONFIG_SHA256 LIB_OBJS= \ aes-cbc.o \ aes-ccm.o \ aes-ctr.o \ aes-eax.o \ aes-encblock.o \ aes-gcm.o \ aes-internal.o \ aes-internal-dec.o \ aes-internal-enc.o \ aes-omac1.o \ aes-unwrap.o \ aes-wrap.o \ des-internal.o \ dh_group5.o \ dh_groups.o \ md4-internal.o \ md5.o \ md5-internal.o \ milenage.o \ ms_funcs.o \ rc4.o \ sha1.o \ sha1-internal.o \ sha1-pbkdf2.o \ sha1-prf.o \ sha1-tlsprf.o \ sha1-tprf.o \ sha256.o \ sha256-prf.o \ sha256-tlsprf.o \ sha256-internal.o LIB_OBJS += crypto_internal.o LIB_OBJS += crypto_internal-cipher.o LIB_OBJS += crypto_internal-modexp.o LIB_OBJS += crypto_internal-rsa.o LIB_OBJS += tls_internal.o LIB_OBJS += fips_prf_internal.o LIB_OBJS += random.o libcrypto.a: $(LIB_OBJS) $(AR) crT $@ $? -include $(OBJS:%.o=%.d) wpa-2.1/src/crypto/aes-cbc.c000066400000000000000000000035641227414666700157420ustar00rootroot00000000000000/* * AES-128 CBC * * Copyright (c) 2003-2007, 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 "aes.h" #include "aes_wrap.h" /** * aes_128_cbc_encrypt - AES-128 CBC encryption * @key: Encryption key * @iv: Encryption IV for CBC mode (16 bytes) * @data: Data to encrypt in-place * @data_len: Length of data in bytes (must be divisible by 16) * Returns: 0 on success, -1 on failure */ int aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len) { void *ctx; u8 cbc[AES_BLOCK_SIZE]; u8 *pos = data; int i, j, blocks; ctx = aes_encrypt_init(key, 16); if (ctx == NULL) return -1; os_memcpy(cbc, iv, AES_BLOCK_SIZE); blocks = data_len / AES_BLOCK_SIZE; for (i = 0; i < blocks; i++) { for (j = 0; j < AES_BLOCK_SIZE; j++) cbc[j] ^= pos[j]; aes_encrypt(ctx, cbc, cbc); os_memcpy(pos, cbc, AES_BLOCK_SIZE); pos += AES_BLOCK_SIZE; } aes_encrypt_deinit(ctx); return 0; } /** * aes_128_cbc_decrypt - AES-128 CBC decryption * @key: Decryption key * @iv: Decryption IV for CBC mode (16 bytes) * @data: Data to decrypt in-place * @data_len: Length of data in bytes (must be divisible by 16) * Returns: 0 on success, -1 on failure */ int aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len) { void *ctx; u8 cbc[AES_BLOCK_SIZE], tmp[AES_BLOCK_SIZE]; u8 *pos = data; int i, j, blocks; ctx = aes_decrypt_init(key, 16); if (ctx == NULL) return -1; os_memcpy(cbc, iv, AES_BLOCK_SIZE); blocks = data_len / AES_BLOCK_SIZE; for (i = 0; i < blocks; i++) { os_memcpy(tmp, pos, AES_BLOCK_SIZE); aes_decrypt(ctx, pos, pos); for (j = 0; j < AES_BLOCK_SIZE; j++) pos[j] ^= cbc[j]; os_memcpy(cbc, tmp, AES_BLOCK_SIZE); pos += AES_BLOCK_SIZE; } aes_decrypt_deinit(ctx); return 0; } wpa-2.1/src/crypto/aes-ccm.c000066400000000000000000000116551227414666700157550ustar00rootroot00000000000000/* * Counter with CBC-MAC (CCM) with AES * * Copyright (c) 2010-2012, 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 "aes.h" #include "aes_wrap.h" static void xor_aes_block(u8 *dst, const u8 *src) { u32 *d = (u32 *) dst; u32 *s = (u32 *) src; *d++ ^= *s++; *d++ ^= *s++; *d++ ^= *s++; *d++ ^= *s++; } static void aes_ccm_auth_start(void *aes, size_t M, size_t L, const u8 *nonce, const u8 *aad, size_t aad_len, size_t plain_len, u8 *x) { u8 aad_buf[2 * AES_BLOCK_SIZE]; u8 b[AES_BLOCK_SIZE]; /* Authentication */ /* B_0: Flags | Nonce N | l(m) */ b[0] = aad_len ? 0x40 : 0 /* Adata */; b[0] |= (((M - 2) / 2) /* M' */ << 3); b[0] |= (L - 1) /* L' */; os_memcpy(&b[1], nonce, 15 - L); WPA_PUT_BE16(&b[AES_BLOCK_SIZE - L], plain_len); wpa_hexdump_key(MSG_EXCESSIVE, "CCM B_0", b, AES_BLOCK_SIZE); aes_encrypt(aes, b, x); /* X_1 = E(K, B_0) */ if (!aad_len) return; WPA_PUT_BE16(aad_buf, aad_len); os_memcpy(aad_buf + 2, aad, aad_len); os_memset(aad_buf + 2 + aad_len, 0, sizeof(aad_buf) - 2 - aad_len); xor_aes_block(aad_buf, x); aes_encrypt(aes, aad_buf, x); /* X_2 = E(K, X_1 XOR B_1) */ if (aad_len > AES_BLOCK_SIZE - 2) { xor_aes_block(&aad_buf[AES_BLOCK_SIZE], x); /* X_3 = E(K, X_2 XOR B_2) */ aes_encrypt(aes, &aad_buf[AES_BLOCK_SIZE], x); } } static void aes_ccm_auth(void *aes, const u8 *data, size_t len, u8 *x) { size_t last = len % AES_BLOCK_SIZE; size_t i; for (i = 0; i < len / AES_BLOCK_SIZE; i++) { /* X_i+1 = E(K, X_i XOR B_i) */ xor_aes_block(x, data); data += AES_BLOCK_SIZE; aes_encrypt(aes, x, x); } if (last) { /* XOR zero-padded last block */ for (i = 0; i < last; i++) x[i] ^= *data++; aes_encrypt(aes, x, x); } } static void aes_ccm_encr_start(size_t L, const u8 *nonce, u8 *a) { /* A_i = Flags | Nonce N | Counter i */ a[0] = L - 1; /* Flags = L' */ os_memcpy(&a[1], nonce, 15 - L); } static void aes_ccm_encr(void *aes, size_t L, const u8 *in, size_t len, u8 *out, u8 *a) { size_t last = len % AES_BLOCK_SIZE; size_t i; /* crypt = msg XOR (S_1 | S_2 | ... | S_n) */ for (i = 1; i <= len / AES_BLOCK_SIZE; i++) { WPA_PUT_BE16(&a[AES_BLOCK_SIZE - 2], i); /* S_i = E(K, A_i) */ aes_encrypt(aes, a, out); xor_aes_block(out, in); out += AES_BLOCK_SIZE; in += AES_BLOCK_SIZE; } if (last) { WPA_PUT_BE16(&a[AES_BLOCK_SIZE - 2], i); aes_encrypt(aes, a, out); /* XOR zero-padded last block */ for (i = 0; i < last; i++) *out++ ^= *in++; } } static void aes_ccm_encr_auth(void *aes, size_t M, u8 *x, u8 *a, u8 *auth) { size_t i; u8 tmp[AES_BLOCK_SIZE]; wpa_hexdump_key(MSG_EXCESSIVE, "CCM T", x, M); /* U = T XOR S_0; S_0 = E(K, A_0) */ WPA_PUT_BE16(&a[AES_BLOCK_SIZE - 2], 0); aes_encrypt(aes, a, tmp); for (i = 0; i < M; i++) auth[i] = x[i] ^ tmp[i]; wpa_hexdump_key(MSG_EXCESSIVE, "CCM U", auth, M); } static void aes_ccm_decr_auth(void *aes, size_t M, u8 *a, const u8 *auth, u8 *t) { size_t i; u8 tmp[AES_BLOCK_SIZE]; wpa_hexdump_key(MSG_EXCESSIVE, "CCM U", auth, M); /* U = T XOR S_0; S_0 = E(K, A_0) */ WPA_PUT_BE16(&a[AES_BLOCK_SIZE - 2], 0); aes_encrypt(aes, a, tmp); for (i = 0; i < M; i++) t[i] = auth[i] ^ tmp[i]; wpa_hexdump_key(MSG_EXCESSIVE, "CCM T", t, M); } /* AES-CCM with fixed L=2 and aad_len <= 30 assumption */ int aes_ccm_ae(const u8 *key, size_t key_len, const u8 *nonce, size_t M, const u8 *plain, size_t plain_len, const u8 *aad, size_t aad_len, u8 *crypt, u8 *auth) { const size_t L = 2; void *aes; u8 x[AES_BLOCK_SIZE], a[AES_BLOCK_SIZE]; if (aad_len > 30 || M > AES_BLOCK_SIZE) return -1; aes = aes_encrypt_init(key, key_len); if (aes == NULL) return -1; aes_ccm_auth_start(aes, M, L, nonce, aad, aad_len, plain_len, x); aes_ccm_auth(aes, plain, plain_len, x); /* Encryption */ aes_ccm_encr_start(L, nonce, a); aes_ccm_encr(aes, L, plain, plain_len, crypt, a); aes_ccm_encr_auth(aes, M, x, a, auth); aes_encrypt_deinit(aes); return 0; } /* AES-CCM with fixed L=2 and aad_len <= 30 assumption */ int aes_ccm_ad(const u8 *key, size_t key_len, const u8 *nonce, size_t M, const u8 *crypt, size_t crypt_len, const u8 *aad, size_t aad_len, const u8 *auth, u8 *plain) { const size_t L = 2; void *aes; u8 x[AES_BLOCK_SIZE], a[AES_BLOCK_SIZE]; u8 t[AES_BLOCK_SIZE]; if (aad_len > 30 || M > AES_BLOCK_SIZE) return -1; aes = aes_encrypt_init(key, key_len); if (aes == NULL) return -1; /* Decryption */ aes_ccm_encr_start(L, nonce, a); aes_ccm_decr_auth(aes, M, a, auth, t); /* plaintext = msg XOR (S_1 | S_2 | ... | S_n) */ aes_ccm_encr(aes, L, crypt, crypt_len, plain, a); aes_ccm_auth_start(aes, M, L, nonce, aad, aad_len, crypt_len, x); aes_ccm_auth(aes, plain, crypt_len, x); aes_encrypt_deinit(aes); if (os_memcmp(x, t, M) != 0) { wpa_printf(MSG_EXCESSIVE, "CCM: Auth mismatch"); return -1; } return 0; } wpa-2.1/src/crypto/aes-ctr.c000066400000000000000000000022271227414666700157760ustar00rootroot00000000000000/* * AES-128 CTR * * Copyright (c) 2003-2007, 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 "aes.h" #include "aes_wrap.h" /** * aes_128_ctr_encrypt - AES-128 CTR mode encryption * @key: Key for encryption (16 bytes) * @nonce: Nonce for counter mode (16 bytes) * @data: Data to encrypt in-place * @data_len: Length of data in bytes * Returns: 0 on success, -1 on failure */ int aes_128_ctr_encrypt(const u8 *key, const u8 *nonce, u8 *data, size_t data_len) { void *ctx; size_t j, len, left = data_len; int i; u8 *pos = data; u8 counter[AES_BLOCK_SIZE], buf[AES_BLOCK_SIZE]; ctx = aes_encrypt_init(key, 16); if (ctx == NULL) return -1; os_memcpy(counter, nonce, AES_BLOCK_SIZE); while (left > 0) { aes_encrypt(ctx, counter, buf); len = (left < AES_BLOCK_SIZE) ? left : AES_BLOCK_SIZE; for (j = 0; j < len; j++) pos[j] ^= buf[j]; pos += len; left -= len; for (i = AES_BLOCK_SIZE - 1; i >= 0; i--) { counter[i]++; if (counter[i]) break; } } aes_encrypt_deinit(ctx); return 0; } wpa-2.1/src/crypto/aes-eax.c000066400000000000000000000063501227414666700157640ustar00rootroot00000000000000/* * AES-128 EAX * * Copyright (c) 2003-2007, 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 "aes.h" #include "aes_wrap.h" /** * aes_128_eax_encrypt - AES-128 EAX mode encryption * @key: Key for encryption (16 bytes) * @nonce: Nonce for counter mode * @nonce_len: Nonce length in bytes * @hdr: Header data to be authenticity protected * @hdr_len: Length of the header data bytes * @data: Data to encrypt in-place * @data_len: Length of data in bytes * @tag: 16-byte tag value * Returns: 0 on success, -1 on failure */ int aes_128_eax_encrypt(const u8 *key, const u8 *nonce, size_t nonce_len, const u8 *hdr, size_t hdr_len, u8 *data, size_t data_len, u8 *tag) { u8 *buf; size_t buf_len; u8 nonce_mac[AES_BLOCK_SIZE], hdr_mac[AES_BLOCK_SIZE], data_mac[AES_BLOCK_SIZE]; int i, ret = -1; if (nonce_len > data_len) buf_len = nonce_len; else buf_len = data_len; if (hdr_len > buf_len) buf_len = hdr_len; buf_len += 16; buf = os_malloc(buf_len); if (buf == NULL) return -1; os_memset(buf, 0, 15); buf[15] = 0; os_memcpy(buf + 16, nonce, nonce_len); if (omac1_aes_128(key, buf, 16 + nonce_len, nonce_mac)) goto fail; buf[15] = 1; os_memcpy(buf + 16, hdr, hdr_len); if (omac1_aes_128(key, buf, 16 + hdr_len, hdr_mac)) goto fail; if (aes_128_ctr_encrypt(key, nonce_mac, data, data_len)) goto fail; buf[15] = 2; os_memcpy(buf + 16, data, data_len); if (omac1_aes_128(key, buf, 16 + data_len, data_mac)) goto fail; for (i = 0; i < AES_BLOCK_SIZE; i++) tag[i] = nonce_mac[i] ^ data_mac[i] ^ hdr_mac[i]; ret = 0; fail: os_free(buf); return ret; } /** * aes_128_eax_decrypt - AES-128 EAX mode decryption * @key: Key for decryption (16 bytes) * @nonce: Nonce for counter mode * @nonce_len: Nonce length in bytes * @hdr: Header data to be authenticity protected * @hdr_len: Length of the header data bytes * @data: Data to encrypt in-place * @data_len: Length of data in bytes * @tag: 16-byte tag value * Returns: 0 on success, -1 on failure, -2 if tag does not match */ int aes_128_eax_decrypt(const u8 *key, const u8 *nonce, size_t nonce_len, const u8 *hdr, size_t hdr_len, u8 *data, size_t data_len, const u8 *tag) { u8 *buf; size_t buf_len; u8 nonce_mac[AES_BLOCK_SIZE], hdr_mac[AES_BLOCK_SIZE], data_mac[AES_BLOCK_SIZE]; int i; if (nonce_len > data_len) buf_len = nonce_len; else buf_len = data_len; if (hdr_len > buf_len) buf_len = hdr_len; buf_len += 16; buf = os_malloc(buf_len); if (buf == NULL) return -1; os_memset(buf, 0, 15); buf[15] = 0; os_memcpy(buf + 16, nonce, nonce_len); if (omac1_aes_128(key, buf, 16 + nonce_len, nonce_mac)) { os_free(buf); return -1; } buf[15] = 1; os_memcpy(buf + 16, hdr, hdr_len); if (omac1_aes_128(key, buf, 16 + hdr_len, hdr_mac)) { os_free(buf); return -1; } buf[15] = 2; os_memcpy(buf + 16, data, data_len); if (omac1_aes_128(key, buf, 16 + data_len, data_mac)) { os_free(buf); return -1; } os_free(buf); for (i = 0; i < AES_BLOCK_SIZE; i++) { if (tag[i] != (nonce_mac[i] ^ data_mac[i] ^ hdr_mac[i])) return -2; } return aes_128_ctr_encrypt(key, nonce_mac, data, data_len); } wpa-2.1/src/crypto/aes-encblock.c000066400000000000000000000013001227414666700167550ustar00rootroot00000000000000/* * AES encrypt_block * * Copyright (c) 2003-2007, 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 "aes.h" #include "aes_wrap.h" /** * aes_128_encrypt_block - Perform one AES 128-bit block operation * @key: Key for AES * @in: Input data (16 bytes) * @out: Output of the AES block operation (16 bytes) * Returns: 0 on success, -1 on failure */ int aes_128_encrypt_block(const u8 *key, const u8 *in, u8 *out) { void *ctx; ctx = aes_encrypt_init(key, 16); if (ctx == NULL) return -1; aes_encrypt(ctx, in, out); aes_encrypt_deinit(ctx); return 0; } wpa-2.1/src/crypto/aes-gcm.c000066400000000000000000000147301227414666700157560ustar00rootroot00000000000000/* * Galois/Counter Mode (GCM) and GMAC with AES * * Copyright (c) 2012, 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 "aes.h" #include "aes_wrap.h" static void inc32(u8 *block) { u32 val; val = WPA_GET_BE32(block + AES_BLOCK_SIZE - 4); val++; WPA_PUT_BE32(block + AES_BLOCK_SIZE - 4, val); } static void xor_block(u8 *dst, const u8 *src) { u32 *d = (u32 *) dst; u32 *s = (u32 *) src; *d++ ^= *s++; *d++ ^= *s++; *d++ ^= *s++; *d++ ^= *s++; } static void shift_right_block(u8 *v) { u32 val; val = WPA_GET_BE32(v + 12); val >>= 1; if (v[11] & 0x01) val |= 0x80000000; WPA_PUT_BE32(v + 12, val); val = WPA_GET_BE32(v + 8); val >>= 1; if (v[7] & 0x01) val |= 0x80000000; WPA_PUT_BE32(v + 8, val); val = WPA_GET_BE32(v + 4); val >>= 1; if (v[3] & 0x01) val |= 0x80000000; WPA_PUT_BE32(v + 4, val); val = WPA_GET_BE32(v); val >>= 1; WPA_PUT_BE32(v, val); } /* Multiplication in GF(2^128) */ static void gf_mult(const u8 *x, const u8 *y, u8 *z) { u8 v[16]; int i, j; os_memset(z, 0, 16); /* Z_0 = 0^128 */ os_memcpy(v, y, 16); /* V_0 = Y */ for (i = 0; i < 16; i++) { for (j = 0; j < 8; j++) { if (x[i] & BIT(7 - j)) { /* Z_(i + 1) = Z_i XOR V_i */ xor_block(z, v); } else { /* Z_(i + 1) = Z_i */ } if (v[15] & 0x01) { /* V_(i + 1) = (V_i >> 1) XOR R */ shift_right_block(v); /* R = 11100001 || 0^120 */ v[0] ^= 0xe1; } else { /* V_(i + 1) = V_i >> 1 */ shift_right_block(v); } } } } static void ghash_start(u8 *y) { /* Y_0 = 0^128 */ os_memset(y, 0, 16); } static void ghash(const u8 *h, const u8 *x, size_t xlen, u8 *y) { size_t m, i; const u8 *xpos = x; u8 tmp[16]; m = xlen / 16; for (i = 0; i < m; i++) { /* Y_i = (Y^(i-1) XOR X_i) dot H */ xor_block(y, xpos); xpos += 16; /* dot operation: * multiplication operation for binary Galois (finite) field of * 2^128 elements */ gf_mult(y, h, tmp); os_memcpy(y, tmp, 16); } if (x + xlen > xpos) { /* Add zero padded last block */ size_t last = x + xlen - xpos; os_memcpy(tmp, xpos, last); os_memset(tmp + last, 0, sizeof(tmp) - last); /* Y_i = (Y^(i-1) XOR X_i) dot H */ xor_block(y, tmp); /* dot operation: * multiplication operation for binary Galois (finite) field of * 2^128 elements */ gf_mult(y, h, tmp); os_memcpy(y, tmp, 16); } /* Return Y_m */ } static void aes_gctr(void *aes, const u8 *icb, const u8 *x, size_t xlen, u8 *y) { size_t i, n, last; u8 cb[AES_BLOCK_SIZE], tmp[AES_BLOCK_SIZE]; const u8 *xpos = x; u8 *ypos = y; if (xlen == 0) return; n = xlen / 16; os_memcpy(cb, icb, AES_BLOCK_SIZE); /* Full blocks */ for (i = 0; i < n; i++) { aes_encrypt(aes, cb, ypos); xor_block(ypos, xpos); xpos += AES_BLOCK_SIZE; ypos += AES_BLOCK_SIZE; inc32(cb); } last = x + xlen - xpos; if (last) { /* Last, partial block */ aes_encrypt(aes, cb, tmp); for (i = 0; i < last; i++) *ypos++ = *xpos++ ^ tmp[i]; } } static void * aes_gcm_init_hash_subkey(const u8 *key, size_t key_len, u8 *H) { void *aes; aes = aes_encrypt_init(key, key_len); if (aes == NULL) return NULL; /* Generate hash subkey H = AES_K(0^128) */ os_memset(H, 0, AES_BLOCK_SIZE); aes_encrypt(aes, H, H); wpa_hexdump_key(MSG_EXCESSIVE, "Hash subkey H for GHASH", H, AES_BLOCK_SIZE); return aes; } static void aes_gcm_prepare_j0(const u8 *iv, size_t iv_len, const u8 *H, u8 *J0) { u8 len_buf[16]; if (iv_len == 12) { /* Prepare block J_0 = IV || 0^31 || 1 [len(IV) = 96] */ os_memcpy(J0, iv, iv_len); os_memset(J0 + iv_len, 0, AES_BLOCK_SIZE - iv_len); J0[AES_BLOCK_SIZE - 1] = 0x01; } else { /* * s = 128 * ceil(len(IV)/128) - len(IV) * J_0 = GHASH_H(IV || 0^(s+64) || [len(IV)]_64) */ ghash_start(J0); ghash(H, iv, iv_len, J0); WPA_PUT_BE64(len_buf, 0); WPA_PUT_BE64(len_buf + 8, iv_len * 8); ghash(H, len_buf, sizeof(len_buf), J0); } } static void aes_gcm_gctr(void *aes, const u8 *J0, const u8 *in, size_t len, u8 *out) { u8 J0inc[AES_BLOCK_SIZE]; if (len == 0) return; os_memcpy(J0inc, J0, AES_BLOCK_SIZE); inc32(J0inc); aes_gctr(aes, J0inc, in, len, out); } static void aes_gcm_ghash(const u8 *H, const u8 *aad, size_t aad_len, const u8 *crypt, size_t crypt_len, u8 *S) { u8 len_buf[16]; /* * u = 128 * ceil[len(C)/128] - len(C) * v = 128 * ceil[len(A)/128] - len(A) * S = GHASH_H(A || 0^v || C || 0^u || [len(A)]64 || [len(C)]64) * (i.e., zero padded to block size A || C and lengths of each in bits) */ ghash_start(S); ghash(H, aad, aad_len, S); ghash(H, crypt, crypt_len, S); WPA_PUT_BE64(len_buf, aad_len * 8); WPA_PUT_BE64(len_buf + 8, crypt_len * 8); ghash(H, len_buf, sizeof(len_buf), S); wpa_hexdump_key(MSG_EXCESSIVE, "S = GHASH_H(...)", S, 16); } /** * aes_gcm_ae - GCM-AE_K(IV, P, A) */ int aes_gcm_ae(const u8 *key, size_t key_len, const u8 *iv, size_t iv_len, const u8 *plain, size_t plain_len, const u8 *aad, size_t aad_len, u8 *crypt, u8 *tag) { u8 H[AES_BLOCK_SIZE]; u8 J0[AES_BLOCK_SIZE]; u8 S[16]; void *aes; aes = aes_gcm_init_hash_subkey(key, key_len, H); if (aes == NULL) return -1; aes_gcm_prepare_j0(iv, iv_len, H, J0); /* C = GCTR_K(inc_32(J_0), P) */ aes_gcm_gctr(aes, J0, plain, plain_len, crypt); aes_gcm_ghash(H, aad, aad_len, crypt, plain_len, S); /* T = MSB_t(GCTR_K(J_0, S)) */ aes_gctr(aes, J0, S, sizeof(S), tag); /* Return (C, T) */ aes_encrypt_deinit(aes); return 0; } /** * aes_gcm_ad - GCM-AD_K(IV, C, A, T) */ int aes_gcm_ad(const u8 *key, size_t key_len, const u8 *iv, size_t iv_len, const u8 *crypt, size_t crypt_len, const u8 *aad, size_t aad_len, const u8 *tag, u8 *plain) { u8 H[AES_BLOCK_SIZE]; u8 J0[AES_BLOCK_SIZE]; u8 S[16], T[16]; void *aes; aes = aes_gcm_init_hash_subkey(key, key_len, H); if (aes == NULL) return -1; aes_gcm_prepare_j0(iv, iv_len, H, J0); /* P = GCTR_K(inc_32(J_0), C) */ aes_gcm_gctr(aes, J0, crypt, crypt_len, plain); aes_gcm_ghash(H, aad, aad_len, crypt, crypt_len, S); /* T' = MSB_t(GCTR_K(J_0, S)) */ aes_gctr(aes, J0, S, sizeof(S), T); aes_encrypt_deinit(aes); if (os_memcmp(tag, T, 16) != 0) { wpa_printf(MSG_EXCESSIVE, "GCM: Tag mismatch"); return -1; } return 0; } int aes_gmac(const u8 *key, size_t key_len, const u8 *iv, size_t iv_len, const u8 *aad, size_t aad_len, u8 *tag) { return aes_gcm_ae(key, key_len, iv, iv_len, NULL, 0, aad, aad_len, NULL, tag); } wpa-2.1/src/crypto/aes-internal-dec.c000066400000000000000000000072011227414666700175500ustar00rootroot00000000000000/* * AES (Rijndael) cipher - decrypt * * Modifications to public domain implementation: * - cleanup * - use C pre-processor to make it easier to change S table access * - added option (AES_SMALL_TABLES) for reducing code size by about 8 kB at * cost of reduced throughput (quite small difference on Pentium 4, * 10-25% when using -O1 or -O2 optimization) * * Copyright (c) 2003-2012, 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.h" #include "aes_i.h" /** * Expand the cipher key into the decryption key schedule. * * @return the number of rounds for the given cipher key size. */ static int rijndaelKeySetupDec(u32 rk[], const u8 cipherKey[], int keyBits) { int Nr, i, j; u32 temp; /* expand the cipher key: */ Nr = rijndaelKeySetupEnc(rk, cipherKey, keyBits); if (Nr < 0) return Nr; /* invert the order of the round keys: */ for (i = 0, j = 4*Nr; i < j; i += 4, j -= 4) { temp = rk[i ]; rk[i ] = rk[j ]; rk[j ] = temp; temp = rk[i + 1]; rk[i + 1] = rk[j + 1]; rk[j + 1] = temp; temp = rk[i + 2]; rk[i + 2] = rk[j + 2]; rk[j + 2] = temp; temp = rk[i + 3]; rk[i + 3] = rk[j + 3]; rk[j + 3] = temp; } /* apply the inverse MixColumn transform to all round keys but the * first and the last: */ for (i = 1; i < Nr; i++) { rk += 4; for (j = 0; j < 4; j++) { rk[j] = TD0_(TE4((rk[j] >> 24) )) ^ TD1_(TE4((rk[j] >> 16) & 0xff)) ^ TD2_(TE4((rk[j] >> 8) & 0xff)) ^ TD3_(TE4((rk[j] ) & 0xff)); } } return Nr; } void * aes_decrypt_init(const u8 *key, size_t len) { u32 *rk; int res; rk = os_malloc(AES_PRIV_SIZE); if (rk == NULL) return NULL; res = rijndaelKeySetupDec(rk, key, len * 8); if (res < 0) { os_free(rk); return NULL; } rk[AES_PRIV_NR_POS] = res; return rk; } static void rijndaelDecrypt(const u32 rk[/*44*/], int Nr, const u8 ct[16], u8 pt[16]) { u32 s0, s1, s2, s3, t0, t1, t2, t3; #ifndef FULL_UNROLL int r; #endif /* ?FULL_UNROLL */ /* * map byte array block to cipher state * and add initial round key: */ s0 = GETU32(ct ) ^ rk[0]; s1 = GETU32(ct + 4) ^ rk[1]; s2 = GETU32(ct + 8) ^ rk[2]; s3 = GETU32(ct + 12) ^ rk[3]; #define ROUND(i,d,s) \ d##0 = TD0(s##0) ^ TD1(s##3) ^ TD2(s##2) ^ TD3(s##1) ^ rk[4 * i]; \ d##1 = TD0(s##1) ^ TD1(s##0) ^ TD2(s##3) ^ TD3(s##2) ^ rk[4 * i + 1]; \ d##2 = TD0(s##2) ^ TD1(s##1) ^ TD2(s##0) ^ TD3(s##3) ^ rk[4 * i + 2]; \ d##3 = TD0(s##3) ^ TD1(s##2) ^ TD2(s##1) ^ TD3(s##0) ^ rk[4 * i + 3] #ifdef FULL_UNROLL ROUND(1,t,s); ROUND(2,s,t); ROUND(3,t,s); ROUND(4,s,t); ROUND(5,t,s); ROUND(6,s,t); ROUND(7,t,s); ROUND(8,s,t); ROUND(9,t,s); if (Nr > 10) { ROUND(10,s,t); ROUND(11,t,s); if (Nr > 12) { ROUND(12,s,t); ROUND(13,t,s); } } rk += Nr << 2; #else /* !FULL_UNROLL */ /* Nr - 1 full rounds: */ r = Nr >> 1; for (;;) { ROUND(1,t,s); rk += 8; if (--r == 0) break; ROUND(0,s,t); } #endif /* ?FULL_UNROLL */ #undef ROUND /* * apply last round and * map cipher state to byte array block: */ s0 = TD41(t0) ^ TD42(t3) ^ TD43(t2) ^ TD44(t1) ^ rk[0]; PUTU32(pt , s0); s1 = TD41(t1) ^ TD42(t0) ^ TD43(t3) ^ TD44(t2) ^ rk[1]; PUTU32(pt + 4, s1); s2 = TD41(t2) ^ TD42(t1) ^ TD43(t0) ^ TD44(t3) ^ rk[2]; PUTU32(pt + 8, s2); s3 = TD41(t3) ^ TD42(t2) ^ TD43(t1) ^ TD44(t0) ^ rk[3]; PUTU32(pt + 12, s3); } void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain) { u32 *rk = ctx; rijndaelDecrypt(ctx, rk[AES_PRIV_NR_POS], crypt, plain); } void aes_decrypt_deinit(void *ctx) { os_memset(ctx, 0, AES_PRIV_SIZE); os_free(ctx); } wpa-2.1/src/crypto/aes-internal-enc.c000066400000000000000000000051641227414666700175700ustar00rootroot00000000000000/* * AES (Rijndael) cipher - encrypt * * Modifications to public domain implementation: * - cleanup * - use C pre-processor to make it easier to change S table access * - added option (AES_SMALL_TABLES) for reducing code size by about 8 kB at * cost of reduced throughput (quite small difference on Pentium 4, * 10-25% when using -O1 or -O2 optimization) * * Copyright (c) 2003-2012, 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.h" #include "aes_i.h" static void rijndaelEncrypt(const u32 rk[], int Nr, const u8 pt[16], u8 ct[16]) { u32 s0, s1, s2, s3, t0, t1, t2, t3; #ifndef FULL_UNROLL int r; #endif /* ?FULL_UNROLL */ /* * map byte array block to cipher state * and add initial round key: */ s0 = GETU32(pt ) ^ rk[0]; s1 = GETU32(pt + 4) ^ rk[1]; s2 = GETU32(pt + 8) ^ rk[2]; s3 = GETU32(pt + 12) ^ rk[3]; #define ROUND(i,d,s) \ d##0 = TE0(s##0) ^ TE1(s##1) ^ TE2(s##2) ^ TE3(s##3) ^ rk[4 * i]; \ d##1 = TE0(s##1) ^ TE1(s##2) ^ TE2(s##3) ^ TE3(s##0) ^ rk[4 * i + 1]; \ d##2 = TE0(s##2) ^ TE1(s##3) ^ TE2(s##0) ^ TE3(s##1) ^ rk[4 * i + 2]; \ d##3 = TE0(s##3) ^ TE1(s##0) ^ TE2(s##1) ^ TE3(s##2) ^ rk[4 * i + 3] #ifdef FULL_UNROLL ROUND(1,t,s); ROUND(2,s,t); ROUND(3,t,s); ROUND(4,s,t); ROUND(5,t,s); ROUND(6,s,t); ROUND(7,t,s); ROUND(8,s,t); ROUND(9,t,s); if (Nr > 10) { ROUND(10,s,t); ROUND(11,t,s); if (Nr > 12) { ROUND(12,s,t); ROUND(13,t,s); } } rk += Nr << 2; #else /* !FULL_UNROLL */ /* Nr - 1 full rounds: */ r = Nr >> 1; for (;;) { ROUND(1,t,s); rk += 8; if (--r == 0) break; ROUND(0,s,t); } #endif /* ?FULL_UNROLL */ #undef ROUND /* * apply last round and * map cipher state to byte array block: */ s0 = TE41(t0) ^ TE42(t1) ^ TE43(t2) ^ TE44(t3) ^ rk[0]; PUTU32(ct , s0); s1 = TE41(t1) ^ TE42(t2) ^ TE43(t3) ^ TE44(t0) ^ rk[1]; PUTU32(ct + 4, s1); s2 = TE41(t2) ^ TE42(t3) ^ TE43(t0) ^ TE44(t1) ^ rk[2]; PUTU32(ct + 8, s2); s3 = TE41(t3) ^ TE42(t0) ^ TE43(t1) ^ TE44(t2) ^ rk[3]; PUTU32(ct + 12, s3); } void * aes_encrypt_init(const u8 *key, size_t len) { u32 *rk; int res; rk = os_malloc(AES_PRIV_SIZE); if (rk == NULL) return NULL; res = rijndaelKeySetupEnc(rk, key, len * 8); if (res < 0) { os_free(rk); return NULL; } rk[AES_PRIV_NR_POS] = res; return rk; } void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt) { u32 *rk = ctx; rijndaelEncrypt(ctx, rk[AES_PRIV_NR_POS], plain, crypt); } void aes_encrypt_deinit(void *ctx) { os_memset(ctx, 0, AES_PRIV_SIZE); os_free(ctx); } wpa-2.1/src/crypto/aes-internal.c000066400000000000000000001223551227414666700170270ustar00rootroot00000000000000/* * AES (Rijndael) cipher * * Modifications to public domain implementation: * - cleanup * - use C pre-processor to make it easier to change S table access * - added option (AES_SMALL_TABLES) for reducing code size by about 8 kB at * cost of reduced throughput (quite small difference on Pentium 4, * 10-25% when using -O1 or -O2 optimization) * * Copyright (c) 2003-2012, 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.h" #include "aes_i.h" /* * rijndael-alg-fst.c * * @version 3.0 (December 2000) * * Optimised ANSI C code for the Rijndael cipher (now AES) * * @author Vincent Rijmen * @author Antoon Bosselaers * @author Paulo Barreto * * This code is hereby placed in the public domain. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''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 AUTHORS 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. */ /* Te0[x] = S [x].[02, 01, 01, 03]; Te1[x] = S [x].[03, 02, 01, 01]; Te2[x] = S [x].[01, 03, 02, 01]; Te3[x] = S [x].[01, 01, 03, 02]; Te4[x] = S [x].[01, 01, 01, 01]; Td0[x] = Si[x].[0e, 09, 0d, 0b]; Td1[x] = Si[x].[0b, 0e, 09, 0d]; Td2[x] = Si[x].[0d, 0b, 0e, 09]; Td3[x] = Si[x].[09, 0d, 0b, 0e]; Td4[x] = Si[x].[01, 01, 01, 01]; */ const u32 Te0[256] = { 0xc66363a5U, 0xf87c7c84U, 0xee777799U, 0xf67b7b8dU, 0xfff2f20dU, 0xd66b6bbdU, 0xde6f6fb1U, 0x91c5c554U, 0x60303050U, 0x02010103U, 0xce6767a9U, 0x562b2b7dU, 0xe7fefe19U, 0xb5d7d762U, 0x4dababe6U, 0xec76769aU, 0x8fcaca45U, 0x1f82829dU, 0x89c9c940U, 0xfa7d7d87U, 0xeffafa15U, 0xb25959ebU, 0x8e4747c9U, 0xfbf0f00bU, 0x41adadecU, 0xb3d4d467U, 0x5fa2a2fdU, 0x45afafeaU, 0x239c9cbfU, 0x53a4a4f7U, 0xe4727296U, 0x9bc0c05bU, 0x75b7b7c2U, 0xe1fdfd1cU, 0x3d9393aeU, 0x4c26266aU, 0x6c36365aU, 0x7e3f3f41U, 0xf5f7f702U, 0x83cccc4fU, 0x6834345cU, 0x51a5a5f4U, 0xd1e5e534U, 0xf9f1f108U, 0xe2717193U, 0xabd8d873U, 0x62313153U, 0x2a15153fU, 0x0804040cU, 0x95c7c752U, 0x46232365U, 0x9dc3c35eU, 0x30181828U, 0x379696a1U, 0x0a05050fU, 0x2f9a9ab5U, 0x0e070709U, 0x24121236U, 0x1b80809bU, 0xdfe2e23dU, 0xcdebeb26U, 0x4e272769U, 0x7fb2b2cdU, 0xea75759fU, 0x1209091bU, 0x1d83839eU, 0x582c2c74U, 0x341a1a2eU, 0x361b1b2dU, 0xdc6e6eb2U, 0xb45a5aeeU, 0x5ba0a0fbU, 0xa45252f6U, 0x763b3b4dU, 0xb7d6d661U, 0x7db3b3ceU, 0x5229297bU, 0xdde3e33eU, 0x5e2f2f71U, 0x13848497U, 0xa65353f5U, 0xb9d1d168U, 0x00000000U, 0xc1eded2cU, 0x40202060U, 0xe3fcfc1fU, 0x79b1b1c8U, 0xb65b5bedU, 0xd46a6abeU, 0x8dcbcb46U, 0x67bebed9U, 0x7239394bU, 0x944a4adeU, 0x984c4cd4U, 0xb05858e8U, 0x85cfcf4aU, 0xbbd0d06bU, 0xc5efef2aU, 0x4faaaae5U, 0xedfbfb16U, 0x864343c5U, 0x9a4d4dd7U, 0x66333355U, 0x11858594U, 0x8a4545cfU, 0xe9f9f910U, 0x04020206U, 0xfe7f7f81U, 0xa05050f0U, 0x783c3c44U, 0x259f9fbaU, 0x4ba8a8e3U, 0xa25151f3U, 0x5da3a3feU, 0x804040c0U, 0x058f8f8aU, 0x3f9292adU, 0x219d9dbcU, 0x70383848U, 0xf1f5f504U, 0x63bcbcdfU, 0x77b6b6c1U, 0xafdada75U, 0x42212163U, 0x20101030U, 0xe5ffff1aU, 0xfdf3f30eU, 0xbfd2d26dU, 0x81cdcd4cU, 0x180c0c14U, 0x26131335U, 0xc3ecec2fU, 0xbe5f5fe1U, 0x359797a2U, 0x884444ccU, 0x2e171739U, 0x93c4c457U, 0x55a7a7f2U, 0xfc7e7e82U, 0x7a3d3d47U, 0xc86464acU, 0xba5d5de7U, 0x3219192bU, 0xe6737395U, 0xc06060a0U, 0x19818198U, 0x9e4f4fd1U, 0xa3dcdc7fU, 0x44222266U, 0x542a2a7eU, 0x3b9090abU, 0x0b888883U, 0x8c4646caU, 0xc7eeee29U, 0x6bb8b8d3U, 0x2814143cU, 0xa7dede79U, 0xbc5e5ee2U, 0x160b0b1dU, 0xaddbdb76U, 0xdbe0e03bU, 0x64323256U, 0x743a3a4eU, 0x140a0a1eU, 0x924949dbU, 0x0c06060aU, 0x4824246cU, 0xb85c5ce4U, 0x9fc2c25dU, 0xbdd3d36eU, 0x43acacefU, 0xc46262a6U, 0x399191a8U, 0x319595a4U, 0xd3e4e437U, 0xf279798bU, 0xd5e7e732U, 0x8bc8c843U, 0x6e373759U, 0xda6d6db7U, 0x018d8d8cU, 0xb1d5d564U, 0x9c4e4ed2U, 0x49a9a9e0U, 0xd86c6cb4U, 0xac5656faU, 0xf3f4f407U, 0xcfeaea25U, 0xca6565afU, 0xf47a7a8eU, 0x47aeaee9U, 0x10080818U, 0x6fbabad5U, 0xf0787888U, 0x4a25256fU, 0x5c2e2e72U, 0x381c1c24U, 0x57a6a6f1U, 0x73b4b4c7U, 0x97c6c651U, 0xcbe8e823U, 0xa1dddd7cU, 0xe874749cU, 0x3e1f1f21U, 0x964b4bddU, 0x61bdbddcU, 0x0d8b8b86U, 0x0f8a8a85U, 0xe0707090U, 0x7c3e3e42U, 0x71b5b5c4U, 0xcc6666aaU, 0x904848d8U, 0x06030305U, 0xf7f6f601U, 0x1c0e0e12U, 0xc26161a3U, 0x6a35355fU, 0xae5757f9U, 0x69b9b9d0U, 0x17868691U, 0x99c1c158U, 0x3a1d1d27U, 0x279e9eb9U, 0xd9e1e138U, 0xebf8f813U, 0x2b9898b3U, 0x22111133U, 0xd26969bbU, 0xa9d9d970U, 0x078e8e89U, 0x339494a7U, 0x2d9b9bb6U, 0x3c1e1e22U, 0x15878792U, 0xc9e9e920U, 0x87cece49U, 0xaa5555ffU, 0x50282878U, 0xa5dfdf7aU, 0x038c8c8fU, 0x59a1a1f8U, 0x09898980U, 0x1a0d0d17U, 0x65bfbfdaU, 0xd7e6e631U, 0x844242c6U, 0xd06868b8U, 0x824141c3U, 0x299999b0U, 0x5a2d2d77U, 0x1e0f0f11U, 0x7bb0b0cbU, 0xa85454fcU, 0x6dbbbbd6U, 0x2c16163aU, }; #ifndef AES_SMALL_TABLES const u32 Te1[256] = { 0xa5c66363U, 0x84f87c7cU, 0x99ee7777U, 0x8df67b7bU, 0x0dfff2f2U, 0xbdd66b6bU, 0xb1de6f6fU, 0x5491c5c5U, 0x50603030U, 0x03020101U, 0xa9ce6767U, 0x7d562b2bU, 0x19e7fefeU, 0x62b5d7d7U, 0xe64dababU, 0x9aec7676U, 0x458fcacaU, 0x9d1f8282U, 0x4089c9c9U, 0x87fa7d7dU, 0x15effafaU, 0xebb25959U, 0xc98e4747U, 0x0bfbf0f0U, 0xec41adadU, 0x67b3d4d4U, 0xfd5fa2a2U, 0xea45afafU, 0xbf239c9cU, 0xf753a4a4U, 0x96e47272U, 0x5b9bc0c0U, 0xc275b7b7U, 0x1ce1fdfdU, 0xae3d9393U, 0x6a4c2626U, 0x5a6c3636U, 0x417e3f3fU, 0x02f5f7f7U, 0x4f83ccccU, 0x5c683434U, 0xf451a5a5U, 0x34d1e5e5U, 0x08f9f1f1U, 0x93e27171U, 0x73abd8d8U, 0x53623131U, 0x3f2a1515U, 0x0c080404U, 0x5295c7c7U, 0x65462323U, 0x5e9dc3c3U, 0x28301818U, 0xa1379696U, 0x0f0a0505U, 0xb52f9a9aU, 0x090e0707U, 0x36241212U, 0x9b1b8080U, 0x3ddfe2e2U, 0x26cdebebU, 0x694e2727U, 0xcd7fb2b2U, 0x9fea7575U, 0x1b120909U, 0x9e1d8383U, 0x74582c2cU, 0x2e341a1aU, 0x2d361b1bU, 0xb2dc6e6eU, 0xeeb45a5aU, 0xfb5ba0a0U, 0xf6a45252U, 0x4d763b3bU, 0x61b7d6d6U, 0xce7db3b3U, 0x7b522929U, 0x3edde3e3U, 0x715e2f2fU, 0x97138484U, 0xf5a65353U, 0x68b9d1d1U, 0x00000000U, 0x2cc1ededU, 0x60402020U, 0x1fe3fcfcU, 0xc879b1b1U, 0xedb65b5bU, 0xbed46a6aU, 0x468dcbcbU, 0xd967bebeU, 0x4b723939U, 0xde944a4aU, 0xd4984c4cU, 0xe8b05858U, 0x4a85cfcfU, 0x6bbbd0d0U, 0x2ac5efefU, 0xe54faaaaU, 0x16edfbfbU, 0xc5864343U, 0xd79a4d4dU, 0x55663333U, 0x94118585U, 0xcf8a4545U, 0x10e9f9f9U, 0x06040202U, 0x81fe7f7fU, 0xf0a05050U, 0x44783c3cU, 0xba259f9fU, 0xe34ba8a8U, 0xf3a25151U, 0xfe5da3a3U, 0xc0804040U, 0x8a058f8fU, 0xad3f9292U, 0xbc219d9dU, 0x48703838U, 0x04f1f5f5U, 0xdf63bcbcU, 0xc177b6b6U, 0x75afdadaU, 0x63422121U, 0x30201010U, 0x1ae5ffffU, 0x0efdf3f3U, 0x6dbfd2d2U, 0x4c81cdcdU, 0x14180c0cU, 0x35261313U, 0x2fc3ececU, 0xe1be5f5fU, 0xa2359797U, 0xcc884444U, 0x392e1717U, 0x5793c4c4U, 0xf255a7a7U, 0x82fc7e7eU, 0x477a3d3dU, 0xacc86464U, 0xe7ba5d5dU, 0x2b321919U, 0x95e67373U, 0xa0c06060U, 0x98198181U, 0xd19e4f4fU, 0x7fa3dcdcU, 0x66442222U, 0x7e542a2aU, 0xab3b9090U, 0x830b8888U, 0xca8c4646U, 0x29c7eeeeU, 0xd36bb8b8U, 0x3c281414U, 0x79a7dedeU, 0xe2bc5e5eU, 0x1d160b0bU, 0x76addbdbU, 0x3bdbe0e0U, 0x56643232U, 0x4e743a3aU, 0x1e140a0aU, 0xdb924949U, 0x0a0c0606U, 0x6c482424U, 0xe4b85c5cU, 0x5d9fc2c2U, 0x6ebdd3d3U, 0xef43acacU, 0xa6c46262U, 0xa8399191U, 0xa4319595U, 0x37d3e4e4U, 0x8bf27979U, 0x32d5e7e7U, 0x438bc8c8U, 0x596e3737U, 0xb7da6d6dU, 0x8c018d8dU, 0x64b1d5d5U, 0xd29c4e4eU, 0xe049a9a9U, 0xb4d86c6cU, 0xfaac5656U, 0x07f3f4f4U, 0x25cfeaeaU, 0xafca6565U, 0x8ef47a7aU, 0xe947aeaeU, 0x18100808U, 0xd56fbabaU, 0x88f07878U, 0x6f4a2525U, 0x725c2e2eU, 0x24381c1cU, 0xf157a6a6U, 0xc773b4b4U, 0x5197c6c6U, 0x23cbe8e8U, 0x7ca1ddddU, 0x9ce87474U, 0x213e1f1fU, 0xdd964b4bU, 0xdc61bdbdU, 0x860d8b8bU, 0x850f8a8aU, 0x90e07070U, 0x427c3e3eU, 0xc471b5b5U, 0xaacc6666U, 0xd8904848U, 0x05060303U, 0x01f7f6f6U, 0x121c0e0eU, 0xa3c26161U, 0x5f6a3535U, 0xf9ae5757U, 0xd069b9b9U, 0x91178686U, 0x5899c1c1U, 0x273a1d1dU, 0xb9279e9eU, 0x38d9e1e1U, 0x13ebf8f8U, 0xb32b9898U, 0x33221111U, 0xbbd26969U, 0x70a9d9d9U, 0x89078e8eU, 0xa7339494U, 0xb62d9b9bU, 0x223c1e1eU, 0x92158787U, 0x20c9e9e9U, 0x4987ceceU, 0xffaa5555U, 0x78502828U, 0x7aa5dfdfU, 0x8f038c8cU, 0xf859a1a1U, 0x80098989U, 0x171a0d0dU, 0xda65bfbfU, 0x31d7e6e6U, 0xc6844242U, 0xb8d06868U, 0xc3824141U, 0xb0299999U, 0x775a2d2dU, 0x111e0f0fU, 0xcb7bb0b0U, 0xfca85454U, 0xd66dbbbbU, 0x3a2c1616U, }; const u32 Te2[256] = { 0x63a5c663U, 0x7c84f87cU, 0x7799ee77U, 0x7b8df67bU, 0xf20dfff2U, 0x6bbdd66bU, 0x6fb1de6fU, 0xc55491c5U, 0x30506030U, 0x01030201U, 0x67a9ce67U, 0x2b7d562bU, 0xfe19e7feU, 0xd762b5d7U, 0xabe64dabU, 0x769aec76U, 0xca458fcaU, 0x829d1f82U, 0xc94089c9U, 0x7d87fa7dU, 0xfa15effaU, 0x59ebb259U, 0x47c98e47U, 0xf00bfbf0U, 0xadec41adU, 0xd467b3d4U, 0xa2fd5fa2U, 0xafea45afU, 0x9cbf239cU, 0xa4f753a4U, 0x7296e472U, 0xc05b9bc0U, 0xb7c275b7U, 0xfd1ce1fdU, 0x93ae3d93U, 0x266a4c26U, 0x365a6c36U, 0x3f417e3fU, 0xf702f5f7U, 0xcc4f83ccU, 0x345c6834U, 0xa5f451a5U, 0xe534d1e5U, 0xf108f9f1U, 0x7193e271U, 0xd873abd8U, 0x31536231U, 0x153f2a15U, 0x040c0804U, 0xc75295c7U, 0x23654623U, 0xc35e9dc3U, 0x18283018U, 0x96a13796U, 0x050f0a05U, 0x9ab52f9aU, 0x07090e07U, 0x12362412U, 0x809b1b80U, 0xe23ddfe2U, 0xeb26cdebU, 0x27694e27U, 0xb2cd7fb2U, 0x759fea75U, 0x091b1209U, 0x839e1d83U, 0x2c74582cU, 0x1a2e341aU, 0x1b2d361bU, 0x6eb2dc6eU, 0x5aeeb45aU, 0xa0fb5ba0U, 0x52f6a452U, 0x3b4d763bU, 0xd661b7d6U, 0xb3ce7db3U, 0x297b5229U, 0xe33edde3U, 0x2f715e2fU, 0x84971384U, 0x53f5a653U, 0xd168b9d1U, 0x00000000U, 0xed2cc1edU, 0x20604020U, 0xfc1fe3fcU, 0xb1c879b1U, 0x5bedb65bU, 0x6abed46aU, 0xcb468dcbU, 0xbed967beU, 0x394b7239U, 0x4ade944aU, 0x4cd4984cU, 0x58e8b058U, 0xcf4a85cfU, 0xd06bbbd0U, 0xef2ac5efU, 0xaae54faaU, 0xfb16edfbU, 0x43c58643U, 0x4dd79a4dU, 0x33556633U, 0x85941185U, 0x45cf8a45U, 0xf910e9f9U, 0x02060402U, 0x7f81fe7fU, 0x50f0a050U, 0x3c44783cU, 0x9fba259fU, 0xa8e34ba8U, 0x51f3a251U, 0xa3fe5da3U, 0x40c08040U, 0x8f8a058fU, 0x92ad3f92U, 0x9dbc219dU, 0x38487038U, 0xf504f1f5U, 0xbcdf63bcU, 0xb6c177b6U, 0xda75afdaU, 0x21634221U, 0x10302010U, 0xff1ae5ffU, 0xf30efdf3U, 0xd26dbfd2U, 0xcd4c81cdU, 0x0c14180cU, 0x13352613U, 0xec2fc3ecU, 0x5fe1be5fU, 0x97a23597U, 0x44cc8844U, 0x17392e17U, 0xc45793c4U, 0xa7f255a7U, 0x7e82fc7eU, 0x3d477a3dU, 0x64acc864U, 0x5de7ba5dU, 0x192b3219U, 0x7395e673U, 0x60a0c060U, 0x81981981U, 0x4fd19e4fU, 0xdc7fa3dcU, 0x22664422U, 0x2a7e542aU, 0x90ab3b90U, 0x88830b88U, 0x46ca8c46U, 0xee29c7eeU, 0xb8d36bb8U, 0x143c2814U, 0xde79a7deU, 0x5ee2bc5eU, 0x0b1d160bU, 0xdb76addbU, 0xe03bdbe0U, 0x32566432U, 0x3a4e743aU, 0x0a1e140aU, 0x49db9249U, 0x060a0c06U, 0x246c4824U, 0x5ce4b85cU, 0xc25d9fc2U, 0xd36ebdd3U, 0xacef43acU, 0x62a6c462U, 0x91a83991U, 0x95a43195U, 0xe437d3e4U, 0x798bf279U, 0xe732d5e7U, 0xc8438bc8U, 0x37596e37U, 0x6db7da6dU, 0x8d8c018dU, 0xd564b1d5U, 0x4ed29c4eU, 0xa9e049a9U, 0x6cb4d86cU, 0x56faac56U, 0xf407f3f4U, 0xea25cfeaU, 0x65afca65U, 0x7a8ef47aU, 0xaee947aeU, 0x08181008U, 0xbad56fbaU, 0x7888f078U, 0x256f4a25U, 0x2e725c2eU, 0x1c24381cU, 0xa6f157a6U, 0xb4c773b4U, 0xc65197c6U, 0xe823cbe8U, 0xdd7ca1ddU, 0x749ce874U, 0x1f213e1fU, 0x4bdd964bU, 0xbddc61bdU, 0x8b860d8bU, 0x8a850f8aU, 0x7090e070U, 0x3e427c3eU, 0xb5c471b5U, 0x66aacc66U, 0x48d89048U, 0x03050603U, 0xf601f7f6U, 0x0e121c0eU, 0x61a3c261U, 0x355f6a35U, 0x57f9ae57U, 0xb9d069b9U, 0x86911786U, 0xc15899c1U, 0x1d273a1dU, 0x9eb9279eU, 0xe138d9e1U, 0xf813ebf8U, 0x98b32b98U, 0x11332211U, 0x69bbd269U, 0xd970a9d9U, 0x8e89078eU, 0x94a73394U, 0x9bb62d9bU, 0x1e223c1eU, 0x87921587U, 0xe920c9e9U, 0xce4987ceU, 0x55ffaa55U, 0x28785028U, 0xdf7aa5dfU, 0x8c8f038cU, 0xa1f859a1U, 0x89800989U, 0x0d171a0dU, 0xbfda65bfU, 0xe631d7e6U, 0x42c68442U, 0x68b8d068U, 0x41c38241U, 0x99b02999U, 0x2d775a2dU, 0x0f111e0fU, 0xb0cb7bb0U, 0x54fca854U, 0xbbd66dbbU, 0x163a2c16U, }; const u32 Te3[256] = { 0x6363a5c6U, 0x7c7c84f8U, 0x777799eeU, 0x7b7b8df6U, 0xf2f20dffU, 0x6b6bbdd6U, 0x6f6fb1deU, 0xc5c55491U, 0x30305060U, 0x01010302U, 0x6767a9ceU, 0x2b2b7d56U, 0xfefe19e7U, 0xd7d762b5U, 0xababe64dU, 0x76769aecU, 0xcaca458fU, 0x82829d1fU, 0xc9c94089U, 0x7d7d87faU, 0xfafa15efU, 0x5959ebb2U, 0x4747c98eU, 0xf0f00bfbU, 0xadadec41U, 0xd4d467b3U, 0xa2a2fd5fU, 0xafafea45U, 0x9c9cbf23U, 0xa4a4f753U, 0x727296e4U, 0xc0c05b9bU, 0xb7b7c275U, 0xfdfd1ce1U, 0x9393ae3dU, 0x26266a4cU, 0x36365a6cU, 0x3f3f417eU, 0xf7f702f5U, 0xcccc4f83U, 0x34345c68U, 0xa5a5f451U, 0xe5e534d1U, 0xf1f108f9U, 0x717193e2U, 0xd8d873abU, 0x31315362U, 0x15153f2aU, 0x04040c08U, 0xc7c75295U, 0x23236546U, 0xc3c35e9dU, 0x18182830U, 0x9696a137U, 0x05050f0aU, 0x9a9ab52fU, 0x0707090eU, 0x12123624U, 0x80809b1bU, 0xe2e23ddfU, 0xebeb26cdU, 0x2727694eU, 0xb2b2cd7fU, 0x75759feaU, 0x09091b12U, 0x83839e1dU, 0x2c2c7458U, 0x1a1a2e34U, 0x1b1b2d36U, 0x6e6eb2dcU, 0x5a5aeeb4U, 0xa0a0fb5bU, 0x5252f6a4U, 0x3b3b4d76U, 0xd6d661b7U, 0xb3b3ce7dU, 0x29297b52U, 0xe3e33eddU, 0x2f2f715eU, 0x84849713U, 0x5353f5a6U, 0xd1d168b9U, 0x00000000U, 0xeded2cc1U, 0x20206040U, 0xfcfc1fe3U, 0xb1b1c879U, 0x5b5bedb6U, 0x6a6abed4U, 0xcbcb468dU, 0xbebed967U, 0x39394b72U, 0x4a4ade94U, 0x4c4cd498U, 0x5858e8b0U, 0xcfcf4a85U, 0xd0d06bbbU, 0xefef2ac5U, 0xaaaae54fU, 0xfbfb16edU, 0x4343c586U, 0x4d4dd79aU, 0x33335566U, 0x85859411U, 0x4545cf8aU, 0xf9f910e9U, 0x02020604U, 0x7f7f81feU, 0x5050f0a0U, 0x3c3c4478U, 0x9f9fba25U, 0xa8a8e34bU, 0x5151f3a2U, 0xa3a3fe5dU, 0x4040c080U, 0x8f8f8a05U, 0x9292ad3fU, 0x9d9dbc21U, 0x38384870U, 0xf5f504f1U, 0xbcbcdf63U, 0xb6b6c177U, 0xdada75afU, 0x21216342U, 0x10103020U, 0xffff1ae5U, 0xf3f30efdU, 0xd2d26dbfU, 0xcdcd4c81U, 0x0c0c1418U, 0x13133526U, 0xecec2fc3U, 0x5f5fe1beU, 0x9797a235U, 0x4444cc88U, 0x1717392eU, 0xc4c45793U, 0xa7a7f255U, 0x7e7e82fcU, 0x3d3d477aU, 0x6464acc8U, 0x5d5de7baU, 0x19192b32U, 0x737395e6U, 0x6060a0c0U, 0x81819819U, 0x4f4fd19eU, 0xdcdc7fa3U, 0x22226644U, 0x2a2a7e54U, 0x9090ab3bU, 0x8888830bU, 0x4646ca8cU, 0xeeee29c7U, 0xb8b8d36bU, 0x14143c28U, 0xdede79a7U, 0x5e5ee2bcU, 0x0b0b1d16U, 0xdbdb76adU, 0xe0e03bdbU, 0x32325664U, 0x3a3a4e74U, 0x0a0a1e14U, 0x4949db92U, 0x06060a0cU, 0x24246c48U, 0x5c5ce4b8U, 0xc2c25d9fU, 0xd3d36ebdU, 0xacacef43U, 0x6262a6c4U, 0x9191a839U, 0x9595a431U, 0xe4e437d3U, 0x79798bf2U, 0xe7e732d5U, 0xc8c8438bU, 0x3737596eU, 0x6d6db7daU, 0x8d8d8c01U, 0xd5d564b1U, 0x4e4ed29cU, 0xa9a9e049U, 0x6c6cb4d8U, 0x5656faacU, 0xf4f407f3U, 0xeaea25cfU, 0x6565afcaU, 0x7a7a8ef4U, 0xaeaee947U, 0x08081810U, 0xbabad56fU, 0x787888f0U, 0x25256f4aU, 0x2e2e725cU, 0x1c1c2438U, 0xa6a6f157U, 0xb4b4c773U, 0xc6c65197U, 0xe8e823cbU, 0xdddd7ca1U, 0x74749ce8U, 0x1f1f213eU, 0x4b4bdd96U, 0xbdbddc61U, 0x8b8b860dU, 0x8a8a850fU, 0x707090e0U, 0x3e3e427cU, 0xb5b5c471U, 0x6666aaccU, 0x4848d890U, 0x03030506U, 0xf6f601f7U, 0x0e0e121cU, 0x6161a3c2U, 0x35355f6aU, 0x5757f9aeU, 0xb9b9d069U, 0x86869117U, 0xc1c15899U, 0x1d1d273aU, 0x9e9eb927U, 0xe1e138d9U, 0xf8f813ebU, 0x9898b32bU, 0x11113322U, 0x6969bbd2U, 0xd9d970a9U, 0x8e8e8907U, 0x9494a733U, 0x9b9bb62dU, 0x1e1e223cU, 0x87879215U, 0xe9e920c9U, 0xcece4987U, 0x5555ffaaU, 0x28287850U, 0xdfdf7aa5U, 0x8c8c8f03U, 0xa1a1f859U, 0x89898009U, 0x0d0d171aU, 0xbfbfda65U, 0xe6e631d7U, 0x4242c684U, 0x6868b8d0U, 0x4141c382U, 0x9999b029U, 0x2d2d775aU, 0x0f0f111eU, 0xb0b0cb7bU, 0x5454fca8U, 0xbbbbd66dU, 0x16163a2cU, }; const u32 Te4[256] = { 0x63636363U, 0x7c7c7c7cU, 0x77777777U, 0x7b7b7b7bU, 0xf2f2f2f2U, 0x6b6b6b6bU, 0x6f6f6f6fU, 0xc5c5c5c5U, 0x30303030U, 0x01010101U, 0x67676767U, 0x2b2b2b2bU, 0xfefefefeU, 0xd7d7d7d7U, 0xababababU, 0x76767676U, 0xcacacacaU, 0x82828282U, 0xc9c9c9c9U, 0x7d7d7d7dU, 0xfafafafaU, 0x59595959U, 0x47474747U, 0xf0f0f0f0U, 0xadadadadU, 0xd4d4d4d4U, 0xa2a2a2a2U, 0xafafafafU, 0x9c9c9c9cU, 0xa4a4a4a4U, 0x72727272U, 0xc0c0c0c0U, 0xb7b7b7b7U, 0xfdfdfdfdU, 0x93939393U, 0x26262626U, 0x36363636U, 0x3f3f3f3fU, 0xf7f7f7f7U, 0xccccccccU, 0x34343434U, 0xa5a5a5a5U, 0xe5e5e5e5U, 0xf1f1f1f1U, 0x71717171U, 0xd8d8d8d8U, 0x31313131U, 0x15151515U, 0x04040404U, 0xc7c7c7c7U, 0x23232323U, 0xc3c3c3c3U, 0x18181818U, 0x96969696U, 0x05050505U, 0x9a9a9a9aU, 0x07070707U, 0x12121212U, 0x80808080U, 0xe2e2e2e2U, 0xebebebebU, 0x27272727U, 0xb2b2b2b2U, 0x75757575U, 0x09090909U, 0x83838383U, 0x2c2c2c2cU, 0x1a1a1a1aU, 0x1b1b1b1bU, 0x6e6e6e6eU, 0x5a5a5a5aU, 0xa0a0a0a0U, 0x52525252U, 0x3b3b3b3bU, 0xd6d6d6d6U, 0xb3b3b3b3U, 0x29292929U, 0xe3e3e3e3U, 0x2f2f2f2fU, 0x84848484U, 0x53535353U, 0xd1d1d1d1U, 0x00000000U, 0xededededU, 0x20202020U, 0xfcfcfcfcU, 0xb1b1b1b1U, 0x5b5b5b5bU, 0x6a6a6a6aU, 0xcbcbcbcbU, 0xbebebebeU, 0x39393939U, 0x4a4a4a4aU, 0x4c4c4c4cU, 0x58585858U, 0xcfcfcfcfU, 0xd0d0d0d0U, 0xefefefefU, 0xaaaaaaaaU, 0xfbfbfbfbU, 0x43434343U, 0x4d4d4d4dU, 0x33333333U, 0x85858585U, 0x45454545U, 0xf9f9f9f9U, 0x02020202U, 0x7f7f7f7fU, 0x50505050U, 0x3c3c3c3cU, 0x9f9f9f9fU, 0xa8a8a8a8U, 0x51515151U, 0xa3a3a3a3U, 0x40404040U, 0x8f8f8f8fU, 0x92929292U, 0x9d9d9d9dU, 0x38383838U, 0xf5f5f5f5U, 0xbcbcbcbcU, 0xb6b6b6b6U, 0xdadadadaU, 0x21212121U, 0x10101010U, 0xffffffffU, 0xf3f3f3f3U, 0xd2d2d2d2U, 0xcdcdcdcdU, 0x0c0c0c0cU, 0x13131313U, 0xececececU, 0x5f5f5f5fU, 0x97979797U, 0x44444444U, 0x17171717U, 0xc4c4c4c4U, 0xa7a7a7a7U, 0x7e7e7e7eU, 0x3d3d3d3dU, 0x64646464U, 0x5d5d5d5dU, 0x19191919U, 0x73737373U, 0x60606060U, 0x81818181U, 0x4f4f4f4fU, 0xdcdcdcdcU, 0x22222222U, 0x2a2a2a2aU, 0x90909090U, 0x88888888U, 0x46464646U, 0xeeeeeeeeU, 0xb8b8b8b8U, 0x14141414U, 0xdedededeU, 0x5e5e5e5eU, 0x0b0b0b0bU, 0xdbdbdbdbU, 0xe0e0e0e0U, 0x32323232U, 0x3a3a3a3aU, 0x0a0a0a0aU, 0x49494949U, 0x06060606U, 0x24242424U, 0x5c5c5c5cU, 0xc2c2c2c2U, 0xd3d3d3d3U, 0xacacacacU, 0x62626262U, 0x91919191U, 0x95959595U, 0xe4e4e4e4U, 0x79797979U, 0xe7e7e7e7U, 0xc8c8c8c8U, 0x37373737U, 0x6d6d6d6dU, 0x8d8d8d8dU, 0xd5d5d5d5U, 0x4e4e4e4eU, 0xa9a9a9a9U, 0x6c6c6c6cU, 0x56565656U, 0xf4f4f4f4U, 0xeaeaeaeaU, 0x65656565U, 0x7a7a7a7aU, 0xaeaeaeaeU, 0x08080808U, 0xbabababaU, 0x78787878U, 0x25252525U, 0x2e2e2e2eU, 0x1c1c1c1cU, 0xa6a6a6a6U, 0xb4b4b4b4U, 0xc6c6c6c6U, 0xe8e8e8e8U, 0xddddddddU, 0x74747474U, 0x1f1f1f1fU, 0x4b4b4b4bU, 0xbdbdbdbdU, 0x8b8b8b8bU, 0x8a8a8a8aU, 0x70707070U, 0x3e3e3e3eU, 0xb5b5b5b5U, 0x66666666U, 0x48484848U, 0x03030303U, 0xf6f6f6f6U, 0x0e0e0e0eU, 0x61616161U, 0x35353535U, 0x57575757U, 0xb9b9b9b9U, 0x86868686U, 0xc1c1c1c1U, 0x1d1d1d1dU, 0x9e9e9e9eU, 0xe1e1e1e1U, 0xf8f8f8f8U, 0x98989898U, 0x11111111U, 0x69696969U, 0xd9d9d9d9U, 0x8e8e8e8eU, 0x94949494U, 0x9b9b9b9bU, 0x1e1e1e1eU, 0x87878787U, 0xe9e9e9e9U, 0xcecececeU, 0x55555555U, 0x28282828U, 0xdfdfdfdfU, 0x8c8c8c8cU, 0xa1a1a1a1U, 0x89898989U, 0x0d0d0d0dU, 0xbfbfbfbfU, 0xe6e6e6e6U, 0x42424242U, 0x68686868U, 0x41414141U, 0x99999999U, 0x2d2d2d2dU, 0x0f0f0f0fU, 0xb0b0b0b0U, 0x54545454U, 0xbbbbbbbbU, 0x16161616U, }; #endif /* AES_SMALL_TABLES */ const u32 Td0[256] = { 0x51f4a750U, 0x7e416553U, 0x1a17a4c3U, 0x3a275e96U, 0x3bab6bcbU, 0x1f9d45f1U, 0xacfa58abU, 0x4be30393U, 0x2030fa55U, 0xad766df6U, 0x88cc7691U, 0xf5024c25U, 0x4fe5d7fcU, 0xc52acbd7U, 0x26354480U, 0xb562a38fU, 0xdeb15a49U, 0x25ba1b67U, 0x45ea0e98U, 0x5dfec0e1U, 0xc32f7502U, 0x814cf012U, 0x8d4697a3U, 0x6bd3f9c6U, 0x038f5fe7U, 0x15929c95U, 0xbf6d7aebU, 0x955259daU, 0xd4be832dU, 0x587421d3U, 0x49e06929U, 0x8ec9c844U, 0x75c2896aU, 0xf48e7978U, 0x99583e6bU, 0x27b971ddU, 0xbee14fb6U, 0xf088ad17U, 0xc920ac66U, 0x7dce3ab4U, 0x63df4a18U, 0xe51a3182U, 0x97513360U, 0x62537f45U, 0xb16477e0U, 0xbb6bae84U, 0xfe81a01cU, 0xf9082b94U, 0x70486858U, 0x8f45fd19U, 0x94de6c87U, 0x527bf8b7U, 0xab73d323U, 0x724b02e2U, 0xe31f8f57U, 0x6655ab2aU, 0xb2eb2807U, 0x2fb5c203U, 0x86c57b9aU, 0xd33708a5U, 0x302887f2U, 0x23bfa5b2U, 0x02036abaU, 0xed16825cU, 0x8acf1c2bU, 0xa779b492U, 0xf307f2f0U, 0x4e69e2a1U, 0x65daf4cdU, 0x0605bed5U, 0xd134621fU, 0xc4a6fe8aU, 0x342e539dU, 0xa2f355a0U, 0x058ae132U, 0xa4f6eb75U, 0x0b83ec39U, 0x4060efaaU, 0x5e719f06U, 0xbd6e1051U, 0x3e218af9U, 0x96dd063dU, 0xdd3e05aeU, 0x4de6bd46U, 0x91548db5U, 0x71c45d05U, 0x0406d46fU, 0x605015ffU, 0x1998fb24U, 0xd6bde997U, 0x894043ccU, 0x67d99e77U, 0xb0e842bdU, 0x07898b88U, 0xe7195b38U, 0x79c8eedbU, 0xa17c0a47U, 0x7c420fe9U, 0xf8841ec9U, 0x00000000U, 0x09808683U, 0x322bed48U, 0x1e1170acU, 0x6c5a724eU, 0xfd0efffbU, 0x0f853856U, 0x3daed51eU, 0x362d3927U, 0x0a0fd964U, 0x685ca621U, 0x9b5b54d1U, 0x24362e3aU, 0x0c0a67b1U, 0x9357e70fU, 0xb4ee96d2U, 0x1b9b919eU, 0x80c0c54fU, 0x61dc20a2U, 0x5a774b69U, 0x1c121a16U, 0xe293ba0aU, 0xc0a02ae5U, 0x3c22e043U, 0x121b171dU, 0x0e090d0bU, 0xf28bc7adU, 0x2db6a8b9U, 0x141ea9c8U, 0x57f11985U, 0xaf75074cU, 0xee99ddbbU, 0xa37f60fdU, 0xf701269fU, 0x5c72f5bcU, 0x44663bc5U, 0x5bfb7e34U, 0x8b432976U, 0xcb23c6dcU, 0xb6edfc68U, 0xb8e4f163U, 0xd731dccaU, 0x42638510U, 0x13972240U, 0x84c61120U, 0x854a247dU, 0xd2bb3df8U, 0xaef93211U, 0xc729a16dU, 0x1d9e2f4bU, 0xdcb230f3U, 0x0d8652ecU, 0x77c1e3d0U, 0x2bb3166cU, 0xa970b999U, 0x119448faU, 0x47e96422U, 0xa8fc8cc4U, 0xa0f03f1aU, 0x567d2cd8U, 0x223390efU, 0x87494ec7U, 0xd938d1c1U, 0x8ccaa2feU, 0x98d40b36U, 0xa6f581cfU, 0xa57ade28U, 0xdab78e26U, 0x3fadbfa4U, 0x2c3a9de4U, 0x5078920dU, 0x6a5fcc9bU, 0x547e4662U, 0xf68d13c2U, 0x90d8b8e8U, 0x2e39f75eU, 0x82c3aff5U, 0x9f5d80beU, 0x69d0937cU, 0x6fd52da9U, 0xcf2512b3U, 0xc8ac993bU, 0x10187da7U, 0xe89c636eU, 0xdb3bbb7bU, 0xcd267809U, 0x6e5918f4U, 0xec9ab701U, 0x834f9aa8U, 0xe6956e65U, 0xaaffe67eU, 0x21bccf08U, 0xef15e8e6U, 0xbae79bd9U, 0x4a6f36ceU, 0xea9f09d4U, 0x29b07cd6U, 0x31a4b2afU, 0x2a3f2331U, 0xc6a59430U, 0x35a266c0U, 0x744ebc37U, 0xfc82caa6U, 0xe090d0b0U, 0x33a7d815U, 0xf104984aU, 0x41ecdaf7U, 0x7fcd500eU, 0x1791f62fU, 0x764dd68dU, 0x43efb04dU, 0xccaa4d54U, 0xe49604dfU, 0x9ed1b5e3U, 0x4c6a881bU, 0xc12c1fb8U, 0x4665517fU, 0x9d5eea04U, 0x018c355dU, 0xfa877473U, 0xfb0b412eU, 0xb3671d5aU, 0x92dbd252U, 0xe9105633U, 0x6dd64713U, 0x9ad7618cU, 0x37a10c7aU, 0x59f8148eU, 0xeb133c89U, 0xcea927eeU, 0xb761c935U, 0xe11ce5edU, 0x7a47b13cU, 0x9cd2df59U, 0x55f2733fU, 0x1814ce79U, 0x73c737bfU, 0x53f7cdeaU, 0x5ffdaa5bU, 0xdf3d6f14U, 0x7844db86U, 0xcaaff381U, 0xb968c43eU, 0x3824342cU, 0xc2a3405fU, 0x161dc372U, 0xbce2250cU, 0x283c498bU, 0xff0d9541U, 0x39a80171U, 0x080cb3deU, 0xd8b4e49cU, 0x6456c190U, 0x7bcb8461U, 0xd532b670U, 0x486c5c74U, 0xd0b85742U, }; #ifndef AES_SMALL_TABLES const u32 Td1[256] = { 0x5051f4a7U, 0x537e4165U, 0xc31a17a4U, 0x963a275eU, 0xcb3bab6bU, 0xf11f9d45U, 0xabacfa58U, 0x934be303U, 0x552030faU, 0xf6ad766dU, 0x9188cc76U, 0x25f5024cU, 0xfc4fe5d7U, 0xd7c52acbU, 0x80263544U, 0x8fb562a3U, 0x49deb15aU, 0x6725ba1bU, 0x9845ea0eU, 0xe15dfec0U, 0x02c32f75U, 0x12814cf0U, 0xa38d4697U, 0xc66bd3f9U, 0xe7038f5fU, 0x9515929cU, 0xebbf6d7aU, 0xda955259U, 0x2dd4be83U, 0xd3587421U, 0x2949e069U, 0x448ec9c8U, 0x6a75c289U, 0x78f48e79U, 0x6b99583eU, 0xdd27b971U, 0xb6bee14fU, 0x17f088adU, 0x66c920acU, 0xb47dce3aU, 0x1863df4aU, 0x82e51a31U, 0x60975133U, 0x4562537fU, 0xe0b16477U, 0x84bb6baeU, 0x1cfe81a0U, 0x94f9082bU, 0x58704868U, 0x198f45fdU, 0x8794de6cU, 0xb7527bf8U, 0x23ab73d3U, 0xe2724b02U, 0x57e31f8fU, 0x2a6655abU, 0x07b2eb28U, 0x032fb5c2U, 0x9a86c57bU, 0xa5d33708U, 0xf2302887U, 0xb223bfa5U, 0xba02036aU, 0x5ced1682U, 0x2b8acf1cU, 0x92a779b4U, 0xf0f307f2U, 0xa14e69e2U, 0xcd65daf4U, 0xd50605beU, 0x1fd13462U, 0x8ac4a6feU, 0x9d342e53U, 0xa0a2f355U, 0x32058ae1U, 0x75a4f6ebU, 0x390b83ecU, 0xaa4060efU, 0x065e719fU, 0x51bd6e10U, 0xf93e218aU, 0x3d96dd06U, 0xaedd3e05U, 0x464de6bdU, 0xb591548dU, 0x0571c45dU, 0x6f0406d4U, 0xff605015U, 0x241998fbU, 0x97d6bde9U, 0xcc894043U, 0x7767d99eU, 0xbdb0e842U, 0x8807898bU, 0x38e7195bU, 0xdb79c8eeU, 0x47a17c0aU, 0xe97c420fU, 0xc9f8841eU, 0x00000000U, 0x83098086U, 0x48322bedU, 0xac1e1170U, 0x4e6c5a72U, 0xfbfd0effU, 0x560f8538U, 0x1e3daed5U, 0x27362d39U, 0x640a0fd9U, 0x21685ca6U, 0xd19b5b54U, 0x3a24362eU, 0xb10c0a67U, 0x0f9357e7U, 0xd2b4ee96U, 0x9e1b9b91U, 0x4f80c0c5U, 0xa261dc20U, 0x695a774bU, 0x161c121aU, 0x0ae293baU, 0xe5c0a02aU, 0x433c22e0U, 0x1d121b17U, 0x0b0e090dU, 0xadf28bc7U, 0xb92db6a8U, 0xc8141ea9U, 0x8557f119U, 0x4caf7507U, 0xbbee99ddU, 0xfda37f60U, 0x9ff70126U, 0xbc5c72f5U, 0xc544663bU, 0x345bfb7eU, 0x768b4329U, 0xdccb23c6U, 0x68b6edfcU, 0x63b8e4f1U, 0xcad731dcU, 0x10426385U, 0x40139722U, 0x2084c611U, 0x7d854a24U, 0xf8d2bb3dU, 0x11aef932U, 0x6dc729a1U, 0x4b1d9e2fU, 0xf3dcb230U, 0xec0d8652U, 0xd077c1e3U, 0x6c2bb316U, 0x99a970b9U, 0xfa119448U, 0x2247e964U, 0xc4a8fc8cU, 0x1aa0f03fU, 0xd8567d2cU, 0xef223390U, 0xc787494eU, 0xc1d938d1U, 0xfe8ccaa2U, 0x3698d40bU, 0xcfa6f581U, 0x28a57adeU, 0x26dab78eU, 0xa43fadbfU, 0xe42c3a9dU, 0x0d507892U, 0x9b6a5fccU, 0x62547e46U, 0xc2f68d13U, 0xe890d8b8U, 0x5e2e39f7U, 0xf582c3afU, 0xbe9f5d80U, 0x7c69d093U, 0xa96fd52dU, 0xb3cf2512U, 0x3bc8ac99U, 0xa710187dU, 0x6ee89c63U, 0x7bdb3bbbU, 0x09cd2678U, 0xf46e5918U, 0x01ec9ab7U, 0xa8834f9aU, 0x65e6956eU, 0x7eaaffe6U, 0x0821bccfU, 0xe6ef15e8U, 0xd9bae79bU, 0xce4a6f36U, 0xd4ea9f09U, 0xd629b07cU, 0xaf31a4b2U, 0x312a3f23U, 0x30c6a594U, 0xc035a266U, 0x37744ebcU, 0xa6fc82caU, 0xb0e090d0U, 0x1533a7d8U, 0x4af10498U, 0xf741ecdaU, 0x0e7fcd50U, 0x2f1791f6U, 0x8d764dd6U, 0x4d43efb0U, 0x54ccaa4dU, 0xdfe49604U, 0xe39ed1b5U, 0x1b4c6a88U, 0xb8c12c1fU, 0x7f466551U, 0x049d5eeaU, 0x5d018c35U, 0x73fa8774U, 0x2efb0b41U, 0x5ab3671dU, 0x5292dbd2U, 0x33e91056U, 0x136dd647U, 0x8c9ad761U, 0x7a37a10cU, 0x8e59f814U, 0x89eb133cU, 0xeecea927U, 0x35b761c9U, 0xede11ce5U, 0x3c7a47b1U, 0x599cd2dfU, 0x3f55f273U, 0x791814ceU, 0xbf73c737U, 0xea53f7cdU, 0x5b5ffdaaU, 0x14df3d6fU, 0x867844dbU, 0x81caaff3U, 0x3eb968c4U, 0x2c382434U, 0x5fc2a340U, 0x72161dc3U, 0x0cbce225U, 0x8b283c49U, 0x41ff0d95U, 0x7139a801U, 0xde080cb3U, 0x9cd8b4e4U, 0x906456c1U, 0x617bcb84U, 0x70d532b6U, 0x74486c5cU, 0x42d0b857U, }; const u32 Td2[256] = { 0xa75051f4U, 0x65537e41U, 0xa4c31a17U, 0x5e963a27U, 0x6bcb3babU, 0x45f11f9dU, 0x58abacfaU, 0x03934be3U, 0xfa552030U, 0x6df6ad76U, 0x769188ccU, 0x4c25f502U, 0xd7fc4fe5U, 0xcbd7c52aU, 0x44802635U, 0xa38fb562U, 0x5a49deb1U, 0x1b6725baU, 0x0e9845eaU, 0xc0e15dfeU, 0x7502c32fU, 0xf012814cU, 0x97a38d46U, 0xf9c66bd3U, 0x5fe7038fU, 0x9c951592U, 0x7aebbf6dU, 0x59da9552U, 0x832dd4beU, 0x21d35874U, 0x692949e0U, 0xc8448ec9U, 0x896a75c2U, 0x7978f48eU, 0x3e6b9958U, 0x71dd27b9U, 0x4fb6bee1U, 0xad17f088U, 0xac66c920U, 0x3ab47dceU, 0x4a1863dfU, 0x3182e51aU, 0x33609751U, 0x7f456253U, 0x77e0b164U, 0xae84bb6bU, 0xa01cfe81U, 0x2b94f908U, 0x68587048U, 0xfd198f45U, 0x6c8794deU, 0xf8b7527bU, 0xd323ab73U, 0x02e2724bU, 0x8f57e31fU, 0xab2a6655U, 0x2807b2ebU, 0xc2032fb5U, 0x7b9a86c5U, 0x08a5d337U, 0x87f23028U, 0xa5b223bfU, 0x6aba0203U, 0x825ced16U, 0x1c2b8acfU, 0xb492a779U, 0xf2f0f307U, 0xe2a14e69U, 0xf4cd65daU, 0xbed50605U, 0x621fd134U, 0xfe8ac4a6U, 0x539d342eU, 0x55a0a2f3U, 0xe132058aU, 0xeb75a4f6U, 0xec390b83U, 0xefaa4060U, 0x9f065e71U, 0x1051bd6eU, 0x8af93e21U, 0x063d96ddU, 0x05aedd3eU, 0xbd464de6U, 0x8db59154U, 0x5d0571c4U, 0xd46f0406U, 0x15ff6050U, 0xfb241998U, 0xe997d6bdU, 0x43cc8940U, 0x9e7767d9U, 0x42bdb0e8U, 0x8b880789U, 0x5b38e719U, 0xeedb79c8U, 0x0a47a17cU, 0x0fe97c42U, 0x1ec9f884U, 0x00000000U, 0x86830980U, 0xed48322bU, 0x70ac1e11U, 0x724e6c5aU, 0xfffbfd0eU, 0x38560f85U, 0xd51e3daeU, 0x3927362dU, 0xd9640a0fU, 0xa621685cU, 0x54d19b5bU, 0x2e3a2436U, 0x67b10c0aU, 0xe70f9357U, 0x96d2b4eeU, 0x919e1b9bU, 0xc54f80c0U, 0x20a261dcU, 0x4b695a77U, 0x1a161c12U, 0xba0ae293U, 0x2ae5c0a0U, 0xe0433c22U, 0x171d121bU, 0x0d0b0e09U, 0xc7adf28bU, 0xa8b92db6U, 0xa9c8141eU, 0x198557f1U, 0x074caf75U, 0xddbbee99U, 0x60fda37fU, 0x269ff701U, 0xf5bc5c72U, 0x3bc54466U, 0x7e345bfbU, 0x29768b43U, 0xc6dccb23U, 0xfc68b6edU, 0xf163b8e4U, 0xdccad731U, 0x85104263U, 0x22401397U, 0x112084c6U, 0x247d854aU, 0x3df8d2bbU, 0x3211aef9U, 0xa16dc729U, 0x2f4b1d9eU, 0x30f3dcb2U, 0x52ec0d86U, 0xe3d077c1U, 0x166c2bb3U, 0xb999a970U, 0x48fa1194U, 0x642247e9U, 0x8cc4a8fcU, 0x3f1aa0f0U, 0x2cd8567dU, 0x90ef2233U, 0x4ec78749U, 0xd1c1d938U, 0xa2fe8ccaU, 0x0b3698d4U, 0x81cfa6f5U, 0xde28a57aU, 0x8e26dab7U, 0xbfa43fadU, 0x9de42c3aU, 0x920d5078U, 0xcc9b6a5fU, 0x4662547eU, 0x13c2f68dU, 0xb8e890d8U, 0xf75e2e39U, 0xaff582c3U, 0x80be9f5dU, 0x937c69d0U, 0x2da96fd5U, 0x12b3cf25U, 0x993bc8acU, 0x7da71018U, 0x636ee89cU, 0xbb7bdb3bU, 0x7809cd26U, 0x18f46e59U, 0xb701ec9aU, 0x9aa8834fU, 0x6e65e695U, 0xe67eaaffU, 0xcf0821bcU, 0xe8e6ef15U, 0x9bd9bae7U, 0x36ce4a6fU, 0x09d4ea9fU, 0x7cd629b0U, 0xb2af31a4U, 0x23312a3fU, 0x9430c6a5U, 0x66c035a2U, 0xbc37744eU, 0xcaa6fc82U, 0xd0b0e090U, 0xd81533a7U, 0x984af104U, 0xdaf741ecU, 0x500e7fcdU, 0xf62f1791U, 0xd68d764dU, 0xb04d43efU, 0x4d54ccaaU, 0x04dfe496U, 0xb5e39ed1U, 0x881b4c6aU, 0x1fb8c12cU, 0x517f4665U, 0xea049d5eU, 0x355d018cU, 0x7473fa87U, 0x412efb0bU, 0x1d5ab367U, 0xd25292dbU, 0x5633e910U, 0x47136dd6U, 0x618c9ad7U, 0x0c7a37a1U, 0x148e59f8U, 0x3c89eb13U, 0x27eecea9U, 0xc935b761U, 0xe5ede11cU, 0xb13c7a47U, 0xdf599cd2U, 0x733f55f2U, 0xce791814U, 0x37bf73c7U, 0xcdea53f7U, 0xaa5b5ffdU, 0x6f14df3dU, 0xdb867844U, 0xf381caafU, 0xc43eb968U, 0x342c3824U, 0x405fc2a3U, 0xc372161dU, 0x250cbce2U, 0x498b283cU, 0x9541ff0dU, 0x017139a8U, 0xb3de080cU, 0xe49cd8b4U, 0xc1906456U, 0x84617bcbU, 0xb670d532U, 0x5c74486cU, 0x5742d0b8U, }; const u32 Td3[256] = { 0xf4a75051U, 0x4165537eU, 0x17a4c31aU, 0x275e963aU, 0xab6bcb3bU, 0x9d45f11fU, 0xfa58abacU, 0xe303934bU, 0x30fa5520U, 0x766df6adU, 0xcc769188U, 0x024c25f5U, 0xe5d7fc4fU, 0x2acbd7c5U, 0x35448026U, 0x62a38fb5U, 0xb15a49deU, 0xba1b6725U, 0xea0e9845U, 0xfec0e15dU, 0x2f7502c3U, 0x4cf01281U, 0x4697a38dU, 0xd3f9c66bU, 0x8f5fe703U, 0x929c9515U, 0x6d7aebbfU, 0x5259da95U, 0xbe832dd4U, 0x7421d358U, 0xe0692949U, 0xc9c8448eU, 0xc2896a75U, 0x8e7978f4U, 0x583e6b99U, 0xb971dd27U, 0xe14fb6beU, 0x88ad17f0U, 0x20ac66c9U, 0xce3ab47dU, 0xdf4a1863U, 0x1a3182e5U, 0x51336097U, 0x537f4562U, 0x6477e0b1U, 0x6bae84bbU, 0x81a01cfeU, 0x082b94f9U, 0x48685870U, 0x45fd198fU, 0xde6c8794U, 0x7bf8b752U, 0x73d323abU, 0x4b02e272U, 0x1f8f57e3U, 0x55ab2a66U, 0xeb2807b2U, 0xb5c2032fU, 0xc57b9a86U, 0x3708a5d3U, 0x2887f230U, 0xbfa5b223U, 0x036aba02U, 0x16825cedU, 0xcf1c2b8aU, 0x79b492a7U, 0x07f2f0f3U, 0x69e2a14eU, 0xdaf4cd65U, 0x05bed506U, 0x34621fd1U, 0xa6fe8ac4U, 0x2e539d34U, 0xf355a0a2U, 0x8ae13205U, 0xf6eb75a4U, 0x83ec390bU, 0x60efaa40U, 0x719f065eU, 0x6e1051bdU, 0x218af93eU, 0xdd063d96U, 0x3e05aeddU, 0xe6bd464dU, 0x548db591U, 0xc45d0571U, 0x06d46f04U, 0x5015ff60U, 0x98fb2419U, 0xbde997d6U, 0x4043cc89U, 0xd99e7767U, 0xe842bdb0U, 0x898b8807U, 0x195b38e7U, 0xc8eedb79U, 0x7c0a47a1U, 0x420fe97cU, 0x841ec9f8U, 0x00000000U, 0x80868309U, 0x2bed4832U, 0x1170ac1eU, 0x5a724e6cU, 0x0efffbfdU, 0x8538560fU, 0xaed51e3dU, 0x2d392736U, 0x0fd9640aU, 0x5ca62168U, 0x5b54d19bU, 0x362e3a24U, 0x0a67b10cU, 0x57e70f93U, 0xee96d2b4U, 0x9b919e1bU, 0xc0c54f80U, 0xdc20a261U, 0x774b695aU, 0x121a161cU, 0x93ba0ae2U, 0xa02ae5c0U, 0x22e0433cU, 0x1b171d12U, 0x090d0b0eU, 0x8bc7adf2U, 0xb6a8b92dU, 0x1ea9c814U, 0xf1198557U, 0x75074cafU, 0x99ddbbeeU, 0x7f60fda3U, 0x01269ff7U, 0x72f5bc5cU, 0x663bc544U, 0xfb7e345bU, 0x4329768bU, 0x23c6dccbU, 0xedfc68b6U, 0xe4f163b8U, 0x31dccad7U, 0x63851042U, 0x97224013U, 0xc6112084U, 0x4a247d85U, 0xbb3df8d2U, 0xf93211aeU, 0x29a16dc7U, 0x9e2f4b1dU, 0xb230f3dcU, 0x8652ec0dU, 0xc1e3d077U, 0xb3166c2bU, 0x70b999a9U, 0x9448fa11U, 0xe9642247U, 0xfc8cc4a8U, 0xf03f1aa0U, 0x7d2cd856U, 0x3390ef22U, 0x494ec787U, 0x38d1c1d9U, 0xcaa2fe8cU, 0xd40b3698U, 0xf581cfa6U, 0x7ade28a5U, 0xb78e26daU, 0xadbfa43fU, 0x3a9de42cU, 0x78920d50U, 0x5fcc9b6aU, 0x7e466254U, 0x8d13c2f6U, 0xd8b8e890U, 0x39f75e2eU, 0xc3aff582U, 0x5d80be9fU, 0xd0937c69U, 0xd52da96fU, 0x2512b3cfU, 0xac993bc8U, 0x187da710U, 0x9c636ee8U, 0x3bbb7bdbU, 0x267809cdU, 0x5918f46eU, 0x9ab701ecU, 0x4f9aa883U, 0x956e65e6U, 0xffe67eaaU, 0xbccf0821U, 0x15e8e6efU, 0xe79bd9baU, 0x6f36ce4aU, 0x9f09d4eaU, 0xb07cd629U, 0xa4b2af31U, 0x3f23312aU, 0xa59430c6U, 0xa266c035U, 0x4ebc3774U, 0x82caa6fcU, 0x90d0b0e0U, 0xa7d81533U, 0x04984af1U, 0xecdaf741U, 0xcd500e7fU, 0x91f62f17U, 0x4dd68d76U, 0xefb04d43U, 0xaa4d54ccU, 0x9604dfe4U, 0xd1b5e39eU, 0x6a881b4cU, 0x2c1fb8c1U, 0x65517f46U, 0x5eea049dU, 0x8c355d01U, 0x877473faU, 0x0b412efbU, 0x671d5ab3U, 0xdbd25292U, 0x105633e9U, 0xd647136dU, 0xd7618c9aU, 0xa10c7a37U, 0xf8148e59U, 0x133c89ebU, 0xa927eeceU, 0x61c935b7U, 0x1ce5ede1U, 0x47b13c7aU, 0xd2df599cU, 0xf2733f55U, 0x14ce7918U, 0xc737bf73U, 0xf7cdea53U, 0xfdaa5b5fU, 0x3d6f14dfU, 0x44db8678U, 0xaff381caU, 0x68c43eb9U, 0x24342c38U, 0xa3405fc2U, 0x1dc37216U, 0xe2250cbcU, 0x3c498b28U, 0x0d9541ffU, 0xa8017139U, 0x0cb3de08U, 0xb4e49cd8U, 0x56c19064U, 0xcb84617bU, 0x32b670d5U, 0x6c5c7448U, 0xb85742d0U, }; const u32 Td4[256] = { 0x52525252U, 0x09090909U, 0x6a6a6a6aU, 0xd5d5d5d5U, 0x30303030U, 0x36363636U, 0xa5a5a5a5U, 0x38383838U, 0xbfbfbfbfU, 0x40404040U, 0xa3a3a3a3U, 0x9e9e9e9eU, 0x81818181U, 0xf3f3f3f3U, 0xd7d7d7d7U, 0xfbfbfbfbU, 0x7c7c7c7cU, 0xe3e3e3e3U, 0x39393939U, 0x82828282U, 0x9b9b9b9bU, 0x2f2f2f2fU, 0xffffffffU, 0x87878787U, 0x34343434U, 0x8e8e8e8eU, 0x43434343U, 0x44444444U, 0xc4c4c4c4U, 0xdedededeU, 0xe9e9e9e9U, 0xcbcbcbcbU, 0x54545454U, 0x7b7b7b7bU, 0x94949494U, 0x32323232U, 0xa6a6a6a6U, 0xc2c2c2c2U, 0x23232323U, 0x3d3d3d3dU, 0xeeeeeeeeU, 0x4c4c4c4cU, 0x95959595U, 0x0b0b0b0bU, 0x42424242U, 0xfafafafaU, 0xc3c3c3c3U, 0x4e4e4e4eU, 0x08080808U, 0x2e2e2e2eU, 0xa1a1a1a1U, 0x66666666U, 0x28282828U, 0xd9d9d9d9U, 0x24242424U, 0xb2b2b2b2U, 0x76767676U, 0x5b5b5b5bU, 0xa2a2a2a2U, 0x49494949U, 0x6d6d6d6dU, 0x8b8b8b8bU, 0xd1d1d1d1U, 0x25252525U, 0x72727272U, 0xf8f8f8f8U, 0xf6f6f6f6U, 0x64646464U, 0x86868686U, 0x68686868U, 0x98989898U, 0x16161616U, 0xd4d4d4d4U, 0xa4a4a4a4U, 0x5c5c5c5cU, 0xccccccccU, 0x5d5d5d5dU, 0x65656565U, 0xb6b6b6b6U, 0x92929292U, 0x6c6c6c6cU, 0x70707070U, 0x48484848U, 0x50505050U, 0xfdfdfdfdU, 0xededededU, 0xb9b9b9b9U, 0xdadadadaU, 0x5e5e5e5eU, 0x15151515U, 0x46464646U, 0x57575757U, 0xa7a7a7a7U, 0x8d8d8d8dU, 0x9d9d9d9dU, 0x84848484U, 0x90909090U, 0xd8d8d8d8U, 0xababababU, 0x00000000U, 0x8c8c8c8cU, 0xbcbcbcbcU, 0xd3d3d3d3U, 0x0a0a0a0aU, 0xf7f7f7f7U, 0xe4e4e4e4U, 0x58585858U, 0x05050505U, 0xb8b8b8b8U, 0xb3b3b3b3U, 0x45454545U, 0x06060606U, 0xd0d0d0d0U, 0x2c2c2c2cU, 0x1e1e1e1eU, 0x8f8f8f8fU, 0xcacacacaU, 0x3f3f3f3fU, 0x0f0f0f0fU, 0x02020202U, 0xc1c1c1c1U, 0xafafafafU, 0xbdbdbdbdU, 0x03030303U, 0x01010101U, 0x13131313U, 0x8a8a8a8aU, 0x6b6b6b6bU, 0x3a3a3a3aU, 0x91919191U, 0x11111111U, 0x41414141U, 0x4f4f4f4fU, 0x67676767U, 0xdcdcdcdcU, 0xeaeaeaeaU, 0x97979797U, 0xf2f2f2f2U, 0xcfcfcfcfU, 0xcecececeU, 0xf0f0f0f0U, 0xb4b4b4b4U, 0xe6e6e6e6U, 0x73737373U, 0x96969696U, 0xacacacacU, 0x74747474U, 0x22222222U, 0xe7e7e7e7U, 0xadadadadU, 0x35353535U, 0x85858585U, 0xe2e2e2e2U, 0xf9f9f9f9U, 0x37373737U, 0xe8e8e8e8U, 0x1c1c1c1cU, 0x75757575U, 0xdfdfdfdfU, 0x6e6e6e6eU, 0x47474747U, 0xf1f1f1f1U, 0x1a1a1a1aU, 0x71717171U, 0x1d1d1d1dU, 0x29292929U, 0xc5c5c5c5U, 0x89898989U, 0x6f6f6f6fU, 0xb7b7b7b7U, 0x62626262U, 0x0e0e0e0eU, 0xaaaaaaaaU, 0x18181818U, 0xbebebebeU, 0x1b1b1b1bU, 0xfcfcfcfcU, 0x56565656U, 0x3e3e3e3eU, 0x4b4b4b4bU, 0xc6c6c6c6U, 0xd2d2d2d2U, 0x79797979U, 0x20202020U, 0x9a9a9a9aU, 0xdbdbdbdbU, 0xc0c0c0c0U, 0xfefefefeU, 0x78787878U, 0xcdcdcdcdU, 0x5a5a5a5aU, 0xf4f4f4f4U, 0x1f1f1f1fU, 0xddddddddU, 0xa8a8a8a8U, 0x33333333U, 0x88888888U, 0x07070707U, 0xc7c7c7c7U, 0x31313131U, 0xb1b1b1b1U, 0x12121212U, 0x10101010U, 0x59595959U, 0x27272727U, 0x80808080U, 0xececececU, 0x5f5f5f5fU, 0x60606060U, 0x51515151U, 0x7f7f7f7fU, 0xa9a9a9a9U, 0x19191919U, 0xb5b5b5b5U, 0x4a4a4a4aU, 0x0d0d0d0dU, 0x2d2d2d2dU, 0xe5e5e5e5U, 0x7a7a7a7aU, 0x9f9f9f9fU, 0x93939393U, 0xc9c9c9c9U, 0x9c9c9c9cU, 0xefefefefU, 0xa0a0a0a0U, 0xe0e0e0e0U, 0x3b3b3b3bU, 0x4d4d4d4dU, 0xaeaeaeaeU, 0x2a2a2a2aU, 0xf5f5f5f5U, 0xb0b0b0b0U, 0xc8c8c8c8U, 0xebebebebU, 0xbbbbbbbbU, 0x3c3c3c3cU, 0x83838383U, 0x53535353U, 0x99999999U, 0x61616161U, 0x17171717U, 0x2b2b2b2bU, 0x04040404U, 0x7e7e7e7eU, 0xbabababaU, 0x77777777U, 0xd6d6d6d6U, 0x26262626U, 0xe1e1e1e1U, 0x69696969U, 0x14141414U, 0x63636363U, 0x55555555U, 0x21212121U, 0x0c0c0c0cU, 0x7d7d7d7dU, }; const u32 rcon[] = { 0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000, 0x20000000, 0x40000000, 0x80000000, 0x1B000000, 0x36000000, /* for 128-bit blocks, Rijndael never uses more than 10 rcon values */ }; #else /* AES_SMALL_TABLES */ const u8 Td4s[256] = { 0x52U, 0x09U, 0x6aU, 0xd5U, 0x30U, 0x36U, 0xa5U, 0x38U, 0xbfU, 0x40U, 0xa3U, 0x9eU, 0x81U, 0xf3U, 0xd7U, 0xfbU, 0x7cU, 0xe3U, 0x39U, 0x82U, 0x9bU, 0x2fU, 0xffU, 0x87U, 0x34U, 0x8eU, 0x43U, 0x44U, 0xc4U, 0xdeU, 0xe9U, 0xcbU, 0x54U, 0x7bU, 0x94U, 0x32U, 0xa6U, 0xc2U, 0x23U, 0x3dU, 0xeeU, 0x4cU, 0x95U, 0x0bU, 0x42U, 0xfaU, 0xc3U, 0x4eU, 0x08U, 0x2eU, 0xa1U, 0x66U, 0x28U, 0xd9U, 0x24U, 0xb2U, 0x76U, 0x5bU, 0xa2U, 0x49U, 0x6dU, 0x8bU, 0xd1U, 0x25U, 0x72U, 0xf8U, 0xf6U, 0x64U, 0x86U, 0x68U, 0x98U, 0x16U, 0xd4U, 0xa4U, 0x5cU, 0xccU, 0x5dU, 0x65U, 0xb6U, 0x92U, 0x6cU, 0x70U, 0x48U, 0x50U, 0xfdU, 0xedU, 0xb9U, 0xdaU, 0x5eU, 0x15U, 0x46U, 0x57U, 0xa7U, 0x8dU, 0x9dU, 0x84U, 0x90U, 0xd8U, 0xabU, 0x00U, 0x8cU, 0xbcU, 0xd3U, 0x0aU, 0xf7U, 0xe4U, 0x58U, 0x05U, 0xb8U, 0xb3U, 0x45U, 0x06U, 0xd0U, 0x2cU, 0x1eU, 0x8fU, 0xcaU, 0x3fU, 0x0fU, 0x02U, 0xc1U, 0xafU, 0xbdU, 0x03U, 0x01U, 0x13U, 0x8aU, 0x6bU, 0x3aU, 0x91U, 0x11U, 0x41U, 0x4fU, 0x67U, 0xdcU, 0xeaU, 0x97U, 0xf2U, 0xcfU, 0xceU, 0xf0U, 0xb4U, 0xe6U, 0x73U, 0x96U, 0xacU, 0x74U, 0x22U, 0xe7U, 0xadU, 0x35U, 0x85U, 0xe2U, 0xf9U, 0x37U, 0xe8U, 0x1cU, 0x75U, 0xdfU, 0x6eU, 0x47U, 0xf1U, 0x1aU, 0x71U, 0x1dU, 0x29U, 0xc5U, 0x89U, 0x6fU, 0xb7U, 0x62U, 0x0eU, 0xaaU, 0x18U, 0xbeU, 0x1bU, 0xfcU, 0x56U, 0x3eU, 0x4bU, 0xc6U, 0xd2U, 0x79U, 0x20U, 0x9aU, 0xdbU, 0xc0U, 0xfeU, 0x78U, 0xcdU, 0x5aU, 0xf4U, 0x1fU, 0xddU, 0xa8U, 0x33U, 0x88U, 0x07U, 0xc7U, 0x31U, 0xb1U, 0x12U, 0x10U, 0x59U, 0x27U, 0x80U, 0xecU, 0x5fU, 0x60U, 0x51U, 0x7fU, 0xa9U, 0x19U, 0xb5U, 0x4aU, 0x0dU, 0x2dU, 0xe5U, 0x7aU, 0x9fU, 0x93U, 0xc9U, 0x9cU, 0xefU, 0xa0U, 0xe0U, 0x3bU, 0x4dU, 0xaeU, 0x2aU, 0xf5U, 0xb0U, 0xc8U, 0xebU, 0xbbU, 0x3cU, 0x83U, 0x53U, 0x99U, 0x61U, 0x17U, 0x2bU, 0x04U, 0x7eU, 0xbaU, 0x77U, 0xd6U, 0x26U, 0xe1U, 0x69U, 0x14U, 0x63U, 0x55U, 0x21U, 0x0cU, 0x7dU, }; const u8 rcons[] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36 /* for 128-bit blocks, Rijndael never uses more than 10 rcon values */ }; #endif /* AES_SMALL_TABLES */ /** * Expand the cipher key into the encryption key schedule. * * @return the number of rounds for the given cipher key size. */ int rijndaelKeySetupEnc(u32 rk[], const u8 cipherKey[], int keyBits) { int i; u32 temp; rk[0] = GETU32(cipherKey ); rk[1] = GETU32(cipherKey + 4); rk[2] = GETU32(cipherKey + 8); rk[3] = GETU32(cipherKey + 12); if (keyBits == 128) { for (i = 0; i < 10; i++) { temp = rk[3]; rk[4] = rk[0] ^ TE421(temp) ^ TE432(temp) ^ TE443(temp) ^ TE414(temp) ^ RCON(i); rk[5] = rk[1] ^ rk[4]; rk[6] = rk[2] ^ rk[5]; rk[7] = rk[3] ^ rk[6]; rk += 4; } return 10; } rk[4] = GETU32(cipherKey + 16); rk[5] = GETU32(cipherKey + 20); if (keyBits == 192) { for (i = 0; i < 8; i++) { temp = rk[5]; rk[6] = rk[0] ^ TE421(temp) ^ TE432(temp) ^ TE443(temp) ^ TE414(temp) ^ RCON(i); rk[7] = rk[1] ^ rk[6]; rk[8] = rk[2] ^ rk[7]; rk[9] = rk[3] ^ rk[8]; if (i == 7) return 12; rk[10] = rk[4] ^ rk[9]; rk[11] = rk[5] ^ rk[10]; rk += 6; } } rk[6] = GETU32(cipherKey + 24); rk[7] = GETU32(cipherKey + 28); if (keyBits == 256) { for (i = 0; i < 7; i++) { temp = rk[7]; rk[8] = rk[0] ^ TE421(temp) ^ TE432(temp) ^ TE443(temp) ^ TE414(temp) ^ RCON(i); rk[9] = rk[1] ^ rk[8]; rk[10] = rk[2] ^ rk[9]; rk[11] = rk[3] ^ rk[10]; if (i == 6) return 14; temp = rk[11]; rk[12] = rk[4] ^ TE411(temp) ^ TE422(temp) ^ TE433(temp) ^ TE444(temp); rk[13] = rk[5] ^ rk[12]; rk[14] = rk[6] ^ rk[13]; rk[15] = rk[7] ^ rk[14]; rk += 8; } } return -1; } wpa-2.1/src/crypto/aes-omac1.c000066400000000000000000000054171227414666700162120ustar00rootroot00000000000000/* * One-key CBC MAC (OMAC1) hash with AES-128 * * Copyright (c) 2003-2007, 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 "aes.h" #include "aes_wrap.h" static void gf_mulx(u8 *pad) { int i, carry; carry = pad[0] & 0x80; for (i = 0; i < AES_BLOCK_SIZE - 1; i++) pad[i] = (pad[i] << 1) | (pad[i + 1] >> 7); pad[AES_BLOCK_SIZE - 1] <<= 1; if (carry) pad[AES_BLOCK_SIZE - 1] ^= 0x87; } /** * omac1_aes_128_vector - One-Key CBC MAC (OMAC1) hash with AES-128 * @key: 128-bit key for the hash operation * @num_elem: Number of elements in the data vector * @addr: Pointers to the data areas * @len: Lengths of the data blocks * @mac: Buffer for MAC (128 bits, i.e., 16 bytes) * Returns: 0 on success, -1 on failure * * This is a mode for using block cipher (AES in this case) for authentication. * OMAC1 was standardized with the name CMAC by NIST in a Special Publication * (SP) 800-38B. */ int omac1_aes_128_vector(const u8 *key, size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { void *ctx; u8 cbc[AES_BLOCK_SIZE], pad[AES_BLOCK_SIZE]; const u8 *pos, *end; size_t i, e, left, total_len; ctx = aes_encrypt_init(key, 16); if (ctx == NULL) return -1; os_memset(cbc, 0, AES_BLOCK_SIZE); total_len = 0; for (e = 0; e < num_elem; e++) total_len += len[e]; left = total_len; e = 0; pos = addr[0]; end = pos + len[0]; while (left >= AES_BLOCK_SIZE) { for (i = 0; i < AES_BLOCK_SIZE; i++) { cbc[i] ^= *pos++; if (pos >= end) { e++; pos = addr[e]; end = pos + len[e]; } } if (left > AES_BLOCK_SIZE) aes_encrypt(ctx, cbc, cbc); left -= AES_BLOCK_SIZE; } os_memset(pad, 0, AES_BLOCK_SIZE); aes_encrypt(ctx, pad, pad); gf_mulx(pad); if (left || total_len == 0) { for (i = 0; i < left; i++) { cbc[i] ^= *pos++; if (pos >= end) { e++; pos = addr[e]; end = pos + len[e]; } } cbc[left] ^= 0x80; gf_mulx(pad); } for (i = 0; i < AES_BLOCK_SIZE; i++) pad[i] ^= cbc[i]; aes_encrypt(ctx, pad, mac); aes_encrypt_deinit(ctx); return 0; } /** * omac1_aes_128 - One-Key CBC MAC (OMAC1) hash with AES-128 (aka AES-CMAC) * @key: 128-bit key for the hash operation * @data: Data buffer for which a MAC is determined * @data_len: Length of data buffer in bytes * @mac: Buffer for MAC (128 bits, i.e., 16 bytes) * Returns: 0 on success, -1 on failure * * This is a mode for using block cipher (AES in this case) for authentication. * OMAC1 was standardized with the name CMAC by NIST in a Special Publication * (SP) 800-38B. */ int omac1_aes_128(const u8 *key, const u8 *data, size_t data_len, u8 *mac) { return omac1_aes_128_vector(key, 1, &data, &data_len, mac); } wpa-2.1/src/crypto/aes-unwrap.c000066400000000000000000000032211227414666700165150ustar00rootroot00000000000000/* * AES key unwrap (128-bit KEK, RFC3394) * * Copyright (c) 2003-2007, 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 "aes.h" #include "aes_wrap.h" /** * aes_unwrap - Unwrap key with AES Key Wrap Algorithm (128-bit KEK) (RFC3394) * @kek: Key encryption key (KEK) * @n: Length of the plaintext key in 64-bit units; e.g., 2 = 128-bit = 16 * bytes * @cipher: Wrapped key to be unwrapped, (n + 1) * 64 bits * @plain: Plaintext key, n * 64 bits * Returns: 0 on success, -1 on failure (e.g., integrity verification failed) */ int aes_unwrap(const u8 *kek, int n, const u8 *cipher, u8 *plain) { u8 a[8], *r, b[16]; int i, j; void *ctx; /* 1) Initialize variables. */ os_memcpy(a, cipher, 8); r = plain; os_memcpy(r, cipher + 8, 8 * n); ctx = aes_decrypt_init(kek, 16); if (ctx == NULL) return -1; /* 2) Compute intermediate values. * For j = 5 to 0 * For i = n to 1 * B = AES-1(K, (A ^ t) | R[i]) where t = n*j+i * A = MSB(64, B) * R[i] = LSB(64, B) */ for (j = 5; j >= 0; j--) { r = plain + (n - 1) * 8; for (i = n; i >= 1; i--) { os_memcpy(b, a, 8); b[7] ^= n * j + i; os_memcpy(b + 8, r, 8); aes_decrypt(ctx, b, b); os_memcpy(a, b, 8); os_memcpy(r, b + 8, 8); r -= 8; } } aes_decrypt_deinit(ctx); /* 3) Output results. * * These are already in @plain due to the location of temporary * variables. Just verify that the IV matches with the expected value. */ for (i = 0; i < 8; i++) { if (a[i] != 0xa6) return -1; } return 0; } wpa-2.1/src/crypto/aes-wrap.c000066400000000000000000000030001227414666700161450ustar00rootroot00000000000000/* * AES Key Wrap Algorithm (128-bit KEK) (RFC3394) * * Copyright (c) 2003-2007, 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 "aes.h" #include "aes_wrap.h" /** * aes_wrap - Wrap keys with AES Key Wrap Algorithm (128-bit KEK) (RFC3394) * @kek: 16-octet Key encryption key (KEK) * @n: Length of the plaintext key in 64-bit units; e.g., 2 = 128-bit = 16 * bytes * @plain: Plaintext key to be wrapped, n * 64 bits * @cipher: Wrapped key, (n + 1) * 64 bits * Returns: 0 on success, -1 on failure */ int aes_wrap(const u8 *kek, int n, const u8 *plain, u8 *cipher) { u8 *a, *r, b[16]; int i, j; void *ctx; a = cipher; r = cipher + 8; /* 1) Initialize variables. */ os_memset(a, 0xa6, 8); os_memcpy(r, plain, 8 * n); ctx = aes_encrypt_init(kek, 16); if (ctx == NULL) return -1; /* 2) Calculate intermediate values. * For j = 0 to 5 * For i=1 to n * B = AES(K, A | R[i]) * A = MSB(64, B) ^ t where t = (n*j)+i * R[i] = LSB(64, B) */ for (j = 0; j <= 5; j++) { r = cipher + 8; for (i = 1; i <= n; i++) { os_memcpy(b, a, 8); os_memcpy(b + 8, r, 8); aes_encrypt(ctx, b, b); os_memcpy(a, b, 8); a[7] ^= n * j + i; os_memcpy(r, b + 8, 8); r += 8; } } aes_encrypt_deinit(ctx); /* 3) Output the results. * * These are already in @cipher due to the location of temporary * variables. */ return 0; } wpa-2.1/src/crypto/aes.h000066400000000000000000000010461227414666700152130ustar00rootroot00000000000000/* * AES functions * Copyright (c) 2003-2006, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef AES_H #define AES_H #define AES_BLOCK_SIZE 16 void * aes_encrypt_init(const u8 *key, size_t len); void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt); void aes_encrypt_deinit(void *ctx); void * aes_decrypt_init(const u8 *key, size_t len); void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain); void aes_decrypt_deinit(void *ctx); #endif /* AES_H */ wpa-2.1/src/crypto/aes_i.h000066400000000000000000000102041227414666700155170ustar00rootroot00000000000000/* * AES (Rijndael) cipher * Copyright (c) 2003-2012, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef AES_I_H #define AES_I_H #include "aes.h" /* #define FULL_UNROLL */ #define AES_SMALL_TABLES extern const u32 Te0[256]; extern const u32 Te1[256]; extern const u32 Te2[256]; extern const u32 Te3[256]; extern const u32 Te4[256]; extern const u32 Td0[256]; extern const u32 Td1[256]; extern const u32 Td2[256]; extern const u32 Td3[256]; extern const u32 Td4[256]; extern const u32 rcon[10]; extern const u8 Td4s[256]; extern const u8 rcons[10]; #ifndef AES_SMALL_TABLES #define RCON(i) rcon[(i)] #define TE0(i) Te0[((i) >> 24) & 0xff] #define TE1(i) Te1[((i) >> 16) & 0xff] #define TE2(i) Te2[((i) >> 8) & 0xff] #define TE3(i) Te3[(i) & 0xff] #define TE41(i) (Te4[((i) >> 24) & 0xff] & 0xff000000) #define TE42(i) (Te4[((i) >> 16) & 0xff] & 0x00ff0000) #define TE43(i) (Te4[((i) >> 8) & 0xff] & 0x0000ff00) #define TE44(i) (Te4[(i) & 0xff] & 0x000000ff) #define TE421(i) (Te4[((i) >> 16) & 0xff] & 0xff000000) #define TE432(i) (Te4[((i) >> 8) & 0xff] & 0x00ff0000) #define TE443(i) (Te4[(i) & 0xff] & 0x0000ff00) #define TE414(i) (Te4[((i) >> 24) & 0xff] & 0x000000ff) #define TE411(i) (Te4[((i) >> 24) & 0xff] & 0xff000000) #define TE422(i) (Te4[((i) >> 16) & 0xff] & 0x00ff0000) #define TE433(i) (Te4[((i) >> 8) & 0xff] & 0x0000ff00) #define TE444(i) (Te4[(i) & 0xff] & 0x000000ff) #define TE4(i) (Te4[(i)] & 0x000000ff) #define TD0(i) Td0[((i) >> 24) & 0xff] #define TD1(i) Td1[((i) >> 16) & 0xff] #define TD2(i) Td2[((i) >> 8) & 0xff] #define TD3(i) Td3[(i) & 0xff] #define TD41(i) (Td4[((i) >> 24) & 0xff] & 0xff000000) #define TD42(i) (Td4[((i) >> 16) & 0xff] & 0x00ff0000) #define TD43(i) (Td4[((i) >> 8) & 0xff] & 0x0000ff00) #define TD44(i) (Td4[(i) & 0xff] & 0x000000ff) #define TD0_(i) Td0[(i) & 0xff] #define TD1_(i) Td1[(i) & 0xff] #define TD2_(i) Td2[(i) & 0xff] #define TD3_(i) Td3[(i) & 0xff] #else /* AES_SMALL_TABLES */ #define RCON(i) (rcons[(i)] << 24) static inline u32 rotr(u32 val, int bits) { return (val >> bits) | (val << (32 - bits)); } #define TE0(i) Te0[((i) >> 24) & 0xff] #define TE1(i) rotr(Te0[((i) >> 16) & 0xff], 8) #define TE2(i) rotr(Te0[((i) >> 8) & 0xff], 16) #define TE3(i) rotr(Te0[(i) & 0xff], 24) #define TE41(i) ((Te0[((i) >> 24) & 0xff] << 8) & 0xff000000) #define TE42(i) (Te0[((i) >> 16) & 0xff] & 0x00ff0000) #define TE43(i) (Te0[((i) >> 8) & 0xff] & 0x0000ff00) #define TE44(i) ((Te0[(i) & 0xff] >> 8) & 0x000000ff) #define TE421(i) ((Te0[((i) >> 16) & 0xff] << 8) & 0xff000000) #define TE432(i) (Te0[((i) >> 8) & 0xff] & 0x00ff0000) #define TE443(i) (Te0[(i) & 0xff] & 0x0000ff00) #define TE414(i) ((Te0[((i) >> 24) & 0xff] >> 8) & 0x000000ff) #define TE411(i) ((Te0[((i) >> 24) & 0xff] << 8) & 0xff000000) #define TE422(i) (Te0[((i) >> 16) & 0xff] & 0x00ff0000) #define TE433(i) (Te0[((i) >> 8) & 0xff] & 0x0000ff00) #define TE444(i) ((Te0[(i) & 0xff] >> 8) & 0x000000ff) #define TE4(i) ((Te0[(i)] >> 8) & 0x000000ff) #define TD0(i) Td0[((i) >> 24) & 0xff] #define TD1(i) rotr(Td0[((i) >> 16) & 0xff], 8) #define TD2(i) rotr(Td0[((i) >> 8) & 0xff], 16) #define TD3(i) rotr(Td0[(i) & 0xff], 24) #define TD41(i) (Td4s[((i) >> 24) & 0xff] << 24) #define TD42(i) (Td4s[((i) >> 16) & 0xff] << 16) #define TD43(i) (Td4s[((i) >> 8) & 0xff] << 8) #define TD44(i) (Td4s[(i) & 0xff]) #define TD0_(i) Td0[(i) & 0xff] #define TD1_(i) rotr(Td0[(i) & 0xff], 8) #define TD2_(i) rotr(Td0[(i) & 0xff], 16) #define TD3_(i) rotr(Td0[(i) & 0xff], 24) #endif /* AES_SMALL_TABLES */ #ifdef _MSC_VER #define SWAP(x) (_lrotl(x, 8) & 0x00ff00ff | _lrotr(x, 8) & 0xff00ff00) #define GETU32(p) SWAP(*((u32 *)(p))) #define PUTU32(ct, st) { *((u32 *)(ct)) = SWAP((st)); } #else #define GETU32(pt) (((u32)(pt)[0] << 24) ^ ((u32)(pt)[1] << 16) ^ \ ((u32)(pt)[2] << 8) ^ ((u32)(pt)[3])) #define PUTU32(ct, st) { \ (ct)[0] = (u8)((st) >> 24); (ct)[1] = (u8)((st) >> 16); \ (ct)[2] = (u8)((st) >> 8); (ct)[3] = (u8)(st); } #endif #define AES_PRIV_SIZE (4 * 4 * 15 + 4) #define AES_PRIV_NR_POS (4 * 15) int rijndaelKeySetupEnc(u32 rk[], const u8 cipherKey[], int keyBits); #endif /* AES_I_H */ wpa-2.1/src/crypto/aes_wrap.h000066400000000000000000000050131227414666700162420ustar00rootroot00000000000000/* * AES-based functions * * - AES Key Wrap Algorithm (128-bit KEK) (RFC3394) * - One-Key CBC MAC (OMAC1) hash with AES-128 * - AES-128 CTR mode encryption * - AES-128 EAX mode encryption/decryption * - AES-128 CBC * - AES-GCM * - AES-CCM * * Copyright (c) 2003-2012, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef AES_WRAP_H #define AES_WRAP_H int __must_check aes_wrap(const u8 *kek, int n, const u8 *plain, u8 *cipher); int __must_check aes_unwrap(const u8 *kek, int n, const u8 *cipher, u8 *plain); int __must_check omac1_aes_128_vector(const u8 *key, size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac); int __must_check omac1_aes_128(const u8 *key, const u8 *data, size_t data_len, u8 *mac); int __must_check aes_128_encrypt_block(const u8 *key, const u8 *in, u8 *out); int __must_check aes_128_ctr_encrypt(const u8 *key, const u8 *nonce, u8 *data, size_t data_len); int __must_check aes_128_eax_encrypt(const u8 *key, const u8 *nonce, size_t nonce_len, const u8 *hdr, size_t hdr_len, u8 *data, size_t data_len, u8 *tag); int __must_check aes_128_eax_decrypt(const u8 *key, const u8 *nonce, size_t nonce_len, const u8 *hdr, size_t hdr_len, u8 *data, size_t data_len, const u8 *tag); int __must_check aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len); int __must_check aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len); int __must_check aes_gcm_ae(const u8 *key, size_t key_len, const u8 *iv, size_t iv_len, const u8 *plain, size_t plain_len, const u8 *aad, size_t aad_len, u8 *crypt, u8 *tag); int __must_check aes_gcm_ad(const u8 *key, size_t key_len, const u8 *iv, size_t iv_len, const u8 *crypt, size_t crypt_len, const u8 *aad, size_t aad_len, const u8 *tag, u8 *plain); int __must_check aes_gmac(const u8 *key, size_t key_len, const u8 *iv, size_t iv_len, const u8 *aad, size_t aad_len, u8 *tag); int __must_check aes_ccm_ae(const u8 *key, size_t key_len, const u8 *nonce, size_t M, const u8 *plain, size_t plain_len, const u8 *aad, size_t aad_len, u8 *crypt, u8 *auth); int __must_check aes_ccm_ad(const u8 *key, size_t key_len, const u8 *nonce, size_t M, const u8 *crypt, size_t crypt_len, const u8 *aad, size_t aad_len, const u8 *auth, u8 *plain); #endif /* AES_WRAP_H */ wpa-2.1/src/crypto/crypto.h000066400000000000000000000626641227414666700160000ustar00rootroot00000000000000/* * Wrapper functions for crypto libraries * Copyright (c) 2004-2013, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. * * This file defines the cryptographic functions that need to be implemented * for wpa_supplicant and hostapd. When TLS is not used, internal * implementation of MD5, SHA1, and AES is used and no external libraries are * required. When TLS is enabled (e.g., by enabling EAP-TLS or EAP-PEAP), the * crypto library used by the TLS implementation is expected to be used for * non-TLS needs, too, in order to save space by not implementing these * functions twice. * * Wrapper code for using each crypto library is in its own file (crypto*.c) * and one of these files is build and linked in to provide the functions * defined here. */ #ifndef CRYPTO_H #define CRYPTO_H /** * md4_vector - MD4 hash for data vector * @num_elem: Number of elements in the data vector * @addr: Pointers to the data areas * @len: Lengths of the data blocks * @mac: Buffer for the hash * Returns: 0 on success, -1 on failure */ int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac); /** * md5_vector - MD5 hash for data vector * @num_elem: Number of elements in the data vector * @addr: Pointers to the data areas * @len: Lengths of the data blocks * @mac: Buffer for the hash * Returns: 0 on success, -1 on failure */ int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac); /** * sha1_vector - SHA-1 hash for data vector * @num_elem: Number of elements in the data vector * @addr: Pointers to the data areas * @len: Lengths of the data blocks * @mac: Buffer for the hash * Returns: 0 on success, -1 on failure */ int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac); /** * fips186_2-prf - NIST FIPS Publication 186-2 change notice 1 PRF * @seed: Seed/key for the PRF * @seed_len: Seed length in bytes * @x: Buffer for PRF output * @xlen: Output length in bytes * Returns: 0 on success, -1 on failure * * This function implements random number generation specified in NIST FIPS * Publication 186-2 for EAP-SIM. This PRF uses a function that is similar to * SHA-1, but has different message padding. */ int __must_check fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, size_t xlen); /** * sha256_vector - SHA256 hash for data vector * @num_elem: Number of elements in the data vector * @addr: Pointers to the data areas * @len: Lengths of the data blocks * @mac: Buffer for the hash * Returns: 0 on success, -1 on failure */ int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac); /** * des_encrypt - Encrypt one block with DES * @clear: 8 octets (in) * @key: 7 octets (in) (no parity bits included) * @cypher: 8 octets (out) */ void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher); /** * aes_encrypt_init - Initialize AES for encryption * @key: Encryption key * @len: Key length in bytes (usually 16, i.e., 128 bits) * Returns: Pointer to context data or %NULL on failure */ void * aes_encrypt_init(const u8 *key, size_t len); /** * aes_encrypt - Encrypt one AES block * @ctx: Context pointer from aes_encrypt_init() * @plain: Plaintext data to be encrypted (16 bytes) * @crypt: Buffer for the encrypted data (16 bytes) */ void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt); /** * aes_encrypt_deinit - Deinitialize AES encryption * @ctx: Context pointer from aes_encrypt_init() */ void aes_encrypt_deinit(void *ctx); /** * aes_decrypt_init - Initialize AES for decryption * @key: Decryption key * @len: Key length in bytes (usually 16, i.e., 128 bits) * Returns: Pointer to context data or %NULL on failure */ void * aes_decrypt_init(const u8 *key, size_t len); /** * aes_decrypt - Decrypt one AES block * @ctx: Context pointer from aes_encrypt_init() * @crypt: Encrypted data (16 bytes) * @plain: Buffer for the decrypted data (16 bytes) */ void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain); /** * aes_decrypt_deinit - Deinitialize AES decryption * @ctx: Context pointer from aes_encrypt_init() */ void aes_decrypt_deinit(void *ctx); enum crypto_hash_alg { CRYPTO_HASH_ALG_MD5, CRYPTO_HASH_ALG_SHA1, CRYPTO_HASH_ALG_HMAC_MD5, CRYPTO_HASH_ALG_HMAC_SHA1, CRYPTO_HASH_ALG_SHA256, CRYPTO_HASH_ALG_HMAC_SHA256 }; struct crypto_hash; /** * crypto_hash_init - Initialize hash/HMAC function * @alg: Hash algorithm * @key: Key for keyed hash (e.g., HMAC) or %NULL if not needed * @key_len: Length of the key in bytes * Returns: Pointer to hash context to use with other hash functions or %NULL * on failure * * This function is only used with internal TLSv1 implementation * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need * to implement this. */ struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key, size_t key_len); /** * crypto_hash_update - Add data to hash calculation * @ctx: Context pointer from crypto_hash_init() * @data: Data buffer to add * @len: Length of the buffer * * This function is only used with internal TLSv1 implementation * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need * to implement this. */ void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len); /** * crypto_hash_finish - Complete hash calculation * @ctx: Context pointer from crypto_hash_init() * @hash: Buffer for hash value or %NULL if caller is just freeing the hash * context * @len: Pointer to length of the buffer or %NULL if caller is just freeing the * hash context; on return, this is set to the actual length of the hash value * Returns: 0 on success, -1 if buffer is too small (len set to needed length), * or -2 on other failures (including failed crypto_hash_update() operations) * * This function calculates the hash value and frees the context buffer that * was used for hash calculation. * * This function is only used with internal TLSv1 implementation * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need * to implement this. */ int crypto_hash_finish(struct crypto_hash *ctx, u8 *hash, size_t *len); enum crypto_cipher_alg { CRYPTO_CIPHER_NULL = 0, CRYPTO_CIPHER_ALG_AES, CRYPTO_CIPHER_ALG_3DES, CRYPTO_CIPHER_ALG_DES, CRYPTO_CIPHER_ALG_RC2, CRYPTO_CIPHER_ALG_RC4 }; struct crypto_cipher; /** * crypto_cipher_init - Initialize block/stream cipher function * @alg: Cipher algorithm * @iv: Initialization vector for block ciphers or %NULL for stream ciphers * @key: Cipher key * @key_len: Length of key in bytes * Returns: Pointer to cipher context to use with other cipher functions or * %NULL on failure * * This function is only used with internal TLSv1 implementation * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need * to implement this. */ struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg, const u8 *iv, const u8 *key, size_t key_len); /** * crypto_cipher_encrypt - Cipher encrypt * @ctx: Context pointer from crypto_cipher_init() * @plain: Plaintext to cipher * @crypt: Resulting ciphertext * @len: Length of the plaintext * Returns: 0 on success, -1 on failure * * This function is only used with internal TLSv1 implementation * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need * to implement this. */ int __must_check crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain, u8 *crypt, size_t len); /** * crypto_cipher_decrypt - Cipher decrypt * @ctx: Context pointer from crypto_cipher_init() * @crypt: Ciphertext to decrypt * @plain: Resulting plaintext * @len: Length of the cipher text * Returns: 0 on success, -1 on failure * * This function is only used with internal TLSv1 implementation * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need * to implement this. */ int __must_check crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt, u8 *plain, size_t len); /** * crypto_cipher_decrypt - Free cipher context * @ctx: Context pointer from crypto_cipher_init() * * This function is only used with internal TLSv1 implementation * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need * to implement this. */ void crypto_cipher_deinit(struct crypto_cipher *ctx); struct crypto_public_key; struct crypto_private_key; /** * crypto_public_key_import - Import an RSA public key * @key: Key buffer (DER encoded RSA public key) * @len: Key buffer length in bytes * Returns: Pointer to the public key or %NULL on failure * * This function can just return %NULL if the crypto library supports X.509 * parsing. In that case, crypto_public_key_from_cert() is used to import the * public key from a certificate. * * This function is only used with internal TLSv1 implementation * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need * to implement this. */ struct crypto_public_key * crypto_public_key_import(const u8 *key, size_t len); /** * crypto_private_key_import - Import an RSA private key * @key: Key buffer (DER encoded RSA private key) * @len: Key buffer length in bytes * @passwd: Key encryption password or %NULL if key is not encrypted * Returns: Pointer to the private key or %NULL on failure * * This function is only used with internal TLSv1 implementation * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need * to implement this. */ struct crypto_private_key * crypto_private_key_import(const u8 *key, size_t len, const char *passwd); /** * crypto_public_key_from_cert - Import an RSA public key from a certificate * @buf: DER encoded X.509 certificate * @len: Certificate buffer length in bytes * Returns: Pointer to public key or %NULL on failure * * This function can just return %NULL if the crypto library does not support * X.509 parsing. In that case, internal code will be used to parse the * certificate and public key is imported using crypto_public_key_import(). * * This function is only used with internal TLSv1 implementation * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need * to implement this. */ struct crypto_public_key * crypto_public_key_from_cert(const u8 *buf, size_t len); /** * crypto_public_key_encrypt_pkcs1_v15 - Public key encryption (PKCS #1 v1.5) * @key: Public key * @in: Plaintext buffer * @inlen: Length of plaintext buffer in bytes * @out: Output buffer for encrypted data * @outlen: Length of output buffer in bytes; set to used length on success * Returns: 0 on success, -1 on failure * * This function is only used with internal TLSv1 implementation * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need * to implement this. */ int __must_check crypto_public_key_encrypt_pkcs1_v15( struct crypto_public_key *key, const u8 *in, size_t inlen, u8 *out, size_t *outlen); /** * crypto_private_key_decrypt_pkcs1_v15 - Private key decryption (PKCS #1 v1.5) * @key: Private key * @in: Encrypted buffer * @inlen: Length of encrypted buffer in bytes * @out: Output buffer for encrypted data * @outlen: Length of output buffer in bytes; set to used length on success * Returns: 0 on success, -1 on failure * * This function is only used with internal TLSv1 implementation * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need * to implement this. */ int __must_check crypto_private_key_decrypt_pkcs1_v15( struct crypto_private_key *key, const u8 *in, size_t inlen, u8 *out, size_t *outlen); /** * crypto_private_key_sign_pkcs1 - Sign with private key (PKCS #1) * @key: Private key from crypto_private_key_import() * @in: Plaintext buffer * @inlen: Length of plaintext buffer in bytes * @out: Output buffer for encrypted (signed) data * @outlen: Length of output buffer in bytes; set to used length on success * Returns: 0 on success, -1 on failure * * This function is only used with internal TLSv1 implementation * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need * to implement this. */ int __must_check crypto_private_key_sign_pkcs1(struct crypto_private_key *key, const u8 *in, size_t inlen, u8 *out, size_t *outlen); /** * crypto_public_key_free - Free public key * @key: Public key * * This function is only used with internal TLSv1 implementation * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need * to implement this. */ void crypto_public_key_free(struct crypto_public_key *key); /** * crypto_private_key_free - Free private key * @key: Private key from crypto_private_key_import() * * This function is only used with internal TLSv1 implementation * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need * to implement this. */ void crypto_private_key_free(struct crypto_private_key *key); /** * crypto_public_key_decrypt_pkcs1 - Decrypt PKCS #1 signature * @key: Public key * @crypt: Encrypted signature data (using the private key) * @crypt_len: Encrypted signature data length * @plain: Buffer for plaintext (at least crypt_len bytes) * @plain_len: Plaintext length (max buffer size on input, real len on output); * Returns: 0 on success, -1 on failure */ int __must_check crypto_public_key_decrypt_pkcs1( struct crypto_public_key *key, const u8 *crypt, size_t crypt_len, u8 *plain, size_t *plain_len); /** * crypto_global_init - Initialize crypto wrapper * * This function is only used with internal TLSv1 implementation * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need * to implement this. */ int __must_check crypto_global_init(void); /** * crypto_global_deinit - Deinitialize crypto wrapper * * This function is only used with internal TLSv1 implementation * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need * to implement this. */ void crypto_global_deinit(void); /** * crypto_mod_exp - Modular exponentiation of large integers * @base: Base integer (big endian byte array) * @base_len: Length of base integer in bytes * @power: Power integer (big endian byte array) * @power_len: Length of power integer in bytes * @modulus: Modulus integer (big endian byte array) * @modulus_len: Length of modulus integer in bytes * @result: Buffer for the result * @result_len: Result length (max buffer size on input, real len on output) * Returns: 0 on success, -1 on failure * * This function calculates result = base ^ power mod modulus. modules_len is * used as the maximum size of modulus buffer. It is set to the used size on * success. * * This function is only used with internal TLSv1 implementation * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need * to implement this. */ int __must_check crypto_mod_exp(const u8 *base, size_t base_len, const u8 *power, size_t power_len, const u8 *modulus, size_t modulus_len, u8 *result, size_t *result_len); /** * rc4_skip - XOR RC4 stream to given data with skip-stream-start * @key: RC4 key * @keylen: RC4 key length * @skip: number of bytes to skip from the beginning of the RC4 stream * @data: data to be XOR'ed with RC4 stream * @data_len: buf length * Returns: 0 on success, -1 on failure * * Generate RC4 pseudo random stream for the given key, skip beginning of the * stream, and XOR the end result with the data buffer to perform RC4 * encryption/decryption. */ int rc4_skip(const u8 *key, size_t keylen, size_t skip, u8 *data, size_t data_len); /** * crypto_get_random - Generate cryptographically strong pseudy-random bytes * @buf: Buffer for data * @len: Number of bytes to generate * Returns: 0 on success, -1 on failure * * If the PRNG does not have enough entropy to ensure unpredictable byte * sequence, this functions must return -1. */ int crypto_get_random(void *buf, size_t len); /** * struct crypto_bignum - bignum * * Internal data structure for bignum implementation. The contents is specific * to the used crypto library. */ struct crypto_bignum; /** * crypto_bignum_init - Allocate memory for bignum * Returns: Pointer to allocated bignum or %NULL on failure */ struct crypto_bignum * crypto_bignum_init(void); /** * crypto_bignum_init_set - Allocate memory for bignum and set the value * @buf: Buffer with unsigned binary value * @len: Length of buf in octets * Returns: Pointer to allocated bignum or %NULL on failure */ struct crypto_bignum * crypto_bignum_init_set(const u8 *buf, size_t len); /** * crypto_bignum_deinit - Free bignum * @n: Bignum from crypto_bignum_init() or crypto_bignum_init_set() * @clear: Whether to clear the value from memory */ void crypto_bignum_deinit(struct crypto_bignum *n, int clear); /** * crypto_bignum_to_bin - Set binary buffer to unsigned bignum * @a: Bignum * @buf: Buffer for the binary number * @len: Length of @buf in octets * @padlen: Length in octets to pad the result to or 0 to indicate no padding * Returns: Number of octets written on success, -1 on failure */ int crypto_bignum_to_bin(const struct crypto_bignum *a, u8 *buf, size_t buflen, size_t padlen); /** * crypto_bignum_add - c = a + b * @a: Bignum * @b: Bignum * @c: Bignum; used to store the result of a + b * Returns: 0 on success, -1 on failure */ int crypto_bignum_add(const struct crypto_bignum *a, const struct crypto_bignum *b, struct crypto_bignum *c); /** * crypto_bignum_mod - c = a % b * @a: Bignum * @b: Bignum * @c: Bignum; used to store the result of a % b * Returns: 0 on success, -1 on failure */ int crypto_bignum_mod(const struct crypto_bignum *a, const struct crypto_bignum *b, struct crypto_bignum *c); /** * crypto_bignum_exptmod - Modular exponentiation: d = a^b (mod c) * @a: Bignum; base * @b: Bignum; exponent * @c: Bignum; modulus * @d: Bignum; used to store the result of a^b (mod c) * Returns: 0 on success, -1 on failure */ int crypto_bignum_exptmod(const struct crypto_bignum *a, const struct crypto_bignum *b, const struct crypto_bignum *c, struct crypto_bignum *d); /** * crypto_bignum_rshift - b = a >> n * @a: Bignum * @n: Number of bits to shift * @b: Bignum; used to store the result of a >> n * Returns: 0 on success, -1 on failure */ int crypto_bignum_rshift(const struct crypto_bignum *a, int n, struct crypto_bignum *b); /** * crypto_bignum_inverse - Inverse a bignum so that a * c = 1 (mod b) * @a: Bignum * @b: Bignum * @c: Bignum; used to store the result * Returns: 0 on success, -1 on failure */ int crypto_bignum_inverse(const struct crypto_bignum *a, const struct crypto_bignum *b, struct crypto_bignum *c); /** * crypto_bignum_sub - c = a - b * @a: Bignum * @b: Bignum * @c: Bignum; used to store the result of a - b * Returns: 0 on success, -1 on failure */ int crypto_bignum_sub(const struct crypto_bignum *a, const struct crypto_bignum *b, struct crypto_bignum *c); /** * crypto_bignum_div - c = a / b * @a: Bignum * @b: Bignum * @c: Bignum; used to store the result of a / b * Returns: 0 on success, -1 on failure */ int crypto_bignum_div(const struct crypto_bignum *a, const struct crypto_bignum *b, struct crypto_bignum *c); /** * crypto_bignum_mulmod - d = a * b (mod c) * @a: Bignum * @b: Bignum * @c: Bignum * @d: Bignum; used to store the result of (a * b) % c * Returns: 0 on success, -1 on failure */ int crypto_bignum_mulmod(const struct crypto_bignum *a, const struct crypto_bignum *b, const struct crypto_bignum *c, struct crypto_bignum *d); /** * crypto_bignum_cmp - Compare two bignums * @a: Bignum * @b: Bignum * Returns: -1 if a < b, 0 if a == b, or 1 if a > b */ int crypto_bignum_cmp(const struct crypto_bignum *a, const struct crypto_bignum *b); /** * crypto_bignum_bits - Get size of a bignum in bits * @a: Bignum * Returns: Number of bits in the bignum */ int crypto_bignum_bits(const struct crypto_bignum *a); /** * crypto_bignum_is_zero - Is the given bignum zero * @a: Bignum * Returns: 1 if @a is zero or 0 if not */ int crypto_bignum_is_zero(const struct crypto_bignum *a); /** * crypto_bignum_is_one - Is the given bignum one * @a: Bignum * Returns: 1 if @a is one or 0 if not */ int crypto_bignum_is_one(const struct crypto_bignum *a); /** * struct crypto_ec - Elliptic curve context * * Internal data structure for EC implementation. The contents is specific * to the used crypto library. */ struct crypto_ec; /** * crypto_ec_init - Initialize elliptic curve context * @group: Identifying number for the ECC group (IANA "Group Description" * attribute registrty for RFC 2409) * Returns: Pointer to EC context or %NULL on failure */ struct crypto_ec * crypto_ec_init(int group); /** * crypto_ec_deinit - Deinitialize elliptic curve context * @e: EC context from crypto_ec_init() */ void crypto_ec_deinit(struct crypto_ec *e); /** * crypto_ec_prime_len - Get length of the prime in octets * @e: EC context from crypto_ec_init() * Returns: Length of the prime defining the group */ size_t crypto_ec_prime_len(struct crypto_ec *e); /** * crypto_ec_prime_len_bits - Get length of the prime in bits * @e: EC context from crypto_ec_init() * Returns: Length of the prime defining the group in bits */ size_t crypto_ec_prime_len_bits(struct crypto_ec *e); /** * crypto_ec_get_prime - Get prime defining an EC group * @e: EC context from crypto_ec_init() * Returns: Prime (bignum) defining the group */ const struct crypto_bignum * crypto_ec_get_prime(struct crypto_ec *e); /** * crypto_ec_get_order - Get order of an EC group * @e: EC context from crypto_ec_init() * Returns: Order (bignum) of the group */ const struct crypto_bignum * crypto_ec_get_order(struct crypto_ec *e); /** * struct crypto_ec_point - Elliptic curve point * * Internal data structure for EC implementation to represent a point. The * contents is specific to the used crypto library. */ struct crypto_ec_point; /** * crypto_ec_point_init - Initialize data for an EC point * @e: EC context from crypto_ec_init() * Returns: Pointer to EC point data or %NULL on failure */ struct crypto_ec_point * crypto_ec_point_init(struct crypto_ec *e); /** * crypto_ec_point_deinit - Deinitialize EC point data * @p: EC point data from crypto_ec_point_init() * @clear: Whether to clear the EC point value from memory */ void crypto_ec_point_deinit(struct crypto_ec_point *p, int clear); /** * crypto_ec_point_to_bin - Write EC point value as binary data * @e: EC context from crypto_ec_init() * @p: EC point data from crypto_ec_point_init() * @x: Buffer for writing the binary data for x coordinate or %NULL if not used * @y: Buffer for writing the binary data for y coordinate or %NULL if not used * Returns: 0 on success, -1 on failure * * This function can be used to write an EC point as binary data in a format * that has the x and y coordinates in big endian byte order fields padded to * the length of the prime defining the group. */ int crypto_ec_point_to_bin(struct crypto_ec *e, const struct crypto_ec_point *point, u8 *x, u8 *y); /** * crypto_ec_point_from_bin - Create EC point from binary data * @e: EC context from crypto_ec_init() * @val: Binary data to read the EC point from * Returns: Pointer to EC point data or %NULL on failure * * This function readers x and y coordinates of the EC point from the provided * buffer assuming the values are in big endian byte order with fields padded to * the length of the prime defining the group. */ struct crypto_ec_point * crypto_ec_point_from_bin(struct crypto_ec *e, const u8 *val); /** * crypto_bignum_add - c = a + b * @e: EC context from crypto_ec_init() * @a: Bignum * @b: Bignum * @c: Bignum; used to store the result of a + b * Returns: 0 on success, -1 on failure */ int crypto_ec_point_add(struct crypto_ec *e, const struct crypto_ec_point *a, const struct crypto_ec_point *b, struct crypto_ec_point *c); /** * crypto_bignum_mul - res = b * p * @e: EC context from crypto_ec_init() * @p: EC point * @b: Bignum * @res: EC point; used to store the result of b * p * Returns: 0 on success, -1 on failure */ int crypto_ec_point_mul(struct crypto_ec *e, const struct crypto_ec_point *p, const struct crypto_bignum *b, struct crypto_ec_point *res); /** * crypto_ec_point_invert - Compute inverse of an EC point * @e: EC context from crypto_ec_init() * @p: EC point to invert (and result of the operation) * Returns: 0 on success, -1 on failure */ int crypto_ec_point_invert(struct crypto_ec *e, struct crypto_ec_point *p); /** * crypto_ec_point_solve_y_coord - Solve y coordinate for an x coordinate * @e: EC context from crypto_ec_init() * @p: EC point to use for the returning the result * @x: x coordinate * @y_bit: y-bit (0 or 1) for selecting the y value to use * Returns: 0 on success, -1 on failure */ int crypto_ec_point_solve_y_coord(struct crypto_ec *e, struct crypto_ec_point *p, const struct crypto_bignum *x, int y_bit); /** * crypto_ec_point_is_at_infinity - Check whether EC point is neutral element * @e: EC context from crypto_ec_init() * @p: EC point * Returns: 1 if the specified EC point is the neutral element of the group or * 0 if not */ int crypto_ec_point_is_at_infinity(struct crypto_ec *e, const struct crypto_ec_point *p); /** * crypto_ec_point_is_on_curve - Check whether EC point is on curve * @e: EC context from crypto_ec_init() * @p: EC point * Returns: 1 if the specified EC point is on the curve or 0 if not */ int crypto_ec_point_is_on_curve(struct crypto_ec *e, const struct crypto_ec_point *p); #endif /* CRYPTO_H */ wpa-2.1/src/crypto/crypto_cryptoapi.c000066400000000000000000000377261227414666700200660ustar00rootroot00000000000000/* * Crypto wrapper for Microsoft CryptoAPI * Copyright (c) 2005-2009, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "includes.h" #include #include #include "common.h" #include "crypto.h" #ifndef MS_ENH_RSA_AES_PROV #ifdef UNICODE #define MS_ENH_RSA_AES_PROV \ L"Microsoft Enhanced RSA and AES Cryptographic Provider (Prototype)" #else #define MS_ENH_RSA_AES_PROV \ "Microsoft Enhanced RSA and AES Cryptographic Provider (Prototype)" #endif #endif /* MS_ENH_RSA_AES_PROV */ #ifndef CALG_HMAC #define CALG_HMAC (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_HMAC) #endif #ifdef __MINGW32_VERSION /* * MinGW does not yet include all the needed definitions for CryptoAPI, so * define here whatever extra is needed. */ static BOOL WINAPI (*CryptImportPublicKeyInfo)(HCRYPTPROV hCryptProv, DWORD dwCertEncodingType, PCERT_PUBLIC_KEY_INFO pInfo, HCRYPTKEY *phKey) = NULL; /* to be loaded from crypt32.dll */ static int mingw_load_crypto_func(void) { HINSTANCE dll; /* MinGW does not yet have full CryptoAPI support, so load the needed * function here. */ if (CryptImportPublicKeyInfo) return 0; dll = LoadLibrary("crypt32"); if (dll == NULL) { wpa_printf(MSG_DEBUG, "CryptoAPI: Could not load crypt32 " "library"); return -1; } CryptImportPublicKeyInfo = GetProcAddress( dll, "CryptImportPublicKeyInfo"); if (CryptImportPublicKeyInfo == NULL) { wpa_printf(MSG_DEBUG, "CryptoAPI: Could not get " "CryptImportPublicKeyInfo() address from " "crypt32 library"); return -1; } return 0; } #else /* __MINGW32_VERSION */ static int mingw_load_crypto_func(void) { return 0; } #endif /* __MINGW32_VERSION */ static void cryptoapi_report_error(const char *msg) { char *s, *pos; DWORD err = GetLastError(); if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, err, 0, (LPTSTR) &s, 0, NULL) == 0) { wpa_printf(MSG_DEBUG, "CryptoAPI: %s: %d", msg, (int) err); } pos = s; while (*pos) { if (*pos == '\n' || *pos == '\r') { *pos = '\0'; break; } pos++; } wpa_printf(MSG_DEBUG, "CryptoAPI: %s: %d: (%s)", msg, (int) err, s); LocalFree(s); } int cryptoapi_hash_vector(ALG_ID alg, size_t hash_len, size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { HCRYPTPROV prov; HCRYPTHASH hash; size_t i; DWORD hlen; int ret = 0; if (!CryptAcquireContext(&prov, NULL, NULL, PROV_RSA_FULL, 0)) { cryptoapi_report_error("CryptAcquireContext"); return -1; } if (!CryptCreateHash(prov, alg, 0, 0, &hash)) { cryptoapi_report_error("CryptCreateHash"); CryptReleaseContext(prov, 0); return -1; } for (i = 0; i < num_elem; i++) { if (!CryptHashData(hash, (BYTE *) addr[i], len[i], 0)) { cryptoapi_report_error("CryptHashData"); CryptDestroyHash(hash); CryptReleaseContext(prov, 0); } } hlen = hash_len; if (!CryptGetHashParam(hash, HP_HASHVAL, mac, &hlen, 0)) { cryptoapi_report_error("CryptGetHashParam"); ret = -1; } CryptDestroyHash(hash); CryptReleaseContext(prov, 0); return ret; } int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { return cryptoapi_hash_vector(CALG_MD4, 16, num_elem, addr, len, mac); } void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) { u8 next, tmp; int i; HCRYPTPROV prov; HCRYPTKEY ckey; DWORD dlen; struct { BLOBHEADER hdr; DWORD len; BYTE key[8]; } key_blob; DWORD mode = CRYPT_MODE_ECB; key_blob.hdr.bType = PLAINTEXTKEYBLOB; key_blob.hdr.bVersion = CUR_BLOB_VERSION; key_blob.hdr.reserved = 0; key_blob.hdr.aiKeyAlg = CALG_DES; key_blob.len = 8; /* Add parity bits to the key */ next = 0; for (i = 0; i < 7; i++) { tmp = key[i]; key_blob.key[i] = (tmp >> i) | next | 1; next = tmp << (7 - i); } key_blob.key[i] = next | 1; if (!CryptAcquireContext(&prov, NULL, MS_ENHANCED_PROV, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) { wpa_printf(MSG_DEBUG, "CryptoAPI: CryptAcquireContext failed: " "%d", (int) GetLastError()); return; } if (!CryptImportKey(prov, (BYTE *) &key_blob, sizeof(key_blob), 0, 0, &ckey)) { wpa_printf(MSG_DEBUG, "CryptoAPI: CryptImportKey failed: %d", (int) GetLastError()); CryptReleaseContext(prov, 0); return; } if (!CryptSetKeyParam(ckey, KP_MODE, (BYTE *) &mode, 0)) { wpa_printf(MSG_DEBUG, "CryptoAPI: CryptSetKeyParam(KP_MODE) " "failed: %d", (int) GetLastError()); CryptDestroyKey(ckey); CryptReleaseContext(prov, 0); return; } os_memcpy(cypher, clear, 8); dlen = 8; if (!CryptEncrypt(ckey, 0, FALSE, 0, cypher, &dlen, 8)) { wpa_printf(MSG_DEBUG, "CryptoAPI: CryptEncrypt failed: %d", (int) GetLastError()); os_memset(cypher, 0, 8); } CryptDestroyKey(ckey); CryptReleaseContext(prov, 0); } int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { return cryptoapi_hash_vector(CALG_MD5, 16, num_elem, addr, len, mac); } int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { return cryptoapi_hash_vector(CALG_SHA, 20, num_elem, addr, len, mac); } struct aes_context { HCRYPTPROV prov; HCRYPTKEY ckey; }; void * aes_encrypt_init(const u8 *key, size_t len) { struct aes_context *akey; struct { BLOBHEADER hdr; DWORD len; BYTE key[16]; } key_blob; DWORD mode = CRYPT_MODE_ECB; if (len != 16) return NULL; key_blob.hdr.bType = PLAINTEXTKEYBLOB; key_blob.hdr.bVersion = CUR_BLOB_VERSION; key_blob.hdr.reserved = 0; key_blob.hdr.aiKeyAlg = CALG_AES_128; key_blob.len = len; os_memcpy(key_blob.key, key, len); akey = os_zalloc(sizeof(*akey)); if (akey == NULL) return NULL; if (!CryptAcquireContext(&akey->prov, NULL, MS_ENH_RSA_AES_PROV, PROV_RSA_AES, CRYPT_VERIFYCONTEXT)) { wpa_printf(MSG_DEBUG, "CryptoAPI: CryptAcquireContext failed: " "%d", (int) GetLastError()); os_free(akey); return NULL; } if (!CryptImportKey(akey->prov, (BYTE *) &key_blob, sizeof(key_blob), 0, 0, &akey->ckey)) { wpa_printf(MSG_DEBUG, "CryptoAPI: CryptImportKey failed: %d", (int) GetLastError()); CryptReleaseContext(akey->prov, 0); os_free(akey); return NULL; } if (!CryptSetKeyParam(akey->ckey, KP_MODE, (BYTE *) &mode, 0)) { wpa_printf(MSG_DEBUG, "CryptoAPI: CryptSetKeyParam(KP_MODE) " "failed: %d", (int) GetLastError()); CryptDestroyKey(akey->ckey); CryptReleaseContext(akey->prov, 0); os_free(akey); return NULL; } return akey; } void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt) { struct aes_context *akey = ctx; DWORD dlen; os_memcpy(crypt, plain, 16); dlen = 16; if (!CryptEncrypt(akey->ckey, 0, FALSE, 0, crypt, &dlen, 16)) { wpa_printf(MSG_DEBUG, "CryptoAPI: CryptEncrypt failed: %d", (int) GetLastError()); os_memset(crypt, 0, 16); } } void aes_encrypt_deinit(void *ctx) { struct aes_context *akey = ctx; if (akey) { CryptDestroyKey(akey->ckey); CryptReleaseContext(akey->prov, 0); os_free(akey); } } void * aes_decrypt_init(const u8 *key, size_t len) { return aes_encrypt_init(key, len); } void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain) { struct aes_context *akey = ctx; DWORD dlen; os_memcpy(plain, crypt, 16); dlen = 16; if (!CryptDecrypt(akey->ckey, 0, FALSE, 0, plain, &dlen)) { wpa_printf(MSG_DEBUG, "CryptoAPI: CryptDecrypt failed: %d", (int) GetLastError()); } } void aes_decrypt_deinit(void *ctx) { aes_encrypt_deinit(ctx); } struct crypto_hash { enum crypto_hash_alg alg; int error; HCRYPTPROV prov; HCRYPTHASH hash; HCRYPTKEY key; }; struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key, size_t key_len) { struct crypto_hash *ctx; ALG_ID calg; struct { BLOBHEADER hdr; DWORD len; BYTE key[32]; } key_blob; os_memset(&key_blob, 0, sizeof(key_blob)); switch (alg) { case CRYPTO_HASH_ALG_MD5: calg = CALG_MD5; break; case CRYPTO_HASH_ALG_SHA1: calg = CALG_SHA; break; case CRYPTO_HASH_ALG_HMAC_MD5: case CRYPTO_HASH_ALG_HMAC_SHA1: calg = CALG_HMAC; key_blob.hdr.bType = PLAINTEXTKEYBLOB; key_blob.hdr.bVersion = CUR_BLOB_VERSION; key_blob.hdr.reserved = 0; /* * Note: RC2 is not really used, but that can be used to * import HMAC keys of up to 16 byte long. * CRYPT_IPSEC_HMAC_KEY flag for CryptImportKey() is needed to * be able to import longer keys (HMAC-SHA1 uses 20-byte key). */ key_blob.hdr.aiKeyAlg = CALG_RC2; key_blob.len = key_len; if (key_len > sizeof(key_blob.key)) return NULL; os_memcpy(key_blob.key, key, key_len); break; default: return NULL; } ctx = os_zalloc(sizeof(*ctx)); if (ctx == NULL) return NULL; ctx->alg = alg; if (!CryptAcquireContext(&ctx->prov, NULL, NULL, PROV_RSA_FULL, 0)) { cryptoapi_report_error("CryptAcquireContext"); os_free(ctx); return NULL; } if (calg == CALG_HMAC) { #ifndef CRYPT_IPSEC_HMAC_KEY #define CRYPT_IPSEC_HMAC_KEY 0x00000100 #endif if (!CryptImportKey(ctx->prov, (BYTE *) &key_blob, sizeof(key_blob), 0, CRYPT_IPSEC_HMAC_KEY, &ctx->key)) { cryptoapi_report_error("CryptImportKey"); CryptReleaseContext(ctx->prov, 0); os_free(ctx); return NULL; } } if (!CryptCreateHash(ctx->prov, calg, ctx->key, 0, &ctx->hash)) { cryptoapi_report_error("CryptCreateHash"); CryptReleaseContext(ctx->prov, 0); os_free(ctx); return NULL; } if (calg == CALG_HMAC) { HMAC_INFO info; os_memset(&info, 0, sizeof(info)); switch (alg) { case CRYPTO_HASH_ALG_HMAC_MD5: info.HashAlgid = CALG_MD5; break; case CRYPTO_HASH_ALG_HMAC_SHA1: info.HashAlgid = CALG_SHA; break; default: /* unreachable */ break; } if (!CryptSetHashParam(ctx->hash, HP_HMAC_INFO, (BYTE *) &info, 0)) { cryptoapi_report_error("CryptSetHashParam"); CryptDestroyHash(ctx->hash); CryptReleaseContext(ctx->prov, 0); os_free(ctx); return NULL; } } return ctx; } void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len) { if (ctx == NULL || ctx->error) return; if (!CryptHashData(ctx->hash, (BYTE *) data, len, 0)) { cryptoapi_report_error("CryptHashData"); ctx->error = 1; } } int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len) { int ret = 0; DWORD hlen; if (ctx == NULL) return -2; if (mac == NULL || len == NULL) goto done; if (ctx->error) { ret = -2; goto done; } hlen = *len; if (!CryptGetHashParam(ctx->hash, HP_HASHVAL, mac, &hlen, 0)) { cryptoapi_report_error("CryptGetHashParam"); ret = -2; } *len = hlen; done: if (ctx->alg == CRYPTO_HASH_ALG_HMAC_SHA1 || ctx->alg == CRYPTO_HASH_ALG_HMAC_MD5) CryptDestroyKey(ctx->key); os_free(ctx); return ret; } struct crypto_cipher { HCRYPTPROV prov; HCRYPTKEY key; }; struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg, const u8 *iv, const u8 *key, size_t key_len) { struct crypto_cipher *ctx; struct { BLOBHEADER hdr; DWORD len; BYTE key[32]; } key_blob; DWORD mode = CRYPT_MODE_CBC; key_blob.hdr.bType = PLAINTEXTKEYBLOB; key_blob.hdr.bVersion = CUR_BLOB_VERSION; key_blob.hdr.reserved = 0; key_blob.len = key_len; if (key_len > sizeof(key_blob.key)) return NULL; os_memcpy(key_blob.key, key, key_len); switch (alg) { case CRYPTO_CIPHER_ALG_AES: if (key_len == 32) key_blob.hdr.aiKeyAlg = CALG_AES_256; else if (key_len == 24) key_blob.hdr.aiKeyAlg = CALG_AES_192; else key_blob.hdr.aiKeyAlg = CALG_AES_128; break; case CRYPTO_CIPHER_ALG_3DES: key_blob.hdr.aiKeyAlg = CALG_3DES; break; case CRYPTO_CIPHER_ALG_DES: key_blob.hdr.aiKeyAlg = CALG_DES; break; case CRYPTO_CIPHER_ALG_RC2: key_blob.hdr.aiKeyAlg = CALG_RC2; break; case CRYPTO_CIPHER_ALG_RC4: key_blob.hdr.aiKeyAlg = CALG_RC4; break; default: return NULL; } ctx = os_zalloc(sizeof(*ctx)); if (ctx == NULL) return NULL; if (!CryptAcquireContext(&ctx->prov, NULL, MS_ENH_RSA_AES_PROV, PROV_RSA_AES, CRYPT_VERIFYCONTEXT)) { cryptoapi_report_error("CryptAcquireContext"); goto fail1; } if (!CryptImportKey(ctx->prov, (BYTE *) &key_blob, sizeof(key_blob), 0, 0, &ctx->key)) { cryptoapi_report_error("CryptImportKey"); goto fail2; } if (!CryptSetKeyParam(ctx->key, KP_MODE, (BYTE *) &mode, 0)) { cryptoapi_report_error("CryptSetKeyParam(KP_MODE)"); goto fail3; } if (iv && !CryptSetKeyParam(ctx->key, KP_IV, (BYTE *) iv, 0)) { cryptoapi_report_error("CryptSetKeyParam(KP_IV)"); goto fail3; } return ctx; fail3: CryptDestroyKey(ctx->key); fail2: CryptReleaseContext(ctx->prov, 0); fail1: os_free(ctx); return NULL; } int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain, u8 *crypt, size_t len) { DWORD dlen; os_memcpy(crypt, plain, len); dlen = len; if (!CryptEncrypt(ctx->key, 0, FALSE, 0, crypt, &dlen, len)) { cryptoapi_report_error("CryptEncrypt"); os_memset(crypt, 0, len); return -1; } return 0; } int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt, u8 *plain, size_t len) { DWORD dlen; os_memcpy(plain, crypt, len); dlen = len; if (!CryptDecrypt(ctx->key, 0, FALSE, 0, plain, &dlen)) { cryptoapi_report_error("CryptDecrypt"); return -1; } return 0; } void crypto_cipher_deinit(struct crypto_cipher *ctx) { CryptDestroyKey(ctx->key); CryptReleaseContext(ctx->prov, 0); os_free(ctx); } struct crypto_public_key { HCRYPTPROV prov; HCRYPTKEY rsa; }; struct crypto_private_key { HCRYPTPROV prov; HCRYPTKEY rsa; }; struct crypto_public_key * crypto_public_key_import(const u8 *key, size_t len) { /* Use crypto_public_key_from_cert() instead. */ return NULL; } struct crypto_private_key * crypto_private_key_import(const u8 *key, size_t len, const char *passwd) { /* TODO */ return NULL; } struct crypto_public_key * crypto_public_key_from_cert(const u8 *buf, size_t len) { struct crypto_public_key *pk; PCCERT_CONTEXT cc; pk = os_zalloc(sizeof(*pk)); if (pk == NULL) return NULL; cc = CertCreateCertificateContext(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, buf, len); if (!cc) { cryptoapi_report_error("CryptCreateCertificateContext"); os_free(pk); return NULL; } if (!CryptAcquireContext(&pk->prov, NULL, MS_DEF_PROV, PROV_RSA_FULL, 0)) { cryptoapi_report_error("CryptAcquireContext"); os_free(pk); CertFreeCertificateContext(cc); return NULL; } if (!CryptImportPublicKeyInfo(pk->prov, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, &cc->pCertInfo->SubjectPublicKeyInfo, &pk->rsa)) { cryptoapi_report_error("CryptImportPublicKeyInfo"); CryptReleaseContext(pk->prov, 0); os_free(pk); CertFreeCertificateContext(cc); return NULL; } CertFreeCertificateContext(cc); return pk; } int crypto_public_key_encrypt_pkcs1_v15(struct crypto_public_key *key, const u8 *in, size_t inlen, u8 *out, size_t *outlen) { DWORD clen; u8 *tmp; size_t i; if (*outlen < inlen) return -1; tmp = malloc(*outlen); if (tmp == NULL) return -1; os_memcpy(tmp, in, inlen); clen = inlen; if (!CryptEncrypt(key->rsa, 0, TRUE, 0, tmp, &clen, *outlen)) { wpa_printf(MSG_DEBUG, "CryptoAPI: Failed to encrypt using " "public key: %d", (int) GetLastError()); os_free(tmp); return -1; } *outlen = clen; /* Reverse the output */ for (i = 0; i < *outlen; i++) out[i] = tmp[*outlen - 1 - i]; os_free(tmp); return 0; } int crypto_private_key_sign_pkcs1(struct crypto_private_key *key, const u8 *in, size_t inlen, u8 *out, size_t *outlen) { /* TODO */ return -1; } void crypto_public_key_free(struct crypto_public_key *key) { if (key) { CryptDestroyKey(key->rsa); CryptReleaseContext(key->prov, 0); os_free(key); } } void crypto_private_key_free(struct crypto_private_key *key) { if (key) { CryptDestroyKey(key->rsa); CryptReleaseContext(key->prov, 0); os_free(key); } } int crypto_global_init(void) { return mingw_load_crypto_func(); } void crypto_global_deinit(void) { } int crypto_mod_exp(const u8 *base, size_t base_len, const u8 *power, size_t power_len, const u8 *modulus, size_t modulus_len, u8 *result, size_t *result_len) { /* TODO */ return -1; } wpa-2.1/src/crypto/crypto_gnutls.c000066400000000000000000000152221227414666700173530ustar00rootroot00000000000000/* * WPA Supplicant / wrapper functions for libgcrypt * 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 #include "common.h" #include "crypto.h" int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { gcry_md_hd_t hd; unsigned char *p; size_t i; if (gcry_md_open(&hd, GCRY_MD_MD4, 0) != GPG_ERR_NO_ERROR) return -1; for (i = 0; i < num_elem; i++) gcry_md_write(hd, addr[i], len[i]); p = gcry_md_read(hd, GCRY_MD_MD4); if (p) memcpy(mac, p, gcry_md_get_algo_dlen(GCRY_MD_MD4)); gcry_md_close(hd); return 0; } void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) { gcry_cipher_hd_t hd; u8 pkey[8], next, tmp; int i; /* Add parity bits to the key */ next = 0; for (i = 0; i < 7; i++) { tmp = key[i]; pkey[i] = (tmp >> i) | next | 1; next = tmp << (7 - i); } pkey[i] = next | 1; gcry_cipher_open(&hd, GCRY_CIPHER_DES, GCRY_CIPHER_MODE_ECB, 0); gcry_err_code(gcry_cipher_setkey(hd, pkey, 8)); gcry_cipher_encrypt(hd, cypher, 8, clear, 8); gcry_cipher_close(hd); } int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { gcry_md_hd_t hd; unsigned char *p; size_t i; if (gcry_md_open(&hd, GCRY_MD_MD5, 0) != GPG_ERR_NO_ERROR) return -1; for (i = 0; i < num_elem; i++) gcry_md_write(hd, addr[i], len[i]); p = gcry_md_read(hd, GCRY_MD_MD5); if (p) memcpy(mac, p, gcry_md_get_algo_dlen(GCRY_MD_MD5)); gcry_md_close(hd); return 0; } int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { gcry_md_hd_t hd; unsigned char *p; size_t i; if (gcry_md_open(&hd, GCRY_MD_SHA1, 0) != GPG_ERR_NO_ERROR) return -1; for (i = 0; i < num_elem; i++) gcry_md_write(hd, addr[i], len[i]); p = gcry_md_read(hd, GCRY_MD_SHA1); if (p) memcpy(mac, p, gcry_md_get_algo_dlen(GCRY_MD_SHA1)); gcry_md_close(hd); return 0; } void * aes_encrypt_init(const u8 *key, size_t len) { gcry_cipher_hd_t hd; if (gcry_cipher_open(&hd, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_ECB, 0) != GPG_ERR_NO_ERROR) { printf("cipher open failed\n"); return NULL; } if (gcry_cipher_setkey(hd, key, len) != GPG_ERR_NO_ERROR) { printf("setkey failed\n"); gcry_cipher_close(hd); return NULL; } return hd; } void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt) { gcry_cipher_hd_t hd = ctx; gcry_cipher_encrypt(hd, crypt, 16, plain, 16); } void aes_encrypt_deinit(void *ctx) { gcry_cipher_hd_t hd = ctx; gcry_cipher_close(hd); } void * aes_decrypt_init(const u8 *key, size_t len) { gcry_cipher_hd_t hd; if (gcry_cipher_open(&hd, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_ECB, 0) != GPG_ERR_NO_ERROR) return NULL; if (gcry_cipher_setkey(hd, key, len) != GPG_ERR_NO_ERROR) { gcry_cipher_close(hd); return NULL; } return hd; } void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain) { gcry_cipher_hd_t hd = ctx; gcry_cipher_decrypt(hd, plain, 16, crypt, 16); } void aes_decrypt_deinit(void *ctx) { gcry_cipher_hd_t hd = ctx; gcry_cipher_close(hd); } int crypto_mod_exp(const u8 *base, size_t base_len, const u8 *power, size_t power_len, const u8 *modulus, size_t modulus_len, u8 *result, size_t *result_len) { gcry_mpi_t bn_base = NULL, bn_exp = NULL, bn_modulus = NULL, bn_result = NULL; int ret = -1; if (gcry_mpi_scan(&bn_base, GCRYMPI_FMT_USG, base, base_len, NULL) != GPG_ERR_NO_ERROR || gcry_mpi_scan(&bn_exp, GCRYMPI_FMT_USG, power, power_len, NULL) != GPG_ERR_NO_ERROR || gcry_mpi_scan(&bn_modulus, GCRYMPI_FMT_USG, modulus, modulus_len, NULL) != GPG_ERR_NO_ERROR) goto error; bn_result = gcry_mpi_new(modulus_len * 8); gcry_mpi_powm(bn_result, bn_base, bn_exp, bn_modulus); if (gcry_mpi_print(GCRYMPI_FMT_USG, result, *result_len, result_len, bn_result) != GPG_ERR_NO_ERROR) goto error; ret = 0; error: gcry_mpi_release(bn_base); gcry_mpi_release(bn_exp); gcry_mpi_release(bn_modulus); gcry_mpi_release(bn_result); return ret; } struct crypto_cipher { gcry_cipher_hd_t enc; gcry_cipher_hd_t dec; }; struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg, const u8 *iv, const u8 *key, size_t key_len) { struct crypto_cipher *ctx; gcry_error_t res; enum gcry_cipher_algos a; int ivlen; ctx = os_zalloc(sizeof(*ctx)); if (ctx == NULL) return NULL; switch (alg) { case CRYPTO_CIPHER_ALG_RC4: a = GCRY_CIPHER_ARCFOUR; res = gcry_cipher_open(&ctx->enc, a, GCRY_CIPHER_MODE_STREAM, 0); gcry_cipher_open(&ctx->dec, a, GCRY_CIPHER_MODE_STREAM, 0); break; case CRYPTO_CIPHER_ALG_AES: if (key_len == 24) a = GCRY_CIPHER_AES192; else if (key_len == 32) a = GCRY_CIPHER_AES256; else a = GCRY_CIPHER_AES; res = gcry_cipher_open(&ctx->enc, a, GCRY_CIPHER_MODE_CBC, 0); gcry_cipher_open(&ctx->dec, a, GCRY_CIPHER_MODE_CBC, 0); break; case CRYPTO_CIPHER_ALG_3DES: a = GCRY_CIPHER_3DES; res = gcry_cipher_open(&ctx->enc, a, GCRY_CIPHER_MODE_CBC, 0); gcry_cipher_open(&ctx->dec, a, GCRY_CIPHER_MODE_CBC, 0); break; case CRYPTO_CIPHER_ALG_DES: a = GCRY_CIPHER_DES; res = gcry_cipher_open(&ctx->enc, a, GCRY_CIPHER_MODE_CBC, 0); gcry_cipher_open(&ctx->dec, a, GCRY_CIPHER_MODE_CBC, 0); break; case CRYPTO_CIPHER_ALG_RC2: if (key_len == 5) a = GCRY_CIPHER_RFC2268_40; else a = GCRY_CIPHER_RFC2268_128; res = gcry_cipher_open(&ctx->enc, a, GCRY_CIPHER_MODE_CBC, 0); gcry_cipher_open(&ctx->dec, a, GCRY_CIPHER_MODE_CBC, 0); break; default: os_free(ctx); return NULL; } if (res != GPG_ERR_NO_ERROR) { os_free(ctx); return NULL; } if (gcry_cipher_setkey(ctx->enc, key, key_len) != GPG_ERR_NO_ERROR || gcry_cipher_setkey(ctx->dec, key, key_len) != GPG_ERR_NO_ERROR) { gcry_cipher_close(ctx->enc); gcry_cipher_close(ctx->dec); os_free(ctx); return NULL; } ivlen = gcry_cipher_get_algo_blklen(a); if (gcry_cipher_setiv(ctx->enc, iv, ivlen) != GPG_ERR_NO_ERROR || gcry_cipher_setiv(ctx->dec, iv, ivlen) != GPG_ERR_NO_ERROR) { gcry_cipher_close(ctx->enc); gcry_cipher_close(ctx->dec); os_free(ctx); return NULL; } return ctx; } int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain, u8 *crypt, size_t len) { if (gcry_cipher_encrypt(ctx->enc, crypt, len, plain, len) != GPG_ERR_NO_ERROR) return -1; return 0; } int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt, u8 *plain, size_t len) { if (gcry_cipher_decrypt(ctx->dec, plain, len, crypt, len) != GPG_ERR_NO_ERROR) return -1; return 0; } void crypto_cipher_deinit(struct crypto_cipher *ctx) { gcry_cipher_close(ctx->enc); gcry_cipher_close(ctx->dec); os_free(ctx); } wpa-2.1/src/crypto/crypto_internal-cipher.c000066400000000000000000000121171227414666700211230ustar00rootroot00000000000000/* * Crypto wrapper for internal crypto implementation - Cipher wrappers * Copyright (c) 2006-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 "crypto.h" #include "aes.h" #include "des_i.h" struct crypto_cipher { enum crypto_cipher_alg alg; union { struct { size_t used_bytes; u8 key[16]; size_t keylen; } rc4; struct { u8 cbc[32]; void *ctx_enc; void *ctx_dec; } aes; struct { struct des3_key_s key; u8 cbc[8]; } des3; struct { u32 ek[32]; u32 dk[32]; u8 cbc[8]; } des; } u; }; struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg, const u8 *iv, const u8 *key, size_t key_len) { struct crypto_cipher *ctx; ctx = os_zalloc(sizeof(*ctx)); if (ctx == NULL) return NULL; ctx->alg = alg; switch (alg) { case CRYPTO_CIPHER_ALG_RC4: if (key_len > sizeof(ctx->u.rc4.key)) { os_free(ctx); return NULL; } ctx->u.rc4.keylen = key_len; os_memcpy(ctx->u.rc4.key, key, key_len); break; case CRYPTO_CIPHER_ALG_AES: ctx->u.aes.ctx_enc = aes_encrypt_init(key, key_len); if (ctx->u.aes.ctx_enc == NULL) { os_free(ctx); return NULL; } ctx->u.aes.ctx_dec = aes_decrypt_init(key, key_len); if (ctx->u.aes.ctx_dec == NULL) { aes_encrypt_deinit(ctx->u.aes.ctx_enc); os_free(ctx); return NULL; } os_memcpy(ctx->u.aes.cbc, iv, AES_BLOCK_SIZE); break; case CRYPTO_CIPHER_ALG_3DES: if (key_len != 24) { os_free(ctx); return NULL; } des3_key_setup(key, &ctx->u.des3.key); os_memcpy(ctx->u.des3.cbc, iv, 8); break; case CRYPTO_CIPHER_ALG_DES: if (key_len != 8) { os_free(ctx); return NULL; } des_key_setup(key, ctx->u.des.ek, ctx->u.des.dk); os_memcpy(ctx->u.des.cbc, iv, 8); break; default: os_free(ctx); return NULL; } return ctx; } int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain, u8 *crypt, size_t len) { size_t i, j, blocks; switch (ctx->alg) { case CRYPTO_CIPHER_ALG_RC4: if (plain != crypt) os_memcpy(crypt, plain, len); rc4_skip(ctx->u.rc4.key, ctx->u.rc4.keylen, ctx->u.rc4.used_bytes, crypt, len); ctx->u.rc4.used_bytes += len; break; case CRYPTO_CIPHER_ALG_AES: if (len % AES_BLOCK_SIZE) return -1; blocks = len / AES_BLOCK_SIZE; for (i = 0; i < blocks; i++) { for (j = 0; j < AES_BLOCK_SIZE; j++) ctx->u.aes.cbc[j] ^= plain[j]; aes_encrypt(ctx->u.aes.ctx_enc, ctx->u.aes.cbc, ctx->u.aes.cbc); os_memcpy(crypt, ctx->u.aes.cbc, AES_BLOCK_SIZE); plain += AES_BLOCK_SIZE; crypt += AES_BLOCK_SIZE; } break; case CRYPTO_CIPHER_ALG_3DES: if (len % 8) return -1; blocks = len / 8; for (i = 0; i < blocks; i++) { for (j = 0; j < 8; j++) ctx->u.des3.cbc[j] ^= plain[j]; des3_encrypt(ctx->u.des3.cbc, &ctx->u.des3.key, ctx->u.des3.cbc); os_memcpy(crypt, ctx->u.des3.cbc, 8); plain += 8; crypt += 8; } break; case CRYPTO_CIPHER_ALG_DES: if (len % 8) return -1; blocks = len / 8; for (i = 0; i < blocks; i++) { for (j = 0; j < 8; j++) ctx->u.des3.cbc[j] ^= plain[j]; des_block_encrypt(ctx->u.des.cbc, ctx->u.des.ek, ctx->u.des.cbc); os_memcpy(crypt, ctx->u.des.cbc, 8); plain += 8; crypt += 8; } break; default: return -1; } return 0; } int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt, u8 *plain, size_t len) { size_t i, j, blocks; u8 tmp[32]; switch (ctx->alg) { case CRYPTO_CIPHER_ALG_RC4: if (plain != crypt) os_memcpy(plain, crypt, len); rc4_skip(ctx->u.rc4.key, ctx->u.rc4.keylen, ctx->u.rc4.used_bytes, plain, len); ctx->u.rc4.used_bytes += len; break; case CRYPTO_CIPHER_ALG_AES: if (len % AES_BLOCK_SIZE) return -1; blocks = len / AES_BLOCK_SIZE; for (i = 0; i < blocks; i++) { os_memcpy(tmp, crypt, AES_BLOCK_SIZE); aes_decrypt(ctx->u.aes.ctx_dec, crypt, plain); for (j = 0; j < AES_BLOCK_SIZE; j++) plain[j] ^= ctx->u.aes.cbc[j]; os_memcpy(ctx->u.aes.cbc, tmp, AES_BLOCK_SIZE); plain += AES_BLOCK_SIZE; crypt += AES_BLOCK_SIZE; } break; case CRYPTO_CIPHER_ALG_3DES: if (len % 8) return -1; blocks = len / 8; for (i = 0; i < blocks; i++) { os_memcpy(tmp, crypt, 8); des3_decrypt(crypt, &ctx->u.des3.key, plain); for (j = 0; j < 8; j++) plain[j] ^= ctx->u.des3.cbc[j]; os_memcpy(ctx->u.des3.cbc, tmp, 8); plain += 8; crypt += 8; } break; case CRYPTO_CIPHER_ALG_DES: if (len % 8) return -1; blocks = len / 8; for (i = 0; i < blocks; i++) { os_memcpy(tmp, crypt, 8); des_block_decrypt(crypt, ctx->u.des.dk, plain); for (j = 0; j < 8; j++) plain[j] ^= ctx->u.des.cbc[j]; os_memcpy(ctx->u.des.cbc, tmp, 8); plain += 8; crypt += 8; } break; default: return -1; } return 0; } void crypto_cipher_deinit(struct crypto_cipher *ctx) { switch (ctx->alg) { case CRYPTO_CIPHER_ALG_AES: aes_encrypt_deinit(ctx->u.aes.ctx_enc); aes_decrypt_deinit(ctx->u.aes.ctx_dec); break; case CRYPTO_CIPHER_ALG_3DES: break; default: break; } os_free(ctx); } wpa-2.1/src/crypto/crypto_internal-modexp.c000066400000000000000000000023501227414666700211430ustar00rootroot00000000000000/* * Crypto wrapper for internal crypto implementation - modexp * Copyright (c) 2006-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 "tls/bignum.h" #include "crypto.h" int crypto_mod_exp(const u8 *base, size_t base_len, const u8 *power, size_t power_len, const u8 *modulus, size_t modulus_len, u8 *result, size_t *result_len) { struct bignum *bn_base, *bn_exp, *bn_modulus, *bn_result; int ret = -1; bn_base = bignum_init(); bn_exp = bignum_init(); bn_modulus = bignum_init(); bn_result = bignum_init(); if (bn_base == NULL || bn_exp == NULL || bn_modulus == NULL || bn_result == NULL) goto error; if (bignum_set_unsigned_bin(bn_base, base, base_len) < 0 || bignum_set_unsigned_bin(bn_exp, power, power_len) < 0 || bignum_set_unsigned_bin(bn_modulus, modulus, modulus_len) < 0) goto error; if (bignum_exptmod(bn_base, bn_exp, bn_modulus, bn_result) < 0) goto error; ret = bignum_get_unsigned_bin(bn_result, result, result_len); error: bignum_deinit(bn_base); bignum_deinit(bn_exp); bignum_deinit(bn_modulus); bignum_deinit(bn_result); return ret; } wpa-2.1/src/crypto/crypto_internal-rsa.c000066400000000000000000000051361227414666700204410ustar00rootroot00000000000000/* * Crypto wrapper for internal crypto implementation - RSA parts * Copyright (c) 2006-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 "crypto.h" #include "tls/rsa.h" #include "tls/pkcs1.h" #include "tls/pkcs8.h" /* Dummy structures; these are just typecast to struct crypto_rsa_key */ struct crypto_public_key; struct crypto_private_key; struct crypto_public_key * crypto_public_key_import(const u8 *key, size_t len) { return (struct crypto_public_key *) crypto_rsa_import_public_key(key, len); } struct crypto_private_key * crypto_private_key_import(const u8 *key, size_t len, const char *passwd) { struct crypto_private_key *res; /* First, check for possible PKCS #8 encoding */ res = pkcs8_key_import(key, len); if (res) return res; if (passwd) { /* Try to parse as encrypted PKCS #8 */ res = pkcs8_enc_key_import(key, len, passwd); if (res) return res; } /* Not PKCS#8, so try to import PKCS #1 encoded RSA private key */ wpa_printf(MSG_DEBUG, "Trying to parse PKCS #1 encoded RSA private " "key"); return (struct crypto_private_key *) crypto_rsa_import_private_key(key, len); } struct crypto_public_key * crypto_public_key_from_cert(const u8 *buf, size_t len) { /* No X.509 support in crypto_internal.c */ return NULL; } int crypto_public_key_encrypt_pkcs1_v15(struct crypto_public_key *key, const u8 *in, size_t inlen, u8 *out, size_t *outlen) { return pkcs1_encrypt(2, (struct crypto_rsa_key *) key, 0, in, inlen, out, outlen); } int crypto_private_key_decrypt_pkcs1_v15(struct crypto_private_key *key, const u8 *in, size_t inlen, u8 *out, size_t *outlen) { return pkcs1_v15_private_key_decrypt((struct crypto_rsa_key *) key, in, inlen, out, outlen); } int crypto_private_key_sign_pkcs1(struct crypto_private_key *key, const u8 *in, size_t inlen, u8 *out, size_t *outlen) { return pkcs1_encrypt(1, (struct crypto_rsa_key *) key, 1, in, inlen, out, outlen); } void crypto_public_key_free(struct crypto_public_key *key) { crypto_rsa_free((struct crypto_rsa_key *) key); } void crypto_private_key_free(struct crypto_private_key *key) { crypto_rsa_free((struct crypto_rsa_key *) key); } int crypto_public_key_decrypt_pkcs1(struct crypto_public_key *key, const u8 *crypt, size_t crypt_len, u8 *plain, size_t *plain_len) { return pkcs1_decrypt_public_key((struct crypto_rsa_key *) key, crypt, crypt_len, plain, plain_len); } wpa-2.1/src/crypto/crypto_internal.c000066400000000000000000000131351227414666700176540ustar00rootroot00000000000000/* * Crypto wrapper for internal crypto implementation * Copyright (c) 2006-2011, 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.h" #include "sha256_i.h" #include "sha1_i.h" #include "md5_i.h" struct crypto_hash { enum crypto_hash_alg alg; union { struct MD5Context md5; struct SHA1Context sha1; #ifdef CONFIG_SHA256 struct sha256_state sha256; #endif /* CONFIG_SHA256 */ } u; u8 key[64]; size_t key_len; }; struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key, size_t key_len) { struct crypto_hash *ctx; u8 k_pad[64]; u8 tk[32]; size_t i; ctx = os_zalloc(sizeof(*ctx)); if (ctx == NULL) return NULL; ctx->alg = alg; switch (alg) { case CRYPTO_HASH_ALG_MD5: MD5Init(&ctx->u.md5); break; case CRYPTO_HASH_ALG_SHA1: SHA1Init(&ctx->u.sha1); break; #ifdef CONFIG_SHA256 case CRYPTO_HASH_ALG_SHA256: sha256_init(&ctx->u.sha256); break; #endif /* CONFIG_SHA256 */ case CRYPTO_HASH_ALG_HMAC_MD5: if (key_len > sizeof(k_pad)) { MD5Init(&ctx->u.md5); MD5Update(&ctx->u.md5, key, key_len); MD5Final(tk, &ctx->u.md5); key = tk; key_len = 16; } os_memcpy(ctx->key, key, key_len); ctx->key_len = key_len; os_memcpy(k_pad, key, key_len); if (key_len < sizeof(k_pad)) os_memset(k_pad + key_len, 0, sizeof(k_pad) - key_len); for (i = 0; i < sizeof(k_pad); i++) k_pad[i] ^= 0x36; MD5Init(&ctx->u.md5); MD5Update(&ctx->u.md5, k_pad, sizeof(k_pad)); break; case CRYPTO_HASH_ALG_HMAC_SHA1: if (key_len > sizeof(k_pad)) { SHA1Init(&ctx->u.sha1); SHA1Update(&ctx->u.sha1, key, key_len); SHA1Final(tk, &ctx->u.sha1); key = tk; key_len = 20; } os_memcpy(ctx->key, key, key_len); ctx->key_len = key_len; os_memcpy(k_pad, key, key_len); if (key_len < sizeof(k_pad)) os_memset(k_pad + key_len, 0, sizeof(k_pad) - key_len); for (i = 0; i < sizeof(k_pad); i++) k_pad[i] ^= 0x36; SHA1Init(&ctx->u.sha1); SHA1Update(&ctx->u.sha1, k_pad, sizeof(k_pad)); break; #ifdef CONFIG_SHA256 case CRYPTO_HASH_ALG_HMAC_SHA256: if (key_len > sizeof(k_pad)) { sha256_init(&ctx->u.sha256); sha256_process(&ctx->u.sha256, key, key_len); sha256_done(&ctx->u.sha256, tk); key = tk; key_len = 32; } os_memcpy(ctx->key, key, key_len); ctx->key_len = key_len; os_memcpy(k_pad, key, key_len); if (key_len < sizeof(k_pad)) os_memset(k_pad + key_len, 0, sizeof(k_pad) - key_len); for (i = 0; i < sizeof(k_pad); i++) k_pad[i] ^= 0x36; sha256_init(&ctx->u.sha256); sha256_process(&ctx->u.sha256, k_pad, sizeof(k_pad)); break; #endif /* CONFIG_SHA256 */ default: os_free(ctx); return NULL; } return ctx; } void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len) { if (ctx == NULL) return; switch (ctx->alg) { case CRYPTO_HASH_ALG_MD5: case CRYPTO_HASH_ALG_HMAC_MD5: MD5Update(&ctx->u.md5, data, len); break; case CRYPTO_HASH_ALG_SHA1: case CRYPTO_HASH_ALG_HMAC_SHA1: SHA1Update(&ctx->u.sha1, data, len); break; #ifdef CONFIG_SHA256 case CRYPTO_HASH_ALG_SHA256: case CRYPTO_HASH_ALG_HMAC_SHA256: sha256_process(&ctx->u.sha256, data, len); break; #endif /* CONFIG_SHA256 */ default: break; } } int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len) { u8 k_pad[64]; size_t i; if (ctx == NULL) return -2; if (mac == NULL || len == NULL) { os_free(ctx); return 0; } switch (ctx->alg) { case CRYPTO_HASH_ALG_MD5: if (*len < 16) { *len = 16; os_free(ctx); return -1; } *len = 16; MD5Final(mac, &ctx->u.md5); break; case CRYPTO_HASH_ALG_SHA1: if (*len < 20) { *len = 20; os_free(ctx); return -1; } *len = 20; SHA1Final(mac, &ctx->u.sha1); break; #ifdef CONFIG_SHA256 case CRYPTO_HASH_ALG_SHA256: if (*len < 32) { *len = 32; os_free(ctx); return -1; } *len = 32; sha256_done(&ctx->u.sha256, mac); break; #endif /* CONFIG_SHA256 */ case CRYPTO_HASH_ALG_HMAC_MD5: if (*len < 16) { *len = 16; os_free(ctx); return -1; } *len = 16; MD5Final(mac, &ctx->u.md5); os_memcpy(k_pad, ctx->key, ctx->key_len); os_memset(k_pad + ctx->key_len, 0, sizeof(k_pad) - ctx->key_len); for (i = 0; i < sizeof(k_pad); i++) k_pad[i] ^= 0x5c; MD5Init(&ctx->u.md5); MD5Update(&ctx->u.md5, k_pad, sizeof(k_pad)); MD5Update(&ctx->u.md5, mac, 16); MD5Final(mac, &ctx->u.md5); break; case CRYPTO_HASH_ALG_HMAC_SHA1: if (*len < 20) { *len = 20; os_free(ctx); return -1; } *len = 20; SHA1Final(mac, &ctx->u.sha1); os_memcpy(k_pad, ctx->key, ctx->key_len); os_memset(k_pad + ctx->key_len, 0, sizeof(k_pad) - ctx->key_len); for (i = 0; i < sizeof(k_pad); i++) k_pad[i] ^= 0x5c; SHA1Init(&ctx->u.sha1); SHA1Update(&ctx->u.sha1, k_pad, sizeof(k_pad)); SHA1Update(&ctx->u.sha1, mac, 20); SHA1Final(mac, &ctx->u.sha1); break; #ifdef CONFIG_SHA256 case CRYPTO_HASH_ALG_HMAC_SHA256: if (*len < 32) { *len = 32; os_free(ctx); return -1; } *len = 32; sha256_done(&ctx->u.sha256, mac); os_memcpy(k_pad, ctx->key, ctx->key_len); os_memset(k_pad + ctx->key_len, 0, sizeof(k_pad) - ctx->key_len); for (i = 0; i < sizeof(k_pad); i++) k_pad[i] ^= 0x5c; sha256_init(&ctx->u.sha256); sha256_process(&ctx->u.sha256, k_pad, sizeof(k_pad)); sha256_process(&ctx->u.sha256, mac, 32); sha256_done(&ctx->u.sha256, mac); break; #endif /* CONFIG_SHA256 */ default: os_free(ctx); return -1; } os_free(ctx); return 0; } int crypto_global_init(void) { return 0; } void crypto_global_deinit(void) { } wpa-2.1/src/crypto/crypto_libtomcrypt.c000066400000000000000000000340151227414666700204100ustar00rootroot00000000000000/* * WPA Supplicant / Crypto wrapper for LibTomCrypt (for internal TLSv1) * Copyright (c) 2005-2006, 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.h" #include "crypto.h" #ifndef mp_init_multi #define mp_init_multi ltc_init_multi #define mp_clear_multi ltc_deinit_multi #define mp_unsigned_bin_size(a) ltc_mp.unsigned_size(a) #define mp_to_unsigned_bin(a, b) ltc_mp.unsigned_write(a, b) #define mp_read_unsigned_bin(a, b, c) ltc_mp.unsigned_read(a, b, c) #define mp_exptmod(a,b,c,d) ltc_mp.exptmod(a,b,c,d) #endif int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { hash_state md; size_t i; md4_init(&md); for (i = 0; i < num_elem; i++) md4_process(&md, addr[i], len[i]); md4_done(&md, mac); return 0; } void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) { u8 pkey[8], next, tmp; int i; symmetric_key skey; /* Add parity bits to the key */ next = 0; for (i = 0; i < 7; i++) { tmp = key[i]; pkey[i] = (tmp >> i) | next | 1; next = tmp << (7 - i); } pkey[i] = next | 1; des_setup(pkey, 8, 0, &skey); des_ecb_encrypt(clear, cypher, &skey); des_done(&skey); } int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { hash_state md; size_t i; md5_init(&md); for (i = 0; i < num_elem; i++) md5_process(&md, addr[i], len[i]); md5_done(&md, mac); return 0; } int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { hash_state md; size_t i; sha1_init(&md); for (i = 0; i < num_elem; i++) sha1_process(&md, addr[i], len[i]); sha1_done(&md, mac); return 0; } void * aes_encrypt_init(const u8 *key, size_t len) { symmetric_key *skey; skey = os_malloc(sizeof(*skey)); if (skey == NULL) return NULL; if (aes_setup(key, len, 0, skey) != CRYPT_OK) { os_free(skey); return NULL; } return skey; } void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt) { symmetric_key *skey = ctx; aes_ecb_encrypt(plain, crypt, skey); } void aes_encrypt_deinit(void *ctx) { symmetric_key *skey = ctx; aes_done(skey); os_free(skey); } void * aes_decrypt_init(const u8 *key, size_t len) { symmetric_key *skey; skey = os_malloc(sizeof(*skey)); if (skey == NULL) return NULL; if (aes_setup(key, len, 0, skey) != CRYPT_OK) { os_free(skey); return NULL; } return skey; } void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain) { symmetric_key *skey = ctx; aes_ecb_encrypt(plain, (u8 *) crypt, skey); } void aes_decrypt_deinit(void *ctx) { symmetric_key *skey = ctx; aes_done(skey); os_free(skey); } struct crypto_hash { enum crypto_hash_alg alg; int error; union { hash_state md; hmac_state hmac; } u; }; struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key, size_t key_len) { struct crypto_hash *ctx; ctx = os_zalloc(sizeof(*ctx)); if (ctx == NULL) return NULL; ctx->alg = alg; switch (alg) { case CRYPTO_HASH_ALG_MD5: if (md5_init(&ctx->u.md) != CRYPT_OK) goto fail; break; case CRYPTO_HASH_ALG_SHA1: if (sha1_init(&ctx->u.md) != CRYPT_OK) goto fail; break; case CRYPTO_HASH_ALG_HMAC_MD5: if (hmac_init(&ctx->u.hmac, find_hash("md5"), key, key_len) != CRYPT_OK) goto fail; break; case CRYPTO_HASH_ALG_HMAC_SHA1: if (hmac_init(&ctx->u.hmac, find_hash("sha1"), key, key_len) != CRYPT_OK) goto fail; break; default: goto fail; } return ctx; fail: os_free(ctx); return NULL; } void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len) { if (ctx == NULL || ctx->error) return; switch (ctx->alg) { case CRYPTO_HASH_ALG_MD5: ctx->error = md5_process(&ctx->u.md, data, len) != CRYPT_OK; break; case CRYPTO_HASH_ALG_SHA1: ctx->error = sha1_process(&ctx->u.md, data, len) != CRYPT_OK; break; case CRYPTO_HASH_ALG_HMAC_MD5: case CRYPTO_HASH_ALG_HMAC_SHA1: ctx->error = hmac_process(&ctx->u.hmac, data, len) != CRYPT_OK; break; } } int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len) { int ret = 0; unsigned long clen; if (ctx == NULL) return -2; if (mac == NULL || len == NULL) { os_free(ctx); return 0; } if (ctx->error) { os_free(ctx); return -2; } switch (ctx->alg) { case CRYPTO_HASH_ALG_MD5: if (*len < 16) { *len = 16; os_free(ctx); return -1; } *len = 16; if (md5_done(&ctx->u.md, mac) != CRYPT_OK) ret = -2; break; case CRYPTO_HASH_ALG_SHA1: if (*len < 20) { *len = 20; os_free(ctx); return -1; } *len = 20; if (sha1_done(&ctx->u.md, mac) != CRYPT_OK) ret = -2; break; case CRYPTO_HASH_ALG_HMAC_SHA1: if (*len < 20) { *len = 20; os_free(ctx); return -1; } /* continue */ case CRYPTO_HASH_ALG_HMAC_MD5: if (*len < 16) { *len = 16; os_free(ctx); return -1; } clen = *len; if (hmac_done(&ctx->u.hmac, mac, &clen) != CRYPT_OK) { os_free(ctx); return -1; } *len = clen; break; default: ret = -2; break; } os_free(ctx); return ret; } struct crypto_cipher { int rc4; union { symmetric_CBC cbc; struct { size_t used_bytes; u8 key[16]; size_t keylen; } rc4; } u; }; struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg, const u8 *iv, const u8 *key, size_t key_len) { struct crypto_cipher *ctx; int idx, res, rc4 = 0; switch (alg) { case CRYPTO_CIPHER_ALG_AES: idx = find_cipher("aes"); break; case CRYPTO_CIPHER_ALG_3DES: idx = find_cipher("3des"); break; case CRYPTO_CIPHER_ALG_DES: idx = find_cipher("des"); break; case CRYPTO_CIPHER_ALG_RC2: idx = find_cipher("rc2"); break; case CRYPTO_CIPHER_ALG_RC4: idx = -1; rc4 = 1; break; default: return NULL; } ctx = os_zalloc(sizeof(*ctx)); if (ctx == NULL) return NULL; if (rc4) { ctx->rc4 = 1; if (key_len > sizeof(ctx->u.rc4.key)) { os_free(ctx); return NULL; } ctx->u.rc4.keylen = key_len; os_memcpy(ctx->u.rc4.key, key, key_len); } else { res = cbc_start(idx, iv, key, key_len, 0, &ctx->u.cbc); if (res != CRYPT_OK) { wpa_printf(MSG_DEBUG, "LibTomCrypt: Cipher start " "failed: %s", error_to_string(res)); os_free(ctx); return NULL; } } return ctx; } int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain, u8 *crypt, size_t len) { int res; if (ctx->rc4) { if (plain != crypt) os_memcpy(crypt, plain, len); rc4_skip(ctx->u.rc4.key, ctx->u.rc4.keylen, ctx->u.rc4.used_bytes, crypt, len); ctx->u.rc4.used_bytes += len; return 0; } res = cbc_encrypt(plain, crypt, len, &ctx->u.cbc); if (res != CRYPT_OK) { wpa_printf(MSG_DEBUG, "LibTomCrypt: CBC encryption " "failed: %s", error_to_string(res)); return -1; } return 0; } int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt, u8 *plain, size_t len) { int res; if (ctx->rc4) { if (plain != crypt) os_memcpy(plain, crypt, len); rc4_skip(ctx->u.rc4.key, ctx->u.rc4.keylen, ctx->u.rc4.used_bytes, plain, len); ctx->u.rc4.used_bytes += len; return 0; } res = cbc_decrypt(crypt, plain, len, &ctx->u.cbc); if (res != CRYPT_OK) { wpa_printf(MSG_DEBUG, "LibTomCrypt: CBC decryption " "failed: %s", error_to_string(res)); return -1; } return 0; } void crypto_cipher_deinit(struct crypto_cipher *ctx) { if (!ctx->rc4) cbc_done(&ctx->u.cbc); os_free(ctx); } struct crypto_public_key { rsa_key rsa; }; struct crypto_private_key { rsa_key rsa; }; struct crypto_public_key * crypto_public_key_import(const u8 *key, size_t len) { int res; struct crypto_public_key *pk; pk = os_zalloc(sizeof(*pk)); if (pk == NULL) return NULL; res = rsa_import(key, len, &pk->rsa); if (res != CRYPT_OK) { wpa_printf(MSG_ERROR, "LibTomCrypt: Failed to import " "public key (res=%d '%s')", res, error_to_string(res)); os_free(pk); return NULL; } if (pk->rsa.type != PK_PUBLIC) { wpa_printf(MSG_ERROR, "LibTomCrypt: Public key was not of " "correct type"); rsa_free(&pk->rsa); os_free(pk); return NULL; } return pk; } struct crypto_private_key * crypto_private_key_import(const u8 *key, size_t len, const char *passwd) { int res; struct crypto_private_key *pk; pk = os_zalloc(sizeof(*pk)); if (pk == NULL) return NULL; res = rsa_import(key, len, &pk->rsa); if (res != CRYPT_OK) { wpa_printf(MSG_ERROR, "LibTomCrypt: Failed to import " "private key (res=%d '%s')", res, error_to_string(res)); os_free(pk); return NULL; } if (pk->rsa.type != PK_PRIVATE) { wpa_printf(MSG_ERROR, "LibTomCrypt: Private key was not of " "correct type"); rsa_free(&pk->rsa); os_free(pk); return NULL; } return pk; } struct crypto_public_key * crypto_public_key_from_cert(const u8 *buf, size_t len) { /* No X.509 support in LibTomCrypt */ return NULL; } static int pkcs1_generate_encryption_block(u8 block_type, size_t modlen, const u8 *in, size_t inlen, u8 *out, size_t *outlen) { size_t ps_len; u8 *pos; /* * PKCS #1 v1.5, 8.1: * * EB = 00 || BT || PS || 00 || D * BT = 00 or 01 for private-key operation; 02 for public-key operation * PS = k-3-||D||; at least eight octets * (BT=0: PS=0x00, BT=1: PS=0xff, BT=2: PS=pseudorandom non-zero) * k = length of modulus in octets (modlen) */ if (modlen < 12 || modlen > *outlen || inlen > modlen - 11) { wpa_printf(MSG_DEBUG, "PKCS #1: %s - Invalid buffer " "lengths (modlen=%lu outlen=%lu inlen=%lu)", __func__, (unsigned long) modlen, (unsigned long) *outlen, (unsigned long) inlen); return -1; } pos = out; *pos++ = 0x00; *pos++ = block_type; /* BT */ ps_len = modlen - inlen - 3; switch (block_type) { case 0: os_memset(pos, 0x00, ps_len); pos += ps_len; break; case 1: os_memset(pos, 0xff, ps_len); pos += ps_len; break; case 2: if (os_get_random(pos, ps_len) < 0) { wpa_printf(MSG_DEBUG, "PKCS #1: %s - Failed to get " "random data for PS", __func__); return -1; } while (ps_len--) { if (*pos == 0x00) *pos = 0x01; pos++; } break; default: wpa_printf(MSG_DEBUG, "PKCS #1: %s - Unsupported block type " "%d", __func__, block_type); return -1; } *pos++ = 0x00; os_memcpy(pos, in, inlen); /* D */ return 0; } static int crypto_rsa_encrypt_pkcs1(int block_type, rsa_key *key, int key_type, const u8 *in, size_t inlen, u8 *out, size_t *outlen) { unsigned long len, modlen; int res; modlen = mp_unsigned_bin_size(key->N); if (pkcs1_generate_encryption_block(block_type, modlen, in, inlen, out, outlen) < 0) return -1; len = *outlen; res = rsa_exptmod(out, modlen, out, &len, key_type, key); if (res != CRYPT_OK) { wpa_printf(MSG_DEBUG, "LibTomCrypt: rsa_exptmod failed: %s", error_to_string(res)); return -1; } *outlen = len; return 0; } int crypto_public_key_encrypt_pkcs1_v15(struct crypto_public_key *key, const u8 *in, size_t inlen, u8 *out, size_t *outlen) { return crypto_rsa_encrypt_pkcs1(2, &key->rsa, PK_PUBLIC, in, inlen, out, outlen); } int crypto_private_key_sign_pkcs1(struct crypto_private_key *key, const u8 *in, size_t inlen, u8 *out, size_t *outlen) { return crypto_rsa_encrypt_pkcs1(1, &key->rsa, PK_PRIVATE, in, inlen, out, outlen); } void crypto_public_key_free(struct crypto_public_key *key) { if (key) { rsa_free(&key->rsa); os_free(key); } } void crypto_private_key_free(struct crypto_private_key *key) { if (key) { rsa_free(&key->rsa); os_free(key); } } int crypto_public_key_decrypt_pkcs1(struct crypto_public_key *key, const u8 *crypt, size_t crypt_len, u8 *plain, size_t *plain_len) { int res; unsigned long len; u8 *pos; len = *plain_len; res = rsa_exptmod(crypt, crypt_len, plain, &len, PK_PUBLIC, &key->rsa); if (res != CRYPT_OK) { wpa_printf(MSG_DEBUG, "LibTomCrypt: rsa_exptmod failed: %s", error_to_string(res)); return -1; } /* * PKCS #1 v1.5, 8.1: * * EB = 00 || BT || PS || 00 || D * BT = 01 * PS = k-3-||D|| times FF * k = length of modulus in octets */ if (len < 3 + 8 + 16 /* min hash len */ || plain[0] != 0x00 || plain[1] != 0x01 || plain[2] != 0xff) { wpa_printf(MSG_INFO, "LibTomCrypt: Invalid signature EB " "structure"); return -1; } pos = plain + 3; while (pos < plain + len && *pos == 0xff) pos++; if (pos - plain - 2 < 8) { /* PKCS #1 v1.5, 8.1: At least eight octets long PS */ wpa_printf(MSG_INFO, "LibTomCrypt: Too short signature " "padding"); return -1; } if (pos + 16 /* min hash len */ >= plain + len || *pos != 0x00) { wpa_printf(MSG_INFO, "LibTomCrypt: Invalid signature EB " "structure (2)"); return -1; } pos++; len -= pos - plain; /* Strip PKCS #1 header */ os_memmove(plain, pos, len); *plain_len = len; return 0; } int crypto_global_init(void) { ltc_mp = tfm_desc; /* TODO: only register algorithms that are really needed */ if (register_hash(&md4_desc) < 0 || register_hash(&md5_desc) < 0 || register_hash(&sha1_desc) < 0 || register_cipher(&aes_desc) < 0 || register_cipher(&des_desc) < 0 || register_cipher(&des3_desc) < 0) { wpa_printf(MSG_ERROR, "TLSv1: Failed to register " "hash/cipher functions"); return -1; } return 0; } void crypto_global_deinit(void) { } #ifdef CONFIG_MODEXP int crypto_mod_exp(const u8 *base, size_t base_len, const u8 *power, size_t power_len, const u8 *modulus, size_t modulus_len, u8 *result, size_t *result_len) { void *b, *p, *m, *r; if (mp_init_multi(&b, &p, &m, &r, NULL) != CRYPT_OK) return -1; if (mp_read_unsigned_bin(b, (u8 *) base, base_len) != CRYPT_OK || mp_read_unsigned_bin(p, (u8 *) power, power_len) != CRYPT_OK || mp_read_unsigned_bin(m, (u8 *) modulus, modulus_len) != CRYPT_OK) goto fail; if (mp_exptmod(b, p, m, r) != CRYPT_OK) goto fail; *result_len = mp_unsigned_bin_size(r); if (mp_to_unsigned_bin(r, result) != CRYPT_OK) goto fail; mp_clear_multi(b, p, m, r, NULL); return 0; fail: mp_clear_multi(b, p, m, r, NULL); return -1; } #endif /* CONFIG_MODEXP */ wpa-2.1/src/crypto/crypto_none.c000066400000000000000000000007031227414666700167740ustar00rootroot00000000000000/* * WPA Supplicant / Empty template functions for crypto wrapper * 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.h" int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { return 0; } void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) { } wpa-2.1/src/crypto/crypto_nss.c000066400000000000000000000075151227414666700166500ustar00rootroot00000000000000/* * Crypto wrapper functions for NSS * Copyright (c) 2009, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "includes.h" #include #include #include #include #include #include #include #include #include #include "common.h" #include "crypto.h" static int nss_hash(HASH_HashType type, unsigned int max_res_len, size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { HASHContext *ctx; size_t i; unsigned int reslen; ctx = HASH_Create(type); if (ctx == NULL) return -1; HASH_Begin(ctx); for (i = 0; i < num_elem; i++) HASH_Update(ctx, addr[i], len[i]); HASH_End(ctx, mac, &reslen, max_res_len); HASH_Destroy(ctx); return 0; } void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) { PK11Context *ctx = NULL; PK11SlotInfo *slot; SECItem *param = NULL; PK11SymKey *symkey = NULL; SECItem item; int olen; u8 pkey[8], next, tmp; int i; /* Add parity bits to the key */ next = 0; for (i = 0; i < 7; i++) { tmp = key[i]; pkey[i] = (tmp >> i) | next | 1; next = tmp << (7 - i); } pkey[i] = next | 1; slot = PK11_GetBestSlot(CKM_DES_ECB, NULL); if (slot == NULL) { wpa_printf(MSG_ERROR, "NSS: PK11_GetBestSlot failed"); goto out; } item.type = siBuffer; item.data = pkey; item.len = 8; symkey = PK11_ImportSymKey(slot, CKM_DES_ECB, PK11_OriginDerive, CKA_ENCRYPT, &item, NULL); if (symkey == NULL) { wpa_printf(MSG_ERROR, "NSS: PK11_ImportSymKey failed"); goto out; } param = PK11_GenerateNewParam(CKM_DES_ECB, symkey); if (param == NULL) { wpa_printf(MSG_ERROR, "NSS: PK11_GenerateNewParam failed"); goto out; } ctx = PK11_CreateContextBySymKey(CKM_DES_ECB, CKA_ENCRYPT, symkey, param); if (ctx == NULL) { wpa_printf(MSG_ERROR, "NSS: PK11_CreateContextBySymKey(" "CKM_DES_ECB) failed"); goto out; } if (PK11_CipherOp(ctx, cypher, &olen, 8, (void *) clear, 8) != SECSuccess) { wpa_printf(MSG_ERROR, "NSS: PK11_CipherOp failed"); goto out; } out: if (ctx) PK11_DestroyContext(ctx, PR_TRUE); if (symkey) PK11_FreeSymKey(symkey); if (param) SECITEM_FreeItem(param, PR_TRUE); } int rc4_skip(const u8 *key, size_t keylen, size_t skip, u8 *data, size_t data_len) { return -1; } int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { return nss_hash(HASH_AlgMD5, 16, num_elem, addr, len, mac); } int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { return nss_hash(HASH_AlgSHA1, 20, num_elem, addr, len, mac); } int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { return nss_hash(HASH_AlgSHA256, 32, num_elem, addr, len, mac); } void * aes_encrypt_init(const u8 *key, size_t len) { return NULL; } void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt) { } void aes_encrypt_deinit(void *ctx) { } void * aes_decrypt_init(const u8 *key, size_t len) { return NULL; } void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain) { } void aes_decrypt_deinit(void *ctx) { } int crypto_mod_exp(const u8 *base, size_t base_len, const u8 *power, size_t power_len, const u8 *modulus, size_t modulus_len, u8 *result, size_t *result_len) { return -1; } struct crypto_cipher { }; struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg, const u8 *iv, const u8 *key, size_t key_len) { return NULL; } int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain, u8 *crypt, size_t len) { return -1; } int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt, u8 *plain, size_t len) { return -1; } void crypto_cipher_deinit(struct crypto_cipher *ctx) { } wpa-2.1/src/crypto/crypto_openssl.c000066400000000000000000000631661227414666700175340ustar00rootroot00000000000000/* * Wrapper functions for OpenSSL libcrypto * Copyright (c) 2004-2013, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "includes.h" #include #include #include #include #include #include #include #include #include #ifdef CONFIG_OPENSSL_CMAC #include #endif /* CONFIG_OPENSSL_CMAC */ #ifdef CONFIG_ECC #include #endif /* CONFIG_ECC */ #include "common.h" #include "wpabuf.h" #include "dh_group5.h" #include "sha1.h" #include "sha256.h" #include "crypto.h" #if OPENSSL_VERSION_NUMBER < 0x00907000 #define DES_key_schedule des_key_schedule #define DES_cblock des_cblock #define DES_set_key(key, schedule) des_set_key((key), *(schedule)) #define DES_ecb_encrypt(input, output, ks, enc) \ des_ecb_encrypt((input), (output), *(ks), (enc)) #endif /* openssl < 0.9.7 */ static BIGNUM * get_group5_prime(void) { #if OPENSSL_VERSION_NUMBER < 0x00908000 static const unsigned char RFC3526_PRIME_1536[] = { 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xC9,0x0F,0xDA,0xA2, 0x21,0x68,0xC2,0x34,0xC4,0xC6,0x62,0x8B,0x80,0xDC,0x1C,0xD1, 0x29,0x02,0x4E,0x08,0x8A,0x67,0xCC,0x74,0x02,0x0B,0xBE,0xA6, 0x3B,0x13,0x9B,0x22,0x51,0x4A,0x08,0x79,0x8E,0x34,0x04,0xDD, 0xEF,0x95,0x19,0xB3,0xCD,0x3A,0x43,0x1B,0x30,0x2B,0x0A,0x6D, 0xF2,0x5F,0x14,0x37,0x4F,0xE1,0x35,0x6D,0x6D,0x51,0xC2,0x45, 0xE4,0x85,0xB5,0x76,0x62,0x5E,0x7E,0xC6,0xF4,0x4C,0x42,0xE9, 0xA6,0x37,0xED,0x6B,0x0B,0xFF,0x5C,0xB6,0xF4,0x06,0xB7,0xED, 0xEE,0x38,0x6B,0xFB,0x5A,0x89,0x9F,0xA5,0xAE,0x9F,0x24,0x11, 0x7C,0x4B,0x1F,0xE6,0x49,0x28,0x66,0x51,0xEC,0xE4,0x5B,0x3D, 0xC2,0x00,0x7C,0xB8,0xA1,0x63,0xBF,0x05,0x98,0xDA,0x48,0x36, 0x1C,0x55,0xD3,0x9A,0x69,0x16,0x3F,0xA8,0xFD,0x24,0xCF,0x5F, 0x83,0x65,0x5D,0x23,0xDC,0xA3,0xAD,0x96,0x1C,0x62,0xF3,0x56, 0x20,0x85,0x52,0xBB,0x9E,0xD5,0x29,0x07,0x70,0x96,0x96,0x6D, 0x67,0x0C,0x35,0x4E,0x4A,0xBC,0x98,0x04,0xF1,0x74,0x6C,0x08, 0xCA,0x23,0x73,0x27,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, }; return BN_bin2bn(RFC3526_PRIME_1536, sizeof(RFC3526_PRIME_1536), NULL); #else /* openssl < 0.9.8 */ return get_rfc3526_prime_1536(NULL); #endif /* openssl < 0.9.8 */ } #if OPENSSL_VERSION_NUMBER < 0x00908000 #ifndef OPENSSL_NO_SHA256 #ifndef OPENSSL_FIPS #define NO_SHA256_WRAPPER #endif #endif #endif /* openssl < 0.9.8 */ #ifdef OPENSSL_NO_SHA256 #define NO_SHA256_WRAPPER #endif static int openssl_digest_vector(const EVP_MD *type, size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { EVP_MD_CTX ctx; size_t i; unsigned int mac_len; EVP_MD_CTX_init(&ctx); if (!EVP_DigestInit_ex(&ctx, type, NULL)) { wpa_printf(MSG_ERROR, "OpenSSL: EVP_DigestInit_ex failed: %s", ERR_error_string(ERR_get_error(), NULL)); return -1; } for (i = 0; i < num_elem; i++) { if (!EVP_DigestUpdate(&ctx, addr[i], len[i])) { wpa_printf(MSG_ERROR, "OpenSSL: EVP_DigestUpdate " "failed: %s", ERR_error_string(ERR_get_error(), NULL)); return -1; } } if (!EVP_DigestFinal(&ctx, mac, &mac_len)) { wpa_printf(MSG_ERROR, "OpenSSL: EVP_DigestFinal failed: %s", ERR_error_string(ERR_get_error(), NULL)); return -1; } return 0; } int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { return openssl_digest_vector(EVP_md4(), num_elem, addr, len, mac); } void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) { u8 pkey[8], next, tmp; int i; DES_key_schedule ks; /* Add parity bits to the key */ next = 0; for (i = 0; i < 7; i++) { tmp = key[i]; pkey[i] = (tmp >> i) | next | 1; next = tmp << (7 - i); } pkey[i] = next | 1; DES_set_key(&pkey, &ks); DES_ecb_encrypt((DES_cblock *) clear, (DES_cblock *) cypher, &ks, DES_ENCRYPT); } int rc4_skip(const u8 *key, size_t keylen, size_t skip, u8 *data, size_t data_len) { #ifdef OPENSSL_NO_RC4 return -1; #else /* OPENSSL_NO_RC4 */ EVP_CIPHER_CTX ctx; int outl; int res = -1; unsigned char skip_buf[16]; EVP_CIPHER_CTX_init(&ctx); if (!EVP_CIPHER_CTX_set_padding(&ctx, 0) || !EVP_CipherInit_ex(&ctx, EVP_rc4(), NULL, NULL, NULL, 1) || !EVP_CIPHER_CTX_set_key_length(&ctx, keylen) || !EVP_CipherInit_ex(&ctx, NULL, NULL, key, NULL, 1)) goto out; while (skip >= sizeof(skip_buf)) { size_t len = skip; if (len > sizeof(skip_buf)) len = sizeof(skip_buf); if (!EVP_CipherUpdate(&ctx, skip_buf, &outl, skip_buf, len)) goto out; skip -= len; } if (EVP_CipherUpdate(&ctx, data, &outl, data, data_len)) res = 0; out: EVP_CIPHER_CTX_cleanup(&ctx); return res; #endif /* OPENSSL_NO_RC4 */ } int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { return openssl_digest_vector(EVP_md5(), num_elem, addr, len, mac); } int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { return openssl_digest_vector(EVP_sha1(), num_elem, addr, len, mac); } #ifndef NO_SHA256_WRAPPER int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { return openssl_digest_vector(EVP_sha256(), num_elem, addr, len, mac); } #endif /* NO_SHA256_WRAPPER */ static const EVP_CIPHER * aes_get_evp_cipher(size_t keylen) { switch (keylen) { case 16: return EVP_aes_128_ecb(); case 24: return EVP_aes_192_ecb(); case 32: return EVP_aes_256_ecb(); } return NULL; } void * aes_encrypt_init(const u8 *key, size_t len) { EVP_CIPHER_CTX *ctx; const EVP_CIPHER *type; type = aes_get_evp_cipher(len); if (type == NULL) return NULL; ctx = os_malloc(sizeof(*ctx)); if (ctx == NULL) return NULL; EVP_CIPHER_CTX_init(ctx); if (EVP_EncryptInit_ex(ctx, type, NULL, key, NULL) != 1) { os_free(ctx); return NULL; } EVP_CIPHER_CTX_set_padding(ctx, 0); return ctx; } void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt) { EVP_CIPHER_CTX *c = ctx; int clen = 16; if (EVP_EncryptUpdate(c, crypt, &clen, plain, 16) != 1) { wpa_printf(MSG_ERROR, "OpenSSL: EVP_EncryptUpdate failed: %s", ERR_error_string(ERR_get_error(), NULL)); } } void aes_encrypt_deinit(void *ctx) { EVP_CIPHER_CTX *c = ctx; u8 buf[16]; int len = sizeof(buf); if (EVP_EncryptFinal_ex(c, buf, &len) != 1) { wpa_printf(MSG_ERROR, "OpenSSL: EVP_EncryptFinal_ex failed: " "%s", ERR_error_string(ERR_get_error(), NULL)); } if (len != 0) { wpa_printf(MSG_ERROR, "OpenSSL: Unexpected padding length %d " "in AES encrypt", len); } EVP_CIPHER_CTX_cleanup(c); os_free(c); } void * aes_decrypt_init(const u8 *key, size_t len) { EVP_CIPHER_CTX *ctx; const EVP_CIPHER *type; type = aes_get_evp_cipher(len); if (type == NULL) return NULL; ctx = os_malloc(sizeof(*ctx)); if (ctx == NULL) return NULL; EVP_CIPHER_CTX_init(ctx); if (EVP_DecryptInit_ex(ctx, type, NULL, key, NULL) != 1) { os_free(ctx); return NULL; } EVP_CIPHER_CTX_set_padding(ctx, 0); return ctx; } void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain) { EVP_CIPHER_CTX *c = ctx; int plen = 16; if (EVP_DecryptUpdate(c, plain, &plen, crypt, 16) != 1) { wpa_printf(MSG_ERROR, "OpenSSL: EVP_DecryptUpdate failed: %s", ERR_error_string(ERR_get_error(), NULL)); } } void aes_decrypt_deinit(void *ctx) { EVP_CIPHER_CTX *c = ctx; u8 buf[16]; int len = sizeof(buf); if (EVP_DecryptFinal_ex(c, buf, &len) != 1) { wpa_printf(MSG_ERROR, "OpenSSL: EVP_DecryptFinal_ex failed: " "%s", ERR_error_string(ERR_get_error(), NULL)); } if (len != 0) { wpa_printf(MSG_ERROR, "OpenSSL: Unexpected padding length %d " "in AES decrypt", len); } EVP_CIPHER_CTX_cleanup(c); os_free(ctx); } int crypto_mod_exp(const u8 *base, size_t base_len, const u8 *power, size_t power_len, const u8 *modulus, size_t modulus_len, u8 *result, size_t *result_len) { BIGNUM *bn_base, *bn_exp, *bn_modulus, *bn_result; int ret = -1; BN_CTX *ctx; ctx = BN_CTX_new(); if (ctx == NULL) return -1; bn_base = BN_bin2bn(base, base_len, NULL); bn_exp = BN_bin2bn(power, power_len, NULL); bn_modulus = BN_bin2bn(modulus, modulus_len, NULL); bn_result = BN_new(); if (bn_base == NULL || bn_exp == NULL || bn_modulus == NULL || bn_result == NULL) goto error; if (BN_mod_exp(bn_result, bn_base, bn_exp, bn_modulus, ctx) != 1) goto error; *result_len = BN_bn2bin(bn_result, result); ret = 0; error: BN_free(bn_base); BN_free(bn_exp); BN_free(bn_modulus); BN_free(bn_result); BN_CTX_free(ctx); return ret; } struct crypto_cipher { EVP_CIPHER_CTX enc; EVP_CIPHER_CTX dec; }; struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg, const u8 *iv, const u8 *key, size_t key_len) { struct crypto_cipher *ctx; const EVP_CIPHER *cipher; ctx = os_zalloc(sizeof(*ctx)); if (ctx == NULL) return NULL; switch (alg) { #ifndef OPENSSL_NO_RC4 case CRYPTO_CIPHER_ALG_RC4: cipher = EVP_rc4(); break; #endif /* OPENSSL_NO_RC4 */ #ifndef OPENSSL_NO_AES case CRYPTO_CIPHER_ALG_AES: switch (key_len) { case 16: cipher = EVP_aes_128_cbc(); break; case 24: cipher = EVP_aes_192_cbc(); break; case 32: cipher = EVP_aes_256_cbc(); break; default: os_free(ctx); return NULL; } break; #endif /* OPENSSL_NO_AES */ #ifndef OPENSSL_NO_DES case CRYPTO_CIPHER_ALG_3DES: cipher = EVP_des_ede3_cbc(); break; case CRYPTO_CIPHER_ALG_DES: cipher = EVP_des_cbc(); break; #endif /* OPENSSL_NO_DES */ #ifndef OPENSSL_NO_RC2 case CRYPTO_CIPHER_ALG_RC2: cipher = EVP_rc2_ecb(); break; #endif /* OPENSSL_NO_RC2 */ default: os_free(ctx); return NULL; } EVP_CIPHER_CTX_init(&ctx->enc); EVP_CIPHER_CTX_set_padding(&ctx->enc, 0); if (!EVP_EncryptInit_ex(&ctx->enc, cipher, NULL, NULL, NULL) || !EVP_CIPHER_CTX_set_key_length(&ctx->enc, key_len) || !EVP_EncryptInit_ex(&ctx->enc, NULL, NULL, key, iv)) { EVP_CIPHER_CTX_cleanup(&ctx->enc); os_free(ctx); return NULL; } EVP_CIPHER_CTX_init(&ctx->dec); EVP_CIPHER_CTX_set_padding(&ctx->dec, 0); if (!EVP_DecryptInit_ex(&ctx->dec, cipher, NULL, NULL, NULL) || !EVP_CIPHER_CTX_set_key_length(&ctx->dec, key_len) || !EVP_DecryptInit_ex(&ctx->dec, NULL, NULL, key, iv)) { EVP_CIPHER_CTX_cleanup(&ctx->enc); EVP_CIPHER_CTX_cleanup(&ctx->dec); os_free(ctx); return NULL; } return ctx; } int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain, u8 *crypt, size_t len) { int outl; if (!EVP_EncryptUpdate(&ctx->enc, crypt, &outl, plain, len)) return -1; return 0; } int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt, u8 *plain, size_t len) { int outl; outl = len; if (!EVP_DecryptUpdate(&ctx->dec, plain, &outl, crypt, len)) return -1; return 0; } void crypto_cipher_deinit(struct crypto_cipher *ctx) { EVP_CIPHER_CTX_cleanup(&ctx->enc); EVP_CIPHER_CTX_cleanup(&ctx->dec); os_free(ctx); } void * dh5_init(struct wpabuf **priv, struct wpabuf **publ) { DH *dh; struct wpabuf *pubkey = NULL, *privkey = NULL; size_t publen, privlen; *priv = NULL; *publ = NULL; dh = DH_new(); if (dh == NULL) return NULL; dh->g = BN_new(); if (dh->g == NULL || BN_set_word(dh->g, 2) != 1) goto err; dh->p = get_group5_prime(); if (dh->p == NULL) goto err; if (DH_generate_key(dh) != 1) goto err; publen = BN_num_bytes(dh->pub_key); pubkey = wpabuf_alloc(publen); if (pubkey == NULL) goto err; privlen = BN_num_bytes(dh->priv_key); privkey = wpabuf_alloc(privlen); if (privkey == NULL) goto err; BN_bn2bin(dh->pub_key, wpabuf_put(pubkey, publen)); BN_bn2bin(dh->priv_key, wpabuf_put(privkey, privlen)); *priv = privkey; *publ = pubkey; return dh; err: wpabuf_free(pubkey); wpabuf_free(privkey); DH_free(dh); return NULL; } void * dh5_init_fixed(const struct wpabuf *priv, const struct wpabuf *publ) { DH *dh; dh = DH_new(); if (dh == NULL) return NULL; dh->g = BN_new(); if (dh->g == NULL || BN_set_word(dh->g, 2) != 1) goto err; dh->p = get_group5_prime(); if (dh->p == NULL) goto err; dh->priv_key = BN_bin2bn(wpabuf_head(priv), wpabuf_len(priv), NULL); if (dh->priv_key == NULL) goto err; dh->pub_key = BN_bin2bn(wpabuf_head(publ), wpabuf_len(publ), NULL); if (dh->pub_key == NULL) goto err; if (DH_generate_key(dh) != 1) goto err; return dh; err: DH_free(dh); return NULL; } struct wpabuf * dh5_derive_shared(void *ctx, const struct wpabuf *peer_public, const struct wpabuf *own_private) { BIGNUM *pub_key; struct wpabuf *res = NULL; size_t rlen; DH *dh = ctx; int keylen; if (ctx == NULL) return NULL; pub_key = BN_bin2bn(wpabuf_head(peer_public), wpabuf_len(peer_public), NULL); if (pub_key == NULL) return NULL; rlen = DH_size(dh); res = wpabuf_alloc(rlen); if (res == NULL) goto err; keylen = DH_compute_key(wpabuf_mhead(res), pub_key, dh); if (keylen < 0) goto err; wpabuf_put(res, keylen); BN_free(pub_key); return res; err: BN_free(pub_key); wpabuf_free(res); return NULL; } void dh5_free(void *ctx) { DH *dh; if (ctx == NULL) return; dh = ctx; DH_free(dh); } struct crypto_hash { HMAC_CTX ctx; }; struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key, size_t key_len) { struct crypto_hash *ctx; const EVP_MD *md; switch (alg) { #ifndef OPENSSL_NO_MD5 case CRYPTO_HASH_ALG_HMAC_MD5: md = EVP_md5(); break; #endif /* OPENSSL_NO_MD5 */ #ifndef OPENSSL_NO_SHA case CRYPTO_HASH_ALG_HMAC_SHA1: md = EVP_sha1(); break; #endif /* OPENSSL_NO_SHA */ #ifndef OPENSSL_NO_SHA256 #ifdef CONFIG_SHA256 case CRYPTO_HASH_ALG_HMAC_SHA256: md = EVP_sha256(); break; #endif /* CONFIG_SHA256 */ #endif /* OPENSSL_NO_SHA256 */ default: return NULL; } ctx = os_zalloc(sizeof(*ctx)); if (ctx == NULL) return NULL; HMAC_CTX_init(&ctx->ctx); #if OPENSSL_VERSION_NUMBER < 0x00909000 HMAC_Init_ex(&ctx->ctx, key, key_len, md, NULL); #else /* openssl < 0.9.9 */ if (HMAC_Init_ex(&ctx->ctx, key, key_len, md, NULL) != 1) { os_free(ctx); return NULL; } #endif /* openssl < 0.9.9 */ return ctx; } void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len) { if (ctx == NULL) return; HMAC_Update(&ctx->ctx, data, len); } int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len) { unsigned int mdlen; int res; if (ctx == NULL) return -2; if (mac == NULL || len == NULL) { os_free(ctx); return 0; } mdlen = *len; #if OPENSSL_VERSION_NUMBER < 0x00909000 HMAC_Final(&ctx->ctx, mac, &mdlen); res = 1; #else /* openssl < 0.9.9 */ res = HMAC_Final(&ctx->ctx, mac, &mdlen); #endif /* openssl < 0.9.9 */ HMAC_CTX_cleanup(&ctx->ctx); os_free(ctx); if (res == 1) { *len = mdlen; return 0; } return -1; } int pbkdf2_sha1(const char *passphrase, const u8 *ssid, size_t ssid_len, int iterations, u8 *buf, size_t buflen) { #if OPENSSL_VERSION_NUMBER < 0x00908000 if (PKCS5_PBKDF2_HMAC_SHA1(passphrase, os_strlen(passphrase), (unsigned char *) ssid, ssid_len, 4096, buflen, buf) != 1) return -1; #else /* openssl < 0.9.8 */ if (PKCS5_PBKDF2_HMAC_SHA1(passphrase, os_strlen(passphrase), ssid, ssid_len, 4096, buflen, buf) != 1) return -1; #endif /* openssl < 0.9.8 */ return 0; } int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { HMAC_CTX ctx; size_t i; unsigned int mdlen; int res; HMAC_CTX_init(&ctx); #if OPENSSL_VERSION_NUMBER < 0x00909000 HMAC_Init_ex(&ctx, key, key_len, EVP_sha1(), NULL); #else /* openssl < 0.9.9 */ if (HMAC_Init_ex(&ctx, key, key_len, EVP_sha1(), NULL) != 1) return -1; #endif /* openssl < 0.9.9 */ for (i = 0; i < num_elem; i++) HMAC_Update(&ctx, addr[i], len[i]); mdlen = 20; #if OPENSSL_VERSION_NUMBER < 0x00909000 HMAC_Final(&ctx, mac, &mdlen); res = 1; #else /* openssl < 0.9.9 */ res = HMAC_Final(&ctx, mac, &mdlen); #endif /* openssl < 0.9.9 */ HMAC_CTX_cleanup(&ctx); return res == 1 ? 0 : -1; } int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len, u8 *mac) { return hmac_sha1_vector(key, key_len, 1, &data, &data_len, mac); } #ifdef CONFIG_SHA256 int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { HMAC_CTX ctx; size_t i; unsigned int mdlen; int res; HMAC_CTX_init(&ctx); #if OPENSSL_VERSION_NUMBER < 0x00909000 HMAC_Init_ex(&ctx, key, key_len, EVP_sha256(), NULL); #else /* openssl < 0.9.9 */ if (HMAC_Init_ex(&ctx, key, key_len, EVP_sha256(), NULL) != 1) return -1; #endif /* openssl < 0.9.9 */ for (i = 0; i < num_elem; i++) HMAC_Update(&ctx, addr[i], len[i]); mdlen = 32; #if OPENSSL_VERSION_NUMBER < 0x00909000 HMAC_Final(&ctx, mac, &mdlen); res = 1; #else /* openssl < 0.9.9 */ res = HMAC_Final(&ctx, mac, &mdlen); #endif /* openssl < 0.9.9 */ HMAC_CTX_cleanup(&ctx); return res == 1 ? 0 : -1; } int hmac_sha256(const u8 *key, size_t key_len, const u8 *data, size_t data_len, u8 *mac) { return hmac_sha256_vector(key, key_len, 1, &data, &data_len, mac); } #endif /* CONFIG_SHA256 */ int crypto_get_random(void *buf, size_t len) { if (RAND_bytes(buf, len) != 1) return -1; return 0; } #ifdef CONFIG_OPENSSL_CMAC int omac1_aes_128_vector(const u8 *key, size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { CMAC_CTX *ctx; int ret = -1; size_t outlen, i; ctx = CMAC_CTX_new(); if (ctx == NULL) return -1; if (!CMAC_Init(ctx, key, 16, EVP_aes_128_cbc(), NULL)) goto fail; for (i = 0; i < num_elem; i++) { if (!CMAC_Update(ctx, addr[i], len[i])) goto fail; } if (!CMAC_Final(ctx, mac, &outlen) || outlen != 16) goto fail; ret = 0; fail: CMAC_CTX_free(ctx); return ret; } int omac1_aes_128(const u8 *key, const u8 *data, size_t data_len, u8 *mac) { return omac1_aes_128_vector(key, 1, &data, &data_len, mac); } #endif /* CONFIG_OPENSSL_CMAC */ struct crypto_bignum * crypto_bignum_init(void) { return (struct crypto_bignum *) BN_new(); } struct crypto_bignum * crypto_bignum_init_set(const u8 *buf, size_t len) { BIGNUM *bn = BN_bin2bn(buf, len, NULL); return (struct crypto_bignum *) bn; } void crypto_bignum_deinit(struct crypto_bignum *n, int clear) { if (clear) BN_clear_free((BIGNUM *) n); else BN_free((BIGNUM *) n); } int crypto_bignum_to_bin(const struct crypto_bignum *a, u8 *buf, size_t buflen, size_t padlen) { int num_bytes, offset; if (padlen > buflen) return -1; num_bytes = BN_num_bytes((const BIGNUM *) a); if ((size_t) num_bytes > buflen) return -1; if (padlen > (size_t) num_bytes) offset = padlen - num_bytes; else offset = 0; os_memset(buf, 0, offset); BN_bn2bin((const BIGNUM *) a, buf + offset); return num_bytes + offset; } int crypto_bignum_add(const struct crypto_bignum *a, const struct crypto_bignum *b, struct crypto_bignum *c) { return BN_add((BIGNUM *) c, (const BIGNUM *) a, (const BIGNUM *) b) ? 0 : -1; } int crypto_bignum_mod(const struct crypto_bignum *a, const struct crypto_bignum *b, struct crypto_bignum *c) { int res; BN_CTX *bnctx; bnctx = BN_CTX_new(); if (bnctx == NULL) return -1; res = BN_mod((BIGNUM *) c, (const BIGNUM *) a, (const BIGNUM *) b, bnctx); BN_CTX_free(bnctx); return res ? 0 : -1; } int crypto_bignum_exptmod(const struct crypto_bignum *a, const struct crypto_bignum *b, const struct crypto_bignum *c, struct crypto_bignum *d) { int res; BN_CTX *bnctx; bnctx = BN_CTX_new(); if (bnctx == NULL) return -1; res = BN_mod_exp((BIGNUM *) d, (const BIGNUM *) a, (const BIGNUM *) b, (const BIGNUM *) c, bnctx); BN_CTX_free(bnctx); return res ? 0 : -1; } int crypto_bignum_rshift(const struct crypto_bignum *a, int n, struct crypto_bignum *b) { return BN_rshift((BIGNUM *) b, (const BIGNUM *) a, n) ? 0 : -1; } int crypto_bignum_inverse(const struct crypto_bignum *a, const struct crypto_bignum *b, struct crypto_bignum *c) { BIGNUM *res; BN_CTX *bnctx; bnctx = BN_CTX_new(); if (bnctx == NULL) return -1; res = BN_mod_inverse((BIGNUM *) c, (const BIGNUM *) a, (const BIGNUM *) b, bnctx); BN_CTX_free(bnctx); return res ? 0 : -1; } int crypto_bignum_sub(const struct crypto_bignum *a, const struct crypto_bignum *b, struct crypto_bignum *c) { return BN_sub((BIGNUM *) c, (const BIGNUM *) a, (const BIGNUM *) b) ? 0 : -1; } int crypto_bignum_div(const struct crypto_bignum *a, const struct crypto_bignum *b, struct crypto_bignum *c) { int res; BN_CTX *bnctx; bnctx = BN_CTX_new(); if (bnctx == NULL) return -1; res = BN_div((BIGNUM *) c, NULL, (const BIGNUM *) a, (const BIGNUM *) b, bnctx); BN_CTX_free(bnctx); return res ? 0 : -1; } int crypto_bignum_mulmod(const struct crypto_bignum *a, const struct crypto_bignum *b, const struct crypto_bignum *c, struct crypto_bignum *d) { int res; BN_CTX *bnctx; bnctx = BN_CTX_new(); if (bnctx == NULL) return -1; res = BN_mod_mul((BIGNUM *) d, (const BIGNUM *) a, (const BIGNUM *) b, (const BIGNUM *) c, bnctx); BN_CTX_free(bnctx); return res ? 0 : -1; } int crypto_bignum_cmp(const struct crypto_bignum *a, const struct crypto_bignum *b) { return BN_cmp((const BIGNUM *) a, (const BIGNUM *) b); } int crypto_bignum_bits(const struct crypto_bignum *a) { return BN_num_bits((const BIGNUM *) a); } int crypto_bignum_is_zero(const struct crypto_bignum *a) { return BN_is_zero((const BIGNUM *) a); } int crypto_bignum_is_one(const struct crypto_bignum *a) { return BN_is_one((const BIGNUM *) a); } #ifdef CONFIG_ECC struct crypto_ec { EC_GROUP *group; BN_CTX *bnctx; BIGNUM *prime; BIGNUM *order; }; struct crypto_ec * crypto_ec_init(int group) { struct crypto_ec *e; int nid; /* Map from IANA registry for IKE D-H groups to OpenSSL NID */ switch (group) { case 19: nid = NID_X9_62_prime256v1; break; case 20: nid = NID_secp384r1; break; case 21: nid = NID_secp521r1; break; case 25: nid = NID_X9_62_prime192v1; break; case 26: nid = NID_secp224r1; break; default: return NULL; } e = os_zalloc(sizeof(*e)); if (e == NULL) return NULL; e->bnctx = BN_CTX_new(); e->group = EC_GROUP_new_by_curve_name(nid); e->prime = BN_new(); e->order = BN_new(); if (e->group == NULL || e->bnctx == NULL || e->prime == NULL || e->order == NULL || !EC_GROUP_get_curve_GFp(e->group, e->prime, NULL, NULL, e->bnctx) || !EC_GROUP_get_order(e->group, e->order, e->bnctx)) { crypto_ec_deinit(e); e = NULL; } return e; } void crypto_ec_deinit(struct crypto_ec *e) { if (e == NULL) return; BN_free(e->order); EC_GROUP_free(e->group); BN_CTX_free(e->bnctx); os_free(e); } struct crypto_ec_point * crypto_ec_point_init(struct crypto_ec *e) { if (e == NULL) return NULL; return (struct crypto_ec_point *) EC_POINT_new(e->group); } size_t crypto_ec_prime_len(struct crypto_ec *e) { return BN_num_bytes(e->prime); } size_t crypto_ec_prime_len_bits(struct crypto_ec *e) { return BN_num_bits(e->prime); } const struct crypto_bignum * crypto_ec_get_prime(struct crypto_ec *e) { return (const struct crypto_bignum *) e->prime; } const struct crypto_bignum * crypto_ec_get_order(struct crypto_ec *e) { return (const struct crypto_bignum *) e->order; } void crypto_ec_point_deinit(struct crypto_ec_point *p, int clear) { if (clear) EC_POINT_clear_free((EC_POINT *) p); else EC_POINT_free((EC_POINT *) p); } int crypto_ec_point_to_bin(struct crypto_ec *e, const struct crypto_ec_point *point, u8 *x, u8 *y) { BIGNUM *x_bn, *y_bn; int ret = -1; int len = BN_num_bytes(e->prime); x_bn = BN_new(); y_bn = BN_new(); if (x_bn && y_bn && EC_POINT_get_affine_coordinates_GFp(e->group, (EC_POINT *) point, x_bn, y_bn, e->bnctx)) { if (x) { crypto_bignum_to_bin((struct crypto_bignum *) x_bn, x, len, len); } if (y) { crypto_bignum_to_bin((struct crypto_bignum *) y_bn, y, len, len); } ret = 0; } BN_free(x_bn); BN_free(y_bn); return ret; } struct crypto_ec_point * crypto_ec_point_from_bin(struct crypto_ec *e, const u8 *val) { BIGNUM *x, *y; EC_POINT *elem; int len = BN_num_bytes(e->prime); x = BN_bin2bn(val, len, NULL); y = BN_bin2bn(val + len, len, NULL); elem = EC_POINT_new(e->group); if (x == NULL || y == NULL || elem == NULL) { BN_free(x); BN_free(y); EC_POINT_free(elem); return NULL; } if (!EC_POINT_set_affine_coordinates_GFp(e->group, elem, x, y, e->bnctx)) { EC_POINT_free(elem); elem = NULL; } BN_free(x); BN_free(y); return (struct crypto_ec_point *) elem; } int crypto_ec_point_add(struct crypto_ec *e, const struct crypto_ec_point *a, const struct crypto_ec_point *b, struct crypto_ec_point *c) { return EC_POINT_add(e->group, (EC_POINT *) c, (const EC_POINT *) a, (const EC_POINT *) b, e->bnctx) ? 0 : -1; } int crypto_ec_point_mul(struct crypto_ec *e, const struct crypto_ec_point *p, const struct crypto_bignum *b, struct crypto_ec_point *res) { return EC_POINT_mul(e->group, (EC_POINT *) res, NULL, (const EC_POINT *) p, (const BIGNUM *) b, e->bnctx) ? 0 : -1; } int crypto_ec_point_invert(struct crypto_ec *e, struct crypto_ec_point *p) { return EC_POINT_invert(e->group, (EC_POINT *) p, e->bnctx) ? 0 : -1; } int crypto_ec_point_solve_y_coord(struct crypto_ec *e, struct crypto_ec_point *p, const struct crypto_bignum *x, int y_bit) { if (!EC_POINT_set_compressed_coordinates_GFp(e->group, (EC_POINT *) p, (const BIGNUM *) x, y_bit, e->bnctx) || !EC_POINT_is_on_curve(e->group, (EC_POINT *) p, e->bnctx)) return -1; return 0; } int crypto_ec_point_is_at_infinity(struct crypto_ec *e, const struct crypto_ec_point *p) { return EC_POINT_is_at_infinity(e->group, (const EC_POINT *) p); } int crypto_ec_point_is_on_curve(struct crypto_ec *e, const struct crypto_ec_point *p) { return EC_POINT_is_on_curve(e->group, (const EC_POINT *) p, e->bnctx); } #endif /* CONFIG_ECC */ wpa-2.1/src/crypto/des-internal.c000066400000000000000000000355221227414666700170310ustar00rootroot00000000000000/* * DES and 3DES-EDE ciphers * * Modifications to LibTomCrypt implementation: * Copyright (c) 2006-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 "crypto.h" #include "des_i.h" /* * This implementation is based on a DES implementation included in * LibTomCrypt. The version here is modified to fit in wpa_supplicant/hostapd * coding style. */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtomcrypt.com */ /** DES code submitted by Dobes Vandermeer */ #define ROLc(x, y) \ ((((unsigned long) (x) << (unsigned long) ((y) & 31)) | \ (((unsigned long) (x) & 0xFFFFFFFFUL) >> \ (unsigned long) (32 - ((y) & 31)))) & 0xFFFFFFFFUL) #define RORc(x, y) \ (((((unsigned long) (x) & 0xFFFFFFFFUL) >> \ (unsigned long) ((y) & 31)) | \ ((unsigned long) (x) << (unsigned long) (32 - ((y) & 31)))) & \ 0xFFFFFFFFUL) static const u32 bytebit[8] = { 0200, 0100, 040, 020, 010, 04, 02, 01 }; static const u32 bigbyte[24] = { 0x800000UL, 0x400000UL, 0x200000UL, 0x100000UL, 0x80000UL, 0x40000UL, 0x20000UL, 0x10000UL, 0x8000UL, 0x4000UL, 0x2000UL, 0x1000UL, 0x800UL, 0x400UL, 0x200UL, 0x100UL, 0x80UL, 0x40UL, 0x20UL, 0x10UL, 0x8UL, 0x4UL, 0x2UL, 0x1L }; /* Use the key schedule specific in the standard (ANSI X3.92-1981) */ static const u8 pc1[56] = { 56, 48, 40, 32, 24, 16, 8, 0, 57, 49, 41, 33, 25, 17, 9, 1, 58, 50, 42, 34, 26, 18, 10, 2, 59, 51, 43, 35, 62, 54, 46, 38, 30, 22, 14, 6, 61, 53, 45, 37, 29, 21, 13, 5, 60, 52, 44, 36, 28, 20, 12, 4, 27, 19, 11, 3 }; static const u8 totrot[16] = { 1, 2, 4, 6, 8, 10, 12, 14, 15, 17, 19, 21, 23, 25, 27, 28 }; static const u8 pc2[48] = { 13, 16, 10, 23, 0, 4, 2, 27, 14, 5, 20, 9, 22, 18, 11, 3, 25, 7, 15, 6, 26, 19, 12, 1, 40, 51, 30, 36, 46, 54, 29, 39, 50, 44, 32, 47, 43, 48, 38, 55, 33, 52, 45, 41, 49, 35, 28, 31 }; static const u32 SP1[64] = { 0x01010400UL, 0x00000000UL, 0x00010000UL, 0x01010404UL, 0x01010004UL, 0x00010404UL, 0x00000004UL, 0x00010000UL, 0x00000400UL, 0x01010400UL, 0x01010404UL, 0x00000400UL, 0x01000404UL, 0x01010004UL, 0x01000000UL, 0x00000004UL, 0x00000404UL, 0x01000400UL, 0x01000400UL, 0x00010400UL, 0x00010400UL, 0x01010000UL, 0x01010000UL, 0x01000404UL, 0x00010004UL, 0x01000004UL, 0x01000004UL, 0x00010004UL, 0x00000000UL, 0x00000404UL, 0x00010404UL, 0x01000000UL, 0x00010000UL, 0x01010404UL, 0x00000004UL, 0x01010000UL, 0x01010400UL, 0x01000000UL, 0x01000000UL, 0x00000400UL, 0x01010004UL, 0x00010000UL, 0x00010400UL, 0x01000004UL, 0x00000400UL, 0x00000004UL, 0x01000404UL, 0x00010404UL, 0x01010404UL, 0x00010004UL, 0x01010000UL, 0x01000404UL, 0x01000004UL, 0x00000404UL, 0x00010404UL, 0x01010400UL, 0x00000404UL, 0x01000400UL, 0x01000400UL, 0x00000000UL, 0x00010004UL, 0x00010400UL, 0x00000000UL, 0x01010004UL }; static const u32 SP2[64] = { 0x80108020UL, 0x80008000UL, 0x00008000UL, 0x00108020UL, 0x00100000UL, 0x00000020UL, 0x80100020UL, 0x80008020UL, 0x80000020UL, 0x80108020UL, 0x80108000UL, 0x80000000UL, 0x80008000UL, 0x00100000UL, 0x00000020UL, 0x80100020UL, 0x00108000UL, 0x00100020UL, 0x80008020UL, 0x00000000UL, 0x80000000UL, 0x00008000UL, 0x00108020UL, 0x80100000UL, 0x00100020UL, 0x80000020UL, 0x00000000UL, 0x00108000UL, 0x00008020UL, 0x80108000UL, 0x80100000UL, 0x00008020UL, 0x00000000UL, 0x00108020UL, 0x80100020UL, 0x00100000UL, 0x80008020UL, 0x80100000UL, 0x80108000UL, 0x00008000UL, 0x80100000UL, 0x80008000UL, 0x00000020UL, 0x80108020UL, 0x00108020UL, 0x00000020UL, 0x00008000UL, 0x80000000UL, 0x00008020UL, 0x80108000UL, 0x00100000UL, 0x80000020UL, 0x00100020UL, 0x80008020UL, 0x80000020UL, 0x00100020UL, 0x00108000UL, 0x00000000UL, 0x80008000UL, 0x00008020UL, 0x80000000UL, 0x80100020UL, 0x80108020UL, 0x00108000UL }; static const u32 SP3[64] = { 0x00000208UL, 0x08020200UL, 0x00000000UL, 0x08020008UL, 0x08000200UL, 0x00000000UL, 0x00020208UL, 0x08000200UL, 0x00020008UL, 0x08000008UL, 0x08000008UL, 0x00020000UL, 0x08020208UL, 0x00020008UL, 0x08020000UL, 0x00000208UL, 0x08000000UL, 0x00000008UL, 0x08020200UL, 0x00000200UL, 0x00020200UL, 0x08020000UL, 0x08020008UL, 0x00020208UL, 0x08000208UL, 0x00020200UL, 0x00020000UL, 0x08000208UL, 0x00000008UL, 0x08020208UL, 0x00000200UL, 0x08000000UL, 0x08020200UL, 0x08000000UL, 0x00020008UL, 0x00000208UL, 0x00020000UL, 0x08020200UL, 0x08000200UL, 0x00000000UL, 0x00000200UL, 0x00020008UL, 0x08020208UL, 0x08000200UL, 0x08000008UL, 0x00000200UL, 0x00000000UL, 0x08020008UL, 0x08000208UL, 0x00020000UL, 0x08000000UL, 0x08020208UL, 0x00000008UL, 0x00020208UL, 0x00020200UL, 0x08000008UL, 0x08020000UL, 0x08000208UL, 0x00000208UL, 0x08020000UL, 0x00020208UL, 0x00000008UL, 0x08020008UL, 0x00020200UL }; static const u32 SP4[64] = { 0x00802001UL, 0x00002081UL, 0x00002081UL, 0x00000080UL, 0x00802080UL, 0x00800081UL, 0x00800001UL, 0x00002001UL, 0x00000000UL, 0x00802000UL, 0x00802000UL, 0x00802081UL, 0x00000081UL, 0x00000000UL, 0x00800080UL, 0x00800001UL, 0x00000001UL, 0x00002000UL, 0x00800000UL, 0x00802001UL, 0x00000080UL, 0x00800000UL, 0x00002001UL, 0x00002080UL, 0x00800081UL, 0x00000001UL, 0x00002080UL, 0x00800080UL, 0x00002000UL, 0x00802080UL, 0x00802081UL, 0x00000081UL, 0x00800080UL, 0x00800001UL, 0x00802000UL, 0x00802081UL, 0x00000081UL, 0x00000000UL, 0x00000000UL, 0x00802000UL, 0x00002080UL, 0x00800080UL, 0x00800081UL, 0x00000001UL, 0x00802001UL, 0x00002081UL, 0x00002081UL, 0x00000080UL, 0x00802081UL, 0x00000081UL, 0x00000001UL, 0x00002000UL, 0x00800001UL, 0x00002001UL, 0x00802080UL, 0x00800081UL, 0x00002001UL, 0x00002080UL, 0x00800000UL, 0x00802001UL, 0x00000080UL, 0x00800000UL, 0x00002000UL, 0x00802080UL }; static const u32 SP5[64] = { 0x00000100UL, 0x02080100UL, 0x02080000UL, 0x42000100UL, 0x00080000UL, 0x00000100UL, 0x40000000UL, 0x02080000UL, 0x40080100UL, 0x00080000UL, 0x02000100UL, 0x40080100UL, 0x42000100UL, 0x42080000UL, 0x00080100UL, 0x40000000UL, 0x02000000UL, 0x40080000UL, 0x40080000UL, 0x00000000UL, 0x40000100UL, 0x42080100UL, 0x42080100UL, 0x02000100UL, 0x42080000UL, 0x40000100UL, 0x00000000UL, 0x42000000UL, 0x02080100UL, 0x02000000UL, 0x42000000UL, 0x00080100UL, 0x00080000UL, 0x42000100UL, 0x00000100UL, 0x02000000UL, 0x40000000UL, 0x02080000UL, 0x42000100UL, 0x40080100UL, 0x02000100UL, 0x40000000UL, 0x42080000UL, 0x02080100UL, 0x40080100UL, 0x00000100UL, 0x02000000UL, 0x42080000UL, 0x42080100UL, 0x00080100UL, 0x42000000UL, 0x42080100UL, 0x02080000UL, 0x00000000UL, 0x40080000UL, 0x42000000UL, 0x00080100UL, 0x02000100UL, 0x40000100UL, 0x00080000UL, 0x00000000UL, 0x40080000UL, 0x02080100UL, 0x40000100UL }; static const u32 SP6[64] = { 0x20000010UL, 0x20400000UL, 0x00004000UL, 0x20404010UL, 0x20400000UL, 0x00000010UL, 0x20404010UL, 0x00400000UL, 0x20004000UL, 0x00404010UL, 0x00400000UL, 0x20000010UL, 0x00400010UL, 0x20004000UL, 0x20000000UL, 0x00004010UL, 0x00000000UL, 0x00400010UL, 0x20004010UL, 0x00004000UL, 0x00404000UL, 0x20004010UL, 0x00000010UL, 0x20400010UL, 0x20400010UL, 0x00000000UL, 0x00404010UL, 0x20404000UL, 0x00004010UL, 0x00404000UL, 0x20404000UL, 0x20000000UL, 0x20004000UL, 0x00000010UL, 0x20400010UL, 0x00404000UL, 0x20404010UL, 0x00400000UL, 0x00004010UL, 0x20000010UL, 0x00400000UL, 0x20004000UL, 0x20000000UL, 0x00004010UL, 0x20000010UL, 0x20404010UL, 0x00404000UL, 0x20400000UL, 0x00404010UL, 0x20404000UL, 0x00000000UL, 0x20400010UL, 0x00000010UL, 0x00004000UL, 0x20400000UL, 0x00404010UL, 0x00004000UL, 0x00400010UL, 0x20004010UL, 0x00000000UL, 0x20404000UL, 0x20000000UL, 0x00400010UL, 0x20004010UL }; static const u32 SP7[64] = { 0x00200000UL, 0x04200002UL, 0x04000802UL, 0x00000000UL, 0x00000800UL, 0x04000802UL, 0x00200802UL, 0x04200800UL, 0x04200802UL, 0x00200000UL, 0x00000000UL, 0x04000002UL, 0x00000002UL, 0x04000000UL, 0x04200002UL, 0x00000802UL, 0x04000800UL, 0x00200802UL, 0x00200002UL, 0x04000800UL, 0x04000002UL, 0x04200000UL, 0x04200800UL, 0x00200002UL, 0x04200000UL, 0x00000800UL, 0x00000802UL, 0x04200802UL, 0x00200800UL, 0x00000002UL, 0x04000000UL, 0x00200800UL, 0x04000000UL, 0x00200800UL, 0x00200000UL, 0x04000802UL, 0x04000802UL, 0x04200002UL, 0x04200002UL, 0x00000002UL, 0x00200002UL, 0x04000000UL, 0x04000800UL, 0x00200000UL, 0x04200800UL, 0x00000802UL, 0x00200802UL, 0x04200800UL, 0x00000802UL, 0x04000002UL, 0x04200802UL, 0x04200000UL, 0x00200800UL, 0x00000000UL, 0x00000002UL, 0x04200802UL, 0x00000000UL, 0x00200802UL, 0x04200000UL, 0x00000800UL, 0x04000002UL, 0x04000800UL, 0x00000800UL, 0x00200002UL }; static const u32 SP8[64] = { 0x10001040UL, 0x00001000UL, 0x00040000UL, 0x10041040UL, 0x10000000UL, 0x10001040UL, 0x00000040UL, 0x10000000UL, 0x00040040UL, 0x10040000UL, 0x10041040UL, 0x00041000UL, 0x10041000UL, 0x00041040UL, 0x00001000UL, 0x00000040UL, 0x10040000UL, 0x10000040UL, 0x10001000UL, 0x00001040UL, 0x00041000UL, 0x00040040UL, 0x10040040UL, 0x10041000UL, 0x00001040UL, 0x00000000UL, 0x00000000UL, 0x10040040UL, 0x10000040UL, 0x10001000UL, 0x00041040UL, 0x00040000UL, 0x00041040UL, 0x00040000UL, 0x10041000UL, 0x00001000UL, 0x00000040UL, 0x10040040UL, 0x00001000UL, 0x00041040UL, 0x10001000UL, 0x00000040UL, 0x10000040UL, 0x10040000UL, 0x10040040UL, 0x10000000UL, 0x00040000UL, 0x10001040UL, 0x00000000UL, 0x10041040UL, 0x00040040UL, 0x10000040UL, 0x10040000UL, 0x10001000UL, 0x10001040UL, 0x00000000UL, 0x10041040UL, 0x00041000UL, 0x00041000UL, 0x00001040UL, 0x00001040UL, 0x00040040UL, 0x10000000UL, 0x10041000UL }; static void cookey(const u32 *raw1, u32 *keyout) { u32 *cook; const u32 *raw0; u32 dough[32]; int i; cook = dough; for (i = 0; i < 16; i++, raw1++) { raw0 = raw1++; *cook = (*raw0 & 0x00fc0000L) << 6; *cook |= (*raw0 & 0x00000fc0L) << 10; *cook |= (*raw1 & 0x00fc0000L) >> 10; *cook++ |= (*raw1 & 0x00000fc0L) >> 6; *cook = (*raw0 & 0x0003f000L) << 12; *cook |= (*raw0 & 0x0000003fL) << 16; *cook |= (*raw1 & 0x0003f000L) >> 4; *cook++ |= (*raw1 & 0x0000003fL); } os_memcpy(keyout, dough, sizeof(dough)); } static void deskey(const u8 *key, int decrypt, u32 *keyout) { u32 i, j, l, m, n, kn[32]; u8 pc1m[56], pcr[56]; for (j = 0; j < 56; j++) { l = (u32) pc1[j]; m = l & 7; pc1m[j] = (u8) ((key[l >> 3U] & bytebit[m]) == bytebit[m] ? 1 : 0); } for (i = 0; i < 16; i++) { if (decrypt) m = (15 - i) << 1; else m = i << 1; n = m + 1; kn[m] = kn[n] = 0L; for (j = 0; j < 28; j++) { l = j + (u32) totrot[i]; if (l < 28) pcr[j] = pc1m[l]; else pcr[j] = pc1m[l - 28]; } for (/* j = 28 */; j < 56; j++) { l = j + (u32) totrot[i]; if (l < 56) pcr[j] = pc1m[l]; else pcr[j] = pc1m[l - 28]; } for (j = 0; j < 24; j++) { if ((int) pcr[(int) pc2[j]] != 0) kn[m] |= bigbyte[j]; if ((int) pcr[(int) pc2[j + 24]] != 0) kn[n] |= bigbyte[j]; } } cookey(kn, keyout); } static void desfunc(u32 *block, const u32 *keys) { u32 work, right, leftt; int cur_round; leftt = block[0]; right = block[1]; work = ((leftt >> 4) ^ right) & 0x0f0f0f0fL; right ^= work; leftt ^= (work << 4); work = ((leftt >> 16) ^ right) & 0x0000ffffL; right ^= work; leftt ^= (work << 16); work = ((right >> 2) ^ leftt) & 0x33333333L; leftt ^= work; right ^= (work << 2); work = ((right >> 8) ^ leftt) & 0x00ff00ffL; leftt ^= work; right ^= (work << 8); right = ROLc(right, 1); work = (leftt ^ right) & 0xaaaaaaaaL; leftt ^= work; right ^= work; leftt = ROLc(leftt, 1); for (cur_round = 0; cur_round < 8; cur_round++) { work = RORc(right, 4) ^ *keys++; leftt ^= SP7[work & 0x3fL] ^ SP5[(work >> 8) & 0x3fL] ^ SP3[(work >> 16) & 0x3fL] ^ SP1[(work >> 24) & 0x3fL]; work = right ^ *keys++; leftt ^= SP8[ work & 0x3fL] ^ SP6[(work >> 8) & 0x3fL] ^ SP4[(work >> 16) & 0x3fL] ^ SP2[(work >> 24) & 0x3fL]; work = RORc(leftt, 4) ^ *keys++; right ^= SP7[ work & 0x3fL] ^ SP5[(work >> 8) & 0x3fL] ^ SP3[(work >> 16) & 0x3fL] ^ SP1[(work >> 24) & 0x3fL]; work = leftt ^ *keys++; right ^= SP8[ work & 0x3fL] ^ SP6[(work >> 8) & 0x3fL] ^ SP4[(work >> 16) & 0x3fL] ^ SP2[(work >> 24) & 0x3fL]; } right = RORc(right, 1); work = (leftt ^ right) & 0xaaaaaaaaL; leftt ^= work; right ^= work; leftt = RORc(leftt, 1); work = ((leftt >> 8) ^ right) & 0x00ff00ffL; right ^= work; leftt ^= (work << 8); /* -- */ work = ((leftt >> 2) ^ right) & 0x33333333L; right ^= work; leftt ^= (work << 2); work = ((right >> 16) ^ leftt) & 0x0000ffffL; leftt ^= work; right ^= (work << 16); work = ((right >> 4) ^ leftt) & 0x0f0f0f0fL; leftt ^= work; right ^= (work << 4); block[0] = right; block[1] = leftt; } /* wpa_supplicant/hostapd specific wrapper */ void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) { u8 pkey[8], next, tmp; int i; u32 ek[32], work[2]; /* Add parity bits to the key */ next = 0; for (i = 0; i < 7; i++) { tmp = key[i]; pkey[i] = (tmp >> i) | next | 1; next = tmp << (7 - i); } pkey[i] = next | 1; deskey(pkey, 0, ek); work[0] = WPA_GET_BE32(clear); work[1] = WPA_GET_BE32(clear + 4); desfunc(work, ek); WPA_PUT_BE32(cypher, work[0]); WPA_PUT_BE32(cypher + 4, work[1]); os_memset(pkey, 0, sizeof(pkey)); os_memset(ek, 0, sizeof(ek)); } void des_key_setup(const u8 *key, u32 *ek, u32 *dk) { deskey(key, 0, ek); deskey(key, 1, dk); } void des_block_encrypt(const u8 *plain, const u32 *ek, u8 *crypt) { u32 work[2]; work[0] = WPA_GET_BE32(plain); work[1] = WPA_GET_BE32(plain + 4); desfunc(work, ek); WPA_PUT_BE32(crypt, work[0]); WPA_PUT_BE32(crypt + 4, work[1]); } void des_block_decrypt(const u8 *crypt, const u32 *dk, u8 *plain) { u32 work[2]; work[0] = WPA_GET_BE32(crypt); work[1] = WPA_GET_BE32(crypt + 4); desfunc(work, dk); WPA_PUT_BE32(plain, work[0]); WPA_PUT_BE32(plain + 4, work[1]); } void des3_key_setup(const u8 *key, struct des3_key_s *dkey) { deskey(key, 0, dkey->ek[0]); deskey(key + 8, 1, dkey->ek[1]); deskey(key + 16, 0, dkey->ek[2]); deskey(key, 1, dkey->dk[2]); deskey(key + 8, 0, dkey->dk[1]); deskey(key + 16, 1, dkey->dk[0]); } void des3_encrypt(const u8 *plain, const struct des3_key_s *key, u8 *crypt) { u32 work[2]; work[0] = WPA_GET_BE32(plain); work[1] = WPA_GET_BE32(plain + 4); desfunc(work, key->ek[0]); desfunc(work, key->ek[1]); desfunc(work, key->ek[2]); WPA_PUT_BE32(crypt, work[0]); WPA_PUT_BE32(crypt + 4, work[1]); } void des3_decrypt(const u8 *crypt, const struct des3_key_s *key, u8 *plain) { u32 work[2]; work[0] = WPA_GET_BE32(crypt); work[1] = WPA_GET_BE32(crypt + 4); desfunc(work, key->dk[0]); desfunc(work, key->dk[1]); desfunc(work, key->dk[2]); WPA_PUT_BE32(plain, work[0]); WPA_PUT_BE32(plain + 4, work[1]); } wpa-2.1/src/crypto/des_i.h000066400000000000000000000013051227414666700155240ustar00rootroot00000000000000/* * DES and 3DES-EDE ciphers * Copyright (c) 2006-2009, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef DES_I_H #define DES_I_H struct des3_key_s { u32 ek[3][32]; u32 dk[3][32]; }; void des_key_setup(const u8 *key, u32 *ek, u32 *dk); void des_block_encrypt(const u8 *plain, const u32 *ek, u8 *crypt); void des_block_decrypt(const u8 *crypt, const u32 *dk, u8 *plain); void des3_key_setup(const u8 *key, struct des3_key_s *dkey); void des3_encrypt(const u8 *plain, const struct des3_key_s *key, u8 *crypt); void des3_decrypt(const u8 *crypt, const struct des3_key_s *key, u8 *plain); #endif /* DES_I_H */ wpa-2.1/src/crypto/dh_group5.c000066400000000000000000000014211227414666700163270ustar00rootroot00000000000000/* * Diffie-Hellman group 5 operations * Copyright (c) 2009, 2012, 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 "dh_groups.h" #include "dh_group5.h" void * dh5_init(struct wpabuf **priv, struct wpabuf **publ) { *publ = dh_init(dh_groups_get(5), priv); if (*publ == NULL) return NULL; return (void *) 1; } void * dh5_init_fixed(const struct wpabuf *priv, const struct wpabuf *publ) { return (void *) 1; } struct wpabuf * dh5_derive_shared(void *ctx, const struct wpabuf *peer_public, const struct wpabuf *own_private) { return dh_derive_shared(peer_public, own_private, dh_groups_get(5)); } void dh5_free(void *ctx) { } wpa-2.1/src/crypto/dh_group5.h000066400000000000000000000010541227414666700163360ustar00rootroot00000000000000/* * Diffie-Hellman group 5 operations * Copyright (c) 2009, 2012, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef DH_GROUP5_H #define DH_GROUP5_H void * dh5_init(struct wpabuf **priv, struct wpabuf **publ); void * dh5_init_fixed(const struct wpabuf *priv, const struct wpabuf *publ); struct wpabuf * dh5_derive_shared(void *ctx, const struct wpabuf *peer_public, const struct wpabuf *own_private); void dh5_free(void *ctx); #endif /* DH_GROUP5_H */ wpa-2.1/src/crypto/dh_groups.c000066400000000000000000001545631227414666700164450ustar00rootroot00000000000000/* * Diffie-Hellman groups * Copyright (c) 2007, 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.h" #include "random.h" #include "dh_groups.h" #ifdef ALL_DH_GROUPS /* RFC 4306, B.1. Group 1 - 768 Bit MODP * Generator: 2 * Prime: 2^768 - 2 ^704 - 1 + 2^64 * { [2^638 pi] + 149686 } */ static const u8 dh_group1_generator[1] = { 0x02 }; static const u8 dh_group1_prime[96] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x3A, 0x36, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; static const u8 dh_group1_order[96] = { 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE4, 0x87, 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A, 0x62, 0x63, 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68, 0x94, 0x81, 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A, 0x01, 0x05, 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91, 0x28, 0xA5, 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E, 0xF7, 0xCA, 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D, 0x98, 0x15, 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B, 0xA7, 0xF0, 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22, 0xF2, 0x42, 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63, 0x7A, 0x26, 0x21, 0x74, 0xD3, 0x1D, 0x1B, 0x10, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; /* RFC 4306, B.2. Group 2 - 1024 Bit MODP * Generator: 2 * Prime: 2^1024 - 2^960 - 1 + 2^64 * { [2^894 pi] + 129093 } */ static const u8 dh_group2_generator[1] = { 0x02 }; static const u8 dh_group2_prime[128] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6, 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE6, 0x53, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; static const u8 dh_group2_order[128] = { 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE4, 0x87, 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A, 0x62, 0x63, 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68, 0x94, 0x81, 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A, 0x01, 0x05, 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91, 0x28, 0xA5, 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E, 0xF7, 0xCA, 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D, 0x98, 0x15, 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B, 0xA7, 0xF0, 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22, 0xF2, 0x42, 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63, 0x7A, 0x26, 0x21, 0x74, 0xD3, 0x1B, 0xF6, 0xB5, 0x85, 0xFF, 0xAE, 0x5B, 0x7A, 0x03, 0x5B, 0xF6, 0xF7, 0x1C, 0x35, 0xFD, 0xAD, 0x44, 0xCF, 0xD2, 0xD7, 0x4F, 0x92, 0x08, 0xBE, 0x25, 0x8F, 0xF3, 0x24, 0x94, 0x33, 0x28, 0xF6, 0x73, 0x29, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; #endif /* ALL_DH_GROUPS */ /* RFC 3526, 2. Group 5 - 1536 Bit MODP * Generator: 2 * Prime: 2^1536 - 2^1472 - 1 + 2^64 * { [2^1406 pi] + 741804 } */ static const u8 dh_group5_generator[1] = { 0x02 }; static const u8 dh_group5_prime[192] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6, 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D, 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, 0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A, 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F, 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, 0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB, 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D, 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, 0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x23, 0x73, 0x27, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; static const u8 dh_group5_order[192] = { 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE4, 0x87, 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A, 0x62, 0x63, 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68, 0x94, 0x81, 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A, 0x01, 0x05, 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91, 0x28, 0xA5, 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E, 0xF7, 0xCA, 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D, 0x98, 0x15, 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B, 0xA7, 0xF0, 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22, 0xF2, 0x42, 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63, 0x7A, 0x26, 0x21, 0x74, 0xD3, 0x1B, 0xF6, 0xB5, 0x85, 0xFF, 0xAE, 0x5B, 0x7A, 0x03, 0x5B, 0xF6, 0xF7, 0x1C, 0x35, 0xFD, 0xAD, 0x44, 0xCF, 0xD2, 0xD7, 0x4F, 0x92, 0x08, 0xBE, 0x25, 0x8F, 0xF3, 0x24, 0x94, 0x33, 0x28, 0xF6, 0x72, 0x2D, 0x9E, 0xE1, 0x00, 0x3E, 0x5C, 0x50, 0xB1, 0xDF, 0x82, 0xCC, 0x6D, 0x24, 0x1B, 0x0E, 0x2A, 0xE9, 0xCD, 0x34, 0x8B, 0x1F, 0xD4, 0x7E, 0x92, 0x67, 0xAF, 0xC1, 0xB2, 0xAE, 0x91, 0xEE, 0x51, 0xD6, 0xCB, 0x0E, 0x31, 0x79, 0xAB, 0x10, 0x42, 0xA9, 0x5D, 0xCF, 0x6A, 0x94, 0x83, 0xB8, 0x4B, 0x4B, 0x36, 0xB3, 0x86, 0x1A, 0xA7, 0x25, 0x5E, 0x4C, 0x02, 0x78, 0xBA, 0x36, 0x04, 0x65, 0x11, 0xB9, 0x93, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; #ifdef ALL_DH_GROUPS /* RFC 3526, 3. Group 14 - 2048 Bit MODP * Generator: 2 * Prime: 2^2048 - 2^1984 - 1 + 2^64 * { [2^1918 pi] + 124476 } */ static const u8 dh_group14_generator[1] = { 0x02 }; static const u8 dh_group14_prime[256] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6, 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D, 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, 0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A, 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F, 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, 0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB, 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D, 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, 0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C, 0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B, 0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03, 0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F, 0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9, 0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18, 0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5, 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10, 0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAC, 0xAA, 0x68, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; static const u8 dh_group14_order[256] = { 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE4, 0x87, 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A, 0x62, 0x63, 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68, 0x94, 0x81, 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A, 0x01, 0x05, 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91, 0x28, 0xA5, 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E, 0xF7, 0xCA, 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D, 0x98, 0x15, 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B, 0xA7, 0xF0, 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22, 0xF2, 0x42, 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63, 0x7A, 0x26, 0x21, 0x74, 0xD3, 0x1B, 0xF6, 0xB5, 0x85, 0xFF, 0xAE, 0x5B, 0x7A, 0x03, 0x5B, 0xF6, 0xF7, 0x1C, 0x35, 0xFD, 0xAD, 0x44, 0xCF, 0xD2, 0xD7, 0x4F, 0x92, 0x08, 0xBE, 0x25, 0x8F, 0xF3, 0x24, 0x94, 0x33, 0x28, 0xF6, 0x72, 0x2D, 0x9E, 0xE1, 0x00, 0x3E, 0x5C, 0x50, 0xB1, 0xDF, 0x82, 0xCC, 0x6D, 0x24, 0x1B, 0x0E, 0x2A, 0xE9, 0xCD, 0x34, 0x8B, 0x1F, 0xD4, 0x7E, 0x92, 0x67, 0xAF, 0xC1, 0xB2, 0xAE, 0x91, 0xEE, 0x51, 0xD6, 0xCB, 0x0E, 0x31, 0x79, 0xAB, 0x10, 0x42, 0xA9, 0x5D, 0xCF, 0x6A, 0x94, 0x83, 0xB8, 0x4B, 0x4B, 0x36, 0xB3, 0x86, 0x1A, 0xA7, 0x25, 0x5E, 0x4C, 0x02, 0x78, 0xBA, 0x36, 0x04, 0x65, 0x0C, 0x10, 0xBE, 0x19, 0x48, 0x2F, 0x23, 0x17, 0x1B, 0x67, 0x1D, 0xF1, 0xCF, 0x3B, 0x96, 0x0C, 0x07, 0x43, 0x01, 0xCD, 0x93, 0xC1, 0xD1, 0x76, 0x03, 0xD1, 0x47, 0xDA, 0xE2, 0xAE, 0xF8, 0x37, 0xA6, 0x29, 0x64, 0xEF, 0x15, 0xE5, 0xFB, 0x4A, 0xAC, 0x0B, 0x8C, 0x1C, 0xCA, 0xA4, 0xBE, 0x75, 0x4A, 0xB5, 0x72, 0x8A, 0xE9, 0x13, 0x0C, 0x4C, 0x7D, 0x02, 0x88, 0x0A, 0xB9, 0x47, 0x2D, 0x45, 0x56, 0x55, 0x34, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; /* RFC 3526, 4. Group 15 - 3072 Bit MODP * Generator: 2 * Prime: 2^3072 - 2^3008 - 1 + 2^64 * { [2^2942 pi] + 1690314 } */ static const u8 dh_group15_generator[1] = { 0x02 }; static const u8 dh_group15_prime[384] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6, 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D, 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, 0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A, 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F, 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, 0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB, 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D, 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, 0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C, 0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B, 0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03, 0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F, 0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9, 0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18, 0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5, 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10, 0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAA, 0xC4, 0x2D, 0xAD, 0x33, 0x17, 0x0D, 0x04, 0x50, 0x7A, 0x33, 0xA8, 0x55, 0x21, 0xAB, 0xDF, 0x1C, 0xBA, 0x64, 0xEC, 0xFB, 0x85, 0x04, 0x58, 0xDB, 0xEF, 0x0A, 0x8A, 0xEA, 0x71, 0x57, 0x5D, 0x06, 0x0C, 0x7D, 0xB3, 0x97, 0x0F, 0x85, 0xA6, 0xE1, 0xE4, 0xC7, 0xAB, 0xF5, 0xAE, 0x8C, 0xDB, 0x09, 0x33, 0xD7, 0x1E, 0x8C, 0x94, 0xE0, 0x4A, 0x25, 0x61, 0x9D, 0xCE, 0xE3, 0xD2, 0x26, 0x1A, 0xD2, 0xEE, 0x6B, 0xF1, 0x2F, 0xFA, 0x06, 0xD9, 0x8A, 0x08, 0x64, 0xD8, 0x76, 0x02, 0x73, 0x3E, 0xC8, 0x6A, 0x64, 0x52, 0x1F, 0x2B, 0x18, 0x17, 0x7B, 0x20, 0x0C, 0xBB, 0xE1, 0x17, 0x57, 0x7A, 0x61, 0x5D, 0x6C, 0x77, 0x09, 0x88, 0xC0, 0xBA, 0xD9, 0x46, 0xE2, 0x08, 0xE2, 0x4F, 0xA0, 0x74, 0xE5, 0xAB, 0x31, 0x43, 0xDB, 0x5B, 0xFC, 0xE0, 0xFD, 0x10, 0x8E, 0x4B, 0x82, 0xD1, 0x20, 0xA9, 0x3A, 0xD2, 0xCA, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; static const u8 dh_group15_order[384] = { 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE4, 0x87, 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A, 0x62, 0x63, 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68, 0x94, 0x81, 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A, 0x01, 0x05, 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91, 0x28, 0xA5, 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E, 0xF7, 0xCA, 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D, 0x98, 0x15, 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B, 0xA7, 0xF0, 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22, 0xF2, 0x42, 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63, 0x7A, 0x26, 0x21, 0x74, 0xD3, 0x1B, 0xF6, 0xB5, 0x85, 0xFF, 0xAE, 0x5B, 0x7A, 0x03, 0x5B, 0xF6, 0xF7, 0x1C, 0x35, 0xFD, 0xAD, 0x44, 0xCF, 0xD2, 0xD7, 0x4F, 0x92, 0x08, 0xBE, 0x25, 0x8F, 0xF3, 0x24, 0x94, 0x33, 0x28, 0xF6, 0x72, 0x2D, 0x9E, 0xE1, 0x00, 0x3E, 0x5C, 0x50, 0xB1, 0xDF, 0x82, 0xCC, 0x6D, 0x24, 0x1B, 0x0E, 0x2A, 0xE9, 0xCD, 0x34, 0x8B, 0x1F, 0xD4, 0x7E, 0x92, 0x67, 0xAF, 0xC1, 0xB2, 0xAE, 0x91, 0xEE, 0x51, 0xD6, 0xCB, 0x0E, 0x31, 0x79, 0xAB, 0x10, 0x42, 0xA9, 0x5D, 0xCF, 0x6A, 0x94, 0x83, 0xB8, 0x4B, 0x4B, 0x36, 0xB3, 0x86, 0x1A, 0xA7, 0x25, 0x5E, 0x4C, 0x02, 0x78, 0xBA, 0x36, 0x04, 0x65, 0x0C, 0x10, 0xBE, 0x19, 0x48, 0x2F, 0x23, 0x17, 0x1B, 0x67, 0x1D, 0xF1, 0xCF, 0x3B, 0x96, 0x0C, 0x07, 0x43, 0x01, 0xCD, 0x93, 0xC1, 0xD1, 0x76, 0x03, 0xD1, 0x47, 0xDA, 0xE2, 0xAE, 0xF8, 0x37, 0xA6, 0x29, 0x64, 0xEF, 0x15, 0xE5, 0xFB, 0x4A, 0xAC, 0x0B, 0x8C, 0x1C, 0xCA, 0xA4, 0xBE, 0x75, 0x4A, 0xB5, 0x72, 0x8A, 0xE9, 0x13, 0x0C, 0x4C, 0x7D, 0x02, 0x88, 0x0A, 0xB9, 0x47, 0x2D, 0x45, 0x55, 0x62, 0x16, 0xD6, 0x99, 0x8B, 0x86, 0x82, 0x28, 0x3D, 0x19, 0xD4, 0x2A, 0x90, 0xD5, 0xEF, 0x8E, 0x5D, 0x32, 0x76, 0x7D, 0xC2, 0x82, 0x2C, 0x6D, 0xF7, 0x85, 0x45, 0x75, 0x38, 0xAB, 0xAE, 0x83, 0x06, 0x3E, 0xD9, 0xCB, 0x87, 0xC2, 0xD3, 0x70, 0xF2, 0x63, 0xD5, 0xFA, 0xD7, 0x46, 0x6D, 0x84, 0x99, 0xEB, 0x8F, 0x46, 0x4A, 0x70, 0x25, 0x12, 0xB0, 0xCE, 0xE7, 0x71, 0xE9, 0x13, 0x0D, 0x69, 0x77, 0x35, 0xF8, 0x97, 0xFD, 0x03, 0x6C, 0xC5, 0x04, 0x32, 0x6C, 0x3B, 0x01, 0x39, 0x9F, 0x64, 0x35, 0x32, 0x29, 0x0F, 0x95, 0x8C, 0x0B, 0xBD, 0x90, 0x06, 0x5D, 0xF0, 0x8B, 0xAB, 0xBD, 0x30, 0xAE, 0xB6, 0x3B, 0x84, 0xC4, 0x60, 0x5D, 0x6C, 0xA3, 0x71, 0x04, 0x71, 0x27, 0xD0, 0x3A, 0x72, 0xD5, 0x98, 0xA1, 0xED, 0xAD, 0xFE, 0x70, 0x7E, 0x88, 0x47, 0x25, 0xC1, 0x68, 0x90, 0x54, 0x9D, 0x69, 0x65, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; /* RFC 3526, 5. Group 16 - 4096 Bit MODP * Generator: 2 * Prime: 2^4096 - 2^4032 - 1 + 2^64 * { [2^3966 pi] + 240904 } */ static const u8 dh_group16_generator[1] = { 0x02 }; static const u8 dh_group16_prime[512] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6, 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D, 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, 0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A, 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F, 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, 0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB, 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D, 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, 0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C, 0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B, 0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03, 0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F, 0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9, 0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18, 0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5, 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10, 0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAA, 0xC4, 0x2D, 0xAD, 0x33, 0x17, 0x0D, 0x04, 0x50, 0x7A, 0x33, 0xA8, 0x55, 0x21, 0xAB, 0xDF, 0x1C, 0xBA, 0x64, 0xEC, 0xFB, 0x85, 0x04, 0x58, 0xDB, 0xEF, 0x0A, 0x8A, 0xEA, 0x71, 0x57, 0x5D, 0x06, 0x0C, 0x7D, 0xB3, 0x97, 0x0F, 0x85, 0xA6, 0xE1, 0xE4, 0xC7, 0xAB, 0xF5, 0xAE, 0x8C, 0xDB, 0x09, 0x33, 0xD7, 0x1E, 0x8C, 0x94, 0xE0, 0x4A, 0x25, 0x61, 0x9D, 0xCE, 0xE3, 0xD2, 0x26, 0x1A, 0xD2, 0xEE, 0x6B, 0xF1, 0x2F, 0xFA, 0x06, 0xD9, 0x8A, 0x08, 0x64, 0xD8, 0x76, 0x02, 0x73, 0x3E, 0xC8, 0x6A, 0x64, 0x52, 0x1F, 0x2B, 0x18, 0x17, 0x7B, 0x20, 0x0C, 0xBB, 0xE1, 0x17, 0x57, 0x7A, 0x61, 0x5D, 0x6C, 0x77, 0x09, 0x88, 0xC0, 0xBA, 0xD9, 0x46, 0xE2, 0x08, 0xE2, 0x4F, 0xA0, 0x74, 0xE5, 0xAB, 0x31, 0x43, 0xDB, 0x5B, 0xFC, 0xE0, 0xFD, 0x10, 0x8E, 0x4B, 0x82, 0xD1, 0x20, 0xA9, 0x21, 0x08, 0x01, 0x1A, 0x72, 0x3C, 0x12, 0xA7, 0x87, 0xE6, 0xD7, 0x88, 0x71, 0x9A, 0x10, 0xBD, 0xBA, 0x5B, 0x26, 0x99, 0xC3, 0x27, 0x18, 0x6A, 0xF4, 0xE2, 0x3C, 0x1A, 0x94, 0x68, 0x34, 0xB6, 0x15, 0x0B, 0xDA, 0x25, 0x83, 0xE9, 0xCA, 0x2A, 0xD4, 0x4C, 0xE8, 0xDB, 0xBB, 0xC2, 0xDB, 0x04, 0xDE, 0x8E, 0xF9, 0x2E, 0x8E, 0xFC, 0x14, 0x1F, 0xBE, 0xCA, 0xA6, 0x28, 0x7C, 0x59, 0x47, 0x4E, 0x6B, 0xC0, 0x5D, 0x99, 0xB2, 0x96, 0x4F, 0xA0, 0x90, 0xC3, 0xA2, 0x23, 0x3B, 0xA1, 0x86, 0x51, 0x5B, 0xE7, 0xED, 0x1F, 0x61, 0x29, 0x70, 0xCE, 0xE2, 0xD7, 0xAF, 0xB8, 0x1B, 0xDD, 0x76, 0x21, 0x70, 0x48, 0x1C, 0xD0, 0x06, 0x91, 0x27, 0xD5, 0xB0, 0x5A, 0xA9, 0x93, 0xB4, 0xEA, 0x98, 0x8D, 0x8F, 0xDD, 0xC1, 0x86, 0xFF, 0xB7, 0xDC, 0x90, 0xA6, 0xC0, 0x8F, 0x4D, 0xF4, 0x35, 0xC9, 0x34, 0x06, 0x31, 0x99, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; static const u8 dh_group16_order[512] = { 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE4, 0x87, 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A, 0x62, 0x63, 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68, 0x94, 0x81, 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A, 0x01, 0x05, 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91, 0x28, 0xA5, 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E, 0xF7, 0xCA, 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D, 0x98, 0x15, 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B, 0xA7, 0xF0, 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22, 0xF2, 0x42, 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63, 0x7A, 0x26, 0x21, 0x74, 0xD3, 0x1B, 0xF6, 0xB5, 0x85, 0xFF, 0xAE, 0x5B, 0x7A, 0x03, 0x5B, 0xF6, 0xF7, 0x1C, 0x35, 0xFD, 0xAD, 0x44, 0xCF, 0xD2, 0xD7, 0x4F, 0x92, 0x08, 0xBE, 0x25, 0x8F, 0xF3, 0x24, 0x94, 0x33, 0x28, 0xF6, 0x72, 0x2D, 0x9E, 0xE1, 0x00, 0x3E, 0x5C, 0x50, 0xB1, 0xDF, 0x82, 0xCC, 0x6D, 0x24, 0x1B, 0x0E, 0x2A, 0xE9, 0xCD, 0x34, 0x8B, 0x1F, 0xD4, 0x7E, 0x92, 0x67, 0xAF, 0xC1, 0xB2, 0xAE, 0x91, 0xEE, 0x51, 0xD6, 0xCB, 0x0E, 0x31, 0x79, 0xAB, 0x10, 0x42, 0xA9, 0x5D, 0xCF, 0x6A, 0x94, 0x83, 0xB8, 0x4B, 0x4B, 0x36, 0xB3, 0x86, 0x1A, 0xA7, 0x25, 0x5E, 0x4C, 0x02, 0x78, 0xBA, 0x36, 0x04, 0x65, 0x0C, 0x10, 0xBE, 0x19, 0x48, 0x2F, 0x23, 0x17, 0x1B, 0x67, 0x1D, 0xF1, 0xCF, 0x3B, 0x96, 0x0C, 0x07, 0x43, 0x01, 0xCD, 0x93, 0xC1, 0xD1, 0x76, 0x03, 0xD1, 0x47, 0xDA, 0xE2, 0xAE, 0xF8, 0x37, 0xA6, 0x29, 0x64, 0xEF, 0x15, 0xE5, 0xFB, 0x4A, 0xAC, 0x0B, 0x8C, 0x1C, 0xCA, 0xA4, 0xBE, 0x75, 0x4A, 0xB5, 0x72, 0x8A, 0xE9, 0x13, 0x0C, 0x4C, 0x7D, 0x02, 0x88, 0x0A, 0xB9, 0x47, 0x2D, 0x45, 0x55, 0x62, 0x16, 0xD6, 0x99, 0x8B, 0x86, 0x82, 0x28, 0x3D, 0x19, 0xD4, 0x2A, 0x90, 0xD5, 0xEF, 0x8E, 0x5D, 0x32, 0x76, 0x7D, 0xC2, 0x82, 0x2C, 0x6D, 0xF7, 0x85, 0x45, 0x75, 0x38, 0xAB, 0xAE, 0x83, 0x06, 0x3E, 0xD9, 0xCB, 0x87, 0xC2, 0xD3, 0x70, 0xF2, 0x63, 0xD5, 0xFA, 0xD7, 0x46, 0x6D, 0x84, 0x99, 0xEB, 0x8F, 0x46, 0x4A, 0x70, 0x25, 0x12, 0xB0, 0xCE, 0xE7, 0x71, 0xE9, 0x13, 0x0D, 0x69, 0x77, 0x35, 0xF8, 0x97, 0xFD, 0x03, 0x6C, 0xC5, 0x04, 0x32, 0x6C, 0x3B, 0x01, 0x39, 0x9F, 0x64, 0x35, 0x32, 0x29, 0x0F, 0x95, 0x8C, 0x0B, 0xBD, 0x90, 0x06, 0x5D, 0xF0, 0x8B, 0xAB, 0xBD, 0x30, 0xAE, 0xB6, 0x3B, 0x84, 0xC4, 0x60, 0x5D, 0x6C, 0xA3, 0x71, 0x04, 0x71, 0x27, 0xD0, 0x3A, 0x72, 0xD5, 0x98, 0xA1, 0xED, 0xAD, 0xFE, 0x70, 0x7E, 0x88, 0x47, 0x25, 0xC1, 0x68, 0x90, 0x54, 0x90, 0x84, 0x00, 0x8D, 0x39, 0x1E, 0x09, 0x53, 0xC3, 0xF3, 0x6B, 0xC4, 0x38, 0xCD, 0x08, 0x5E, 0xDD, 0x2D, 0x93, 0x4C, 0xE1, 0x93, 0x8C, 0x35, 0x7A, 0x71, 0x1E, 0x0D, 0x4A, 0x34, 0x1A, 0x5B, 0x0A, 0x85, 0xED, 0x12, 0xC1, 0xF4, 0xE5, 0x15, 0x6A, 0x26, 0x74, 0x6D, 0xDD, 0xE1, 0x6D, 0x82, 0x6F, 0x47, 0x7C, 0x97, 0x47, 0x7E, 0x0A, 0x0F, 0xDF, 0x65, 0x53, 0x14, 0x3E, 0x2C, 0xA3, 0xA7, 0x35, 0xE0, 0x2E, 0xCC, 0xD9, 0x4B, 0x27, 0xD0, 0x48, 0x61, 0xD1, 0x11, 0x9D, 0xD0, 0xC3, 0x28, 0xAD, 0xF3, 0xF6, 0x8F, 0xB0, 0x94, 0xB8, 0x67, 0x71, 0x6B, 0xD7, 0xDC, 0x0D, 0xEE, 0xBB, 0x10, 0xB8, 0x24, 0x0E, 0x68, 0x03, 0x48, 0x93, 0xEA, 0xD8, 0x2D, 0x54, 0xC9, 0xDA, 0x75, 0x4C, 0x46, 0xC7, 0xEE, 0xE0, 0xC3, 0x7F, 0xDB, 0xEE, 0x48, 0x53, 0x60, 0x47, 0xA6, 0xFA, 0x1A, 0xE4, 0x9A, 0x03, 0x18, 0xCC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; /* RFC 3526, 6. Group 17 - 6144 Bit MODP * Generator: 2 * Prime: 2^6144 - 2^6080 - 1 + 2^64 * { [2^6014 pi] + 929484 } */ static const u8 dh_group17_generator[1] = { 0x02 }; static const u8 dh_group17_prime[768] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6, 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D, 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, 0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A, 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F, 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, 0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB, 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D, 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, 0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C, 0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B, 0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03, 0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F, 0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9, 0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18, 0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5, 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10, 0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAA, 0xC4, 0x2D, 0xAD, 0x33, 0x17, 0x0D, 0x04, 0x50, 0x7A, 0x33, 0xA8, 0x55, 0x21, 0xAB, 0xDF, 0x1C, 0xBA, 0x64, 0xEC, 0xFB, 0x85, 0x04, 0x58, 0xDB, 0xEF, 0x0A, 0x8A, 0xEA, 0x71, 0x57, 0x5D, 0x06, 0x0C, 0x7D, 0xB3, 0x97, 0x0F, 0x85, 0xA6, 0xE1, 0xE4, 0xC7, 0xAB, 0xF5, 0xAE, 0x8C, 0xDB, 0x09, 0x33, 0xD7, 0x1E, 0x8C, 0x94, 0xE0, 0x4A, 0x25, 0x61, 0x9D, 0xCE, 0xE3, 0xD2, 0x26, 0x1A, 0xD2, 0xEE, 0x6B, 0xF1, 0x2F, 0xFA, 0x06, 0xD9, 0x8A, 0x08, 0x64, 0xD8, 0x76, 0x02, 0x73, 0x3E, 0xC8, 0x6A, 0x64, 0x52, 0x1F, 0x2B, 0x18, 0x17, 0x7B, 0x20, 0x0C, 0xBB, 0xE1, 0x17, 0x57, 0x7A, 0x61, 0x5D, 0x6C, 0x77, 0x09, 0x88, 0xC0, 0xBA, 0xD9, 0x46, 0xE2, 0x08, 0xE2, 0x4F, 0xA0, 0x74, 0xE5, 0xAB, 0x31, 0x43, 0xDB, 0x5B, 0xFC, 0xE0, 0xFD, 0x10, 0x8E, 0x4B, 0x82, 0xD1, 0x20, 0xA9, 0x21, 0x08, 0x01, 0x1A, 0x72, 0x3C, 0x12, 0xA7, 0x87, 0xE6, 0xD7, 0x88, 0x71, 0x9A, 0x10, 0xBD, 0xBA, 0x5B, 0x26, 0x99, 0xC3, 0x27, 0x18, 0x6A, 0xF4, 0xE2, 0x3C, 0x1A, 0x94, 0x68, 0x34, 0xB6, 0x15, 0x0B, 0xDA, 0x25, 0x83, 0xE9, 0xCA, 0x2A, 0xD4, 0x4C, 0xE8, 0xDB, 0xBB, 0xC2, 0xDB, 0x04, 0xDE, 0x8E, 0xF9, 0x2E, 0x8E, 0xFC, 0x14, 0x1F, 0xBE, 0xCA, 0xA6, 0x28, 0x7C, 0x59, 0x47, 0x4E, 0x6B, 0xC0, 0x5D, 0x99, 0xB2, 0x96, 0x4F, 0xA0, 0x90, 0xC3, 0xA2, 0x23, 0x3B, 0xA1, 0x86, 0x51, 0x5B, 0xE7, 0xED, 0x1F, 0x61, 0x29, 0x70, 0xCE, 0xE2, 0xD7, 0xAF, 0xB8, 0x1B, 0xDD, 0x76, 0x21, 0x70, 0x48, 0x1C, 0xD0, 0x06, 0x91, 0x27, 0xD5, 0xB0, 0x5A, 0xA9, 0x93, 0xB4, 0xEA, 0x98, 0x8D, 0x8F, 0xDD, 0xC1, 0x86, 0xFF, 0xB7, 0xDC, 0x90, 0xA6, 0xC0, 0x8F, 0x4D, 0xF4, 0x35, 0xC9, 0x34, 0x02, 0x84, 0x92, 0x36, 0xC3, 0xFA, 0xB4, 0xD2, 0x7C, 0x70, 0x26, 0xC1, 0xD4, 0xDC, 0xB2, 0x60, 0x26, 0x46, 0xDE, 0xC9, 0x75, 0x1E, 0x76, 0x3D, 0xBA, 0x37, 0xBD, 0xF8, 0xFF, 0x94, 0x06, 0xAD, 0x9E, 0x53, 0x0E, 0xE5, 0xDB, 0x38, 0x2F, 0x41, 0x30, 0x01, 0xAE, 0xB0, 0x6A, 0x53, 0xED, 0x90, 0x27, 0xD8, 0x31, 0x17, 0x97, 0x27, 0xB0, 0x86, 0x5A, 0x89, 0x18, 0xDA, 0x3E, 0xDB, 0xEB, 0xCF, 0x9B, 0x14, 0xED, 0x44, 0xCE, 0x6C, 0xBA, 0xCE, 0xD4, 0xBB, 0x1B, 0xDB, 0x7F, 0x14, 0x47, 0xE6, 0xCC, 0x25, 0x4B, 0x33, 0x20, 0x51, 0x51, 0x2B, 0xD7, 0xAF, 0x42, 0x6F, 0xB8, 0xF4, 0x01, 0x37, 0x8C, 0xD2, 0xBF, 0x59, 0x83, 0xCA, 0x01, 0xC6, 0x4B, 0x92, 0xEC, 0xF0, 0x32, 0xEA, 0x15, 0xD1, 0x72, 0x1D, 0x03, 0xF4, 0x82, 0xD7, 0xCE, 0x6E, 0x74, 0xFE, 0xF6, 0xD5, 0x5E, 0x70, 0x2F, 0x46, 0x98, 0x0C, 0x82, 0xB5, 0xA8, 0x40, 0x31, 0x90, 0x0B, 0x1C, 0x9E, 0x59, 0xE7, 0xC9, 0x7F, 0xBE, 0xC7, 0xE8, 0xF3, 0x23, 0xA9, 0x7A, 0x7E, 0x36, 0xCC, 0x88, 0xBE, 0x0F, 0x1D, 0x45, 0xB7, 0xFF, 0x58, 0x5A, 0xC5, 0x4B, 0xD4, 0x07, 0xB2, 0x2B, 0x41, 0x54, 0xAA, 0xCC, 0x8F, 0x6D, 0x7E, 0xBF, 0x48, 0xE1, 0xD8, 0x14, 0xCC, 0x5E, 0xD2, 0x0F, 0x80, 0x37, 0xE0, 0xA7, 0x97, 0x15, 0xEE, 0xF2, 0x9B, 0xE3, 0x28, 0x06, 0xA1, 0xD5, 0x8B, 0xB7, 0xC5, 0xDA, 0x76, 0xF5, 0x50, 0xAA, 0x3D, 0x8A, 0x1F, 0xBF, 0xF0, 0xEB, 0x19, 0xCC, 0xB1, 0xA3, 0x13, 0xD5, 0x5C, 0xDA, 0x56, 0xC9, 0xEC, 0x2E, 0xF2, 0x96, 0x32, 0x38, 0x7F, 0xE8, 0xD7, 0x6E, 0x3C, 0x04, 0x68, 0x04, 0x3E, 0x8F, 0x66, 0x3F, 0x48, 0x60, 0xEE, 0x12, 0xBF, 0x2D, 0x5B, 0x0B, 0x74, 0x74, 0xD6, 0xE6, 0x94, 0xF9, 0x1E, 0x6D, 0xCC, 0x40, 0x24, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; static const u8 dh_group17_order[768] = { 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE4, 0x87, 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A, 0x62, 0x63, 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68, 0x94, 0x81, 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A, 0x01, 0x05, 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91, 0x28, 0xA5, 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E, 0xF7, 0xCA, 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D, 0x98, 0x15, 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B, 0xA7, 0xF0, 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22, 0xF2, 0x42, 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63, 0x7A, 0x26, 0x21, 0x74, 0xD3, 0x1B, 0xF6, 0xB5, 0x85, 0xFF, 0xAE, 0x5B, 0x7A, 0x03, 0x5B, 0xF6, 0xF7, 0x1C, 0x35, 0xFD, 0xAD, 0x44, 0xCF, 0xD2, 0xD7, 0x4F, 0x92, 0x08, 0xBE, 0x25, 0x8F, 0xF3, 0x24, 0x94, 0x33, 0x28, 0xF6, 0x72, 0x2D, 0x9E, 0xE1, 0x00, 0x3E, 0x5C, 0x50, 0xB1, 0xDF, 0x82, 0xCC, 0x6D, 0x24, 0x1B, 0x0E, 0x2A, 0xE9, 0xCD, 0x34, 0x8B, 0x1F, 0xD4, 0x7E, 0x92, 0x67, 0xAF, 0xC1, 0xB2, 0xAE, 0x91, 0xEE, 0x51, 0xD6, 0xCB, 0x0E, 0x31, 0x79, 0xAB, 0x10, 0x42, 0xA9, 0x5D, 0xCF, 0x6A, 0x94, 0x83, 0xB8, 0x4B, 0x4B, 0x36, 0xB3, 0x86, 0x1A, 0xA7, 0x25, 0x5E, 0x4C, 0x02, 0x78, 0xBA, 0x36, 0x04, 0x65, 0x0C, 0x10, 0xBE, 0x19, 0x48, 0x2F, 0x23, 0x17, 0x1B, 0x67, 0x1D, 0xF1, 0xCF, 0x3B, 0x96, 0x0C, 0x07, 0x43, 0x01, 0xCD, 0x93, 0xC1, 0xD1, 0x76, 0x03, 0xD1, 0x47, 0xDA, 0xE2, 0xAE, 0xF8, 0x37, 0xA6, 0x29, 0x64, 0xEF, 0x15, 0xE5, 0xFB, 0x4A, 0xAC, 0x0B, 0x8C, 0x1C, 0xCA, 0xA4, 0xBE, 0x75, 0x4A, 0xB5, 0x72, 0x8A, 0xE9, 0x13, 0x0C, 0x4C, 0x7D, 0x02, 0x88, 0x0A, 0xB9, 0x47, 0x2D, 0x45, 0x55, 0x62, 0x16, 0xD6, 0x99, 0x8B, 0x86, 0x82, 0x28, 0x3D, 0x19, 0xD4, 0x2A, 0x90, 0xD5, 0xEF, 0x8E, 0x5D, 0x32, 0x76, 0x7D, 0xC2, 0x82, 0x2C, 0x6D, 0xF7, 0x85, 0x45, 0x75, 0x38, 0xAB, 0xAE, 0x83, 0x06, 0x3E, 0xD9, 0xCB, 0x87, 0xC2, 0xD3, 0x70, 0xF2, 0x63, 0xD5, 0xFA, 0xD7, 0x46, 0x6D, 0x84, 0x99, 0xEB, 0x8F, 0x46, 0x4A, 0x70, 0x25, 0x12, 0xB0, 0xCE, 0xE7, 0x71, 0xE9, 0x13, 0x0D, 0x69, 0x77, 0x35, 0xF8, 0x97, 0xFD, 0x03, 0x6C, 0xC5, 0x04, 0x32, 0x6C, 0x3B, 0x01, 0x39, 0x9F, 0x64, 0x35, 0x32, 0x29, 0x0F, 0x95, 0x8C, 0x0B, 0xBD, 0x90, 0x06, 0x5D, 0xF0, 0x8B, 0xAB, 0xBD, 0x30, 0xAE, 0xB6, 0x3B, 0x84, 0xC4, 0x60, 0x5D, 0x6C, 0xA3, 0x71, 0x04, 0x71, 0x27, 0xD0, 0x3A, 0x72, 0xD5, 0x98, 0xA1, 0xED, 0xAD, 0xFE, 0x70, 0x7E, 0x88, 0x47, 0x25, 0xC1, 0x68, 0x90, 0x54, 0x90, 0x84, 0x00, 0x8D, 0x39, 0x1E, 0x09, 0x53, 0xC3, 0xF3, 0x6B, 0xC4, 0x38, 0xCD, 0x08, 0x5E, 0xDD, 0x2D, 0x93, 0x4C, 0xE1, 0x93, 0x8C, 0x35, 0x7A, 0x71, 0x1E, 0x0D, 0x4A, 0x34, 0x1A, 0x5B, 0x0A, 0x85, 0xED, 0x12, 0xC1, 0xF4, 0xE5, 0x15, 0x6A, 0x26, 0x74, 0x6D, 0xDD, 0xE1, 0x6D, 0x82, 0x6F, 0x47, 0x7C, 0x97, 0x47, 0x7E, 0x0A, 0x0F, 0xDF, 0x65, 0x53, 0x14, 0x3E, 0x2C, 0xA3, 0xA7, 0x35, 0xE0, 0x2E, 0xCC, 0xD9, 0x4B, 0x27, 0xD0, 0x48, 0x61, 0xD1, 0x11, 0x9D, 0xD0, 0xC3, 0x28, 0xAD, 0xF3, 0xF6, 0x8F, 0xB0, 0x94, 0xB8, 0x67, 0x71, 0x6B, 0xD7, 0xDC, 0x0D, 0xEE, 0xBB, 0x10, 0xB8, 0x24, 0x0E, 0x68, 0x03, 0x48, 0x93, 0xEA, 0xD8, 0x2D, 0x54, 0xC9, 0xDA, 0x75, 0x4C, 0x46, 0xC7, 0xEE, 0xE0, 0xC3, 0x7F, 0xDB, 0xEE, 0x48, 0x53, 0x60, 0x47, 0xA6, 0xFA, 0x1A, 0xE4, 0x9A, 0x01, 0x42, 0x49, 0x1B, 0x61, 0xFD, 0x5A, 0x69, 0x3E, 0x38, 0x13, 0x60, 0xEA, 0x6E, 0x59, 0x30, 0x13, 0x23, 0x6F, 0x64, 0xBA, 0x8F, 0x3B, 0x1E, 0xDD, 0x1B, 0xDE, 0xFC, 0x7F, 0xCA, 0x03, 0x56, 0xCF, 0x29, 0x87, 0x72, 0xED, 0x9C, 0x17, 0xA0, 0x98, 0x00, 0xD7, 0x58, 0x35, 0x29, 0xF6, 0xC8, 0x13, 0xEC, 0x18, 0x8B, 0xCB, 0x93, 0xD8, 0x43, 0x2D, 0x44, 0x8C, 0x6D, 0x1F, 0x6D, 0xF5, 0xE7, 0xCD, 0x8A, 0x76, 0xA2, 0x67, 0x36, 0x5D, 0x67, 0x6A, 0x5D, 0x8D, 0xED, 0xBF, 0x8A, 0x23, 0xF3, 0x66, 0x12, 0xA5, 0x99, 0x90, 0x28, 0xA8, 0x95, 0xEB, 0xD7, 0xA1, 0x37, 0xDC, 0x7A, 0x00, 0x9B, 0xC6, 0x69, 0x5F, 0xAC, 0xC1, 0xE5, 0x00, 0xE3, 0x25, 0xC9, 0x76, 0x78, 0x19, 0x75, 0x0A, 0xE8, 0xB9, 0x0E, 0x81, 0xFA, 0x41, 0x6B, 0xE7, 0x37, 0x3A, 0x7F, 0x7B, 0x6A, 0xAF, 0x38, 0x17, 0xA3, 0x4C, 0x06, 0x41, 0x5A, 0xD4, 0x20, 0x18, 0xC8, 0x05, 0x8E, 0x4F, 0x2C, 0xF3, 0xE4, 0xBF, 0xDF, 0x63, 0xF4, 0x79, 0x91, 0xD4, 0xBD, 0x3F, 0x1B, 0x66, 0x44, 0x5F, 0x07, 0x8E, 0xA2, 0xDB, 0xFF, 0xAC, 0x2D, 0x62, 0xA5, 0xEA, 0x03, 0xD9, 0x15, 0xA0, 0xAA, 0x55, 0x66, 0x47, 0xB6, 0xBF, 0x5F, 0xA4, 0x70, 0xEC, 0x0A, 0x66, 0x2F, 0x69, 0x07, 0xC0, 0x1B, 0xF0, 0x53, 0xCB, 0x8A, 0xF7, 0x79, 0x4D, 0xF1, 0x94, 0x03, 0x50, 0xEA, 0xC5, 0xDB, 0xE2, 0xED, 0x3B, 0x7A, 0xA8, 0x55, 0x1E, 0xC5, 0x0F, 0xDF, 0xF8, 0x75, 0x8C, 0xE6, 0x58, 0xD1, 0x89, 0xEA, 0xAE, 0x6D, 0x2B, 0x64, 0xF6, 0x17, 0x79, 0x4B, 0x19, 0x1C, 0x3F, 0xF4, 0x6B, 0xB7, 0x1E, 0x02, 0x34, 0x02, 0x1F, 0x47, 0xB3, 0x1F, 0xA4, 0x30, 0x77, 0x09, 0x5F, 0x96, 0xAD, 0x85, 0xBA, 0x3A, 0x6B, 0x73, 0x4A, 0x7C, 0x8F, 0x36, 0xE6, 0x20, 0x12, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; /* RFC 3526, 7. Group 18 - 8192 Bit MODP * Generator: 2 * Prime: 2^8192 - 2^8128 - 1 + 2^64 * { [2^8062 pi] + 4743158 } */ static const u8 dh_group18_generator[1] = { 0x02 }; static const u8 dh_group18_prime[1024] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6, 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D, 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, 0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A, 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F, 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, 0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB, 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D, 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, 0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C, 0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B, 0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03, 0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F, 0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9, 0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18, 0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5, 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10, 0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAA, 0xC4, 0x2D, 0xAD, 0x33, 0x17, 0x0D, 0x04, 0x50, 0x7A, 0x33, 0xA8, 0x55, 0x21, 0xAB, 0xDF, 0x1C, 0xBA, 0x64, 0xEC, 0xFB, 0x85, 0x04, 0x58, 0xDB, 0xEF, 0x0A, 0x8A, 0xEA, 0x71, 0x57, 0x5D, 0x06, 0x0C, 0x7D, 0xB3, 0x97, 0x0F, 0x85, 0xA6, 0xE1, 0xE4, 0xC7, 0xAB, 0xF5, 0xAE, 0x8C, 0xDB, 0x09, 0x33, 0xD7, 0x1E, 0x8C, 0x94, 0xE0, 0x4A, 0x25, 0x61, 0x9D, 0xCE, 0xE3, 0xD2, 0x26, 0x1A, 0xD2, 0xEE, 0x6B, 0xF1, 0x2F, 0xFA, 0x06, 0xD9, 0x8A, 0x08, 0x64, 0xD8, 0x76, 0x02, 0x73, 0x3E, 0xC8, 0x6A, 0x64, 0x52, 0x1F, 0x2B, 0x18, 0x17, 0x7B, 0x20, 0x0C, 0xBB, 0xE1, 0x17, 0x57, 0x7A, 0x61, 0x5D, 0x6C, 0x77, 0x09, 0x88, 0xC0, 0xBA, 0xD9, 0x46, 0xE2, 0x08, 0xE2, 0x4F, 0xA0, 0x74, 0xE5, 0xAB, 0x31, 0x43, 0xDB, 0x5B, 0xFC, 0xE0, 0xFD, 0x10, 0x8E, 0x4B, 0x82, 0xD1, 0x20, 0xA9, 0x21, 0x08, 0x01, 0x1A, 0x72, 0x3C, 0x12, 0xA7, 0x87, 0xE6, 0xD7, 0x88, 0x71, 0x9A, 0x10, 0xBD, 0xBA, 0x5B, 0x26, 0x99, 0xC3, 0x27, 0x18, 0x6A, 0xF4, 0xE2, 0x3C, 0x1A, 0x94, 0x68, 0x34, 0xB6, 0x15, 0x0B, 0xDA, 0x25, 0x83, 0xE9, 0xCA, 0x2A, 0xD4, 0x4C, 0xE8, 0xDB, 0xBB, 0xC2, 0xDB, 0x04, 0xDE, 0x8E, 0xF9, 0x2E, 0x8E, 0xFC, 0x14, 0x1F, 0xBE, 0xCA, 0xA6, 0x28, 0x7C, 0x59, 0x47, 0x4E, 0x6B, 0xC0, 0x5D, 0x99, 0xB2, 0x96, 0x4F, 0xA0, 0x90, 0xC3, 0xA2, 0x23, 0x3B, 0xA1, 0x86, 0x51, 0x5B, 0xE7, 0xED, 0x1F, 0x61, 0x29, 0x70, 0xCE, 0xE2, 0xD7, 0xAF, 0xB8, 0x1B, 0xDD, 0x76, 0x21, 0x70, 0x48, 0x1C, 0xD0, 0x06, 0x91, 0x27, 0xD5, 0xB0, 0x5A, 0xA9, 0x93, 0xB4, 0xEA, 0x98, 0x8D, 0x8F, 0xDD, 0xC1, 0x86, 0xFF, 0xB7, 0xDC, 0x90, 0xA6, 0xC0, 0x8F, 0x4D, 0xF4, 0x35, 0xC9, 0x34, 0x02, 0x84, 0x92, 0x36, 0xC3, 0xFA, 0xB4, 0xD2, 0x7C, 0x70, 0x26, 0xC1, 0xD4, 0xDC, 0xB2, 0x60, 0x26, 0x46, 0xDE, 0xC9, 0x75, 0x1E, 0x76, 0x3D, 0xBA, 0x37, 0xBD, 0xF8, 0xFF, 0x94, 0x06, 0xAD, 0x9E, 0x53, 0x0E, 0xE5, 0xDB, 0x38, 0x2F, 0x41, 0x30, 0x01, 0xAE, 0xB0, 0x6A, 0x53, 0xED, 0x90, 0x27, 0xD8, 0x31, 0x17, 0x97, 0x27, 0xB0, 0x86, 0x5A, 0x89, 0x18, 0xDA, 0x3E, 0xDB, 0xEB, 0xCF, 0x9B, 0x14, 0xED, 0x44, 0xCE, 0x6C, 0xBA, 0xCE, 0xD4, 0xBB, 0x1B, 0xDB, 0x7F, 0x14, 0x47, 0xE6, 0xCC, 0x25, 0x4B, 0x33, 0x20, 0x51, 0x51, 0x2B, 0xD7, 0xAF, 0x42, 0x6F, 0xB8, 0xF4, 0x01, 0x37, 0x8C, 0xD2, 0xBF, 0x59, 0x83, 0xCA, 0x01, 0xC6, 0x4B, 0x92, 0xEC, 0xF0, 0x32, 0xEA, 0x15, 0xD1, 0x72, 0x1D, 0x03, 0xF4, 0x82, 0xD7, 0xCE, 0x6E, 0x74, 0xFE, 0xF6, 0xD5, 0x5E, 0x70, 0x2F, 0x46, 0x98, 0x0C, 0x82, 0xB5, 0xA8, 0x40, 0x31, 0x90, 0x0B, 0x1C, 0x9E, 0x59, 0xE7, 0xC9, 0x7F, 0xBE, 0xC7, 0xE8, 0xF3, 0x23, 0xA9, 0x7A, 0x7E, 0x36, 0xCC, 0x88, 0xBE, 0x0F, 0x1D, 0x45, 0xB7, 0xFF, 0x58, 0x5A, 0xC5, 0x4B, 0xD4, 0x07, 0xB2, 0x2B, 0x41, 0x54, 0xAA, 0xCC, 0x8F, 0x6D, 0x7E, 0xBF, 0x48, 0xE1, 0xD8, 0x14, 0xCC, 0x5E, 0xD2, 0x0F, 0x80, 0x37, 0xE0, 0xA7, 0x97, 0x15, 0xEE, 0xF2, 0x9B, 0xE3, 0x28, 0x06, 0xA1, 0xD5, 0x8B, 0xB7, 0xC5, 0xDA, 0x76, 0xF5, 0x50, 0xAA, 0x3D, 0x8A, 0x1F, 0xBF, 0xF0, 0xEB, 0x19, 0xCC, 0xB1, 0xA3, 0x13, 0xD5, 0x5C, 0xDA, 0x56, 0xC9, 0xEC, 0x2E, 0xF2, 0x96, 0x32, 0x38, 0x7F, 0xE8, 0xD7, 0x6E, 0x3C, 0x04, 0x68, 0x04, 0x3E, 0x8F, 0x66, 0x3F, 0x48, 0x60, 0xEE, 0x12, 0xBF, 0x2D, 0x5B, 0x0B, 0x74, 0x74, 0xD6, 0xE6, 0x94, 0xF9, 0x1E, 0x6D, 0xBE, 0x11, 0x59, 0x74, 0xA3, 0x92, 0x6F, 0x12, 0xFE, 0xE5, 0xE4, 0x38, 0x77, 0x7C, 0xB6, 0xA9, 0x32, 0xDF, 0x8C, 0xD8, 0xBE, 0xC4, 0xD0, 0x73, 0xB9, 0x31, 0xBA, 0x3B, 0xC8, 0x32, 0xB6, 0x8D, 0x9D, 0xD3, 0x00, 0x74, 0x1F, 0xA7, 0xBF, 0x8A, 0xFC, 0x47, 0xED, 0x25, 0x76, 0xF6, 0x93, 0x6B, 0xA4, 0x24, 0x66, 0x3A, 0xAB, 0x63, 0x9C, 0x5A, 0xE4, 0xF5, 0x68, 0x34, 0x23, 0xB4, 0x74, 0x2B, 0xF1, 0xC9, 0x78, 0x23, 0x8F, 0x16, 0xCB, 0xE3, 0x9D, 0x65, 0x2D, 0xE3, 0xFD, 0xB8, 0xBE, 0xFC, 0x84, 0x8A, 0xD9, 0x22, 0x22, 0x2E, 0x04, 0xA4, 0x03, 0x7C, 0x07, 0x13, 0xEB, 0x57, 0xA8, 0x1A, 0x23, 0xF0, 0xC7, 0x34, 0x73, 0xFC, 0x64, 0x6C, 0xEA, 0x30, 0x6B, 0x4B, 0xCB, 0xC8, 0x86, 0x2F, 0x83, 0x85, 0xDD, 0xFA, 0x9D, 0x4B, 0x7F, 0xA2, 0xC0, 0x87, 0xE8, 0x79, 0x68, 0x33, 0x03, 0xED, 0x5B, 0xDD, 0x3A, 0x06, 0x2B, 0x3C, 0xF5, 0xB3, 0xA2, 0x78, 0xA6, 0x6D, 0x2A, 0x13, 0xF8, 0x3F, 0x44, 0xF8, 0x2D, 0xDF, 0x31, 0x0E, 0xE0, 0x74, 0xAB, 0x6A, 0x36, 0x45, 0x97, 0xE8, 0x99, 0xA0, 0x25, 0x5D, 0xC1, 0x64, 0xF3, 0x1C, 0xC5, 0x08, 0x46, 0x85, 0x1D, 0xF9, 0xAB, 0x48, 0x19, 0x5D, 0xED, 0x7E, 0xA1, 0xB1, 0xD5, 0x10, 0xBD, 0x7E, 0xE7, 0x4D, 0x73, 0xFA, 0xF3, 0x6B, 0xC3, 0x1E, 0xCF, 0xA2, 0x68, 0x35, 0x90, 0x46, 0xF4, 0xEB, 0x87, 0x9F, 0x92, 0x40, 0x09, 0x43, 0x8B, 0x48, 0x1C, 0x6C, 0xD7, 0x88, 0x9A, 0x00, 0x2E, 0xD5, 0xEE, 0x38, 0x2B, 0xC9, 0x19, 0x0D, 0xA6, 0xFC, 0x02, 0x6E, 0x47, 0x95, 0x58, 0xE4, 0x47, 0x56, 0x77, 0xE9, 0xAA, 0x9E, 0x30, 0x50, 0xE2, 0x76, 0x56, 0x94, 0xDF, 0xC8, 0x1F, 0x56, 0xE8, 0x80, 0xB9, 0x6E, 0x71, 0x60, 0xC9, 0x80, 0xDD, 0x98, 0xED, 0xD3, 0xDF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; static const u8 dh_group18_order[1024] = { 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE4, 0x87, 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A, 0x62, 0x63, 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68, 0x94, 0x81, 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A, 0x01, 0x05, 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91, 0x28, 0xA5, 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E, 0xF7, 0xCA, 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D, 0x98, 0x15, 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B, 0xA7, 0xF0, 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22, 0xF2, 0x42, 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63, 0x7A, 0x26, 0x21, 0x74, 0xD3, 0x1B, 0xF6, 0xB5, 0x85, 0xFF, 0xAE, 0x5B, 0x7A, 0x03, 0x5B, 0xF6, 0xF7, 0x1C, 0x35, 0xFD, 0xAD, 0x44, 0xCF, 0xD2, 0xD7, 0x4F, 0x92, 0x08, 0xBE, 0x25, 0x8F, 0xF3, 0x24, 0x94, 0x33, 0x28, 0xF6, 0x72, 0x2D, 0x9E, 0xE1, 0x00, 0x3E, 0x5C, 0x50, 0xB1, 0xDF, 0x82, 0xCC, 0x6D, 0x24, 0x1B, 0x0E, 0x2A, 0xE9, 0xCD, 0x34, 0x8B, 0x1F, 0xD4, 0x7E, 0x92, 0x67, 0xAF, 0xC1, 0xB2, 0xAE, 0x91, 0xEE, 0x51, 0xD6, 0xCB, 0x0E, 0x31, 0x79, 0xAB, 0x10, 0x42, 0xA9, 0x5D, 0xCF, 0x6A, 0x94, 0x83, 0xB8, 0x4B, 0x4B, 0x36, 0xB3, 0x86, 0x1A, 0xA7, 0x25, 0x5E, 0x4C, 0x02, 0x78, 0xBA, 0x36, 0x04, 0x65, 0x0C, 0x10, 0xBE, 0x19, 0x48, 0x2F, 0x23, 0x17, 0x1B, 0x67, 0x1D, 0xF1, 0xCF, 0x3B, 0x96, 0x0C, 0x07, 0x43, 0x01, 0xCD, 0x93, 0xC1, 0xD1, 0x76, 0x03, 0xD1, 0x47, 0xDA, 0xE2, 0xAE, 0xF8, 0x37, 0xA6, 0x29, 0x64, 0xEF, 0x15, 0xE5, 0xFB, 0x4A, 0xAC, 0x0B, 0x8C, 0x1C, 0xCA, 0xA4, 0xBE, 0x75, 0x4A, 0xB5, 0x72, 0x8A, 0xE9, 0x13, 0x0C, 0x4C, 0x7D, 0x02, 0x88, 0x0A, 0xB9, 0x47, 0x2D, 0x45, 0x55, 0x62, 0x16, 0xD6, 0x99, 0x8B, 0x86, 0x82, 0x28, 0x3D, 0x19, 0xD4, 0x2A, 0x90, 0xD5, 0xEF, 0x8E, 0x5D, 0x32, 0x76, 0x7D, 0xC2, 0x82, 0x2C, 0x6D, 0xF7, 0x85, 0x45, 0x75, 0x38, 0xAB, 0xAE, 0x83, 0x06, 0x3E, 0xD9, 0xCB, 0x87, 0xC2, 0xD3, 0x70, 0xF2, 0x63, 0xD5, 0xFA, 0xD7, 0x46, 0x6D, 0x84, 0x99, 0xEB, 0x8F, 0x46, 0x4A, 0x70, 0x25, 0x12, 0xB0, 0xCE, 0xE7, 0x71, 0xE9, 0x13, 0x0D, 0x69, 0x77, 0x35, 0xF8, 0x97, 0xFD, 0x03, 0x6C, 0xC5, 0x04, 0x32, 0x6C, 0x3B, 0x01, 0x39, 0x9F, 0x64, 0x35, 0x32, 0x29, 0x0F, 0x95, 0x8C, 0x0B, 0xBD, 0x90, 0x06, 0x5D, 0xF0, 0x8B, 0xAB, 0xBD, 0x30, 0xAE, 0xB6, 0x3B, 0x84, 0xC4, 0x60, 0x5D, 0x6C, 0xA3, 0x71, 0x04, 0x71, 0x27, 0xD0, 0x3A, 0x72, 0xD5, 0x98, 0xA1, 0xED, 0xAD, 0xFE, 0x70, 0x7E, 0x88, 0x47, 0x25, 0xC1, 0x68, 0x90, 0x54, 0x90, 0x84, 0x00, 0x8D, 0x39, 0x1E, 0x09, 0x53, 0xC3, 0xF3, 0x6B, 0xC4, 0x38, 0xCD, 0x08, 0x5E, 0xDD, 0x2D, 0x93, 0x4C, 0xE1, 0x93, 0x8C, 0x35, 0x7A, 0x71, 0x1E, 0x0D, 0x4A, 0x34, 0x1A, 0x5B, 0x0A, 0x85, 0xED, 0x12, 0xC1, 0xF4, 0xE5, 0x15, 0x6A, 0x26, 0x74, 0x6D, 0xDD, 0xE1, 0x6D, 0x82, 0x6F, 0x47, 0x7C, 0x97, 0x47, 0x7E, 0x0A, 0x0F, 0xDF, 0x65, 0x53, 0x14, 0x3E, 0x2C, 0xA3, 0xA7, 0x35, 0xE0, 0x2E, 0xCC, 0xD9, 0x4B, 0x27, 0xD0, 0x48, 0x61, 0xD1, 0x11, 0x9D, 0xD0, 0xC3, 0x28, 0xAD, 0xF3, 0xF6, 0x8F, 0xB0, 0x94, 0xB8, 0x67, 0x71, 0x6B, 0xD7, 0xDC, 0x0D, 0xEE, 0xBB, 0x10, 0xB8, 0x24, 0x0E, 0x68, 0x03, 0x48, 0x93, 0xEA, 0xD8, 0x2D, 0x54, 0xC9, 0xDA, 0x75, 0x4C, 0x46, 0xC7, 0xEE, 0xE0, 0xC3, 0x7F, 0xDB, 0xEE, 0x48, 0x53, 0x60, 0x47, 0xA6, 0xFA, 0x1A, 0xE4, 0x9A, 0x01, 0x42, 0x49, 0x1B, 0x61, 0xFD, 0x5A, 0x69, 0x3E, 0x38, 0x13, 0x60, 0xEA, 0x6E, 0x59, 0x30, 0x13, 0x23, 0x6F, 0x64, 0xBA, 0x8F, 0x3B, 0x1E, 0xDD, 0x1B, 0xDE, 0xFC, 0x7F, 0xCA, 0x03, 0x56, 0xCF, 0x29, 0x87, 0x72, 0xED, 0x9C, 0x17, 0xA0, 0x98, 0x00, 0xD7, 0x58, 0x35, 0x29, 0xF6, 0xC8, 0x13, 0xEC, 0x18, 0x8B, 0xCB, 0x93, 0xD8, 0x43, 0x2D, 0x44, 0x8C, 0x6D, 0x1F, 0x6D, 0xF5, 0xE7, 0xCD, 0x8A, 0x76, 0xA2, 0x67, 0x36, 0x5D, 0x67, 0x6A, 0x5D, 0x8D, 0xED, 0xBF, 0x8A, 0x23, 0xF3, 0x66, 0x12, 0xA5, 0x99, 0x90, 0x28, 0xA8, 0x95, 0xEB, 0xD7, 0xA1, 0x37, 0xDC, 0x7A, 0x00, 0x9B, 0xC6, 0x69, 0x5F, 0xAC, 0xC1, 0xE5, 0x00, 0xE3, 0x25, 0xC9, 0x76, 0x78, 0x19, 0x75, 0x0A, 0xE8, 0xB9, 0x0E, 0x81, 0xFA, 0x41, 0x6B, 0xE7, 0x37, 0x3A, 0x7F, 0x7B, 0x6A, 0xAF, 0x38, 0x17, 0xA3, 0x4C, 0x06, 0x41, 0x5A, 0xD4, 0x20, 0x18, 0xC8, 0x05, 0x8E, 0x4F, 0x2C, 0xF3, 0xE4, 0xBF, 0xDF, 0x63, 0xF4, 0x79, 0x91, 0xD4, 0xBD, 0x3F, 0x1B, 0x66, 0x44, 0x5F, 0x07, 0x8E, 0xA2, 0xDB, 0xFF, 0xAC, 0x2D, 0x62, 0xA5, 0xEA, 0x03, 0xD9, 0x15, 0xA0, 0xAA, 0x55, 0x66, 0x47, 0xB6, 0xBF, 0x5F, 0xA4, 0x70, 0xEC, 0x0A, 0x66, 0x2F, 0x69, 0x07, 0xC0, 0x1B, 0xF0, 0x53, 0xCB, 0x8A, 0xF7, 0x79, 0x4D, 0xF1, 0x94, 0x03, 0x50, 0xEA, 0xC5, 0xDB, 0xE2, 0xED, 0x3B, 0x7A, 0xA8, 0x55, 0x1E, 0xC5, 0x0F, 0xDF, 0xF8, 0x75, 0x8C, 0xE6, 0x58, 0xD1, 0x89, 0xEA, 0xAE, 0x6D, 0x2B, 0x64, 0xF6, 0x17, 0x79, 0x4B, 0x19, 0x1C, 0x3F, 0xF4, 0x6B, 0xB7, 0x1E, 0x02, 0x34, 0x02, 0x1F, 0x47, 0xB3, 0x1F, 0xA4, 0x30, 0x77, 0x09, 0x5F, 0x96, 0xAD, 0x85, 0xBA, 0x3A, 0x6B, 0x73, 0x4A, 0x7C, 0x8F, 0x36, 0xDF, 0x08, 0xAC, 0xBA, 0x51, 0xC9, 0x37, 0x89, 0x7F, 0x72, 0xF2, 0x1C, 0x3B, 0xBE, 0x5B, 0x54, 0x99, 0x6F, 0xC6, 0x6C, 0x5F, 0x62, 0x68, 0x39, 0xDC, 0x98, 0xDD, 0x1D, 0xE4, 0x19, 0x5B, 0x46, 0xCE, 0xE9, 0x80, 0x3A, 0x0F, 0xD3, 0xDF, 0xC5, 0x7E, 0x23, 0xF6, 0x92, 0xBB, 0x7B, 0x49, 0xB5, 0xD2, 0x12, 0x33, 0x1D, 0x55, 0xB1, 0xCE, 0x2D, 0x72, 0x7A, 0xB4, 0x1A, 0x11, 0xDA, 0x3A, 0x15, 0xF8, 0xE4, 0xBC, 0x11, 0xC7, 0x8B, 0x65, 0xF1, 0xCE, 0xB2, 0x96, 0xF1, 0xFE, 0xDC, 0x5F, 0x7E, 0x42, 0x45, 0x6C, 0x91, 0x11, 0x17, 0x02, 0x52, 0x01, 0xBE, 0x03, 0x89, 0xF5, 0xAB, 0xD4, 0x0D, 0x11, 0xF8, 0x63, 0x9A, 0x39, 0xFE, 0x32, 0x36, 0x75, 0x18, 0x35, 0xA5, 0xE5, 0xE4, 0x43, 0x17, 0xC1, 0xC2, 0xEE, 0xFD, 0x4E, 0xA5, 0xBF, 0xD1, 0x60, 0x43, 0xF4, 0x3C, 0xB4, 0x19, 0x81, 0xF6, 0xAD, 0xEE, 0x9D, 0x03, 0x15, 0x9E, 0x7A, 0xD9, 0xD1, 0x3C, 0x53, 0x36, 0x95, 0x09, 0xFC, 0x1F, 0xA2, 0x7C, 0x16, 0xEF, 0x98, 0x87, 0x70, 0x3A, 0x55, 0xB5, 0x1B, 0x22, 0xCB, 0xF4, 0x4C, 0xD0, 0x12, 0xAE, 0xE0, 0xB2, 0x79, 0x8E, 0x62, 0x84, 0x23, 0x42, 0x8E, 0xFC, 0xD5, 0xA4, 0x0C, 0xAE, 0xF6, 0xBF, 0x50, 0xD8, 0xEA, 0x88, 0x5E, 0xBF, 0x73, 0xA6, 0xB9, 0xFD, 0x79, 0xB5, 0xE1, 0x8F, 0x67, 0xD1, 0x34, 0x1A, 0xC8, 0x23, 0x7A, 0x75, 0xC3, 0xCF, 0xC9, 0x20, 0x04, 0xA1, 0xC5, 0xA4, 0x0E, 0x36, 0x6B, 0xC4, 0x4D, 0x00, 0x17, 0x6A, 0xF7, 0x1C, 0x15, 0xE4, 0x8C, 0x86, 0xD3, 0x7E, 0x01, 0x37, 0x23, 0xCA, 0xAC, 0x72, 0x23, 0xAB, 0x3B, 0xF4, 0xD5, 0x4F, 0x18, 0x28, 0x71, 0x3B, 0x2B, 0x4A, 0x6F, 0xE4, 0x0F, 0xAB, 0x74, 0x40, 0x5C, 0xB7, 0x38, 0xB0, 0x64, 0xC0, 0x6E, 0xCC, 0x76, 0xE9, 0xEF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; /* * RFC 5114, 2.1. * Group 22 - 1024-bit MODP Group with 160-bit Prime Order Subgroup */ static const u8 dh_group22_generator[] = { 0xA4, 0xD1, 0xCB, 0xD5, 0xC3, 0xFD, 0x34, 0x12, 0x67, 0x65, 0xA4, 0x42, 0xEF, 0xB9, 0x99, 0x05, 0xF8, 0x10, 0x4D, 0xD2, 0x58, 0xAC, 0x50, 0x7F, 0xD6, 0x40, 0x6C, 0xFF, 0x14, 0x26, 0x6D, 0x31, 0x26, 0x6F, 0xEA, 0x1E, 0x5C, 0x41, 0x56, 0x4B, 0x77, 0x7E, 0x69, 0x0F, 0x55, 0x04, 0xF2, 0x13, 0x16, 0x02, 0x17, 0xB4, 0xB0, 0x1B, 0x88, 0x6A, 0x5E, 0x91, 0x54, 0x7F, 0x9E, 0x27, 0x49, 0xF4, 0xD7, 0xFB, 0xD7, 0xD3, 0xB9, 0xA9, 0x2E, 0xE1, 0x90, 0x9D, 0x0D, 0x22, 0x63, 0xF8, 0x0A, 0x76, 0xA6, 0xA2, 0x4C, 0x08, 0x7A, 0x09, 0x1F, 0x53, 0x1D, 0xBF, 0x0A, 0x01, 0x69, 0xB6, 0xA2, 0x8A, 0xD6, 0x62, 0xA4, 0xD1, 0x8E, 0x73, 0xAF, 0xA3, 0x2D, 0x77, 0x9D, 0x59, 0x18, 0xD0, 0x8B, 0xC8, 0x85, 0x8F, 0x4D, 0xCE, 0xF9, 0x7C, 0x2A, 0x24, 0x85, 0x5E, 0x6E, 0xEB, 0x22, 0xB3, 0xB2, 0xE5 }; static const u8 dh_group22_prime[] = { 0xB1, 0x0B, 0x8F, 0x96, 0xA0, 0x80, 0xE0, 0x1D, 0xDE, 0x92, 0xDE, 0x5E, 0xAE, 0x5D, 0x54, 0xEC, 0x52, 0xC9, 0x9F, 0xBC, 0xFB, 0x06, 0xA3, 0xC6, 0x9A, 0x6A, 0x9D, 0xCA, 0x52, 0xD2, 0x3B, 0x61, 0x60, 0x73, 0xE2, 0x86, 0x75, 0xA2, 0x3D, 0x18, 0x98, 0x38, 0xEF, 0x1E, 0x2E, 0xE6, 0x52, 0xC0, 0x13, 0xEC, 0xB4, 0xAE, 0xA9, 0x06, 0x11, 0x23, 0x24, 0x97, 0x5C, 0x3C, 0xD4, 0x9B, 0x83, 0xBF, 0xAC, 0xCB, 0xDD, 0x7D, 0x90, 0xC4, 0xBD, 0x70, 0x98, 0x48, 0x8E, 0x9C, 0x21, 0x9A, 0x73, 0x72, 0x4E, 0xFF, 0xD6, 0xFA, 0xE5, 0x64, 0x47, 0x38, 0xFA, 0xA3, 0x1A, 0x4F, 0xF5, 0x5B, 0xCC, 0xC0, 0xA1, 0x51, 0xAF, 0x5F, 0x0D, 0xC8, 0xB4, 0xBD, 0x45, 0xBF, 0x37, 0xDF, 0x36, 0x5C, 0x1A, 0x65, 0xE6, 0x8C, 0xFD, 0xA7, 0x6D, 0x4D, 0xA7, 0x08, 0xDF, 0x1F, 0xB2, 0xBC, 0x2E, 0x4A, 0x43, 0x71 }; static const u8 dh_group22_order[] = { 0xF5, 0x18, 0xAA, 0x87, 0x81, 0xA8, 0xDF, 0x27, 0x8A, 0xBA, 0x4E, 0x7D, 0x64, 0xB7, 0xCB, 0x9D, 0x49, 0x46, 0x23, 0x53 }; /* * RFC 5114, 2.2. * Group 23 - 2048-bit MODP Group with 224-bit Prime Order Subgroup */ static const u8 dh_group23_generator[] = { 0xAC, 0x40, 0x32, 0xEF, 0x4F, 0x2D, 0x9A, 0xE3, 0x9D, 0xF3, 0x0B, 0x5C, 0x8F, 0xFD, 0xAC, 0x50, 0x6C, 0xDE, 0xBE, 0x7B, 0x89, 0x99, 0x8C, 0xAF, 0x74, 0x86, 0x6A, 0x08, 0xCF, 0xE4, 0xFF, 0xE3, 0xA6, 0x82, 0x4A, 0x4E, 0x10, 0xB9, 0xA6, 0xF0, 0xDD, 0x92, 0x1F, 0x01, 0xA7, 0x0C, 0x4A, 0xFA, 0xAB, 0x73, 0x9D, 0x77, 0x00, 0xC2, 0x9F, 0x52, 0xC5, 0x7D, 0xB1, 0x7C, 0x62, 0x0A, 0x86, 0x52, 0xBE, 0x5E, 0x90, 0x01, 0xA8, 0xD6, 0x6A, 0xD7, 0xC1, 0x76, 0x69, 0x10, 0x19, 0x99, 0x02, 0x4A, 0xF4, 0xD0, 0x27, 0x27, 0x5A, 0xC1, 0x34, 0x8B, 0xB8, 0xA7, 0x62, 0xD0, 0x52, 0x1B, 0xC9, 0x8A, 0xE2, 0x47, 0x15, 0x04, 0x22, 0xEA, 0x1E, 0xD4, 0x09, 0x93, 0x9D, 0x54, 0xDA, 0x74, 0x60, 0xCD, 0xB5, 0xF6, 0xC6, 0xB2, 0x50, 0x71, 0x7C, 0xBE, 0xF1, 0x80, 0xEB, 0x34, 0x11, 0x8E, 0x98, 0xD1, 0x19, 0x52, 0x9A, 0x45, 0xD6, 0xF8, 0x34, 0x56, 0x6E, 0x30, 0x25, 0xE3, 0x16, 0xA3, 0x30, 0xEF, 0xBB, 0x77, 0xA8, 0x6F, 0x0C, 0x1A, 0xB1, 0x5B, 0x05, 0x1A, 0xE3, 0xD4, 0x28, 0xC8, 0xF8, 0xAC, 0xB7, 0x0A, 0x81, 0x37, 0x15, 0x0B, 0x8E, 0xEB, 0x10, 0xE1, 0x83, 0xED, 0xD1, 0x99, 0x63, 0xDD, 0xD9, 0xE2, 0x63, 0xE4, 0x77, 0x05, 0x89, 0xEF, 0x6A, 0xA2, 0x1E, 0x7F, 0x5F, 0x2F, 0xF3, 0x81, 0xB5, 0x39, 0xCC, 0xE3, 0x40, 0x9D, 0x13, 0xCD, 0x56, 0x6A, 0xFB, 0xB4, 0x8D, 0x6C, 0x01, 0x91, 0x81, 0xE1, 0xBC, 0xFE, 0x94, 0xB3, 0x02, 0x69, 0xED, 0xFE, 0x72, 0xFE, 0x9B, 0x6A, 0xA4, 0xBD, 0x7B, 0x5A, 0x0F, 0x1C, 0x71, 0xCF, 0xFF, 0x4C, 0x19, 0xC4, 0x18, 0xE1, 0xF6, 0xEC, 0x01, 0x79, 0x81, 0xBC, 0x08, 0x7F, 0x2A, 0x70, 0x65, 0xB3, 0x84, 0xB8, 0x90, 0xD3, 0x19, 0x1F, 0x2B, 0xFA }; static const u8 dh_group23_prime[] = { 0xAD, 0x10, 0x7E, 0x1E, 0x91, 0x23, 0xA9, 0xD0, 0xD6, 0x60, 0xFA, 0xA7, 0x95, 0x59, 0xC5, 0x1F, 0xA2, 0x0D, 0x64, 0xE5, 0x68, 0x3B, 0x9F, 0xD1, 0xB5, 0x4B, 0x15, 0x97, 0xB6, 0x1D, 0x0A, 0x75, 0xE6, 0xFA, 0x14, 0x1D, 0xF9, 0x5A, 0x56, 0xDB, 0xAF, 0x9A, 0x3C, 0x40, 0x7B, 0xA1, 0xDF, 0x15, 0xEB, 0x3D, 0x68, 0x8A, 0x30, 0x9C, 0x18, 0x0E, 0x1D, 0xE6, 0xB8, 0x5A, 0x12, 0x74, 0xA0, 0xA6, 0x6D, 0x3F, 0x81, 0x52, 0xAD, 0x6A, 0xC2, 0x12, 0x90, 0x37, 0xC9, 0xED, 0xEF, 0xDA, 0x4D, 0xF8, 0xD9, 0x1E, 0x8F, 0xEF, 0x55, 0xB7, 0x39, 0x4B, 0x7A, 0xD5, 0xB7, 0xD0, 0xB6, 0xC1, 0x22, 0x07, 0xC9, 0xF9, 0x8D, 0x11, 0xED, 0x34, 0xDB, 0xF6, 0xC6, 0xBA, 0x0B, 0x2C, 0x8B, 0xBC, 0x27, 0xBE, 0x6A, 0x00, 0xE0, 0xA0, 0xB9, 0xC4, 0x97, 0x08, 0xB3, 0xBF, 0x8A, 0x31, 0x70, 0x91, 0x88, 0x36, 0x81, 0x28, 0x61, 0x30, 0xBC, 0x89, 0x85, 0xDB, 0x16, 0x02, 0xE7, 0x14, 0x41, 0x5D, 0x93, 0x30, 0x27, 0x82, 0x73, 0xC7, 0xDE, 0x31, 0xEF, 0xDC, 0x73, 0x10, 0xF7, 0x12, 0x1F, 0xD5, 0xA0, 0x74, 0x15, 0x98, 0x7D, 0x9A, 0xDC, 0x0A, 0x48, 0x6D, 0xCD, 0xF9, 0x3A, 0xCC, 0x44, 0x32, 0x83, 0x87, 0x31, 0x5D, 0x75, 0xE1, 0x98, 0xC6, 0x41, 0xA4, 0x80, 0xCD, 0x86, 0xA1, 0xB9, 0xE5, 0x87, 0xE8, 0xBE, 0x60, 0xE6, 0x9C, 0xC9, 0x28, 0xB2, 0xB9, 0xC5, 0x21, 0x72, 0xE4, 0x13, 0x04, 0x2E, 0x9B, 0x23, 0xF1, 0x0B, 0x0E, 0x16, 0xE7, 0x97, 0x63, 0xC9, 0xB5, 0x3D, 0xCF, 0x4B, 0xA8, 0x0A, 0x29, 0xE3, 0xFB, 0x73, 0xC1, 0x6B, 0x8E, 0x75, 0xB9, 0x7E, 0xF3, 0x63, 0xE2, 0xFF, 0xA3, 0x1F, 0x71, 0xCF, 0x9D, 0xE5, 0x38, 0x4E, 0x71, 0xB8, 0x1C, 0x0A, 0xC4, 0xDF, 0xFE, 0x0C, 0x10, 0xE6, 0x4F }; static const u8 dh_group23_order[] = { 0x80, 0x1C, 0x0D, 0x34, 0xC5, 0x8D, 0x93, 0xFE, 0x99, 0x71, 0x77, 0x10, 0x1F, 0x80, 0x53, 0x5A, 0x47, 0x38, 0xCE, 0xBC, 0xBF, 0x38, 0x9A, 0x99, 0xB3, 0x63, 0x71, 0xEB }; /* * RFC 5114, 2.3. * Group 24 - 2048-bit MODP Group with 256-bit Prime Order Subgroup */ static const u8 dh_group24_generator[] = { 0x3F, 0xB3, 0x2C, 0x9B, 0x73, 0x13, 0x4D, 0x0B, 0x2E, 0x77, 0x50, 0x66, 0x60, 0xED, 0xBD, 0x48, 0x4C, 0xA7, 0xB1, 0x8F, 0x21, 0xEF, 0x20, 0x54, 0x07, 0xF4, 0x79, 0x3A, 0x1A, 0x0B, 0xA1, 0x25, 0x10, 0xDB, 0xC1, 0x50, 0x77, 0xBE, 0x46, 0x3F, 0xFF, 0x4F, 0xED, 0x4A, 0xAC, 0x0B, 0xB5, 0x55, 0xBE, 0x3A, 0x6C, 0x1B, 0x0C, 0x6B, 0x47, 0xB1, 0xBC, 0x37, 0x73, 0xBF, 0x7E, 0x8C, 0x6F, 0x62, 0x90, 0x12, 0x28, 0xF8, 0xC2, 0x8C, 0xBB, 0x18, 0xA5, 0x5A, 0xE3, 0x13, 0x41, 0x00, 0x0A, 0x65, 0x01, 0x96, 0xF9, 0x31, 0xC7, 0x7A, 0x57, 0xF2, 0xDD, 0xF4, 0x63, 0xE5, 0xE9, 0xEC, 0x14, 0x4B, 0x77, 0x7D, 0xE6, 0x2A, 0xAA, 0xB8, 0xA8, 0x62, 0x8A, 0xC3, 0x76, 0xD2, 0x82, 0xD6, 0xED, 0x38, 0x64, 0xE6, 0x79, 0x82, 0x42, 0x8E, 0xBC, 0x83, 0x1D, 0x14, 0x34, 0x8F, 0x6F, 0x2F, 0x91, 0x93, 0xB5, 0x04, 0x5A, 0xF2, 0x76, 0x71, 0x64, 0xE1, 0xDF, 0xC9, 0x67, 0xC1, 0xFB, 0x3F, 0x2E, 0x55, 0xA4, 0xBD, 0x1B, 0xFF, 0xE8, 0x3B, 0x9C, 0x80, 0xD0, 0x52, 0xB9, 0x85, 0xD1, 0x82, 0xEA, 0x0A, 0xDB, 0x2A, 0x3B, 0x73, 0x13, 0xD3, 0xFE, 0x14, 0xC8, 0x48, 0x4B, 0x1E, 0x05, 0x25, 0x88, 0xB9, 0xB7, 0xD2, 0xBB, 0xD2, 0xDF, 0x01, 0x61, 0x99, 0xEC, 0xD0, 0x6E, 0x15, 0x57, 0xCD, 0x09, 0x15, 0xB3, 0x35, 0x3B, 0xBB, 0x64, 0xE0, 0xEC, 0x37, 0x7F, 0xD0, 0x28, 0x37, 0x0D, 0xF9, 0x2B, 0x52, 0xC7, 0x89, 0x14, 0x28, 0xCD, 0xC6, 0x7E, 0xB6, 0x18, 0x4B, 0x52, 0x3D, 0x1D, 0xB2, 0x46, 0xC3, 0x2F, 0x63, 0x07, 0x84, 0x90, 0xF0, 0x0E, 0xF8, 0xD6, 0x47, 0xD1, 0x48, 0xD4, 0x79, 0x54, 0x51, 0x5E, 0x23, 0x27, 0xCF, 0xEF, 0x98, 0xC5, 0x82, 0x66, 0x4B, 0x4C, 0x0F, 0x6C, 0xC4, 0x16, 0x59 }; static const u8 dh_group24_prime[] = { 0x87, 0xA8, 0xE6, 0x1D, 0xB4, 0xB6, 0x66, 0x3C, 0xFF, 0xBB, 0xD1, 0x9C, 0x65, 0x19, 0x59, 0x99, 0x8C, 0xEE, 0xF6, 0x08, 0x66, 0x0D, 0xD0, 0xF2, 0x5D, 0x2C, 0xEE, 0xD4, 0x43, 0x5E, 0x3B, 0x00, 0xE0, 0x0D, 0xF8, 0xF1, 0xD6, 0x19, 0x57, 0xD4, 0xFA, 0xF7, 0xDF, 0x45, 0x61, 0xB2, 0xAA, 0x30, 0x16, 0xC3, 0xD9, 0x11, 0x34, 0x09, 0x6F, 0xAA, 0x3B, 0xF4, 0x29, 0x6D, 0x83, 0x0E, 0x9A, 0x7C, 0x20, 0x9E, 0x0C, 0x64, 0x97, 0x51, 0x7A, 0xBD, 0x5A, 0x8A, 0x9D, 0x30, 0x6B, 0xCF, 0x67, 0xED, 0x91, 0xF9, 0xE6, 0x72, 0x5B, 0x47, 0x58, 0xC0, 0x22, 0xE0, 0xB1, 0xEF, 0x42, 0x75, 0xBF, 0x7B, 0x6C, 0x5B, 0xFC, 0x11, 0xD4, 0x5F, 0x90, 0x88, 0xB9, 0x41, 0xF5, 0x4E, 0xB1, 0xE5, 0x9B, 0xB8, 0xBC, 0x39, 0xA0, 0xBF, 0x12, 0x30, 0x7F, 0x5C, 0x4F, 0xDB, 0x70, 0xC5, 0x81, 0xB2, 0x3F, 0x76, 0xB6, 0x3A, 0xCA, 0xE1, 0xCA, 0xA6, 0xB7, 0x90, 0x2D, 0x52, 0x52, 0x67, 0x35, 0x48, 0x8A, 0x0E, 0xF1, 0x3C, 0x6D, 0x9A, 0x51, 0xBF, 0xA4, 0xAB, 0x3A, 0xD8, 0x34, 0x77, 0x96, 0x52, 0x4D, 0x8E, 0xF6, 0xA1, 0x67, 0xB5, 0xA4, 0x18, 0x25, 0xD9, 0x67, 0xE1, 0x44, 0xE5, 0x14, 0x05, 0x64, 0x25, 0x1C, 0xCA, 0xCB, 0x83, 0xE6, 0xB4, 0x86, 0xF6, 0xB3, 0xCA, 0x3F, 0x79, 0x71, 0x50, 0x60, 0x26, 0xC0, 0xB8, 0x57, 0xF6, 0x89, 0x96, 0x28, 0x56, 0xDE, 0xD4, 0x01, 0x0A, 0xBD, 0x0B, 0xE6, 0x21, 0xC3, 0xA3, 0x96, 0x0A, 0x54, 0xE7, 0x10, 0xC3, 0x75, 0xF2, 0x63, 0x75, 0xD7, 0x01, 0x41, 0x03, 0xA4, 0xB5, 0x43, 0x30, 0xC1, 0x98, 0xAF, 0x12, 0x61, 0x16, 0xD2, 0x27, 0x6E, 0x11, 0x71, 0x5F, 0x69, 0x38, 0x77, 0xFA, 0xD7, 0xEF, 0x09, 0xCA, 0xDB, 0x09, 0x4A, 0xE9, 0x1E, 0x1A, 0x15, 0x97 }; static const u8 dh_group24_order[] = { 0x8C, 0xF8, 0x36, 0x42, 0xA7, 0x09, 0xA0, 0x97, 0xB4, 0x47, 0x99, 0x76, 0x40, 0x12, 0x9D, 0xA2, 0x99, 0xB1, 0xA4, 0x7D, 0x1E, 0xB3, 0x75, 0x0B, 0xA3, 0x08, 0xB0, 0xFE, 0x64, 0xF5, 0xFB, 0xD3 }; #endif /* ALL_DH_GROUPS */ #define DH_GROUP(id,safe) \ { id, dh_group ## id ## _generator, sizeof(dh_group ## id ## _generator), \ dh_group ## id ## _prime, sizeof(dh_group ## id ## _prime), \ dh_group ## id ## _order, sizeof(dh_group ## id ## _order), safe } static struct dh_group dh_groups[] = { DH_GROUP(5, 1), #ifdef ALL_DH_GROUPS DH_GROUP(1, 1), DH_GROUP(2, 1), DH_GROUP(14, 1), DH_GROUP(15, 1), DH_GROUP(16, 1), DH_GROUP(17, 1), DH_GROUP(18, 1), DH_GROUP(22, 0), DH_GROUP(23, 0), DH_GROUP(24, 0) #endif /* ALL_DH_GROUPS */ }; #define NUM_DH_GROUPS ARRAY_SIZE(dh_groups) const struct dh_group * dh_groups_get(int id) { size_t i; for (i = 0; i < NUM_DH_GROUPS; i++) { if (dh_groups[i].id == id) return &dh_groups[i]; } return NULL; } /** * dh_init - Initialize Diffie-Hellman handshake * @dh: Selected Diffie-Hellman group * @priv: Pointer for returning Diffie-Hellman private key * Returns: Diffie-Hellman public value */ struct wpabuf * dh_init(const struct dh_group *dh, struct wpabuf **priv) { struct wpabuf *pv; size_t pv_len; if (dh == NULL) return NULL; wpabuf_free(*priv); *priv = wpabuf_alloc(dh->prime_len); if (*priv == NULL) return NULL; if (random_get_bytes(wpabuf_put(*priv, dh->prime_len), dh->prime_len)) { wpabuf_free(*priv); *priv = NULL; return NULL; } if (os_memcmp(wpabuf_head(*priv), dh->prime, dh->prime_len) > 0) { /* Make sure private value is smaller than prime */ *(wpabuf_mhead_u8(*priv)) = 0; } wpa_hexdump_buf_key(MSG_DEBUG, "DH: private value", *priv); pv_len = dh->prime_len; pv = wpabuf_alloc(pv_len); if (pv == NULL) return NULL; if (crypto_mod_exp(dh->generator, dh->generator_len, wpabuf_head(*priv), wpabuf_len(*priv), dh->prime, dh->prime_len, wpabuf_mhead(pv), &pv_len) < 0) { wpabuf_free(pv); wpa_printf(MSG_INFO, "DH: crypto_mod_exp failed"); return NULL; } wpabuf_put(pv, pv_len); wpa_hexdump_buf(MSG_DEBUG, "DH: public value", pv); return pv; } /** * dh_derive_shared - Derive shared Diffie-Hellman key * @peer_public: Diffie-Hellman public value from peer * @own_private: Diffie-Hellman private key from dh_init() * @dh: Selected Diffie-Hellman group * Returns: Diffie-Hellman shared key */ struct wpabuf * dh_derive_shared(const struct wpabuf *peer_public, const struct wpabuf *own_private, const struct dh_group *dh) { struct wpabuf *shared; size_t shared_len; if (dh == NULL || peer_public == NULL || own_private == NULL) return NULL; shared_len = dh->prime_len; shared = wpabuf_alloc(shared_len); if (shared == NULL) return NULL; if (crypto_mod_exp(wpabuf_head(peer_public), wpabuf_len(peer_public), wpabuf_head(own_private), wpabuf_len(own_private), dh->prime, dh->prime_len, wpabuf_mhead(shared), &shared_len) < 0) { wpabuf_free(shared); wpa_printf(MSG_INFO, "DH: crypto_mod_exp failed"); return NULL; } wpabuf_put(shared, shared_len); wpa_hexdump_buf_key(MSG_DEBUG, "DH: shared key", shared); return shared; } wpa-2.1/src/crypto/dh_groups.h000066400000000000000000000012641227414666700164370ustar00rootroot00000000000000/* * Diffie-Hellman groups * Copyright (c) 2007, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef DH_GROUPS_H #define DH_GROUPS_H struct dh_group { int id; const u8 *generator; size_t generator_len; const u8 *prime; size_t prime_len; const u8 *order; size_t order_len; unsigned int safe_prime:1; }; const struct dh_group * dh_groups_get(int id); struct wpabuf * dh_init(const struct dh_group *dh, struct wpabuf **priv); struct wpabuf * dh_derive_shared(const struct wpabuf *peer_public, const struct wpabuf *own_private, const struct dh_group *dh); #endif /* DH_GROUPS_H */ wpa-2.1/src/crypto/fips_prf_cryptoapi.c000066400000000000000000000006171227414666700203430ustar00rootroot00000000000000/* * FIPS 186-2 PRF for Microsoft CryptoAPI * Copyright (c) 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 "crypto.h" int fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, size_t xlen) { /* FIX: how to do this with CryptoAPI? */ return -1; } wpa-2.1/src/crypto/fips_prf_gnutls.c000066400000000000000000000006361227414666700176460ustar00rootroot00000000000000/* * FIPS 186-2 PRF for libgcrypt * 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 #include "common.h" #include "crypto.h" int fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, size_t xlen) { /* FIX: how to do this with libgcrypt? */ return -1; } wpa-2.1/src/crypto/fips_prf_internal.c000066400000000000000000000025751227414666700201520ustar00rootroot00000000000000/* * FIPS 186-2 PRF for internal crypto implementation * Copyright (c) 2006-2007, 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 "sha1.h" #include "sha1_i.h" #include "crypto.h" int fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, size_t xlen) { u8 xkey[64]; u32 t[5], _t[5]; int i, j, m, k; u8 *xpos = x; u32 carry; if (seed_len < sizeof(xkey)) os_memset(xkey + seed_len, 0, sizeof(xkey) - seed_len); else seed_len = sizeof(xkey); /* FIPS 186-2 + change notice 1 */ os_memcpy(xkey, seed, seed_len); t[0] = 0x67452301; t[1] = 0xEFCDAB89; t[2] = 0x98BADCFE; t[3] = 0x10325476; t[4] = 0xC3D2E1F0; m = xlen / 40; for (j = 0; j < m; j++) { /* XSEED_j = 0 */ for (i = 0; i < 2; i++) { /* XVAL = (XKEY + XSEED_j) mod 2^b */ /* w_i = G(t, XVAL) */ os_memcpy(_t, t, 20); SHA1Transform(_t, xkey); _t[0] = host_to_be32(_t[0]); _t[1] = host_to_be32(_t[1]); _t[2] = host_to_be32(_t[2]); _t[3] = host_to_be32(_t[3]); _t[4] = host_to_be32(_t[4]); os_memcpy(xpos, _t, 20); /* XKEY = (1 + XKEY + w_i) mod 2^b */ carry = 1; for (k = 19; k >= 0; k--) { carry += xkey[k] + xpos[k]; xkey[k] = carry & 0xff; carry >>= 8; } xpos += SHA1_MAC_LEN; } /* x_j = w_0|w_1 */ } return 0; } wpa-2.1/src/crypto/fips_prf_nss.c000066400000000000000000000005551227414666700171350ustar00rootroot00000000000000/* * FIPS 186-2 PRF for NSS * Copyright (c) 2009, 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.h" #include "crypto.h" int fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, size_t xlen) { return -1; } wpa-2.1/src/crypto/fips_prf_openssl.c000066400000000000000000000031031227414666700200050ustar00rootroot00000000000000/* * FIPS 186-2 PRF for libcrypto * Copyright (c) 2004-2005, 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.h" #include "crypto.h" static void sha1_transform(u8 *state, const u8 data[64]) { SHA_CTX context; os_memset(&context, 0, sizeof(context)); os_memcpy(&context.h0, state, 5 * 4); SHA1_Transform(&context, data); os_memcpy(state, &context.h0, 5 * 4); } int fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, size_t xlen) { u8 xkey[64]; u32 t[5], _t[5]; int i, j, m, k; u8 *xpos = x; u32 carry; if (seed_len < sizeof(xkey)) os_memset(xkey + seed_len, 0, sizeof(xkey) - seed_len); else seed_len = sizeof(xkey); /* FIPS 186-2 + change notice 1 */ os_memcpy(xkey, seed, seed_len); t[0] = 0x67452301; t[1] = 0xEFCDAB89; t[2] = 0x98BADCFE; t[3] = 0x10325476; t[4] = 0xC3D2E1F0; m = xlen / 40; for (j = 0; j < m; j++) { /* XSEED_j = 0 */ for (i = 0; i < 2; i++) { /* XVAL = (XKEY + XSEED_j) mod 2^b */ /* w_i = G(t, XVAL) */ os_memcpy(_t, t, 20); sha1_transform((u8 *) _t, xkey); _t[0] = host_to_be32(_t[0]); _t[1] = host_to_be32(_t[1]); _t[2] = host_to_be32(_t[2]); _t[3] = host_to_be32(_t[3]); _t[4] = host_to_be32(_t[4]); os_memcpy(xpos, _t, 20); /* XKEY = (1 + XKEY + w_i) mod 2^b */ carry = 1; for (k = 19; k >= 0; k--) { carry += xkey[k] + xpos[k]; xkey[k] = carry & 0xff; carry >>= 8; } xpos += 20; } /* x_j = w_0|w_1 */ } return 0; } wpa-2.1/src/crypto/md4-internal.c000066400000000000000000000175731227414666700167500ustar00rootroot00000000000000/* * MD4 hash implementation * Copyright (c) 2006, 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.h" #define MD4_BLOCK_LENGTH 64 #define MD4_DIGEST_LENGTH 16 typedef struct MD4Context { u32 state[4]; /* state */ u64 count; /* number of bits, mod 2^64 */ u8 buffer[MD4_BLOCK_LENGTH]; /* input buffer */ } MD4_CTX; static void MD4Init(MD4_CTX *ctx); static void MD4Update(MD4_CTX *ctx, const unsigned char *input, size_t len); static void MD4Final(unsigned char digest[MD4_DIGEST_LENGTH], MD4_CTX *ctx); int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { MD4_CTX ctx; size_t i; MD4Init(&ctx); for (i = 0; i < num_elem; i++) MD4Update(&ctx, addr[i], len[i]); MD4Final(mac, &ctx); return 0; } /* ===== start - public domain MD4 implementation ===== */ /* $OpenBSD: md4.c,v 1.7 2005/08/08 08:05:35 espie Exp $ */ /* * This code implements the MD4 message-digest algorithm. * The algorithm is due to Ron Rivest. This code was * written by Colin Plumb in 1993, no copyright is claimed. * This code is in the public domain; do with it what you wish. * Todd C. Miller modified the MD5 code to do MD4 based on RFC 1186. * * Equivalent code is available from RSA Data Security, Inc. * This code has been tested against that, and is equivalent, * except that you don't need to include two pages of legalese * with every copy. * * To compute the message digest of a chunk of bytes, declare an * MD4Context structure, pass it to MD4Init, call MD4Update as * needed on buffers full of bytes, and then call MD4Final, which * will fill a supplied 16-byte array with the digest. */ #define MD4_DIGEST_STRING_LENGTH (MD4_DIGEST_LENGTH * 2 + 1) static void MD4Transform(u32 state[4], const u8 block[MD4_BLOCK_LENGTH]); #define PUT_64BIT_LE(cp, value) do { \ (cp)[7] = (value) >> 56; \ (cp)[6] = (value) >> 48; \ (cp)[5] = (value) >> 40; \ (cp)[4] = (value) >> 32; \ (cp)[3] = (value) >> 24; \ (cp)[2] = (value) >> 16; \ (cp)[1] = (value) >> 8; \ (cp)[0] = (value); } while (0) #define PUT_32BIT_LE(cp, value) do { \ (cp)[3] = (value) >> 24; \ (cp)[2] = (value) >> 16; \ (cp)[1] = (value) >> 8; \ (cp)[0] = (value); } while (0) static u8 PADDING[MD4_BLOCK_LENGTH] = { 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; /* * Start MD4 accumulation. * Set bit count to 0 and buffer to mysterious initialization constants. */ static void MD4Init(MD4_CTX *ctx) { ctx->count = 0; ctx->state[0] = 0x67452301; ctx->state[1] = 0xefcdab89; ctx->state[2] = 0x98badcfe; ctx->state[3] = 0x10325476; } /* * Update context to reflect the concatenation of another buffer full * of bytes. */ static void MD4Update(MD4_CTX *ctx, const unsigned char *input, size_t len) { size_t have, need; /* Check how many bytes we already have and how many more we need. */ have = (size_t)((ctx->count >> 3) & (MD4_BLOCK_LENGTH - 1)); need = MD4_BLOCK_LENGTH - have; /* Update bitcount */ ctx->count += (u64)len << 3; if (len >= need) { if (have != 0) { os_memcpy(ctx->buffer + have, input, need); MD4Transform(ctx->state, ctx->buffer); input += need; len -= need; have = 0; } /* Process data in MD4_BLOCK_LENGTH-byte chunks. */ while (len >= MD4_BLOCK_LENGTH) { MD4Transform(ctx->state, input); input += MD4_BLOCK_LENGTH; len -= MD4_BLOCK_LENGTH; } } /* Handle any remaining bytes of data. */ if (len != 0) os_memcpy(ctx->buffer + have, input, len); } /* * Pad pad to 64-byte boundary with the bit pattern * 1 0* (64-bit count of bits processed, MSB-first) */ static void MD4Pad(MD4_CTX *ctx) { u8 count[8]; size_t padlen; /* Convert count to 8 bytes in little endian order. */ PUT_64BIT_LE(count, ctx->count); /* Pad out to 56 mod 64. */ padlen = MD4_BLOCK_LENGTH - ((ctx->count >> 3) & (MD4_BLOCK_LENGTH - 1)); if (padlen < 1 + 8) padlen += MD4_BLOCK_LENGTH; MD4Update(ctx, PADDING, padlen - 8); /* padlen - 8 <= 64 */ MD4Update(ctx, count, 8); } /* * Final wrapup--call MD4Pad, fill in digest and zero out ctx. */ static void MD4Final(unsigned char digest[MD4_DIGEST_LENGTH], MD4_CTX *ctx) { int i; MD4Pad(ctx); if (digest != NULL) { for (i = 0; i < 4; i++) PUT_32BIT_LE(digest + i * 4, ctx->state[i]); os_memset(ctx, 0, sizeof(*ctx)); } } /* The three core functions - F1 is optimized somewhat */ /* #define F1(x, y, z) (x & y | ~x & z) */ #define F1(x, y, z) (z ^ (x & (y ^ z))) #define F2(x, y, z) ((x & y) | (x & z) | (y & z)) #define F3(x, y, z) (x ^ y ^ z) /* This is the central step in the MD4 algorithm. */ #define MD4STEP(f, w, x, y, z, data, s) \ ( w += f(x, y, z) + data, w = w<>(32-s) ) /* * The core of the MD4 algorithm, this alters an existing MD4 hash to * reflect the addition of 16 longwords of new data. MD4Update blocks * the data and converts bytes into longwords for this routine. */ static void MD4Transform(u32 state[4], const u8 block[MD4_BLOCK_LENGTH]) { u32 a, b, c, d, in[MD4_BLOCK_LENGTH / 4]; #if BYTE_ORDER == LITTLE_ENDIAN os_memcpy(in, block, sizeof(in)); #else for (a = 0; a < MD4_BLOCK_LENGTH / 4; a++) { in[a] = (u32)( (u32)(block[a * 4 + 0]) | (u32)(block[a * 4 + 1]) << 8 | (u32)(block[a * 4 + 2]) << 16 | (u32)(block[a * 4 + 3]) << 24); } #endif a = state[0]; b = state[1]; c = state[2]; d = state[3]; MD4STEP(F1, a, b, c, d, in[ 0], 3); MD4STEP(F1, d, a, b, c, in[ 1], 7); MD4STEP(F1, c, d, a, b, in[ 2], 11); MD4STEP(F1, b, c, d, a, in[ 3], 19); MD4STEP(F1, a, b, c, d, in[ 4], 3); MD4STEP(F1, d, a, b, c, in[ 5], 7); MD4STEP(F1, c, d, a, b, in[ 6], 11); MD4STEP(F1, b, c, d, a, in[ 7], 19); MD4STEP(F1, a, b, c, d, in[ 8], 3); MD4STEP(F1, d, a, b, c, in[ 9], 7); MD4STEP(F1, c, d, a, b, in[10], 11); MD4STEP(F1, b, c, d, a, in[11], 19); MD4STEP(F1, a, b, c, d, in[12], 3); MD4STEP(F1, d, a, b, c, in[13], 7); MD4STEP(F1, c, d, a, b, in[14], 11); MD4STEP(F1, b, c, d, a, in[15], 19); MD4STEP(F2, a, b, c, d, in[ 0] + 0x5a827999, 3); MD4STEP(F2, d, a, b, c, in[ 4] + 0x5a827999, 5); MD4STEP(F2, c, d, a, b, in[ 8] + 0x5a827999, 9); MD4STEP(F2, b, c, d, a, in[12] + 0x5a827999, 13); MD4STEP(F2, a, b, c, d, in[ 1] + 0x5a827999, 3); MD4STEP(F2, d, a, b, c, in[ 5] + 0x5a827999, 5); MD4STEP(F2, c, d, a, b, in[ 9] + 0x5a827999, 9); MD4STEP(F2, b, c, d, a, in[13] + 0x5a827999, 13); MD4STEP(F2, a, b, c, d, in[ 2] + 0x5a827999, 3); MD4STEP(F2, d, a, b, c, in[ 6] + 0x5a827999, 5); MD4STEP(F2, c, d, a, b, in[10] + 0x5a827999, 9); MD4STEP(F2, b, c, d, a, in[14] + 0x5a827999, 13); MD4STEP(F2, a, b, c, d, in[ 3] + 0x5a827999, 3); MD4STEP(F2, d, a, b, c, in[ 7] + 0x5a827999, 5); MD4STEP(F2, c, d, a, b, in[11] + 0x5a827999, 9); MD4STEP(F2, b, c, d, a, in[15] + 0x5a827999, 13); MD4STEP(F3, a, b, c, d, in[ 0] + 0x6ed9eba1, 3); MD4STEP(F3, d, a, b, c, in[ 8] + 0x6ed9eba1, 9); MD4STEP(F3, c, d, a, b, in[ 4] + 0x6ed9eba1, 11); MD4STEP(F3, b, c, d, a, in[12] + 0x6ed9eba1, 15); MD4STEP(F3, a, b, c, d, in[ 2] + 0x6ed9eba1, 3); MD4STEP(F3, d, a, b, c, in[10] + 0x6ed9eba1, 9); MD4STEP(F3, c, d, a, b, in[ 6] + 0x6ed9eba1, 11); MD4STEP(F3, b, c, d, a, in[14] + 0x6ed9eba1, 15); MD4STEP(F3, a, b, c, d, in[ 1] + 0x6ed9eba1, 3); MD4STEP(F3, d, a, b, c, in[ 9] + 0x6ed9eba1, 9); MD4STEP(F3, c, d, a, b, in[ 5] + 0x6ed9eba1, 11); MD4STEP(F3, b, c, d, a, in[13] + 0x6ed9eba1, 15); MD4STEP(F3, a, b, c, d, in[ 3] + 0x6ed9eba1, 3); MD4STEP(F3, d, a, b, c, in[11] + 0x6ed9eba1, 9); MD4STEP(F3, c, d, a, b, in[ 7] + 0x6ed9eba1, 11); MD4STEP(F3, b, c, d, a, in[15] + 0x6ed9eba1, 15); state[0] += a; state[1] += b; state[2] += c; state[3] += d; } /* ===== end - public domain MD4 implementation ===== */ wpa-2.1/src/crypto/md5-internal.c000066400000000000000000000214071227414666700167400ustar00rootroot00000000000000/* * MD5 hash implementation and interface functions * Copyright (c) 2003-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 "md5.h" #include "md5_i.h" #include "crypto.h" static void MD5Transform(u32 buf[4], u32 const in[16]); typedef struct MD5Context MD5_CTX; /** * md5_vector - MD5 hash for data vector * @num_elem: Number of elements in the data vector * @addr: Pointers to the data areas * @len: Lengths of the data blocks * @mac: Buffer for the hash * Returns: 0 on success, -1 of failure */ int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { MD5_CTX ctx; size_t i; MD5Init(&ctx); for (i = 0; i < num_elem; i++) MD5Update(&ctx, addr[i], len[i]); MD5Final(mac, &ctx); return 0; } /* ===== start - public domain MD5 implementation ===== */ /* * This code implements the MD5 message-digest algorithm. * The algorithm is due to Ron Rivest. This code was * written by Colin Plumb in 1993, no copyright is claimed. * This code is in the public domain; do with it what you wish. * * Equivalent code is available from RSA Data Security, Inc. * This code has been tested against that, and is equivalent, * except that you don't need to include two pages of legalese * with every copy. * * To compute the message digest of a chunk of bytes, declare an * MD5Context structure, pass it to MD5Init, call MD5Update as * needed on buffers full of bytes, and then call MD5Final, which * will fill a supplied 16-byte array with the digest. */ #ifndef WORDS_BIGENDIAN #define byteReverse(buf, len) /* Nothing */ #else /* * Note: this code is harmless on little-endian machines. */ static void byteReverse(unsigned char *buf, unsigned longs) { u32 t; do { t = (u32) ((unsigned) buf[3] << 8 | buf[2]) << 16 | ((unsigned) buf[1] << 8 | buf[0]); *(u32 *) buf = t; buf += 4; } while (--longs); } #endif /* * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious * initialization constants. */ void MD5Init(struct MD5Context *ctx) { ctx->buf[0] = 0x67452301; ctx->buf[1] = 0xefcdab89; ctx->buf[2] = 0x98badcfe; ctx->buf[3] = 0x10325476; ctx->bits[0] = 0; ctx->bits[1] = 0; } /* * Update context to reflect the concatenation of another buffer full * of bytes. */ void MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len) { u32 t; /* Update bitcount */ t = ctx->bits[0]; if ((ctx->bits[0] = t + ((u32) len << 3)) < t) ctx->bits[1]++; /* Carry from low to high */ ctx->bits[1] += len >> 29; t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ /* Handle any leading odd-sized chunks */ if (t) { unsigned char *p = (unsigned char *) ctx->in + t; t = 64 - t; if (len < t) { os_memcpy(p, buf, len); return; } os_memcpy(p, buf, t); byteReverse(ctx->in, 16); MD5Transform(ctx->buf, (u32 *) ctx->in); buf += t; len -= t; } /* Process data in 64-byte chunks */ while (len >= 64) { os_memcpy(ctx->in, buf, 64); byteReverse(ctx->in, 16); MD5Transform(ctx->buf, (u32 *) ctx->in); buf += 64; len -= 64; } /* Handle any remaining bytes of data. */ os_memcpy(ctx->in, buf, len); } /* * Final wrapup - pad to 64-byte boundary with the bit pattern * 1 0* (64-bit count of bits processed, MSB-first) */ void MD5Final(unsigned char digest[16], struct MD5Context *ctx) { unsigned count; unsigned char *p; /* Compute number of bytes mod 64 */ count = (ctx->bits[0] >> 3) & 0x3F; /* Set the first char of padding to 0x80. This is safe since there is always at least one byte free */ p = ctx->in + count; *p++ = 0x80; /* Bytes of padding needed to make 64 bytes */ count = 64 - 1 - count; /* Pad out to 56 mod 64 */ if (count < 8) { /* Two lots of padding: Pad the first block to 64 bytes */ os_memset(p, 0, count); byteReverse(ctx->in, 16); MD5Transform(ctx->buf, (u32 *) ctx->in); /* Now fill the next block with 56 bytes */ os_memset(ctx->in, 0, 56); } else { /* Pad block to 56 bytes */ os_memset(p, 0, count - 8); } byteReverse(ctx->in, 14); /* Append length in bits and transform */ ((u32 *) aliasing_hide_typecast(ctx->in, u32))[14] = ctx->bits[0]; ((u32 *) aliasing_hide_typecast(ctx->in, u32))[15] = ctx->bits[1]; MD5Transform(ctx->buf, (u32 *) ctx->in); byteReverse((unsigned char *) ctx->buf, 4); os_memcpy(digest, ctx->buf, 16); os_memset(ctx, 0, sizeof(*ctx)); /* In case it's sensitive */ } /* The four core functions - F1 is optimized somewhat */ /* #define F1(x, y, z) (x & y | ~x & z) */ #define F1(x, y, z) (z ^ (x & (y ^ z))) #define F2(x, y, z) F1(z, x, y) #define F3(x, y, z) (x ^ y ^ z) #define F4(x, y, z) (y ^ (x | ~z)) /* This is the central step in the MD5 algorithm. */ #define MD5STEP(f, w, x, y, z, data, s) \ ( w += f(x, y, z) + data, w = w<>(32-s), w += x ) /* * The core of the MD5 algorithm, this alters an existing MD5 hash to * reflect the addition of 16 longwords of new data. MD5Update blocks * the data and converts bytes into longwords for this routine. */ static void MD5Transform(u32 buf[4], u32 const in[16]) { register u32 a, b, c, d; a = buf[0]; b = buf[1]; c = buf[2]; d = buf[3]; MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); buf[0] += a; buf[1] += b; buf[2] += c; buf[3] += d; } /* ===== end - public domain MD5 implementation ===== */ wpa-2.1/src/crypto/md5.c000066400000000000000000000050671227414666700151320ustar00rootroot00000000000000/* * MD5 hash implementation and interface functions * Copyright (c) 2003-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 "md5.h" #include "crypto.h" /** * hmac_md5_vector - HMAC-MD5 over data vector (RFC 2104) * @key: Key for HMAC operations * @key_len: Length of the key in bytes * @num_elem: Number of elements in the data vector * @addr: Pointers to the data areas * @len: Lengths of the data blocks * @mac: Buffer for the hash (16 bytes) * Returns: 0 on success, -1 on failure */ int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { u8 k_pad[64]; /* padding - key XORd with ipad/opad */ u8 tk[16]; const u8 *_addr[6]; size_t i, _len[6]; if (num_elem > 5) { /* * Fixed limit on the number of fragments to avoid having to * allocate memory (which could fail). */ return -1; } /* if key is longer than 64 bytes reset it to key = MD5(key) */ if (key_len > 64) { if (md5_vector(1, &key, &key_len, tk)) return -1; key = tk; key_len = 16; } /* the HMAC_MD5 transform looks like: * * MD5(K XOR opad, MD5(K XOR ipad, text)) * * where K is an n byte key * ipad is the byte 0x36 repeated 64 times * opad is the byte 0x5c repeated 64 times * and text is the data being protected */ /* start out by storing key in ipad */ os_memset(k_pad, 0, sizeof(k_pad)); os_memcpy(k_pad, key, key_len); /* XOR key with ipad values */ for (i = 0; i < 64; i++) k_pad[i] ^= 0x36; /* perform inner MD5 */ _addr[0] = k_pad; _len[0] = 64; for (i = 0; i < num_elem; i++) { _addr[i + 1] = addr[i]; _len[i + 1] = len[i]; } if (md5_vector(1 + num_elem, _addr, _len, mac)) return -1; os_memset(k_pad, 0, sizeof(k_pad)); os_memcpy(k_pad, key, key_len); /* XOR key with opad values */ for (i = 0; i < 64; i++) k_pad[i] ^= 0x5c; /* perform outer MD5 */ _addr[0] = k_pad; _len[0] = 64; _addr[1] = mac; _len[1] = MD5_MAC_LEN; return md5_vector(2, _addr, _len, mac); } /** * hmac_md5 - HMAC-MD5 over data buffer (RFC 2104) * @key: Key for HMAC operations * @key_len: Length of the key in bytes * @data: Pointers to the data area * @data_len: Length of the data area * @mac: Buffer for the hash (16 bytes) * Returns: 0 on success, -1 on failure */ int hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len, u8 *mac) { return hmac_md5_vector(key, key_len, 1, &data, &data_len, mac); } wpa-2.1/src/crypto/md5.h000066400000000000000000000007711227414666700151340ustar00rootroot00000000000000/* * MD5 hash implementation and interface functions * Copyright (c) 2003-2009, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef MD5_H #define MD5_H #define MD5_MAC_LEN 16 int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac); int hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len, u8 *mac); #endif /* MD5_H */ wpa-2.1/src/crypto/md5_i.h000066400000000000000000000010041227414666700154320ustar00rootroot00000000000000/* * MD5 internal definitions * Copyright (c) 2003-2005, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef MD5_I_H #define MD5_I_H struct MD5Context { u32 buf[4]; u32 bits[2]; u8 in[64]; }; void MD5Init(struct MD5Context *context); void MD5Update(struct MD5Context *context, unsigned char const *buf, unsigned len); void MD5Final(unsigned char digest[16], struct MD5Context *context); #endif /* MD5_I_H */ wpa-2.1/src/crypto/milenage.c000066400000000000000000000227541227414666700162300ustar00rootroot00000000000000/* * 3GPP AKA - Milenage algorithm (3GPP TS 35.205, .206, .207, .208) * Copyright (c) 2006-2007 * * This software may be distributed under the terms of the BSD license. * See README for more details. * * This file implements an example authentication algorithm defined for 3GPP * AKA. This can be used to implement a simple HLR/AuC into hlr_auc_gw to allow * EAP-AKA to be tested properly with real USIM cards. * * This implementations assumes that the r1..r5 and c1..c5 constants defined in * TS 35.206 are used, i.e., r1=64, r2=0, r3=32, r4=64, r5=96, c1=00..00, * c2=00..01, c3=00..02, c4=00..04, c5=00..08. The block cipher is assumed to * be AES (Rijndael). */ #include "includes.h" #include "common.h" #include "crypto/aes_wrap.h" #include "milenage.h" /** * milenage_f1 - Milenage f1 and f1* algorithms * @opc: OPc = 128-bit value derived from OP and K * @k: K = 128-bit subscriber key * @_rand: RAND = 128-bit random challenge * @sqn: SQN = 48-bit sequence number * @amf: AMF = 16-bit authentication management field * @mac_a: Buffer for MAC-A = 64-bit network authentication code, or %NULL * @mac_s: Buffer for MAC-S = 64-bit resync authentication code, or %NULL * Returns: 0 on success, -1 on failure */ int milenage_f1(const u8 *opc, const u8 *k, const u8 *_rand, const u8 *sqn, const u8 *amf, u8 *mac_a, u8 *mac_s) { u8 tmp1[16], tmp2[16], tmp3[16]; int i; /* tmp1 = TEMP = E_K(RAND XOR OP_C) */ for (i = 0; i < 16; i++) tmp1[i] = _rand[i] ^ opc[i]; if (aes_128_encrypt_block(k, tmp1, tmp1)) return -1; /* tmp2 = IN1 = SQN || AMF || SQN || AMF */ os_memcpy(tmp2, sqn, 6); os_memcpy(tmp2 + 6, amf, 2); os_memcpy(tmp2 + 8, tmp2, 8); /* OUT1 = E_K(TEMP XOR rot(IN1 XOR OP_C, r1) XOR c1) XOR OP_C */ /* rotate (tmp2 XOR OP_C) by r1 (= 0x40 = 8 bytes) */ for (i = 0; i < 16; i++) tmp3[(i + 8) % 16] = tmp2[i] ^ opc[i]; /* XOR with TEMP = E_K(RAND XOR OP_C) */ for (i = 0; i < 16; i++) tmp3[i] ^= tmp1[i]; /* XOR with c1 (= ..00, i.e., NOP) */ /* f1 || f1* = E_K(tmp3) XOR OP_c */ if (aes_128_encrypt_block(k, tmp3, tmp1)) return -1; for (i = 0; i < 16; i++) tmp1[i] ^= opc[i]; if (mac_a) os_memcpy(mac_a, tmp1, 8); /* f1 */ if (mac_s) os_memcpy(mac_s, tmp1 + 8, 8); /* f1* */ return 0; } /** * milenage_f2345 - Milenage f2, f3, f4, f5, f5* algorithms * @opc: OPc = 128-bit value derived from OP and K * @k: K = 128-bit subscriber key * @_rand: RAND = 128-bit random challenge * @res: Buffer for RES = 64-bit signed response (f2), or %NULL * @ck: Buffer for CK = 128-bit confidentiality key (f3), or %NULL * @ik: Buffer for IK = 128-bit integrity key (f4), or %NULL * @ak: Buffer for AK = 48-bit anonymity key (f5), or %NULL * @akstar: Buffer for AK = 48-bit anonymity key (f5*), or %NULL * Returns: 0 on success, -1 on failure */ int milenage_f2345(const u8 *opc, const u8 *k, const u8 *_rand, u8 *res, u8 *ck, u8 *ik, u8 *ak, u8 *akstar) { u8 tmp1[16], tmp2[16], tmp3[16]; int i; /* tmp2 = TEMP = E_K(RAND XOR OP_C) */ for (i = 0; i < 16; i++) tmp1[i] = _rand[i] ^ opc[i]; if (aes_128_encrypt_block(k, tmp1, tmp2)) return -1; /* OUT2 = E_K(rot(TEMP XOR OP_C, r2) XOR c2) XOR OP_C */ /* OUT3 = E_K(rot(TEMP XOR OP_C, r3) XOR c3) XOR OP_C */ /* OUT4 = E_K(rot(TEMP XOR OP_C, r4) XOR c4) XOR OP_C */ /* OUT5 = E_K(rot(TEMP XOR OP_C, r5) XOR c5) XOR OP_C */ /* f2 and f5 */ /* rotate by r2 (= 0, i.e., NOP) */ for (i = 0; i < 16; i++) tmp1[i] = tmp2[i] ^ opc[i]; tmp1[15] ^= 1; /* XOR c2 (= ..01) */ /* f5 || f2 = E_K(tmp1) XOR OP_c */ if (aes_128_encrypt_block(k, tmp1, tmp3)) return -1; for (i = 0; i < 16; i++) tmp3[i] ^= opc[i]; if (res) os_memcpy(res, tmp3 + 8, 8); /* f2 */ if (ak) os_memcpy(ak, tmp3, 6); /* f5 */ /* f3 */ if (ck) { /* rotate by r3 = 0x20 = 4 bytes */ for (i = 0; i < 16; i++) tmp1[(i + 12) % 16] = tmp2[i] ^ opc[i]; tmp1[15] ^= 2; /* XOR c3 (= ..02) */ if (aes_128_encrypt_block(k, tmp1, ck)) return -1; for (i = 0; i < 16; i++) ck[i] ^= opc[i]; } /* f4 */ if (ik) { /* rotate by r4 = 0x40 = 8 bytes */ for (i = 0; i < 16; i++) tmp1[(i + 8) % 16] = tmp2[i] ^ opc[i]; tmp1[15] ^= 4; /* XOR c4 (= ..04) */ if (aes_128_encrypt_block(k, tmp1, ik)) return -1; for (i = 0; i < 16; i++) ik[i] ^= opc[i]; } /* f5* */ if (akstar) { /* rotate by r5 = 0x60 = 12 bytes */ for (i = 0; i < 16; i++) tmp1[(i + 4) % 16] = tmp2[i] ^ opc[i]; tmp1[15] ^= 8; /* XOR c5 (= ..08) */ if (aes_128_encrypt_block(k, tmp1, tmp1)) return -1; for (i = 0; i < 6; i++) akstar[i] = tmp1[i] ^ opc[i]; } return 0; } /** * milenage_generate - Generate AKA AUTN,IK,CK,RES * @opc: OPc = 128-bit operator variant algorithm configuration field (encr.) * @amf: AMF = 16-bit authentication management field * @k: K = 128-bit subscriber key * @sqn: SQN = 48-bit sequence number * @_rand: RAND = 128-bit random challenge * @autn: Buffer for AUTN = 128-bit authentication token * @ik: Buffer for IK = 128-bit integrity key (f4), or %NULL * @ck: Buffer for CK = 128-bit confidentiality key (f3), or %NULL * @res: Buffer for RES = 64-bit signed response (f2), or %NULL * @res_len: Max length for res; set to used length or 0 on failure */ void milenage_generate(const u8 *opc, const u8 *amf, const u8 *k, const u8 *sqn, const u8 *_rand, u8 *autn, u8 *ik, u8 *ck, u8 *res, size_t *res_len) { int i; u8 mac_a[8], ak[6]; if (*res_len < 8) { *res_len = 0; return; } if (milenage_f1(opc, k, _rand, sqn, amf, mac_a, NULL) || milenage_f2345(opc, k, _rand, res, ck, ik, ak, NULL)) { *res_len = 0; return; } *res_len = 8; /* AUTN = (SQN ^ AK) || AMF || MAC */ for (i = 0; i < 6; i++) autn[i] = sqn[i] ^ ak[i]; os_memcpy(autn + 6, amf, 2); os_memcpy(autn + 8, mac_a, 8); } /** * milenage_auts - Milenage AUTS validation * @opc: OPc = 128-bit operator variant algorithm configuration field (encr.) * @k: K = 128-bit subscriber key * @_rand: RAND = 128-bit random challenge * @auts: AUTS = 112-bit authentication token from client * @sqn: Buffer for SQN = 48-bit sequence number * Returns: 0 = success (sqn filled), -1 on failure */ int milenage_auts(const u8 *opc, const u8 *k, const u8 *_rand, const u8 *auts, u8 *sqn) { u8 amf[2] = { 0x00, 0x00 }; /* TS 33.102 v7.0.0, 6.3.3 */ u8 ak[6], mac_s[8]; int i; if (milenage_f2345(opc, k, _rand, NULL, NULL, NULL, NULL, ak)) return -1; for (i = 0; i < 6; i++) sqn[i] = auts[i] ^ ak[i]; if (milenage_f1(opc, k, _rand, sqn, amf, NULL, mac_s) || memcmp(mac_s, auts + 6, 8) != 0) return -1; return 0; } /** * gsm_milenage - Generate GSM-Milenage (3GPP TS 55.205) authentication triplet * @opc: OPc = 128-bit operator variant algorithm configuration field (encr.) * @k: K = 128-bit subscriber key * @_rand: RAND = 128-bit random challenge * @sres: Buffer for SRES = 32-bit SRES * @kc: Buffer for Kc = 64-bit Kc * Returns: 0 on success, -1 on failure */ int gsm_milenage(const u8 *opc, const u8 *k, const u8 *_rand, u8 *sres, u8 *kc) { u8 res[8], ck[16], ik[16]; int i; if (milenage_f2345(opc, k, _rand, res, ck, ik, NULL, NULL)) return -1; for (i = 0; i < 8; i++) kc[i] = ck[i] ^ ck[i + 8] ^ ik[i] ^ ik[i + 8]; #ifdef GSM_MILENAGE_ALT_SRES os_memcpy(sres, res, 4); #else /* GSM_MILENAGE_ALT_SRES */ for (i = 0; i < 4; i++) sres[i] = res[i] ^ res[i + 4]; #endif /* GSM_MILENAGE_ALT_SRES */ return 0; } /** * milenage_generate - Generate AKA AUTN,IK,CK,RES * @opc: OPc = 128-bit operator variant algorithm configuration field (encr.) * @k: K = 128-bit subscriber key * @sqn: SQN = 48-bit sequence number * @_rand: RAND = 128-bit random challenge * @autn: AUTN = 128-bit authentication token * @ik: Buffer for IK = 128-bit integrity key (f4), or %NULL * @ck: Buffer for CK = 128-bit confidentiality key (f3), or %NULL * @res: Buffer for RES = 64-bit signed response (f2), or %NULL * @res_len: Variable that will be set to RES length * @auts: 112-bit buffer for AUTS * Returns: 0 on success, -1 on failure, or -2 on synchronization failure */ int milenage_check(const u8 *opc, const u8 *k, const u8 *sqn, const u8 *_rand, const u8 *autn, u8 *ik, u8 *ck, u8 *res, size_t *res_len, u8 *auts) { int i; u8 mac_a[8], ak[6], rx_sqn[6]; const u8 *amf; wpa_hexdump(MSG_DEBUG, "Milenage: AUTN", autn, 16); wpa_hexdump(MSG_DEBUG, "Milenage: RAND", _rand, 16); if (milenage_f2345(opc, k, _rand, res, ck, ik, ak, NULL)) return -1; *res_len = 8; wpa_hexdump_key(MSG_DEBUG, "Milenage: RES", res, *res_len); wpa_hexdump_key(MSG_DEBUG, "Milenage: CK", ck, 16); wpa_hexdump_key(MSG_DEBUG, "Milenage: IK", ik, 16); wpa_hexdump_key(MSG_DEBUG, "Milenage: AK", ak, 6); /* AUTN = (SQN ^ AK) || AMF || MAC */ for (i = 0; i < 6; i++) rx_sqn[i] = autn[i] ^ ak[i]; wpa_hexdump(MSG_DEBUG, "Milenage: SQN", rx_sqn, 6); if (os_memcmp(rx_sqn, sqn, 6) <= 0) { u8 auts_amf[2] = { 0x00, 0x00 }; /* TS 33.102 v7.0.0, 6.3.3 */ if (milenage_f2345(opc, k, _rand, NULL, NULL, NULL, NULL, ak)) return -1; wpa_hexdump_key(MSG_DEBUG, "Milenage: AK*", ak, 6); for (i = 0; i < 6; i++) auts[i] = sqn[i] ^ ak[i]; if (milenage_f1(opc, k, _rand, sqn, auts_amf, NULL, auts + 6)) return -1; wpa_hexdump(MSG_DEBUG, "Milenage: AUTS", auts, 14); return -2; } amf = autn + 6; wpa_hexdump(MSG_DEBUG, "Milenage: AMF", amf, 2); if (milenage_f1(opc, k, _rand, rx_sqn, amf, mac_a, NULL)) return -1; wpa_hexdump(MSG_DEBUG, "Milenage: MAC_A", mac_a, 8); if (os_memcmp(mac_a, autn + 8, 8) != 0) { wpa_printf(MSG_DEBUG, "Milenage: MAC mismatch"); wpa_hexdump(MSG_DEBUG, "Milenage: Received MAC_A", autn + 8, 8); return -1; } return 0; } wpa-2.1/src/crypto/milenage.h000066400000000000000000000017731227414666700162330ustar00rootroot00000000000000/* * UMTS AKA - Milenage algorithm (3GPP TS 35.205, .206, .207, .208) * Copyright (c) 2006-2007 * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef MILENAGE_H #define MILENAGE_H void milenage_generate(const u8 *opc, const u8 *amf, const u8 *k, const u8 *sqn, const u8 *_rand, u8 *autn, u8 *ik, u8 *ck, u8 *res, size_t *res_len); int milenage_auts(const u8 *opc, const u8 *k, const u8 *_rand, const u8 *auts, u8 *sqn); int gsm_milenage(const u8 *opc, const u8 *k, const u8 *_rand, u8 *sres, u8 *kc); int milenage_check(const u8 *opc, const u8 *k, const u8 *sqn, const u8 *_rand, const u8 *autn, u8 *ik, u8 *ck, u8 *res, size_t *res_len, u8 *auts); int milenage_f1(const u8 *opc, const u8 *k, const u8 *_rand, const u8 *sqn, const u8 *amf, u8 *mac_a, u8 *mac_s); int milenage_f2345(const u8 *opc, const u8 *k, const u8 *_rand, u8 *res, u8 *ck, u8 *ik, u8 *ak, u8 *akstar); #endif /* MILENAGE_H */ wpa-2.1/src/crypto/ms_funcs.c000066400000000000000000000377411227414666700162660ustar00rootroot00000000000000/* * WPA Supplicant / shared MSCHAPV2 helper functions / RFC 2433 / RFC 2759 * Copyright (c) 2004-2012, 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 "sha1.h" #include "ms_funcs.h" #include "crypto.h" /** * utf8_to_ucs2 - Convert UTF-8 string to UCS-2 encoding * @utf8_string: UTF-8 string (IN) * @utf8_string_len: Length of utf8_string (IN) * @ucs2_buffer: UCS-2 buffer (OUT) * @ucs2_buffer_size: Length of UCS-2 buffer (IN) * @ucs2_string_size: Number of 2-byte words in the resulting UCS-2 string * Returns: 0 on success, -1 on failure */ static int utf8_to_ucs2(const u8 *utf8_string, size_t utf8_string_len, u8 *ucs2_buffer, size_t ucs2_buffer_size, size_t *ucs2_string_size) { size_t i, j; for (i = 0, j = 0; i < utf8_string_len; i++) { u8 c = utf8_string[i]; if (j >= ucs2_buffer_size) { /* input too long */ return -1; } if (c <= 0x7F) { WPA_PUT_LE16(ucs2_buffer + j, c); j += 2; } else if (i == utf8_string_len - 1 || j >= ucs2_buffer_size - 1) { /* incomplete surrogate */ return -1; } else { u8 c2 = utf8_string[++i]; if ((c & 0xE0) == 0xC0) { /* two-byte encoding */ WPA_PUT_LE16(ucs2_buffer + j, ((c & 0x1F) << 6) | (c2 & 0x3F)); j += 2; } else if (i == utf8_string_len || j >= ucs2_buffer_size - 1) { /* incomplete surrogate */ return -1; } else { /* three-byte encoding */ u8 c3 = utf8_string[++i]; WPA_PUT_LE16(ucs2_buffer + j, ((c & 0xF) << 12) | ((c2 & 0x3F) << 6) | (c3 & 0x3F)); } } } if (ucs2_string_size) *ucs2_string_size = j / 2; return 0; } /** * challenge_hash - ChallengeHash() - RFC 2759, Sect. 8.2 * @peer_challenge: 16-octet PeerChallenge (IN) * @auth_challenge: 16-octet AuthenticatorChallenge (IN) * @username: 0-to-256-char UserName (IN) * @username_len: Length of username * @challenge: 8-octet Challenge (OUT) * Returns: 0 on success, -1 on failure */ static int challenge_hash(const u8 *peer_challenge, const u8 *auth_challenge, const u8 *username, size_t username_len, u8 *challenge) { u8 hash[SHA1_MAC_LEN]; const unsigned char *addr[3]; size_t len[3]; addr[0] = peer_challenge; len[0] = 16; addr[1] = auth_challenge; len[1] = 16; addr[2] = username; len[2] = username_len; if (sha1_vector(3, addr, len, hash)) return -1; os_memcpy(challenge, hash, 8); return 0; } /** * nt_password_hash - NtPasswordHash() - RFC 2759, Sect. 8.3 * @password: 0-to-256-unicode-char Password (IN; UTF-8) * @password_len: Length of password * @password_hash: 16-octet PasswordHash (OUT) * Returns: 0 on success, -1 on failure */ int nt_password_hash(const u8 *password, size_t password_len, u8 *password_hash) { u8 buf[512], *pos; size_t len, max_len; max_len = sizeof(buf); if (utf8_to_ucs2(password, password_len, buf, max_len, &len) < 0) return -1; len *= 2; pos = buf; return md4_vector(1, (const u8 **) &pos, &len, password_hash); } /** * hash_nt_password_hash - HashNtPasswordHash() - RFC 2759, Sect. 8.4 * @password_hash: 16-octet PasswordHash (IN) * @password_hash_hash: 16-octet PasswordHashHash (OUT) * Returns: 0 on success, -1 on failure */ int hash_nt_password_hash(const u8 *password_hash, u8 *password_hash_hash) { size_t len = 16; return md4_vector(1, &password_hash, &len, password_hash_hash); } /** * challenge_response - ChallengeResponse() - RFC 2759, Sect. 8.5 * @challenge: 8-octet Challenge (IN) * @password_hash: 16-octet PasswordHash (IN) * @response: 24-octet Response (OUT) */ void challenge_response(const u8 *challenge, const u8 *password_hash, u8 *response) { u8 zpwd[7]; des_encrypt(challenge, password_hash, response); des_encrypt(challenge, password_hash + 7, response + 8); zpwd[0] = password_hash[14]; zpwd[1] = password_hash[15]; os_memset(zpwd + 2, 0, 5); des_encrypt(challenge, zpwd, response + 16); } /** * generate_nt_response - GenerateNTResponse() - RFC 2759, Sect. 8.1 * @auth_challenge: 16-octet AuthenticatorChallenge (IN) * @peer_challenge: 16-octet PeerChallenge (IN) * @username: 0-to-256-char UserName (IN) * @username_len: Length of username * @password: 0-to-256-unicode-char Password (IN; UTF-8) * @password_len: Length of password * @response: 24-octet Response (OUT) * Returns: 0 on success, -1 on failure */ int generate_nt_response(const u8 *auth_challenge, const u8 *peer_challenge, const u8 *username, size_t username_len, const u8 *password, size_t password_len, u8 *response) { u8 challenge[8]; u8 password_hash[16]; if (challenge_hash(peer_challenge, auth_challenge, username, username_len, challenge)) return -1; if (nt_password_hash(password, password_len, password_hash)) return -1; challenge_response(challenge, password_hash, response); return 0; } /** * generate_nt_response_pwhash - GenerateNTResponse() - RFC 2759, Sect. 8.1 * @auth_challenge: 16-octet AuthenticatorChallenge (IN) * @peer_challenge: 16-octet PeerChallenge (IN) * @username: 0-to-256-char UserName (IN) * @username_len: Length of username * @password_hash: 16-octet PasswordHash (IN) * @response: 24-octet Response (OUT) * Returns: 0 on success, -1 on failure */ int generate_nt_response_pwhash(const u8 *auth_challenge, const u8 *peer_challenge, const u8 *username, size_t username_len, const u8 *password_hash, u8 *response) { u8 challenge[8]; if (challenge_hash(peer_challenge, auth_challenge, username, username_len, challenge)) return -1; challenge_response(challenge, password_hash, response); return 0; } /** * generate_authenticator_response_pwhash - GenerateAuthenticatorResponse() - RFC 2759, Sect. 8.7 * @password_hash: 16-octet PasswordHash (IN) * @nt_response: 24-octet NT-Response (IN) * @peer_challenge: 16-octet PeerChallenge (IN) * @auth_challenge: 16-octet AuthenticatorChallenge (IN) * @username: 0-to-256-char UserName (IN) * @username_len: Length of username * @response: 20-octet AuthenticatorResponse (OUT) (note: this value is usually * encoded as a 42-octet ASCII string (S=hexdump_of_response) * Returns: 0 on success, -1 on failure */ int generate_authenticator_response_pwhash( const u8 *password_hash, const u8 *peer_challenge, const u8 *auth_challenge, const u8 *username, size_t username_len, const u8 *nt_response, u8 *response) { static const u8 magic1[39] = { 0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65, 0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67, 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74 }; static const u8 magic2[41] = { 0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B, 0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F, 0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E, 0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F, 0x6E }; u8 password_hash_hash[16], challenge[8]; const unsigned char *addr1[3]; const size_t len1[3] = { 16, 24, sizeof(magic1) }; const unsigned char *addr2[3]; const size_t len2[3] = { SHA1_MAC_LEN, 8, sizeof(magic2) }; addr1[0] = password_hash_hash; addr1[1] = nt_response; addr1[2] = magic1; addr2[0] = response; addr2[1] = challenge; addr2[2] = magic2; if (hash_nt_password_hash(password_hash, password_hash_hash)) return -1; if (sha1_vector(3, addr1, len1, response)) return -1; if (challenge_hash(peer_challenge, auth_challenge, username, username_len, challenge)) return -1; return sha1_vector(3, addr2, len2, response); } /** * generate_authenticator_response - GenerateAuthenticatorResponse() - RFC 2759, Sect. 8.7 * @password: 0-to-256-unicode-char Password (IN; UTF-8) * @password_len: Length of password * @nt_response: 24-octet NT-Response (IN) * @peer_challenge: 16-octet PeerChallenge (IN) * @auth_challenge: 16-octet AuthenticatorChallenge (IN) * @username: 0-to-256-char UserName (IN) * @username_len: Length of username * @response: 20-octet AuthenticatorResponse (OUT) (note: this value is usually * encoded as a 42-octet ASCII string (S=hexdump_of_response) * Returns: 0 on success, -1 on failure */ int generate_authenticator_response(const u8 *password, size_t password_len, const u8 *peer_challenge, const u8 *auth_challenge, const u8 *username, size_t username_len, const u8 *nt_response, u8 *response) { u8 password_hash[16]; if (nt_password_hash(password, password_len, password_hash)) return -1; return generate_authenticator_response_pwhash( password_hash, peer_challenge, auth_challenge, username, username_len, nt_response, response); } /** * nt_challenge_response - NtChallengeResponse() - RFC 2433, Sect. A.5 * @challenge: 8-octet Challenge (IN) * @password: 0-to-256-unicode-char Password (IN; UTF-8) * @password_len: Length of password * @response: 24-octet Response (OUT) * Returns: 0 on success, -1 on failure */ int nt_challenge_response(const u8 *challenge, const u8 *password, size_t password_len, u8 *response) { u8 password_hash[16]; if (nt_password_hash(password, password_len, password_hash)) return -1; challenge_response(challenge, password_hash, response); return 0; } /** * get_master_key - GetMasterKey() - RFC 3079, Sect. 3.4 * @password_hash_hash: 16-octet PasswordHashHash (IN) * @nt_response: 24-octet NTResponse (IN) * @master_key: 16-octet MasterKey (OUT) * Returns: 0 on success, -1 on failure */ int get_master_key(const u8 *password_hash_hash, const u8 *nt_response, u8 *master_key) { static const u8 magic1[27] = { 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x4d, 0x50, 0x50, 0x45, 0x20, 0x4d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x4b, 0x65, 0x79 }; const unsigned char *addr[3]; const size_t len[3] = { 16, 24, sizeof(magic1) }; u8 hash[SHA1_MAC_LEN]; addr[0] = password_hash_hash; addr[1] = nt_response; addr[2] = magic1; if (sha1_vector(3, addr, len, hash)) return -1; os_memcpy(master_key, hash, 16); return 0; } /** * get_asymetric_start_key - GetAsymetricStartKey() - RFC 3079, Sect. 3.4 * @master_key: 16-octet MasterKey (IN) * @session_key: 8-to-16 octet SessionKey (OUT) * @session_key_len: SessionKeyLength (Length of session_key) (IN) * @is_send: IsSend (IN, BOOLEAN) * @is_server: IsServer (IN, BOOLEAN) * Returns: 0 on success, -1 on failure */ int get_asymetric_start_key(const u8 *master_key, u8 *session_key, size_t session_key_len, int is_send, int is_server) { static const u8 magic2[84] = { 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x6b, 0x65, 0x79, 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20, 0x6b, 0x65, 0x79, 0x2e }; static const u8 magic3[84] = { 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20, 0x6b, 0x65, 0x79, 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x6b, 0x65, 0x79, 0x2e }; static const u8 shs_pad1[40] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static const u8 shs_pad2[40] = { 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2 }; u8 digest[SHA1_MAC_LEN]; const unsigned char *addr[4]; const size_t len[4] = { 16, 40, 84, 40 }; addr[0] = master_key; addr[1] = shs_pad1; if (is_send) { addr[2] = is_server ? magic3 : magic2; } else { addr[2] = is_server ? magic2 : magic3; } addr[3] = shs_pad2; if (sha1_vector(4, addr, len, digest)) return -1; if (session_key_len > SHA1_MAC_LEN) session_key_len = SHA1_MAC_LEN; os_memcpy(session_key, digest, session_key_len); return 0; } #define PWBLOCK_LEN 516 /** * encrypt_pw_block_with_password_hash - EncryptPwBlockWithPasswordHash() - RFC 2759, Sect. 8.10 * @password: 0-to-256-unicode-char Password (IN; UTF-8) * @password_len: Length of password * @password_hash: 16-octet PasswordHash (IN) * @pw_block: 516-byte PwBlock (OUT) * Returns: 0 on success, -1 on failure */ int encrypt_pw_block_with_password_hash( const u8 *password, size_t password_len, const u8 *password_hash, u8 *pw_block) { size_t ucs2_len, offset; u8 *pos; os_memset(pw_block, 0, PWBLOCK_LEN); if (utf8_to_ucs2(password, password_len, pw_block, 512, &ucs2_len) < 0) return -1; if (ucs2_len > 256) return -1; offset = (256 - ucs2_len) * 2; if (offset != 0) { os_memmove(pw_block + offset, pw_block, ucs2_len * 2); if (os_get_random(pw_block, offset) < 0) return -1; } /* * PasswordLength is 4 octets, but since the maximum password length is * 256, only first two (in little endian byte order) can be non-zero. */ pos = &pw_block[2 * 256]; WPA_PUT_LE16(pos, password_len * 2); rc4_skip(password_hash, 16, 0, pw_block, PWBLOCK_LEN); return 0; } /** * new_password_encrypted_with_old_nt_password_hash - NewPasswordEncryptedWithOldNtPasswordHash() - RFC 2759, Sect. 8.9 * @new_password: 0-to-256-unicode-char NewPassword (IN; UTF-8) * @new_password_len: Length of new_password * @old_password: 0-to-256-unicode-char OldPassword (IN; UTF-8) * @old_password_len: Length of old_password * @encrypted_pw_block: 516-octet EncryptedPwBlock (OUT) * Returns: 0 on success, -1 on failure */ int new_password_encrypted_with_old_nt_password_hash( const u8 *new_password, size_t new_password_len, const u8 *old_password, size_t old_password_len, u8 *encrypted_pw_block) { u8 password_hash[16]; if (nt_password_hash(old_password, old_password_len, password_hash)) return -1; if (encrypt_pw_block_with_password_hash(new_password, new_password_len, password_hash, encrypted_pw_block)) return -1; return 0; } /** * nt_password_hash_encrypted_with_block - NtPasswordHashEncryptedWithBlock() - RFC 2759, Sect 8.13 * @password_hash: 16-octer PasswordHash (IN) * @block: 16-octet Block (IN) * @cypher: 16-octer Cypher (OUT) */ void nt_password_hash_encrypted_with_block(const u8 *password_hash, const u8 *block, u8 *cypher) { des_encrypt(password_hash, block, cypher); des_encrypt(password_hash + 8, block + 7, cypher + 8); } /** * old_nt_password_hash_encrypted_with_new_nt_password_hash - OldNtPasswordHashEncryptedWithNewNtPasswordHash() - RFC 2759, Sect. 8.12 * @new_password: 0-to-256-unicode-char NewPassword (IN; UTF-8) * @new_password_len: Length of new_password * @old_password: 0-to-256-unicode-char OldPassword (IN; UTF-8) * @old_password_len: Length of old_password * @encrypted_password_hash: 16-octet EncryptedPasswordHash (OUT) * Returns: 0 on success, -1 on failure */ int old_nt_password_hash_encrypted_with_new_nt_password_hash( const u8 *new_password, size_t new_password_len, const u8 *old_password, size_t old_password_len, u8 *encrypted_password_hash) { u8 old_password_hash[16], new_password_hash[16]; if (nt_password_hash(old_password, old_password_len, old_password_hash) || nt_password_hash(new_password, new_password_len, new_password_hash)) return -1; nt_password_hash_encrypted_with_block(old_password_hash, new_password_hash, encrypted_password_hash); return 0; } wpa-2.1/src/crypto/ms_funcs.h000066400000000000000000000044401227414666700162610ustar00rootroot00000000000000/* * WPA Supplicant / shared MSCHAPV2 helper functions / RFC 2433 / RFC 2759 * Copyright (c) 2004-2009, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef MS_FUNCS_H #define MS_FUNCS_H int generate_nt_response(const u8 *auth_challenge, const u8 *peer_challenge, const u8 *username, size_t username_len, const u8 *password, size_t password_len, u8 *response); int generate_nt_response_pwhash(const u8 *auth_challenge, const u8 *peer_challenge, const u8 *username, size_t username_len, const u8 *password_hash, u8 *response); int generate_authenticator_response(const u8 *password, size_t password_len, const u8 *peer_challenge, const u8 *auth_challenge, const u8 *username, size_t username_len, const u8 *nt_response, u8 *response); int generate_authenticator_response_pwhash( const u8 *password_hash, const u8 *peer_challenge, const u8 *auth_challenge, const u8 *username, size_t username_len, const u8 *nt_response, u8 *response); int nt_challenge_response(const u8 *challenge, const u8 *password, size_t password_len, u8 *response); void challenge_response(const u8 *challenge, const u8 *password_hash, u8 *response); int nt_password_hash(const u8 *password, size_t password_len, u8 *password_hash); int hash_nt_password_hash(const u8 *password_hash, u8 *password_hash_hash); int get_master_key(const u8 *password_hash_hash, const u8 *nt_response, u8 *master_key); int get_asymetric_start_key(const u8 *master_key, u8 *session_key, size_t session_key_len, int is_send, int is_server); int __must_check encrypt_pw_block_with_password_hash( const u8 *password, size_t password_len, const u8 *password_hash, u8 *pw_block); int __must_check new_password_encrypted_with_old_nt_password_hash( const u8 *new_password, size_t new_password_len, const u8 *old_password, size_t old_password_len, u8 *encrypted_pw_block); void nt_password_hash_encrypted_with_block(const u8 *password_hash, const u8 *block, u8 *cypher); int old_nt_password_hash_encrypted_with_new_nt_password_hash( const u8 *new_password, size_t new_password_len, const u8 *old_password, size_t old_password_len, u8 *encrypted_password_hash); #endif /* MS_FUNCS_H */ wpa-2.1/src/crypto/random.c000066400000000000000000000262461227414666700157270ustar00rootroot00000000000000/* * Random number generator * Copyright (c) 2010-2011, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. * * This random number generator is used to provide additional entropy to the * one provided by the operating system (os_get_random()) for session key * generation. The os_get_random() output is expected to be secure and the * implementation here is expected to provide only limited protection against * cases where os_get_random() cannot provide strong randomness. This * implementation shall not be assumed to be secure as the sole source of * randomness. The random_get_bytes() function mixes in randomness from * os_get_random() and as such, calls to os_get_random() can be replaced with * calls to random_get_bytes() without reducing security. * * The design here follows partially the design used in the Linux * drivers/char/random.c, but the implementation here is simpler and not as * strong. This is a compromise to reduce duplicated CPU effort and to avoid * extra code/memory size. As pointed out above, os_get_random() needs to be * guaranteed to be secure for any of the security assumptions to hold. */ #include "utils/includes.h" #ifdef __linux__ #include #endif /* __linux__ */ #include "utils/common.h" #include "utils/eloop.h" #include "crypto/crypto.h" #include "sha1.h" #include "random.h" #define POOL_WORDS 32 #define POOL_WORDS_MASK (POOL_WORDS - 1) #define POOL_TAP1 26 #define POOL_TAP2 20 #define POOL_TAP3 14 #define POOL_TAP4 7 #define POOL_TAP5 1 #define EXTRACT_LEN 16 #define MIN_READY_MARK 2 static u32 pool[POOL_WORDS]; static unsigned int input_rotate = 0; static unsigned int pool_pos = 0; static u8 dummy_key[20]; #ifdef __linux__ static size_t dummy_key_avail = 0; static int random_fd = -1; #endif /* __linux__ */ static unsigned int own_pool_ready = 0; #define RANDOM_ENTROPY_SIZE 20 static char *random_entropy_file = NULL; static int random_entropy_file_read = 0; #define MIN_COLLECT_ENTROPY 1000 static unsigned int entropy = 0; static unsigned int total_collected = 0; static void random_write_entropy(void); static u32 __ROL32(u32 x, u32 y) { return (x << (y & 31)) | (x >> (32 - (y & 31))); } static void random_mix_pool(const void *buf, size_t len) { static const u32 twist[8] = { 0x00000000, 0x3b6e20c8, 0x76dc4190, 0x4db26158, 0xedb88320, 0xd6d6a3e8, 0x9b64c2b0, 0xa00ae278 }; const u8 *pos = buf; u32 w; wpa_hexdump_key(MSG_EXCESSIVE, "random_mix_pool", buf, len); while (len--) { w = __ROL32(*pos++, input_rotate & 31); input_rotate += pool_pos ? 7 : 14; pool_pos = (pool_pos - 1) & POOL_WORDS_MASK; w ^= pool[pool_pos]; w ^= pool[(pool_pos + POOL_TAP1) & POOL_WORDS_MASK]; w ^= pool[(pool_pos + POOL_TAP2) & POOL_WORDS_MASK]; w ^= pool[(pool_pos + POOL_TAP3) & POOL_WORDS_MASK]; w ^= pool[(pool_pos + POOL_TAP4) & POOL_WORDS_MASK]; w ^= pool[(pool_pos + POOL_TAP5) & POOL_WORDS_MASK]; pool[pool_pos] = (w >> 3) ^ twist[w & 7]; } } static void random_extract(u8 *out) { unsigned int i; u8 hash[SHA1_MAC_LEN]; u32 *hash_ptr; u32 buf[POOL_WORDS / 2]; /* First, add hash back to pool to make backtracking more difficult. */ hmac_sha1(dummy_key, sizeof(dummy_key), (const u8 *) pool, sizeof(pool), hash); random_mix_pool(hash, sizeof(hash)); /* Hash half the pool to extra data */ for (i = 0; i < POOL_WORDS / 2; i++) buf[i] = pool[(pool_pos - i) & POOL_WORDS_MASK]; hmac_sha1(dummy_key, sizeof(dummy_key), (const u8 *) buf, sizeof(buf), hash); /* * Fold the hash to further reduce any potential output pattern. * Though, compromise this to reduce CPU use for the most common output * length (32) and return 16 bytes from instead of only half. */ hash_ptr = (u32 *) hash; hash_ptr[0] ^= hash_ptr[4]; os_memcpy(out, hash, EXTRACT_LEN); } void random_add_randomness(const void *buf, size_t len) { struct os_time t; static unsigned int count = 0; count++; if (entropy > MIN_COLLECT_ENTROPY && (count & 0x3ff) != 0) { /* * No need to add more entropy at this point, so save CPU and * skip the update. */ return; } wpa_printf(MSG_EXCESSIVE, "Add randomness: count=%u entropy=%u", count, entropy); os_get_time(&t); wpa_hexdump_key(MSG_EXCESSIVE, "random pool", (const u8 *) pool, sizeof(pool)); random_mix_pool(&t, sizeof(t)); random_mix_pool(buf, len); wpa_hexdump_key(MSG_EXCESSIVE, "random pool", (const u8 *) pool, sizeof(pool)); entropy++; total_collected++; } int random_get_bytes(void *buf, size_t len) { int ret; u8 *bytes = buf; size_t left; wpa_printf(MSG_MSGDUMP, "Get randomness: len=%u entropy=%u", (unsigned int) len, entropy); /* Start with assumed strong randomness from OS */ ret = os_get_random(buf, len); wpa_hexdump_key(MSG_EXCESSIVE, "random from os_get_random", buf, len); /* Mix in additional entropy extracted from the internal pool */ left = len; while (left) { size_t siz, i; u8 tmp[EXTRACT_LEN]; random_extract(tmp); wpa_hexdump_key(MSG_EXCESSIVE, "random from internal pool", tmp, sizeof(tmp)); siz = left > EXTRACT_LEN ? EXTRACT_LEN : left; for (i = 0; i < siz; i++) *bytes++ ^= tmp[i]; left -= siz; } #ifdef CONFIG_FIPS /* Mix in additional entropy from the crypto module */ left = len; while (left) { size_t siz, i; u8 tmp[EXTRACT_LEN]; if (crypto_get_random(tmp, sizeof(tmp)) < 0) { wpa_printf(MSG_ERROR, "random: No entropy available " "for generating strong random bytes"); return -1; } wpa_hexdump_key(MSG_EXCESSIVE, "random from crypto module", tmp, sizeof(tmp)); siz = left > EXTRACT_LEN ? EXTRACT_LEN : left; for (i = 0; i < siz; i++) *bytes++ ^= tmp[i]; left -= siz; } #endif /* CONFIG_FIPS */ wpa_hexdump_key(MSG_EXCESSIVE, "mixed random", buf, len); if (entropy < len) entropy = 0; else entropy -= len; return ret; } int random_pool_ready(void) { #ifdef __linux__ int fd; ssize_t res; /* * Make sure that there is reasonable entropy available before allowing * some key derivation operations to proceed. */ if (dummy_key_avail == sizeof(dummy_key)) return 1; /* Already initialized - good to continue */ /* * Try to fetch some more data from the kernel high quality * /dev/random. There may not be enough data available at this point, * so use non-blocking read to avoid blocking the application * completely. */ fd = open("/dev/random", O_RDONLY | O_NONBLOCK); if (fd < 0) { #ifndef CONFIG_NO_STDOUT_DEBUG int error = errno; perror("open(/dev/random)"); wpa_printf(MSG_ERROR, "random: Cannot open /dev/random: %s", strerror(error)); #endif /* CONFIG_NO_STDOUT_DEBUG */ return -1; } res = read(fd, dummy_key + dummy_key_avail, sizeof(dummy_key) - dummy_key_avail); if (res < 0) { wpa_printf(MSG_ERROR, "random: Cannot read from /dev/random: " "%s", strerror(errno)); res = 0; } wpa_printf(MSG_DEBUG, "random: Got %u/%u bytes from " "/dev/random", (unsigned) res, (unsigned) (sizeof(dummy_key) - dummy_key_avail)); dummy_key_avail += res; close(fd); if (dummy_key_avail == sizeof(dummy_key)) { if (own_pool_ready < MIN_READY_MARK) own_pool_ready = MIN_READY_MARK; random_write_entropy(); return 1; } wpa_printf(MSG_INFO, "random: Only %u/%u bytes of strong " "random data available from /dev/random", (unsigned) dummy_key_avail, (unsigned) sizeof(dummy_key)); if (own_pool_ready >= MIN_READY_MARK || total_collected + 10 * own_pool_ready > MIN_COLLECT_ENTROPY) { wpa_printf(MSG_INFO, "random: Allow operation to proceed " "based on internal entropy"); return 1; } wpa_printf(MSG_INFO, "random: Not enough entropy pool available for " "secure operations"); return 0; #else /* __linux__ */ /* TODO: could do similar checks on non-Linux platforms */ return 1; #endif /* __linux__ */ } void random_mark_pool_ready(void) { own_pool_ready++; wpa_printf(MSG_DEBUG, "random: Mark internal entropy pool to be " "ready (count=%u/%u)", own_pool_ready, MIN_READY_MARK); random_write_entropy(); } #ifdef __linux__ static void random_close_fd(void) { if (random_fd >= 0) { eloop_unregister_read_sock(random_fd); close(random_fd); random_fd = -1; } } static void random_read_fd(int sock, void *eloop_ctx, void *sock_ctx) { ssize_t res; if (dummy_key_avail == sizeof(dummy_key)) { random_close_fd(); return; } res = read(sock, dummy_key + dummy_key_avail, sizeof(dummy_key) - dummy_key_avail); if (res < 0) { wpa_printf(MSG_ERROR, "random: Cannot read from /dev/random: " "%s", strerror(errno)); return; } wpa_printf(MSG_DEBUG, "random: Got %u/%u bytes from /dev/random", (unsigned) res, (unsigned) (sizeof(dummy_key) - dummy_key_avail)); dummy_key_avail += res; if (dummy_key_avail == sizeof(dummy_key)) { random_close_fd(); if (own_pool_ready < MIN_READY_MARK) own_pool_ready = MIN_READY_MARK; random_write_entropy(); } } #endif /* __linux__ */ static void random_read_entropy(void) { char *buf; size_t len; if (!random_entropy_file) return; buf = os_readfile(random_entropy_file, &len); if (buf == NULL) return; /* entropy file not yet available */ if (len != 1 + RANDOM_ENTROPY_SIZE) { wpa_printf(MSG_DEBUG, "random: Invalid entropy file %s", random_entropy_file); os_free(buf); return; } own_pool_ready = (u8) buf[0]; random_add_randomness(buf + 1, RANDOM_ENTROPY_SIZE); random_entropy_file_read = 1; os_free(buf); wpa_printf(MSG_DEBUG, "random: Added entropy from %s " "(own_pool_ready=%u)", random_entropy_file, own_pool_ready); } static void random_write_entropy(void) { char buf[RANDOM_ENTROPY_SIZE]; FILE *f; u8 opr; int fail = 0; if (!random_entropy_file) return; if (random_get_bytes(buf, RANDOM_ENTROPY_SIZE) < 0) return; f = fopen(random_entropy_file, "wb"); if (f == NULL) { wpa_printf(MSG_ERROR, "random: Could not open entropy file %s " "for writing", random_entropy_file); return; } opr = own_pool_ready > 0xff ? 0xff : own_pool_ready; if (fwrite(&opr, 1, 1, f) != 1 || fwrite(buf, RANDOM_ENTROPY_SIZE, 1, f) != 1) fail = 1; fclose(f); if (fail) { wpa_printf(MSG_ERROR, "random: Could not write entropy data " "to %s", random_entropy_file); return; } wpa_printf(MSG_DEBUG, "random: Updated entropy file %s " "(own_pool_ready=%u)", random_entropy_file, own_pool_ready); } void random_init(const char *entropy_file) { os_free(random_entropy_file); if (entropy_file) random_entropy_file = os_strdup(entropy_file); else random_entropy_file = NULL; random_read_entropy(); #ifdef __linux__ if (random_fd >= 0) return; random_fd = open("/dev/random", O_RDONLY | O_NONBLOCK); if (random_fd < 0) { #ifndef CONFIG_NO_STDOUT_DEBUG int error = errno; perror("open(/dev/random)"); wpa_printf(MSG_ERROR, "random: Cannot open /dev/random: %s", strerror(error)); #endif /* CONFIG_NO_STDOUT_DEBUG */ return; } wpa_printf(MSG_DEBUG, "random: Trying to read entropy from " "/dev/random"); eloop_register_read_sock(random_fd, random_read_fd, NULL, NULL); #endif /* __linux__ */ random_write_entropy(); } void random_deinit(void) { #ifdef __linux__ random_close_fd(); #endif /* __linux__ */ random_write_entropy(); os_free(random_entropy_file); random_entropy_file = NULL; } wpa-2.1/src/crypto/random.h000066400000000000000000000015271227414666700157270ustar00rootroot00000000000000/* * Random number generator * Copyright (c) 2010-2011, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef RANDOM_H #define RANDOM_H #ifdef CONFIG_NO_RANDOM_POOL #define random_init(e) do { } while (0) #define random_deinit() do { } while (0) #define random_add_randomness(b, l) do { } while (0) #define random_get_bytes(b, l) os_get_random((b), (l)) #define random_pool_ready() 1 #define random_mark_pool_ready() do { } while (0) #else /* CONFIG_NO_RANDOM_POOL */ void random_init(const char *entropy_file); void random_deinit(void); void random_add_randomness(const void *buf, size_t len); int random_get_bytes(void *buf, size_t len); int random_pool_ready(void); void random_mark_pool_ready(void); #endif /* CONFIG_NO_RANDOM_POOL */ #endif /* RANDOM_H */ wpa-2.1/src/crypto/rc4.c000066400000000000000000000017611227414666700151320ustar00rootroot00000000000000/* * RC4 stream cipher * Copyright (c) 2002-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.h" #define S_SWAP(a,b) do { u8 t = S[a]; S[a] = S[b]; S[b] = t; } while(0) int rc4_skip(const u8 *key, size_t keylen, size_t skip, u8 *data, size_t data_len) { u32 i, j, k; u8 S[256], *pos; size_t kpos; /* Setup RC4 state */ for (i = 0; i < 256; i++) S[i] = i; j = 0; kpos = 0; for (i = 0; i < 256; i++) { j = (j + S[i] + key[kpos]) & 0xff; kpos++; if (kpos >= keylen) kpos = 0; S_SWAP(i, j); } /* Skip the start of the stream */ i = j = 0; for (k = 0; k < skip; k++) { i = (i + 1) & 0xff; j = (j + S[i]) & 0xff; S_SWAP(i, j); } /* Apply RC4 to data */ pos = data; for (k = 0; k < data_len; k++) { i = (i + 1) & 0xff; j = (j + S[i]) & 0xff; S_SWAP(i, j); *pos++ ^= S[(S[i] + S[j]) & 0xff]; } return 0; } wpa-2.1/src/crypto/sha1-internal.c000066400000000000000000000215161227414666700171100ustar00rootroot00000000000000/* * SHA1 hash implementation and interface functions * Copyright (c) 2003-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 "sha1.h" #include "sha1_i.h" #include "md5.h" #include "crypto.h" typedef struct SHA1Context SHA1_CTX; void SHA1Transform(u32 state[5], const unsigned char buffer[64]); /** * sha1_vector - SHA-1 hash for data vector * @num_elem: Number of elements in the data vector * @addr: Pointers to the data areas * @len: Lengths of the data blocks * @mac: Buffer for the hash * Returns: 0 on success, -1 of failure */ int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { SHA1_CTX ctx; size_t i; SHA1Init(&ctx); for (i = 0; i < num_elem; i++) SHA1Update(&ctx, addr[i], len[i]); SHA1Final(mac, &ctx); return 0; } /* ===== start - public domain SHA1 implementation ===== */ /* SHA-1 in C By Steve Reid 100% Public Domain ----------------- Modified 7/98 By James H. Brown Still 100% Public Domain Corrected a problem which generated improper hash values on 16 bit machines Routine SHA1Update changed from void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned int len) to void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned long len) The 'len' parameter was declared an int which works fine on 32 bit machines. However, on 16 bit machines an int is too small for the shifts being done against it. This caused the hash function to generate incorrect values if len was greater than 8191 (8K - 1) due to the 'len << 3' on line 3 of SHA1Update(). Since the file IO in main() reads 16K at a time, any file 8K or larger would be guaranteed to generate the wrong hash (e.g. Test Vector #3, a million "a"s). I also changed the declaration of variables i & j in SHA1Update to unsigned long from unsigned int for the same reason. These changes should make no difference to any 32 bit implementations since an int and a long are the same size in those environments. -- I also corrected a few compiler warnings generated by Borland C. 1. Added #include for exit() prototype 2. Removed unused variable 'j' in SHA1Final 3. Changed exit(0) to return(0) at end of main. ALL changes I made can be located by searching for comments containing 'JHB' ----------------- Modified 8/98 By Steve Reid Still 100% public domain 1- Removed #include and used return() instead of exit() 2- Fixed overwriting of finalcount in SHA1Final() (discovered by Chris Hall) 3- Changed email address from steve@edmweb.com to sreid@sea-to-sky.net ----------------- Modified 4/01 By Saul Kravitz Still 100% PD Modified to run on Compaq Alpha hardware. ----------------- Modified 4/01 By Jouni Malinen Minor changes to match the coding style used in Dynamics. Modified September 24, 2004 By Jouni Malinen Fixed alignment issue in SHA1Transform when SHA1HANDSOFF is defined. */ /* Test Vectors (from FIPS PUB 180-1) "abc" A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 A million repetitions of "a" 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F */ #define SHA1HANDSOFF #define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) /* blk0() and blk() perform the initial expand. */ /* I got the idea of expanding during the round function from SSLeay */ #ifndef WORDS_BIGENDIAN #define blk0(i) (block->l[i] = (rol(block->l[i], 24) & 0xFF00FF00) | \ (rol(block->l[i], 8) & 0x00FF00FF)) #else #define blk0(i) block->l[i] #endif #define blk(i) (block->l[i & 15] = rol(block->l[(i + 13) & 15] ^ \ block->l[(i + 8) & 15] ^ block->l[(i + 2) & 15] ^ block->l[i & 15], 1)) /* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ #define R0(v,w,x,y,z,i) \ z += ((w & (x ^ y)) ^ y) + blk0(i) + 0x5A827999 + rol(v, 5); \ w = rol(w, 30); #define R1(v,w,x,y,z,i) \ z += ((w & (x ^ y)) ^ y) + blk(i) + 0x5A827999 + rol(v, 5); \ w = rol(w, 30); #define R2(v,w,x,y,z,i) \ z += (w ^ x ^ y) + blk(i) + 0x6ED9EBA1 + rol(v, 5); w = rol(w, 30); #define R3(v,w,x,y,z,i) \ z += (((w | x) & y) | (w & x)) + blk(i) + 0x8F1BBCDC + rol(v, 5); \ w = rol(w, 30); #define R4(v,w,x,y,z,i) \ z += (w ^ x ^ y) + blk(i) + 0xCA62C1D6 + rol(v, 5); \ w=rol(w, 30); #ifdef VERBOSE /* SAK */ void SHAPrintContext(SHA1_CTX *context, char *msg) { printf("%s (%d,%d) %x %x %x %x %x\n", msg, context->count[0], context->count[1], context->state[0], context->state[1], context->state[2], context->state[3], context->state[4]); } #endif /* Hash a single 512-bit block. This is the core of the algorithm. */ void SHA1Transform(u32 state[5], const unsigned char buffer[64]) { u32 a, b, c, d, e; typedef union { unsigned char c[64]; u32 l[16]; } CHAR64LONG16; CHAR64LONG16* block; #ifdef SHA1HANDSOFF CHAR64LONG16 workspace; block = &workspace; os_memcpy(block, buffer, 64); #else block = (CHAR64LONG16 *) buffer; #endif /* Copy context->state[] to working vars */ a = state[0]; b = state[1]; c = state[2]; d = state[3]; e = state[4]; /* 4 rounds of 20 operations each. Loop unrolled. */ R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); /* Add the working vars back into context.state[] */ state[0] += a; state[1] += b; state[2] += c; state[3] += d; state[4] += e; /* Wipe variables */ a = b = c = d = e = 0; #ifdef SHA1HANDSOFF os_memset(block, 0, 64); #endif } /* SHA1Init - Initialize new context */ void SHA1Init(SHA1_CTX* context) { /* SHA1 initialization constants */ context->state[0] = 0x67452301; context->state[1] = 0xEFCDAB89; context->state[2] = 0x98BADCFE; context->state[3] = 0x10325476; context->state[4] = 0xC3D2E1F0; context->count[0] = context->count[1] = 0; } /* Run your data through this. */ void SHA1Update(SHA1_CTX* context, const void *_data, u32 len) { u32 i, j; const unsigned char *data = _data; #ifdef VERBOSE SHAPrintContext(context, "before"); #endif j = (context->count[0] >> 3) & 63; if ((context->count[0] += len << 3) < (len << 3)) context->count[1]++; context->count[1] += (len >> 29); if ((j + len) > 63) { os_memcpy(&context->buffer[j], data, (i = 64-j)); SHA1Transform(context->state, context->buffer); for ( ; i + 63 < len; i += 64) { SHA1Transform(context->state, &data[i]); } j = 0; } else i = 0; os_memcpy(&context->buffer[j], &data[i], len - i); #ifdef VERBOSE SHAPrintContext(context, "after "); #endif } /* Add padding and return the message digest. */ void SHA1Final(unsigned char digest[20], SHA1_CTX* context) { u32 i; unsigned char finalcount[8]; for (i = 0; i < 8; i++) { finalcount[i] = (unsigned char) ((context->count[(i >= 4 ? 0 : 1)] >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */ } SHA1Update(context, (unsigned char *) "\200", 1); while ((context->count[0] & 504) != 448) { SHA1Update(context, (unsigned char *) "\0", 1); } SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */ for (i = 0; i < 20; i++) { digest[i] = (unsigned char) ((context->state[i >> 2] >> ((3 - (i & 3)) * 8)) & 255); } /* Wipe variables */ i = 0; os_memset(context->buffer, 0, 64); os_memset(context->state, 0, 20); os_memset(context->count, 0, 8); os_memset(finalcount, 0, 8); } /* ===== end - public domain SHA1 implementation ===== */ wpa-2.1/src/crypto/sha1-pbkdf2.c000066400000000000000000000045311227414666700164420ustar00rootroot00000000000000/* * SHA1-based key derivation function (PBKDF2) for IEEE 802.11i * Copyright (c) 2003-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 "sha1.h" static int pbkdf2_sha1_f(const char *passphrase, const u8 *ssid, size_t ssid_len, int iterations, unsigned int count, u8 *digest) { unsigned char tmp[SHA1_MAC_LEN], tmp2[SHA1_MAC_LEN]; int i, j; unsigned char count_buf[4]; const u8 *addr[2]; size_t len[2]; size_t passphrase_len = os_strlen(passphrase); addr[0] = ssid; len[0] = ssid_len; addr[1] = count_buf; len[1] = 4; /* F(P, S, c, i) = U1 xor U2 xor ... Uc * U1 = PRF(P, S || i) * U2 = PRF(P, U1) * Uc = PRF(P, Uc-1) */ count_buf[0] = (count >> 24) & 0xff; count_buf[1] = (count >> 16) & 0xff; count_buf[2] = (count >> 8) & 0xff; count_buf[3] = count & 0xff; if (hmac_sha1_vector((u8 *) passphrase, passphrase_len, 2, addr, len, tmp)) return -1; os_memcpy(digest, tmp, SHA1_MAC_LEN); for (i = 1; i < iterations; i++) { if (hmac_sha1((u8 *) passphrase, passphrase_len, tmp, SHA1_MAC_LEN, tmp2)) return -1; os_memcpy(tmp, tmp2, SHA1_MAC_LEN); for (j = 0; j < SHA1_MAC_LEN; j++) digest[j] ^= tmp2[j]; } return 0; } /** * pbkdf2_sha1 - SHA1-based key derivation function (PBKDF2) for IEEE 802.11i * @passphrase: ASCII passphrase * @ssid: SSID * @ssid_len: SSID length in bytes * @iterations: Number of iterations to run * @buf: Buffer for the generated key * @buflen: Length of the buffer in bytes * Returns: 0 on success, -1 of failure * * This function is used to derive PSK for WPA-PSK. For this protocol, * iterations is set to 4096 and buflen to 32. This function is described in * IEEE Std 802.11-2004, Clause H.4. The main construction is from PKCS#5 v2.0. */ int pbkdf2_sha1(const char *passphrase, const u8 *ssid, size_t ssid_len, int iterations, u8 *buf, size_t buflen) { unsigned int count = 0; unsigned char *pos = buf; size_t left = buflen, plen; unsigned char digest[SHA1_MAC_LEN]; while (left > 0) { count++; if (pbkdf2_sha1_f(passphrase, ssid, ssid_len, iterations, count, digest)) return -1; plen = left > SHA1_MAC_LEN ? SHA1_MAC_LEN : left; os_memcpy(pos, digest, plen); pos += plen; left -= plen; } return 0; } wpa-2.1/src/crypto/sha1-prf.c000066400000000000000000000030571227414666700160630ustar00rootroot00000000000000/* * SHA1-based PRF * Copyright (c) 2003-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 "sha1.h" #include "crypto.h" /** * sha1_prf - SHA1-based Pseudo-Random Function (PRF) (IEEE 802.11i, 8.5.1.1) * @key: Key for PRF * @key_len: Length of the key in bytes * @label: A unique label for each purpose of the PRF * @data: Extra data to bind into the key * @data_len: Length of the data * @buf: Buffer for the generated pseudo-random key * @buf_len: Number of bytes of key to generate * Returns: 0 on success, -1 of failure * * This function is used to derive new, cryptographically separate keys from a * given key (e.g., PMK in IEEE 802.11i). */ int sha1_prf(const u8 *key, size_t key_len, const char *label, const u8 *data, size_t data_len, u8 *buf, size_t buf_len) { u8 counter = 0; size_t pos, plen; u8 hash[SHA1_MAC_LEN]; size_t label_len = os_strlen(label) + 1; const unsigned char *addr[3]; size_t len[3]; addr[0] = (u8 *) label; len[0] = label_len; addr[1] = data; len[1] = data_len; addr[2] = &counter; len[2] = 1; pos = 0; while (pos < buf_len) { plen = buf_len - pos; if (plen >= SHA1_MAC_LEN) { if (hmac_sha1_vector(key, key_len, 3, addr, len, &buf[pos])) return -1; pos += SHA1_MAC_LEN; } else { if (hmac_sha1_vector(key, key_len, 3, addr, len, hash)) return -1; os_memcpy(&buf[pos], hash, plen); break; } counter++; } return 0; } wpa-2.1/src/crypto/sha1-tlsprf.c000066400000000000000000000051111227414666700165770ustar00rootroot00000000000000/* * TLS PRF (SHA1 + MD5) * Copyright (c) 2003-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 "sha1.h" #include "md5.h" /** * tls_prf_sha1_md5 - Pseudo-Random Function for TLS (TLS-PRF, RFC 2246) * @secret: Key for PRF * @secret_len: Length of the key in bytes * @label: A unique label for each purpose of the PRF * @seed: Seed value to bind into the key * @seed_len: Length of the seed * @out: Buffer for the generated pseudo-random key * @outlen: Number of bytes of key to generate * Returns: 0 on success, -1 on failure. * * This function is used to derive new, cryptographically separate keys from a * given key in TLS. This PRF is defined in RFC 2246, Chapter 5. */ int tls_prf_sha1_md5(const u8 *secret, size_t secret_len, const char *label, const u8 *seed, size_t seed_len, u8 *out, size_t outlen) { size_t L_S1, L_S2, i; const u8 *S1, *S2; u8 A_MD5[MD5_MAC_LEN], A_SHA1[SHA1_MAC_LEN]; u8 P_MD5[MD5_MAC_LEN], P_SHA1[SHA1_MAC_LEN]; int MD5_pos, SHA1_pos; const u8 *MD5_addr[3]; size_t MD5_len[3]; const unsigned char *SHA1_addr[3]; size_t SHA1_len[3]; if (secret_len & 1) return -1; MD5_addr[0] = A_MD5; MD5_len[0] = MD5_MAC_LEN; MD5_addr[1] = (unsigned char *) label; MD5_len[1] = os_strlen(label); MD5_addr[2] = seed; MD5_len[2] = seed_len; SHA1_addr[0] = A_SHA1; SHA1_len[0] = SHA1_MAC_LEN; SHA1_addr[1] = (unsigned char *) label; SHA1_len[1] = os_strlen(label); SHA1_addr[2] = seed; SHA1_len[2] = seed_len; /* RFC 2246, Chapter 5 * A(0) = seed, A(i) = HMAC(secret, A(i-1)) * P_hash = HMAC(secret, A(1) + seed) + HMAC(secret, A(2) + seed) + .. * PRF = P_MD5(S1, label + seed) XOR P_SHA-1(S2, label + seed) */ L_S1 = L_S2 = (secret_len + 1) / 2; S1 = secret; S2 = secret + L_S1; if (secret_len & 1) { /* The last byte of S1 will be shared with S2 */ S2--; } hmac_md5_vector(S1, L_S1, 2, &MD5_addr[1], &MD5_len[1], A_MD5); hmac_sha1_vector(S2, L_S2, 2, &SHA1_addr[1], &SHA1_len[1], A_SHA1); MD5_pos = MD5_MAC_LEN; SHA1_pos = SHA1_MAC_LEN; for (i = 0; i < outlen; i++) { if (MD5_pos == MD5_MAC_LEN) { hmac_md5_vector(S1, L_S1, 3, MD5_addr, MD5_len, P_MD5); MD5_pos = 0; hmac_md5(S1, L_S1, A_MD5, MD5_MAC_LEN, A_MD5); } if (SHA1_pos == SHA1_MAC_LEN) { hmac_sha1_vector(S2, L_S2, 3, SHA1_addr, SHA1_len, P_SHA1); SHA1_pos = 0; hmac_sha1(S2, L_S2, A_SHA1, SHA1_MAC_LEN, A_SHA1); } out[i] = P_MD5[MD5_pos] ^ P_SHA1[SHA1_pos]; MD5_pos++; SHA1_pos++; } return 0; } wpa-2.1/src/crypto/sha1-tprf.c000066400000000000000000000033261227414666700162460ustar00rootroot00000000000000/* * SHA1 T-PRF for EAP-FAST * Copyright (c) 2003-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 "sha1.h" #include "crypto.h" /** * sha1_t_prf - EAP-FAST Pseudo-Random Function (T-PRF) * @key: Key for PRF * @key_len: Length of the key in bytes * @label: A unique label for each purpose of the PRF * @seed: Seed value to bind into the key * @seed_len: Length of the seed * @buf: Buffer for the generated pseudo-random key * @buf_len: Number of bytes of key to generate * Returns: 0 on success, -1 of failure * * This function is used to derive new, cryptographically separate keys from a * given key for EAP-FAST. T-PRF is defined in RFC 4851, Section 5.5. */ int sha1_t_prf(const u8 *key, size_t key_len, const char *label, const u8 *seed, size_t seed_len, u8 *buf, size_t buf_len) { unsigned char counter = 0; size_t pos, plen; u8 hash[SHA1_MAC_LEN]; size_t label_len = os_strlen(label); u8 output_len[2]; const unsigned char *addr[5]; size_t len[5]; addr[0] = hash; len[0] = 0; addr[1] = (unsigned char *) label; len[1] = label_len + 1; addr[2] = seed; len[2] = seed_len; addr[3] = output_len; len[3] = 2; addr[4] = &counter; len[4] = 1; output_len[0] = (buf_len >> 8) & 0xff; output_len[1] = buf_len & 0xff; pos = 0; while (pos < buf_len) { counter++; plen = buf_len - pos; if (hmac_sha1_vector(key, key_len, 5, addr, len, hash)) return -1; if (plen >= SHA1_MAC_LEN) { os_memcpy(&buf[pos], hash, SHA1_MAC_LEN); pos += SHA1_MAC_LEN; } else { os_memcpy(&buf[pos], hash, plen); break; } len[0] = SHA1_MAC_LEN; } return 0; } wpa-2.1/src/crypto/sha1.c000066400000000000000000000051411227414666700152720ustar00rootroot00000000000000/* * SHA1 hash implementation and interface functions * Copyright (c) 2003-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 "sha1.h" #include "crypto.h" /** * hmac_sha1_vector - HMAC-SHA1 over data vector (RFC 2104) * @key: Key for HMAC operations * @key_len: Length of the key in bytes * @num_elem: Number of elements in the data vector * @addr: Pointers to the data areas * @len: Lengths of the data blocks * @mac: Buffer for the hash (20 bytes) * Returns: 0 on success, -1 on failure */ int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { unsigned char k_pad[64]; /* padding - key XORd with ipad/opad */ unsigned char tk[20]; const u8 *_addr[6]; size_t _len[6], i; if (num_elem > 5) { /* * Fixed limit on the number of fragments to avoid having to * allocate memory (which could fail). */ return -1; } /* if key is longer than 64 bytes reset it to key = SHA1(key) */ if (key_len > 64) { if (sha1_vector(1, &key, &key_len, tk)) return -1; key = tk; key_len = 20; } /* the HMAC_SHA1 transform looks like: * * SHA1(K XOR opad, SHA1(K XOR ipad, text)) * * where K is an n byte key * ipad is the byte 0x36 repeated 64 times * opad is the byte 0x5c repeated 64 times * and text is the data being protected */ /* start out by storing key in ipad */ os_memset(k_pad, 0, sizeof(k_pad)); os_memcpy(k_pad, key, key_len); /* XOR key with ipad values */ for (i = 0; i < 64; i++) k_pad[i] ^= 0x36; /* perform inner SHA1 */ _addr[0] = k_pad; _len[0] = 64; for (i = 0; i < num_elem; i++) { _addr[i + 1] = addr[i]; _len[i + 1] = len[i]; } if (sha1_vector(1 + num_elem, _addr, _len, mac)) return -1; os_memset(k_pad, 0, sizeof(k_pad)); os_memcpy(k_pad, key, key_len); /* XOR key with opad values */ for (i = 0; i < 64; i++) k_pad[i] ^= 0x5c; /* perform outer SHA1 */ _addr[0] = k_pad; _len[0] = 64; _addr[1] = mac; _len[1] = SHA1_MAC_LEN; return sha1_vector(2, _addr, _len, mac); } /** * hmac_sha1 - HMAC-SHA1 over data buffer (RFC 2104) * @key: Key for HMAC operations * @key_len: Length of the key in bytes * @data: Pointers to the data area * @data_len: Length of the data area * @mac: Buffer for the hash (20 bytes) * Returns: 0 on success, -1 of failure */ int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len, u8 *mac) { return hmac_sha1_vector(key, key_len, 1, &data, &data_len, mac); } wpa-2.1/src/crypto/sha1.h000066400000000000000000000020321227414666700152730ustar00rootroot00000000000000/* * SHA1 hash implementation and interface functions * Copyright (c) 2003-2009, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef SHA1_H #define SHA1_H #define SHA1_MAC_LEN 20 int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac); int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len, u8 *mac); int sha1_prf(const u8 *key, size_t key_len, const char *label, const u8 *data, size_t data_len, u8 *buf, size_t buf_len); int sha1_t_prf(const u8 *key, size_t key_len, const char *label, const u8 *seed, size_t seed_len, u8 *buf, size_t buf_len); int __must_check tls_prf_sha1_md5(const u8 *secret, size_t secret_len, const char *label, const u8 *seed, size_t seed_len, u8 *out, size_t outlen); int pbkdf2_sha1(const char *passphrase, const u8 *ssid, size_t ssid_len, int iterations, u8 *buf, size_t buflen); #endif /* SHA1_H */ wpa-2.1/src/crypto/sha1_i.h000066400000000000000000000011161227414666700156050ustar00rootroot00000000000000/* * SHA1 internal definitions * Copyright (c) 2003-2005, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef SHA1_I_H #define SHA1_I_H struct SHA1Context { u32 state[5]; u32 count[2]; unsigned char buffer[64]; }; void SHA1Init(struct SHA1Context *context); void SHA1Update(struct SHA1Context *context, const void *data, u32 len); void SHA1Final(unsigned char digest[20], struct SHA1Context *context); void SHA1Transform(u32 state[5], const unsigned char buffer[64]); #endif /* SHA1_I_H */ wpa-2.1/src/crypto/sha256-internal.c000066400000000000000000000137461227414666700172720ustar00rootroot00000000000000/* * SHA-256 hash implementation and interface functions * Copyright (c) 2003-2011, 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 "sha256.h" #include "sha256_i.h" #include "crypto.h" /** * sha256_vector - SHA256 hash for data vector * @num_elem: Number of elements in the data vector * @addr: Pointers to the data areas * @len: Lengths of the data blocks * @mac: Buffer for the hash * Returns: 0 on success, -1 of failure */ int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { struct sha256_state ctx; size_t i; sha256_init(&ctx); for (i = 0; i < num_elem; i++) if (sha256_process(&ctx, addr[i], len[i])) return -1; if (sha256_done(&ctx, mac)) return -1; return 0; } /* ===== start - public domain SHA256 implementation ===== */ /* This is based on SHA256 implementation in LibTomCrypt that was released into * public domain by Tom St Denis. */ /* the K array */ static const unsigned long K[64] = { 0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL, 0x3956c25bUL, 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL, 0xd807aa98UL, 0x12835b01UL, 0x243185beUL, 0x550c7dc3UL, 0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL, 0xc19bf174UL, 0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL, 0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL, 0x983e5152UL, 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL, 0xc6e00bf3UL, 0xd5a79147UL, 0x06ca6351UL, 0x14292967UL, 0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL, 0x53380d13UL, 0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL, 0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL, 0xd192e819UL, 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL, 0x19a4c116UL, 0x1e376c08UL, 0x2748774cUL, 0x34b0bcb5UL, 0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL, 0x682e6ff3UL, 0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL, 0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL }; /* Various logical functions */ #define RORc(x, y) \ ( ((((unsigned long) (x) & 0xFFFFFFFFUL) >> (unsigned long) ((y) & 31)) | \ ((unsigned long) (x) << (unsigned long) (32 - ((y) & 31)))) & 0xFFFFFFFFUL) #define Ch(x,y,z) (z ^ (x & (y ^ z))) #define Maj(x,y,z) (((x | y) & z) | (x & y)) #define S(x, n) RORc((x), (n)) #define R(x, n) (((x)&0xFFFFFFFFUL)>>(n)) #define Sigma0(x) (S(x, 2) ^ S(x, 13) ^ S(x, 22)) #define Sigma1(x) (S(x, 6) ^ S(x, 11) ^ S(x, 25)) #define Gamma0(x) (S(x, 7) ^ S(x, 18) ^ R(x, 3)) #define Gamma1(x) (S(x, 17) ^ S(x, 19) ^ R(x, 10)) #ifndef MIN #define MIN(x, y) (((x) < (y)) ? (x) : (y)) #endif /* compress 512-bits */ static int sha256_compress(struct sha256_state *md, unsigned char *buf) { u32 S[8], W[64], t0, t1; u32 t; int i; /* copy state into S */ for (i = 0; i < 8; i++) { S[i] = md->state[i]; } /* copy the state into 512-bits into W[0..15] */ for (i = 0; i < 16; i++) W[i] = WPA_GET_BE32(buf + (4 * i)); /* fill W[16..63] */ for (i = 16; i < 64; i++) { W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + W[i - 16]; } /* Compress */ #define RND(a,b,c,d,e,f,g,h,i) \ t0 = h + Sigma1(e) + Ch(e, f, g) + K[i] + W[i]; \ t1 = Sigma0(a) + Maj(a, b, c); \ d += t0; \ h = t0 + t1; for (i = 0; i < 64; ++i) { RND(S[0], S[1], S[2], S[3], S[4], S[5], S[6], S[7], i); t = S[7]; S[7] = S[6]; S[6] = S[5]; S[5] = S[4]; S[4] = S[3]; S[3] = S[2]; S[2] = S[1]; S[1] = S[0]; S[0] = t; } /* feedback */ for (i = 0; i < 8; i++) { md->state[i] = md->state[i] + S[i]; } return 0; } /* Initialize the hash state */ void sha256_init(struct sha256_state *md) { md->curlen = 0; md->length = 0; md->state[0] = 0x6A09E667UL; md->state[1] = 0xBB67AE85UL; md->state[2] = 0x3C6EF372UL; md->state[3] = 0xA54FF53AUL; md->state[4] = 0x510E527FUL; md->state[5] = 0x9B05688CUL; md->state[6] = 0x1F83D9ABUL; md->state[7] = 0x5BE0CD19UL; } /** Process a block of memory though the hash @param md The hash state @param in The data to hash @param inlen The length of the data (octets) @return CRYPT_OK if successful */ int sha256_process(struct sha256_state *md, const unsigned char *in, unsigned long inlen) { unsigned long n; if (md->curlen >= sizeof(md->buf)) return -1; while (inlen > 0) { if (md->curlen == 0 && inlen >= SHA256_BLOCK_SIZE) { if (sha256_compress(md, (unsigned char *) in) < 0) return -1; md->length += SHA256_BLOCK_SIZE * 8; in += SHA256_BLOCK_SIZE; inlen -= SHA256_BLOCK_SIZE; } else { n = MIN(inlen, (SHA256_BLOCK_SIZE - md->curlen)); os_memcpy(md->buf + md->curlen, in, n); md->curlen += n; in += n; inlen -= n; if (md->curlen == SHA256_BLOCK_SIZE) { if (sha256_compress(md, md->buf) < 0) return -1; md->length += 8 * SHA256_BLOCK_SIZE; md->curlen = 0; } } } return 0; } /** Terminate the hash to get the digest @param md The hash state @param out [out] The destination of the hash (32 bytes) @return CRYPT_OK if successful */ int sha256_done(struct sha256_state *md, unsigned char *out) { int i; if (md->curlen >= sizeof(md->buf)) return -1; /* increase the length of the message */ md->length += md->curlen * 8; /* append the '1' bit */ md->buf[md->curlen++] = (unsigned char) 0x80; /* if the length is currently above 56 bytes we append zeros * then compress. Then we can fall back to padding zeros and length * encoding like normal. */ if (md->curlen > 56) { while (md->curlen < SHA256_BLOCK_SIZE) { md->buf[md->curlen++] = (unsigned char) 0; } sha256_compress(md, md->buf); md->curlen = 0; } /* pad up to 56 bytes of zeroes */ while (md->curlen < 56) { md->buf[md->curlen++] = (unsigned char) 0; } /* store length */ WPA_PUT_BE64(md->buf + 56, md->length); sha256_compress(md, md->buf); /* copy output */ for (i = 0; i < 8; i++) WPA_PUT_BE32(out + (4 * i), md->state[i]); return 0; } /* ===== end - public domain SHA256 implementation ===== */ wpa-2.1/src/crypto/sha256-prf.c000066400000000000000000000052141227414666700162340ustar00rootroot00000000000000/* * SHA256-based PRF (IEEE 802.11r) * Copyright (c) 2003-2013, 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 "sha256.h" #include "crypto.h" /** * sha256_prf - SHA256-based Pseudo-Random Function (IEEE 802.11r, 8.5.1.5.2) * @key: Key for PRF * @key_len: Length of the key in bytes * @label: A unique label for each purpose of the PRF * @data: Extra data to bind into the key * @data_len: Length of the data * @buf: Buffer for the generated pseudo-random key * @buf_len: Number of bytes of key to generate * * This function is used to derive new, cryptographically separate keys from a * given key. */ void sha256_prf(const u8 *key, size_t key_len, const char *label, const u8 *data, size_t data_len, u8 *buf, size_t buf_len) { sha256_prf_bits(key, key_len, label, data, data_len, buf, buf_len * 8); } /** * sha256_prf_bits - IEEE Std 802.11-2012, 11.6.1.7.2 Key derivation function * @key: Key for KDF * @key_len: Length of the key in bytes * @label: A unique label for each purpose of the PRF * @data: Extra data to bind into the key * @data_len: Length of the data * @buf: Buffer for the generated pseudo-random key * @buf_len: Number of bits of key to generate * * This function is used to derive new, cryptographically separate keys from a * given key. If the requested buf_len is not divisible by eight, the least * significant 1-7 bits of the last octet in the output are not part of the * requested output. */ void sha256_prf_bits(const u8 *key, size_t key_len, const char *label, const u8 *data, size_t data_len, u8 *buf, size_t buf_len_bits) { u16 counter = 1; size_t pos, plen; u8 hash[SHA256_MAC_LEN]; const u8 *addr[4]; size_t len[4]; u8 counter_le[2], length_le[2]; size_t buf_len = (buf_len_bits + 7) / 8; addr[0] = counter_le; len[0] = 2; addr[1] = (u8 *) label; len[1] = os_strlen(label); addr[2] = data; len[2] = data_len; addr[3] = length_le; len[3] = sizeof(length_le); WPA_PUT_LE16(length_le, buf_len_bits); pos = 0; while (pos < buf_len) { plen = buf_len - pos; WPA_PUT_LE16(counter_le, counter); if (plen >= SHA256_MAC_LEN) { hmac_sha256_vector(key, key_len, 4, addr, len, &buf[pos]); pos += SHA256_MAC_LEN; } else { hmac_sha256_vector(key, key_len, 4, addr, len, hash); os_memcpy(&buf[pos], hash, plen); pos += plen; break; } counter++; } /* * Mask out unused bits in the last octet if it does not use all the * bits. */ if (buf_len_bits % 8) { u8 mask = 0xff << (8 - buf_len_bits % 8); buf[pos - 1] &= mask; } } wpa-2.1/src/crypto/sha256-tlsprf.c000066400000000000000000000033651227414666700167640ustar00rootroot00000000000000/* * TLS PRF P_SHA256 * Copyright (c) 2011, 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 "sha256.h" /** * tls_prf_sha256 - Pseudo-Random Function for TLS v1.2 (P_SHA256, RFC 5246) * @secret: Key for PRF * @secret_len: Length of the key in bytes * @label: A unique label for each purpose of the PRF * @seed: Seed value to bind into the key * @seed_len: Length of the seed * @out: Buffer for the generated pseudo-random key * @outlen: Number of bytes of key to generate * Returns: 0 on success, -1 on failure. * * This function is used to derive new, cryptographically separate keys from a * given key in TLS. This PRF is defined in RFC 2246, Chapter 5. */ void tls_prf_sha256(const u8 *secret, size_t secret_len, const char *label, const u8 *seed, size_t seed_len, u8 *out, size_t outlen) { size_t clen; u8 A[SHA256_MAC_LEN]; u8 P[SHA256_MAC_LEN]; size_t pos; const unsigned char *addr[3]; size_t len[3]; addr[0] = A; len[0] = SHA256_MAC_LEN; addr[1] = (unsigned char *) label; len[1] = os_strlen(label); addr[2] = seed; len[2] = seed_len; /* * RFC 5246, Chapter 5 * A(0) = seed, A(i) = HMAC(secret, A(i-1)) * P_hash = HMAC(secret, A(1) + seed) + HMAC(secret, A(2) + seed) + .. * PRF(secret, label, seed) = P_SHA256(secret, label + seed) */ hmac_sha256_vector(secret, secret_len, 2, &addr[1], &len[1], A); pos = 0; while (pos < outlen) { hmac_sha256_vector(secret, secret_len, 3, addr, len, P); hmac_sha256(secret, secret_len, A, SHA256_MAC_LEN, A); clen = outlen - pos; if (clen > SHA256_MAC_LEN) clen = SHA256_MAC_LEN; os_memcpy(out + pos, P, clen); pos += clen; } } wpa-2.1/src/crypto/sha256.c000066400000000000000000000052141227414666700154470ustar00rootroot00000000000000/* * SHA-256 hash implementation and interface functions * Copyright (c) 2003-2012, 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 "sha256.h" #include "crypto.h" /** * hmac_sha256_vector - HMAC-SHA256 over data vector (RFC 2104) * @key: Key for HMAC operations * @key_len: Length of the key in bytes * @num_elem: Number of elements in the data vector * @addr: Pointers to the data areas * @len: Lengths of the data blocks * @mac: Buffer for the hash (32 bytes) * Returns: 0 on success, -1 on failure */ int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { unsigned char k_pad[64]; /* padding - key XORd with ipad/opad */ unsigned char tk[32]; const u8 *_addr[6]; size_t _len[6], i; if (num_elem > 5) { /* * Fixed limit on the number of fragments to avoid having to * allocate memory (which could fail). */ return -1; } /* if key is longer than 64 bytes reset it to key = SHA256(key) */ if (key_len > 64) { if (sha256_vector(1, &key, &key_len, tk) < 0) return -1; key = tk; key_len = 32; } /* the HMAC_SHA256 transform looks like: * * SHA256(K XOR opad, SHA256(K XOR ipad, text)) * * where K is an n byte key * ipad is the byte 0x36 repeated 64 times * opad is the byte 0x5c repeated 64 times * and text is the data being protected */ /* start out by storing key in ipad */ os_memset(k_pad, 0, sizeof(k_pad)); os_memcpy(k_pad, key, key_len); /* XOR key with ipad values */ for (i = 0; i < 64; i++) k_pad[i] ^= 0x36; /* perform inner SHA256 */ _addr[0] = k_pad; _len[0] = 64; for (i = 0; i < num_elem; i++) { _addr[i + 1] = addr[i]; _len[i + 1] = len[i]; } if (sha256_vector(1 + num_elem, _addr, _len, mac) < 0) return -1; os_memset(k_pad, 0, sizeof(k_pad)); os_memcpy(k_pad, key, key_len); /* XOR key with opad values */ for (i = 0; i < 64; i++) k_pad[i] ^= 0x5c; /* perform outer SHA256 */ _addr[0] = k_pad; _len[0] = 64; _addr[1] = mac; _len[1] = SHA256_MAC_LEN; return sha256_vector(2, _addr, _len, mac); } /** * hmac_sha256 - HMAC-SHA256 over data buffer (RFC 2104) * @key: Key for HMAC operations * @key_len: Length of the key in bytes * @data: Pointers to the data area * @data_len: Length of the data area * @mac: Buffer for the hash (32 bytes) * Returns: 0 on success, -1 on failure */ int hmac_sha256(const u8 *key, size_t key_len, const u8 *data, size_t data_len, u8 *mac) { return hmac_sha256_vector(key, key_len, 1, &data, &data_len, mac); } wpa-2.1/src/crypto/sha256.h000066400000000000000000000016701227414666700154560ustar00rootroot00000000000000/* * SHA256 hash implementation and interface functions * Copyright (c) 2003-2013, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef SHA256_H #define SHA256_H #define SHA256_MAC_LEN 32 int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac); int hmac_sha256(const u8 *key, size_t key_len, const u8 *data, size_t data_len, u8 *mac); void sha256_prf(const u8 *key, size_t key_len, const char *label, const u8 *data, size_t data_len, u8 *buf, size_t buf_len); void sha256_prf_bits(const u8 *key, size_t key_len, const char *label, const u8 *data, size_t data_len, u8 *buf, size_t buf_len_bits); void tls_prf_sha256(const u8 *secret, size_t secret_len, const char *label, const u8 *seed, size_t seed_len, u8 *out, size_t outlen); #endif /* SHA256_H */ wpa-2.1/src/crypto/sha256_i.h000066400000000000000000000011101227414666700157530ustar00rootroot00000000000000/* * SHA-256 internal definitions * Copyright (c) 2003-2011, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef SHA256_I_H #define SHA256_I_H #define SHA256_BLOCK_SIZE 64 struct sha256_state { u64 length; u32 state[8], curlen; u8 buf[SHA256_BLOCK_SIZE]; }; void sha256_init(struct sha256_state *md); int sha256_process(struct sha256_state *md, const unsigned char *in, unsigned long inlen); int sha256_done(struct sha256_state *md, unsigned char *out); #endif /* SHA256_I_H */ wpa-2.1/src/crypto/tls.h000066400000000000000000000453361227414666700152570ustar00rootroot00000000000000/* * SSL/TLS interface definition * Copyright (c) 2004-2013, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef TLS_H #define TLS_H struct tls_connection; struct tls_keys { const u8 *master_key; /* TLS master secret */ size_t master_key_len; const u8 *client_random; size_t client_random_len; const u8 *server_random; size_t server_random_len; }; enum tls_event { TLS_CERT_CHAIN_SUCCESS, TLS_CERT_CHAIN_FAILURE, TLS_PEER_CERTIFICATE, TLS_ALERT }; /* * Note: These are used as identifier with external programs and as such, the * values must not be changed. */ enum tls_fail_reason { TLS_FAIL_UNSPECIFIED = 0, TLS_FAIL_UNTRUSTED = 1, TLS_FAIL_REVOKED = 2, TLS_FAIL_NOT_YET_VALID = 3, TLS_FAIL_EXPIRED = 4, TLS_FAIL_SUBJECT_MISMATCH = 5, TLS_FAIL_ALTSUBJECT_MISMATCH = 6, TLS_FAIL_BAD_CERTIFICATE = 7, TLS_FAIL_SERVER_CHAIN_PROBE = 8, TLS_FAIL_DOMAIN_SUFFIX_MISMATCH = 9, TLS_FAIL_SERVER_USED_CLIENT_CERT = 10 }; union tls_event_data { struct { int depth; const char *subject; enum tls_fail_reason reason; const char *reason_txt; const struct wpabuf *cert; } cert_fail; struct { int depth; const char *subject; const struct wpabuf *cert; const u8 *hash; size_t hash_len; } peer_cert; struct { int is_local; const char *type; const char *description; } alert; }; struct tls_config { const char *opensc_engine_path; const char *pkcs11_engine_path; const char *pkcs11_module_path; int fips_mode; int cert_in_cb; void (*event_cb)(void *ctx, enum tls_event ev, union tls_event_data *data); void *cb_ctx; }; #define TLS_CONN_ALLOW_SIGN_RSA_MD5 BIT(0) #define TLS_CONN_DISABLE_TIME_CHECKS BIT(1) #define TLS_CONN_DISABLE_SESSION_TICKET BIT(2) #define TLS_CONN_REQUEST_OCSP BIT(3) #define TLS_CONN_REQUIRE_OCSP BIT(4) /** * struct tls_connection_params - Parameters for TLS connection * @ca_cert: File or reference name for CA X.509 certificate in PEM or DER * format * @ca_cert_blob: ca_cert as inlined data or %NULL if not used * @ca_cert_blob_len: ca_cert_blob length * @ca_path: Path to CA certificates (OpenSSL specific) * @subject_match: String to match in the subject of the peer certificate or * %NULL to allow all subjects * @altsubject_match: String to match in the alternative subject of the peer * certificate or %NULL to allow all alternative subjects * @suffix_match: String to suffix match in the dNSName or CN of the peer * certificate or %NULL to allow all domain names * @client_cert: File or reference name for client X.509 certificate in PEM or * DER format * @client_cert_blob: client_cert as inlined data or %NULL if not used * @client_cert_blob_len: client_cert_blob length * @private_key: File or reference name for client private key in PEM or DER * format (traditional format (RSA PRIVATE KEY) or PKCS#8 (PRIVATE KEY) * @private_key_blob: private_key as inlined data or %NULL if not used * @private_key_blob_len: private_key_blob length * @private_key_passwd: Passphrase for decrypted private key, %NULL if no * passphrase is used. * @dh_file: File name for DH/DSA data in PEM format, or %NULL if not used * @dh_blob: dh_file as inlined data or %NULL if not used * @dh_blob_len: dh_blob length * @engine: 1 = use engine (e.g., a smartcard) for private key operations * (this is OpenSSL specific for now) * @engine_id: engine id string (this is OpenSSL specific for now) * @ppin: pointer to the pin variable in the configuration * (this is OpenSSL specific for now) * @key_id: the private key's id when using engine (this is OpenSSL * specific for now) * @cert_id: the certificate's id when using engine * @ca_cert_id: the CA certificate's id when using engine * @flags: Parameter options (TLS_CONN_*) * @ocsp_stapling_response: DER encoded file with cached OCSP stapling response * or %NULL if OCSP is not enabled * * TLS connection parameters to be configured with tls_connection_set_params() * and tls_global_set_params(). * * Certificates and private key can be configured either as a reference name * (file path or reference to certificate store) or by providing the same data * as a pointer to the data in memory. Only one option will be used for each * field. */ struct tls_connection_params { const char *ca_cert; const u8 *ca_cert_blob; size_t ca_cert_blob_len; const char *ca_path; const char *subject_match; const char *altsubject_match; const char *suffix_match; const char *client_cert; const u8 *client_cert_blob; size_t client_cert_blob_len; const char *private_key; const u8 *private_key_blob; size_t private_key_blob_len; const char *private_key_passwd; const char *dh_file; const u8 *dh_blob; size_t dh_blob_len; /* OpenSSL specific variables */ int engine; const char *engine_id; const char *pin; const char *key_id; const char *cert_id; const char *ca_cert_id; unsigned int flags; const char *ocsp_stapling_response; }; /** * tls_init - Initialize TLS library * @conf: Configuration data for TLS library * Returns: Context data to be used as tls_ctx in calls to other functions, * or %NULL on failure. * * Called once during program startup and once for each RSN pre-authentication * session. In other words, there can be two concurrent TLS contexts. If global * library initialization is needed (i.e., one that is shared between both * authentication types), the TLS library wrapper should maintain a reference * counter and do global initialization only when moving from 0 to 1 reference. */ void * tls_init(const struct tls_config *conf); /** * tls_deinit - Deinitialize TLS library * @tls_ctx: TLS context data from tls_init() * * Called once during program shutdown and once for each RSN pre-authentication * session. If global library deinitialization is needed (i.e., one that is * shared between both authentication types), the TLS library wrapper should * maintain a reference counter and do global deinitialization only when moving * from 1 to 0 references. */ void tls_deinit(void *tls_ctx); /** * tls_get_errors - Process pending errors * @tls_ctx: TLS context data from tls_init() * Returns: Number of found error, 0 if no errors detected. * * Process all pending TLS errors. */ int tls_get_errors(void *tls_ctx); /** * tls_connection_init - Initialize a new TLS connection * @tls_ctx: TLS context data from tls_init() * Returns: Connection context data, conn for other function calls */ struct tls_connection * tls_connection_init(void *tls_ctx); /** * tls_connection_deinit - Free TLS connection data * @tls_ctx: TLS context data from tls_init() * @conn: Connection context data from tls_connection_init() * * Release all resources allocated for TLS connection. */ void tls_connection_deinit(void *tls_ctx, struct tls_connection *conn); /** * tls_connection_established - Has the TLS connection been completed? * @tls_ctx: TLS context data from tls_init() * @conn: Connection context data from tls_connection_init() * Returns: 1 if TLS connection has been completed, 0 if not. */ int tls_connection_established(void *tls_ctx, struct tls_connection *conn); /** * tls_connection_shutdown - Shutdown TLS connection * @tls_ctx: TLS context data from tls_init() * @conn: Connection context data from tls_connection_init() * Returns: 0 on success, -1 on failure * * Shutdown current TLS connection without releasing all resources. New * connection can be started by using the same conn without having to call * tls_connection_init() or setting certificates etc. again. The new * connection should try to use session resumption. */ int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn); enum { TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED = -3, TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED = -2 }; /** * tls_connection_set_params - Set TLS connection parameters * @tls_ctx: TLS context data from tls_init() * @conn: Connection context data from tls_connection_init() * @params: Connection parameters * Returns: 0 on success, -1 on failure, * TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED (-2) on possible PIN error causing * PKCS#11 engine failure, or * TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED (-3) on failure to verify the * PKCS#11 engine private key. */ int __must_check tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, const struct tls_connection_params *params); /** * tls_global_set_params - Set TLS parameters for all TLS connection * @tls_ctx: TLS context data from tls_init() * @params: Global TLS parameters * Returns: 0 on success, -1 on failure, * TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED (-2) on possible PIN error causing * PKCS#11 engine failure, or * TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED (-3) on failure to verify the * PKCS#11 engine private key. */ int __must_check tls_global_set_params( void *tls_ctx, const struct tls_connection_params *params); /** * tls_global_set_verify - Set global certificate verification options * @tls_ctx: TLS context data from tls_init() * @check_crl: 0 = do not verify CRLs, 1 = verify CRL for the user certificate, * 2 = verify CRL for all certificates * Returns: 0 on success, -1 on failure */ int __must_check tls_global_set_verify(void *tls_ctx, int check_crl); /** * tls_connection_set_verify - Set certificate verification options * @tls_ctx: TLS context data from tls_init() * @conn: Connection context data from tls_connection_init() * @verify_peer: 1 = verify peer certificate * Returns: 0 on success, -1 on failure */ int __must_check tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn, int verify_peer); /** * tls_connection_get_keys - Get master key and random data from TLS connection * @tls_ctx: TLS context data from tls_init() * @conn: Connection context data from tls_connection_init() * @keys: Structure of key/random data (filled on success) * Returns: 0 on success, -1 on failure */ int __must_check tls_connection_get_keys(void *tls_ctx, struct tls_connection *conn, struct tls_keys *keys); /** * tls_connection_prf - Use TLS-PRF to derive keying material * @tls_ctx: TLS context data from tls_init() * @conn: Connection context data from tls_connection_init() * @label: Label (e.g., description of the key) for PRF * @server_random_first: seed is 0 = client_random|server_random, * 1 = server_random|client_random * @out: Buffer for output data from TLS-PRF * @out_len: Length of the output buffer * Returns: 0 on success, -1 on failure * * This function is optional to implement if tls_connection_get_keys() provides * access to master secret and server/client random values. If these values are * not exported from the TLS library, tls_connection_prf() is required so that * further keying material can be derived from the master secret. If not * implemented, the function will still need to be defined, but it can just * return -1. Example implementation of this function is in tls_prf_sha1_md5() * when it is called with seed set to client_random|server_random (or * server_random|client_random). */ int __must_check tls_connection_prf(void *tls_ctx, struct tls_connection *conn, const char *label, int server_random_first, u8 *out, size_t out_len); /** * tls_connection_handshake - Process TLS handshake (client side) * @tls_ctx: TLS context data from tls_init() * @conn: Connection context data from tls_connection_init() * @in_data: Input data from TLS server * @appl_data: Pointer to application data pointer, or %NULL if dropped * Returns: Output data, %NULL on failure * * The caller is responsible for freeing the returned output data. If the final * handshake message includes application data, this is decrypted and * appl_data (if not %NULL) is set to point this data. The caller is * responsible for freeing appl_data. * * This function is used during TLS handshake. The first call is done with * in_data == %NULL and the library is expected to return ClientHello packet. * This packet is then send to the server and a response from server is given * to TLS library by calling this function again with in_data pointing to the * TLS message from the server. * * If the TLS handshake fails, this function may return %NULL. However, if the * TLS library has a TLS alert to send out, that should be returned as the * output data. In this case, tls_connection_get_failed() must return failure * (> 0). * * tls_connection_established() should return 1 once the TLS handshake has been * completed successfully. */ struct wpabuf * tls_connection_handshake(void *tls_ctx, struct tls_connection *conn, const struct wpabuf *in_data, struct wpabuf **appl_data); struct wpabuf * tls_connection_handshake2(void *tls_ctx, struct tls_connection *conn, const struct wpabuf *in_data, struct wpabuf **appl_data, int *more_data_needed); /** * tls_connection_server_handshake - Process TLS handshake (server side) * @tls_ctx: TLS context data from tls_init() * @conn: Connection context data from tls_connection_init() * @in_data: Input data from TLS peer * @appl_data: Pointer to application data pointer, or %NULL if dropped * Returns: Output data, %NULL on failure * * The caller is responsible for freeing the returned output data. */ struct wpabuf * tls_connection_server_handshake(void *tls_ctx, struct tls_connection *conn, const struct wpabuf *in_data, struct wpabuf **appl_data); /** * tls_connection_encrypt - Encrypt data into TLS tunnel * @tls_ctx: TLS context data from tls_init() * @conn: Connection context data from tls_connection_init() * @in_data: Plaintext data to be encrypted * Returns: Encrypted TLS data or %NULL on failure * * This function is used after TLS handshake has been completed successfully to * send data in the encrypted tunnel. The caller is responsible for freeing the * returned output data. */ struct wpabuf * tls_connection_encrypt(void *tls_ctx, struct tls_connection *conn, const struct wpabuf *in_data); /** * tls_connection_decrypt - Decrypt data from TLS tunnel * @tls_ctx: TLS context data from tls_init() * @conn: Connection context data from tls_connection_init() * @in_data: Encrypted TLS data * Returns: Decrypted TLS data or %NULL on failure * * This function is used after TLS handshake has been completed successfully to * receive data from the encrypted tunnel. The caller is responsible for * freeing the returned output data. */ struct wpabuf * tls_connection_decrypt(void *tls_ctx, struct tls_connection *conn, const struct wpabuf *in_data); struct wpabuf * tls_connection_decrypt2(void *tls_ctx, struct tls_connection *conn, const struct wpabuf *in_data, int *more_data_needed); /** * tls_connection_resumed - Was session resumption used * @tls_ctx: TLS context data from tls_init() * @conn: Connection context data from tls_connection_init() * Returns: 1 if current session used session resumption, 0 if not */ int tls_connection_resumed(void *tls_ctx, struct tls_connection *conn); enum { TLS_CIPHER_NONE, TLS_CIPHER_RC4_SHA /* 0x0005 */, TLS_CIPHER_AES128_SHA /* 0x002f */, TLS_CIPHER_RSA_DHE_AES128_SHA /* 0x0031 */, TLS_CIPHER_ANON_DH_AES128_SHA /* 0x0034 */ }; /** * tls_connection_set_cipher_list - Configure acceptable cipher suites * @tls_ctx: TLS context data from tls_init() * @conn: Connection context data from tls_connection_init() * @ciphers: Zero (TLS_CIPHER_NONE) terminated list of allowed ciphers * (TLS_CIPHER_*). * Returns: 0 on success, -1 on failure */ int __must_check tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn, u8 *ciphers); /** * tls_get_cipher - Get current cipher name * @tls_ctx: TLS context data from tls_init() * @conn: Connection context data from tls_connection_init() * @buf: Buffer for the cipher name * @buflen: buf size * Returns: 0 on success, -1 on failure * * Get the name of the currently used cipher. */ int __must_check tls_get_cipher(void *tls_ctx, struct tls_connection *conn, char *buf, size_t buflen); /** * tls_connection_enable_workaround - Enable TLS workaround options * @tls_ctx: TLS context data from tls_init() * @conn: Connection context data from tls_connection_init() * Returns: 0 on success, -1 on failure * * This function is used to enable connection-specific workaround options for * buffer SSL/TLS implementations. */ int __must_check tls_connection_enable_workaround(void *tls_ctx, struct tls_connection *conn); /** * tls_connection_client_hello_ext - Set TLS extension for ClientHello * @tls_ctx: TLS context data from tls_init() * @conn: Connection context data from tls_connection_init() * @ext_type: Extension type * @data: Extension payload (%NULL to remove extension) * @data_len: Extension payload length * Returns: 0 on success, -1 on failure */ int __must_check tls_connection_client_hello_ext(void *tls_ctx, struct tls_connection *conn, int ext_type, const u8 *data, size_t data_len); /** * tls_connection_get_failed - Get connection failure status * @tls_ctx: TLS context data from tls_init() * @conn: Connection context data from tls_connection_init() * * Returns >0 if connection has failed, 0 if not. */ int tls_connection_get_failed(void *tls_ctx, struct tls_connection *conn); /** * tls_connection_get_read_alerts - Get connection read alert status * @tls_ctx: TLS context data from tls_init() * @conn: Connection context data from tls_connection_init() * Returns: Number of times a fatal read (remote end reported error) has * happened during this connection. */ int tls_connection_get_read_alerts(void *tls_ctx, struct tls_connection *conn); /** * tls_connection_get_write_alerts - Get connection write alert status * @tls_ctx: TLS context data from tls_init() * @conn: Connection context data from tls_connection_init() * Returns: Number of times a fatal write (locally detected error) has happened * during this connection. */ int tls_connection_get_write_alerts(void *tls_ctx, struct tls_connection *conn); /** * tls_connection_get_keyblock_size - Get TLS key_block size * @tls_ctx: TLS context data from tls_init() * @conn: Connection context data from tls_connection_init() * Returns: Size of the key_block for the negotiated cipher suite or -1 on * failure */ int tls_connection_get_keyblock_size(void *tls_ctx, struct tls_connection *conn); /** * tls_capabilities - Get supported TLS capabilities * @tls_ctx: TLS context data from tls_init() * Returns: Bit field of supported TLS capabilities (TLS_CAPABILITY_*) */ unsigned int tls_capabilities(void *tls_ctx); typedef int (*tls_session_ticket_cb) (void *ctx, const u8 *ticket, size_t len, const u8 *client_random, const u8 *server_random, u8 *master_secret); int __must_check tls_connection_set_session_ticket_cb( void *tls_ctx, struct tls_connection *conn, tls_session_ticket_cb cb, void *ctx); #endif /* TLS_H */ wpa-2.1/src/crypto/tls_gnutls.c000066400000000000000000000725511227414666700166450ustar00rootroot00000000000000/* * SSL/TLS interface functions for GnuTLS * Copyright (c) 2004-2011, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "includes.h" #include #include #ifdef PKCS12_FUNCS #include #endif /* PKCS12_FUNCS */ #include "common.h" #include "tls.h" #define WPA_TLS_RANDOM_SIZE 32 #define WPA_TLS_MASTER_SIZE 48 #if LIBGNUTLS_VERSION_NUMBER < 0x010302 /* GnuTLS 1.3.2 added functions for using master secret. Older versions require * use of internal structures to get the master_secret and * {server,client}_random. */ #define GNUTLS_INTERNAL_STRUCTURE_HACK #endif /* LIBGNUTLS_VERSION_NUMBER < 0x010302 */ #ifdef GNUTLS_INTERNAL_STRUCTURE_HACK /* * It looks like gnutls does not provide access to client/server_random and * master_key. This is somewhat unfortunate since these are needed for key * derivation in EAP-{TLS,TTLS,PEAP,FAST}. Workaround for now is a horrible * hack that copies the gnutls_session_int definition from gnutls_int.h so that * we can get the needed information. */ typedef u8 uint8; typedef unsigned char opaque; typedef struct { uint8 suite[2]; } cipher_suite_st; typedef struct { gnutls_connection_end_t entity; gnutls_kx_algorithm_t kx_algorithm; gnutls_cipher_algorithm_t read_bulk_cipher_algorithm; gnutls_mac_algorithm_t read_mac_algorithm; gnutls_compression_method_t read_compression_algorithm; gnutls_cipher_algorithm_t write_bulk_cipher_algorithm; gnutls_mac_algorithm_t write_mac_algorithm; gnutls_compression_method_t write_compression_algorithm; cipher_suite_st current_cipher_suite; opaque master_secret[WPA_TLS_MASTER_SIZE]; opaque client_random[WPA_TLS_RANDOM_SIZE]; opaque server_random[WPA_TLS_RANDOM_SIZE]; /* followed by stuff we are not interested in */ } security_parameters_st; struct gnutls_session_int { security_parameters_st security_parameters; /* followed by things we are not interested in */ }; #endif /* LIBGNUTLS_VERSION_NUMBER < 0x010302 */ static int tls_gnutls_ref_count = 0; struct tls_global { /* Data for session resumption */ void *session_data; size_t session_data_size; int server; int params_set; gnutls_certificate_credentials_t xcred; }; struct tls_connection { gnutls_session session; char *subject_match, *altsubject_match; int read_alerts, write_alerts, failed; u8 *pre_shared_secret; size_t pre_shared_secret_len; int established; int verify_peer; struct wpabuf *push_buf; struct wpabuf *pull_buf; const u8 *pull_buf_offset; int params_set; gnutls_certificate_credentials_t xcred; }; static void tls_log_func(int level, const char *msg) { char *s, *pos; if (level == 6 || level == 7) { /* These levels seem to be mostly I/O debug and msg dumps */ return; } s = os_strdup(msg); if (s == NULL) return; pos = s; while (*pos != '\0') { if (*pos == '\n') { *pos = '\0'; break; } pos++; } wpa_printf(level > 3 ? MSG_MSGDUMP : MSG_DEBUG, "gnutls<%d> %s", level, s); os_free(s); } void * tls_init(const struct tls_config *conf) { struct tls_global *global; #ifdef GNUTLS_INTERNAL_STRUCTURE_HACK /* Because of the horrible hack to get master_secret and client/server * random, we need to make sure that the gnutls version is something * that is expected to have same structure definition for the session * data.. */ const char *ver; const char *ok_ver[] = { "1.2.3", "1.2.4", "1.2.5", "1.2.6", "1.2.9", "1.3.2", NULL }; int i; #endif /* GNUTLS_INTERNAL_STRUCTURE_HACK */ global = os_zalloc(sizeof(*global)); if (global == NULL) return NULL; if (tls_gnutls_ref_count == 0 && gnutls_global_init() < 0) { os_free(global); return NULL; } tls_gnutls_ref_count++; #ifdef GNUTLS_INTERNAL_STRUCTURE_HACK ver = gnutls_check_version(NULL); if (ver == NULL) { tls_deinit(global); return NULL; } wpa_printf(MSG_DEBUG, "%s - gnutls version %s", __func__, ver); for (i = 0; ok_ver[i]; i++) { if (strcmp(ok_ver[i], ver) == 0) break; } if (ok_ver[i] == NULL) { wpa_printf(MSG_INFO, "Untested gnutls version %s - this needs " "to be tested and enabled in tls_gnutls.c", ver); tls_deinit(global); return NULL; } #endif /* GNUTLS_INTERNAL_STRUCTURE_HACK */ gnutls_global_set_log_function(tls_log_func); if (wpa_debug_show_keys) gnutls_global_set_log_level(11); return global; } void tls_deinit(void *ssl_ctx) { struct tls_global *global = ssl_ctx; if (global) { if (global->params_set) gnutls_certificate_free_credentials(global->xcred); os_free(global->session_data); os_free(global); } tls_gnutls_ref_count--; if (tls_gnutls_ref_count == 0) gnutls_global_deinit(); } int tls_get_errors(void *ssl_ctx) { return 0; } static ssize_t tls_pull_func(gnutls_transport_ptr ptr, void *buf, size_t len) { struct tls_connection *conn = (struct tls_connection *) ptr; const u8 *end; if (conn->pull_buf == NULL) { errno = EWOULDBLOCK; return -1; } end = wpabuf_head_u8(conn->pull_buf) + wpabuf_len(conn->pull_buf); if ((size_t) (end - conn->pull_buf_offset) < len) len = end - conn->pull_buf_offset; os_memcpy(buf, conn->pull_buf_offset, len); conn->pull_buf_offset += len; if (conn->pull_buf_offset == end) { wpa_printf(MSG_DEBUG, "%s - pull_buf consumed", __func__); wpabuf_free(conn->pull_buf); conn->pull_buf = NULL; conn->pull_buf_offset = NULL; } else { wpa_printf(MSG_DEBUG, "%s - %lu bytes remaining in pull_buf", __func__, (unsigned long) (end - conn->pull_buf_offset)); } return len; } static ssize_t tls_push_func(gnutls_transport_ptr ptr, const void *buf, size_t len) { struct tls_connection *conn = (struct tls_connection *) ptr; if (wpabuf_resize(&conn->push_buf, len) < 0) { errno = ENOMEM; return -1; } wpabuf_put_data(conn->push_buf, buf, len); return len; } static int tls_gnutls_init_session(struct tls_global *global, struct tls_connection *conn) { #if LIBGNUTLS_VERSION_NUMBER >= 0x020200 const char *err; #else /* LIBGNUTLS_VERSION_NUMBER >= 0x020200 */ const int cert_types[2] = { GNUTLS_CRT_X509, 0 }; const int protos[2] = { GNUTLS_TLS1, 0 }; #endif /* LIBGNUTLS_VERSION_NUMBER < 0x020200 */ int ret; ret = gnutls_init(&conn->session, global->server ? GNUTLS_SERVER : GNUTLS_CLIENT); if (ret < 0) { wpa_printf(MSG_INFO, "TLS: Failed to initialize new TLS " "connection: %s", gnutls_strerror(ret)); return -1; } ret = gnutls_set_default_priority(conn->session); if (ret < 0) goto fail; #if LIBGNUTLS_VERSION_NUMBER >= 0x020200 ret = gnutls_priority_set_direct(conn->session, "NORMAL:-VERS-SSL3.0", &err); if (ret < 0) { wpa_printf(MSG_ERROR, "GnuTLS: Priority string failure at " "'%s'", err); goto fail; } #else /* LIBGNUTLS_VERSION_NUMBER >= 0x020200 */ ret = gnutls_certificate_type_set_priority(conn->session, cert_types); if (ret < 0) goto fail; ret = gnutls_protocol_set_priority(conn->session, protos); if (ret < 0) goto fail; #endif /* LIBGNUTLS_VERSION_NUMBER < 0x020200 */ gnutls_transport_set_pull_function(conn->session, tls_pull_func); gnutls_transport_set_push_function(conn->session, tls_push_func); gnutls_transport_set_ptr(conn->session, (gnutls_transport_ptr) conn); return 0; fail: wpa_printf(MSG_INFO, "TLS: Failed to setup new TLS connection: %s", gnutls_strerror(ret)); gnutls_deinit(conn->session); return -1; } struct tls_connection * tls_connection_init(void *ssl_ctx) { struct tls_global *global = ssl_ctx; struct tls_connection *conn; int ret; conn = os_zalloc(sizeof(*conn)); if (conn == NULL) return NULL; if (tls_gnutls_init_session(global, conn)) { os_free(conn); return NULL; } if (global->params_set) { ret = gnutls_credentials_set(conn->session, GNUTLS_CRD_CERTIFICATE, global->xcred); if (ret < 0) { wpa_printf(MSG_INFO, "Failed to configure " "credentials: %s", gnutls_strerror(ret)); os_free(conn); return NULL; } } if (gnutls_certificate_allocate_credentials(&conn->xcred)) { os_free(conn); return NULL; } return conn; } void tls_connection_deinit(void *ssl_ctx, struct tls_connection *conn) { if (conn == NULL) return; gnutls_certificate_free_credentials(conn->xcred); gnutls_deinit(conn->session); os_free(conn->pre_shared_secret); os_free(conn->subject_match); os_free(conn->altsubject_match); wpabuf_free(conn->push_buf); wpabuf_free(conn->pull_buf); os_free(conn); } int tls_connection_established(void *ssl_ctx, struct tls_connection *conn) { return conn ? conn->established : 0; } int tls_connection_shutdown(void *ssl_ctx, struct tls_connection *conn) { struct tls_global *global = ssl_ctx; int ret; if (conn == NULL) return -1; /* Shutdown previous TLS connection without notifying the peer * because the connection was already terminated in practice * and "close notify" shutdown alert would confuse AS. */ gnutls_bye(conn->session, GNUTLS_SHUT_RDWR); wpabuf_free(conn->push_buf); conn->push_buf = NULL; conn->established = 0; gnutls_deinit(conn->session); if (tls_gnutls_init_session(global, conn)) { wpa_printf(MSG_INFO, "GnuTLS: Failed to preparare new session " "for session resumption use"); return -1; } ret = gnutls_credentials_set(conn->session, GNUTLS_CRD_CERTIFICATE, conn->params_set ? conn->xcred : global->xcred); if (ret < 0) { wpa_printf(MSG_INFO, "GnuTLS: Failed to configure credentials " "for session resumption: %s", gnutls_strerror(ret)); return -1; } if (global->session_data) { ret = gnutls_session_set_data(conn->session, global->session_data, global->session_data_size); if (ret < 0) { wpa_printf(MSG_INFO, "GnuTLS: Failed to set session " "data: %s", gnutls_strerror(ret)); return -1; } } return 0; } #if 0 static int tls_match_altsubject(X509 *cert, const char *match) { GENERAL_NAME *gen; char *field, *tmp; void *ext; int i, found = 0; size_t len; ext = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL); for (i = 0; ext && i < sk_GENERAL_NAME_num(ext); i++) { gen = sk_GENERAL_NAME_value(ext, i); switch (gen->type) { case GEN_EMAIL: field = "EMAIL"; break; case GEN_DNS: field = "DNS"; break; case GEN_URI: field = "URI"; break; default: field = NULL; wpa_printf(MSG_DEBUG, "TLS: altSubjectName: " "unsupported type=%d", gen->type); break; } if (!field) continue; wpa_printf(MSG_DEBUG, "TLS: altSubjectName: %s:%s", field, gen->d.ia5->data); len = os_strlen(field) + 1 + strlen((char *) gen->d.ia5->data) + 1; tmp = os_malloc(len); if (tmp == NULL) continue; snprintf(tmp, len, "%s:%s", field, gen->d.ia5->data); if (strstr(tmp, match)) found++; os_free(tmp); } return found; } #endif #if 0 static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) { char buf[256]; X509 *err_cert; int err, depth; SSL *ssl; struct tls_connection *conn; char *match, *altmatch; err_cert = X509_STORE_CTX_get_current_cert(x509_ctx); err = X509_STORE_CTX_get_error(x509_ctx); depth = X509_STORE_CTX_get_error_depth(x509_ctx); ssl = X509_STORE_CTX_get_ex_data(x509_ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); X509_NAME_oneline(X509_get_subject_name(err_cert), buf, sizeof(buf)); conn = SSL_get_app_data(ssl); match = conn ? conn->subject_match : NULL; altmatch = conn ? conn->altsubject_match : NULL; if (!preverify_ok) { wpa_printf(MSG_WARNING, "TLS: Certificate verification failed," " error %d (%s) depth %d for '%s'", err, X509_verify_cert_error_string(err), depth, buf); } else { wpa_printf(MSG_DEBUG, "TLS: tls_verify_cb - " "preverify_ok=%d err=%d (%s) depth=%d buf='%s'", preverify_ok, err, X509_verify_cert_error_string(err), depth, buf); if (depth == 0 && match && strstr(buf, match) == NULL) { wpa_printf(MSG_WARNING, "TLS: Subject '%s' did not " "match with '%s'", buf, match); preverify_ok = 0; } else if (depth == 0 && altmatch && !tls_match_altsubject(err_cert, altmatch)) { wpa_printf(MSG_WARNING, "TLS: altSubjectName match " "'%s' not found", altmatch); preverify_ok = 0; } } return preverify_ok; } #endif int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, const struct tls_connection_params *params) { int ret; if (conn == NULL || params == NULL) return -1; os_free(conn->subject_match); conn->subject_match = NULL; if (params->subject_match) { conn->subject_match = os_strdup(params->subject_match); if (conn->subject_match == NULL) return -1; } os_free(conn->altsubject_match); conn->altsubject_match = NULL; if (params->altsubject_match) { conn->altsubject_match = os_strdup(params->altsubject_match); if (conn->altsubject_match == NULL) return -1; } /* TODO: gnutls_certificate_set_verify_flags(xcred, flags); * to force peer validation(?) */ if (params->ca_cert) { conn->verify_peer = 1; ret = gnutls_certificate_set_x509_trust_file( conn->xcred, params->ca_cert, GNUTLS_X509_FMT_PEM); if (ret < 0) { wpa_printf(MSG_DEBUG, "Failed to read CA cert '%s' " "in PEM format: %s", params->ca_cert, gnutls_strerror(ret)); ret = gnutls_certificate_set_x509_trust_file( conn->xcred, params->ca_cert, GNUTLS_X509_FMT_DER); if (ret < 0) { wpa_printf(MSG_DEBUG, "Failed to read CA cert " "'%s' in DER format: %s", params->ca_cert, gnutls_strerror(ret)); return -1; } } if (params->flags & TLS_CONN_ALLOW_SIGN_RSA_MD5) { gnutls_certificate_set_verify_flags( conn->xcred, GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD5); } #if LIBGNUTLS_VERSION_NUMBER >= 0x020800 if (params->flags & TLS_CONN_DISABLE_TIME_CHECKS) { gnutls_certificate_set_verify_flags( conn->xcred, GNUTLS_VERIFY_DISABLE_TIME_CHECKS); } #endif /* LIBGNUTLS_VERSION_NUMBER >= 0x020800 */ } if (params->client_cert && params->private_key) { /* TODO: private_key_passwd? */ ret = gnutls_certificate_set_x509_key_file( conn->xcred, params->client_cert, params->private_key, GNUTLS_X509_FMT_PEM); if (ret < 0) { wpa_printf(MSG_DEBUG, "Failed to read client cert/key " "in PEM format: %s", gnutls_strerror(ret)); ret = gnutls_certificate_set_x509_key_file( conn->xcred, params->client_cert, params->private_key, GNUTLS_X509_FMT_DER); if (ret < 0) { wpa_printf(MSG_DEBUG, "Failed to read client " "cert/key in DER format: %s", gnutls_strerror(ret)); return ret; } } } else if (params->private_key) { int pkcs12_ok = 0; #ifdef PKCS12_FUNCS /* Try to load in PKCS#12 format */ #if LIBGNUTLS_VERSION_NUMBER >= 0x010302 ret = gnutls_certificate_set_x509_simple_pkcs12_file( conn->xcred, params->private_key, GNUTLS_X509_FMT_DER, params->private_key_passwd); if (ret != 0) { wpa_printf(MSG_DEBUG, "Failed to load private_key in " "PKCS#12 format: %s", gnutls_strerror(ret)); return -1; } else pkcs12_ok = 1; #endif /* LIBGNUTLS_VERSION_NUMBER >= 0x010302 */ #endif /* PKCS12_FUNCS */ if (!pkcs12_ok) { wpa_printf(MSG_DEBUG, "GnuTLS: PKCS#12 support not " "included"); return -1; } } conn->params_set = 1; ret = gnutls_credentials_set(conn->session, GNUTLS_CRD_CERTIFICATE, conn->xcred); if (ret < 0) { wpa_printf(MSG_INFO, "Failed to configure credentials: %s", gnutls_strerror(ret)); } return ret; } int tls_global_set_params(void *tls_ctx, const struct tls_connection_params *params) { struct tls_global *global = tls_ctx; int ret; /* Currently, global parameters are only set when running in server * mode. */ global->server = 1; if (global->params_set) { gnutls_certificate_free_credentials(global->xcred); global->params_set = 0; } ret = gnutls_certificate_allocate_credentials(&global->xcred); if (ret) { wpa_printf(MSG_DEBUG, "Failed to allocate global credentials " "%s", gnutls_strerror(ret)); return -1; } if (params->ca_cert) { ret = gnutls_certificate_set_x509_trust_file( global->xcred, params->ca_cert, GNUTLS_X509_FMT_PEM); if (ret < 0) { wpa_printf(MSG_DEBUG, "Failed to read CA cert '%s' " "in PEM format: %s", params->ca_cert, gnutls_strerror(ret)); ret = gnutls_certificate_set_x509_trust_file( global->xcred, params->ca_cert, GNUTLS_X509_FMT_DER); if (ret < 0) { wpa_printf(MSG_DEBUG, "Failed to read CA cert " "'%s' in DER format: %s", params->ca_cert, gnutls_strerror(ret)); goto fail; } } if (params->flags & TLS_CONN_ALLOW_SIGN_RSA_MD5) { gnutls_certificate_set_verify_flags( global->xcred, GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD5); } #if LIBGNUTLS_VERSION_NUMBER >= 0x020800 if (params->flags & TLS_CONN_DISABLE_TIME_CHECKS) { gnutls_certificate_set_verify_flags( global->xcred, GNUTLS_VERIFY_DISABLE_TIME_CHECKS); } #endif /* LIBGNUTLS_VERSION_NUMBER >= 0x020800 */ } if (params->client_cert && params->private_key) { /* TODO: private_key_passwd? */ ret = gnutls_certificate_set_x509_key_file( global->xcred, params->client_cert, params->private_key, GNUTLS_X509_FMT_PEM); if (ret < 0) { wpa_printf(MSG_DEBUG, "Failed to read client cert/key " "in PEM format: %s", gnutls_strerror(ret)); ret = gnutls_certificate_set_x509_key_file( global->xcred, params->client_cert, params->private_key, GNUTLS_X509_FMT_DER); if (ret < 0) { wpa_printf(MSG_DEBUG, "Failed to read client " "cert/key in DER format: %s", gnutls_strerror(ret)); goto fail; } } } else if (params->private_key) { int pkcs12_ok = 0; #ifdef PKCS12_FUNCS /* Try to load in PKCS#12 format */ #if LIBGNUTLS_VERSION_NUMBER >= 0x010302 ret = gnutls_certificate_set_x509_simple_pkcs12_file( global->xcred, params->private_key, GNUTLS_X509_FMT_DER, params->private_key_passwd); if (ret != 0) { wpa_printf(MSG_DEBUG, "Failed to load private_key in " "PKCS#12 format: %s", gnutls_strerror(ret)); goto fail; } else pkcs12_ok = 1; #endif /* LIBGNUTLS_VERSION_NUMBER >= 0x010302 */ #endif /* PKCS12_FUNCS */ if (!pkcs12_ok) { wpa_printf(MSG_DEBUG, "GnuTLS: PKCS#12 support not " "included"); goto fail; } } global->params_set = 1; return 0; fail: gnutls_certificate_free_credentials(global->xcred); return -1; } int tls_global_set_verify(void *ssl_ctx, int check_crl) { /* TODO */ return 0; } int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn, int verify_peer) { if (conn == NULL || conn->session == NULL) return -1; conn->verify_peer = verify_peer; gnutls_certificate_server_set_request(conn->session, verify_peer ? GNUTLS_CERT_REQUIRE : GNUTLS_CERT_REQUEST); return 0; } int tls_connection_get_keys(void *ssl_ctx, struct tls_connection *conn, struct tls_keys *keys) { #ifdef GNUTLS_INTERNAL_STRUCTURE_HACK security_parameters_st *sec; #endif /* GNUTLS_INTERNAL_STRUCTURE_HACK */ if (conn == NULL || conn->session == NULL || keys == NULL) return -1; os_memset(keys, 0, sizeof(*keys)); #if LIBGNUTLS_VERSION_NUMBER < 0x020c00 #ifdef GNUTLS_INTERNAL_STRUCTURE_HACK sec = &conn->session->security_parameters; keys->master_key = sec->master_secret; keys->master_key_len = WPA_TLS_MASTER_SIZE; keys->client_random = sec->client_random; keys->server_random = sec->server_random; #else /* GNUTLS_INTERNAL_STRUCTURE_HACK */ keys->client_random = (u8 *) gnutls_session_get_client_random(conn->session); keys->server_random = (u8 *) gnutls_session_get_server_random(conn->session); /* No access to master_secret */ #endif /* GNUTLS_INTERNAL_STRUCTURE_HACK */ #endif /* LIBGNUTLS_VERSION_NUMBER < 0x020c00 */ #if LIBGNUTLS_VERSION_NUMBER < 0x020c00 keys->client_random_len = WPA_TLS_RANDOM_SIZE; keys->server_random_len = WPA_TLS_RANDOM_SIZE; #endif /* LIBGNUTLS_VERSION_NUMBER < 0x020c00 */ return 0; } int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, const char *label, int server_random_first, u8 *out, size_t out_len) { #if LIBGNUTLS_VERSION_NUMBER >= 0x010302 if (conn == NULL || conn->session == NULL) return -1; return gnutls_prf(conn->session, os_strlen(label), label, server_random_first, 0, NULL, out_len, (char *) out); #else /* LIBGNUTLS_VERSION_NUMBER >= 0x010302 */ return -1; #endif /* LIBGNUTLS_VERSION_NUMBER >= 0x010302 */ } static int tls_connection_verify_peer(struct tls_connection *conn, gnutls_alert_description_t *err) { unsigned int status, num_certs, i; struct os_time now; const gnutls_datum_t *certs; gnutls_x509_crt_t cert; if (gnutls_certificate_verify_peers2(conn->session, &status) < 0) { wpa_printf(MSG_INFO, "TLS: Failed to verify peer " "certificate chain"); *err = GNUTLS_A_INTERNAL_ERROR; return -1; } if (conn->verify_peer && (status & GNUTLS_CERT_INVALID)) { wpa_printf(MSG_INFO, "TLS: Peer certificate not trusted"); *err = GNUTLS_A_INTERNAL_ERROR; if (status & GNUTLS_CERT_INSECURE_ALGORITHM) { wpa_printf(MSG_INFO, "TLS: Certificate uses insecure " "algorithm"); *err = GNUTLS_A_INSUFFICIENT_SECURITY; } #if LIBGNUTLS_VERSION_NUMBER >= 0x020800 if (status & GNUTLS_CERT_NOT_ACTIVATED) { wpa_printf(MSG_INFO, "TLS: Certificate not yet " "activated"); *err = GNUTLS_A_CERTIFICATE_EXPIRED; } if (status & GNUTLS_CERT_EXPIRED) { wpa_printf(MSG_INFO, "TLS: Certificate expired"); *err = GNUTLS_A_CERTIFICATE_EXPIRED; } #endif /* LIBGNUTLS_VERSION_NUMBER >= 0x020800 */ return -1; } if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) { wpa_printf(MSG_INFO, "TLS: Peer certificate does not have a " "known issuer"); *err = GNUTLS_A_UNKNOWN_CA; return -1; } if (status & GNUTLS_CERT_REVOKED) { wpa_printf(MSG_INFO, "TLS: Peer certificate has been revoked"); *err = GNUTLS_A_CERTIFICATE_REVOKED; return -1; } os_get_time(&now); certs = gnutls_certificate_get_peers(conn->session, &num_certs); if (certs == NULL) { wpa_printf(MSG_INFO, "TLS: No peer certificate chain " "received"); *err = GNUTLS_A_UNKNOWN_CA; return -1; } for (i = 0; i < num_certs; i++) { char *buf; size_t len; if (gnutls_x509_crt_init(&cert) < 0) { wpa_printf(MSG_INFO, "TLS: Certificate initialization " "failed"); *err = GNUTLS_A_BAD_CERTIFICATE; return -1; } if (gnutls_x509_crt_import(cert, &certs[i], GNUTLS_X509_FMT_DER) < 0) { wpa_printf(MSG_INFO, "TLS: Could not parse peer " "certificate %d/%d", i + 1, num_certs); gnutls_x509_crt_deinit(cert); *err = GNUTLS_A_BAD_CERTIFICATE; return -1; } gnutls_x509_crt_get_dn(cert, NULL, &len); len++; buf = os_malloc(len + 1); if (buf) { buf[0] = buf[len] = '\0'; gnutls_x509_crt_get_dn(cert, buf, &len); } wpa_printf(MSG_DEBUG, "TLS: Peer cert chain %d/%d: %s", i + 1, num_certs, buf); if (i == 0) { /* TODO: validate subject_match and altsubject_match */ } os_free(buf); if (gnutls_x509_crt_get_expiration_time(cert) < now.sec || gnutls_x509_crt_get_activation_time(cert) > now.sec) { wpa_printf(MSG_INFO, "TLS: Peer certificate %d/%d is " "not valid at this time", i + 1, num_certs); gnutls_x509_crt_deinit(cert); *err = GNUTLS_A_CERTIFICATE_EXPIRED; return -1; } gnutls_x509_crt_deinit(cert); } return 0; } static struct wpabuf * gnutls_get_appl_data(struct tls_connection *conn) { int res; struct wpabuf *ad; wpa_printf(MSG_DEBUG, "GnuTLS: Check for possible Application Data"); ad = wpabuf_alloc((wpabuf_len(conn->pull_buf) + 500) * 3); if (ad == NULL) return NULL; res = gnutls_record_recv(conn->session, wpabuf_mhead(ad), wpabuf_size(ad)); wpa_printf(MSG_DEBUG, "GnuTLS: gnutls_record_recv: %d", res); if (res < 0) { wpa_printf(MSG_DEBUG, "%s - gnutls_record_recv failed: %d " "(%s)", __func__, (int) res, gnutls_strerror(res)); wpabuf_free(ad); return NULL; } wpabuf_put(ad, res); wpa_printf(MSG_DEBUG, "GnuTLS: Received %d bytes of Application Data", res); return ad; } struct wpabuf * tls_connection_handshake(void *tls_ctx, struct tls_connection *conn, const struct wpabuf *in_data, struct wpabuf **appl_data) { struct tls_global *global = tls_ctx; struct wpabuf *out_data; int ret; if (appl_data) *appl_data = NULL; if (in_data && wpabuf_len(in_data) > 0) { if (conn->pull_buf) { wpa_printf(MSG_DEBUG, "%s - %lu bytes remaining in " "pull_buf", __func__, (unsigned long) wpabuf_len(conn->pull_buf)); wpabuf_free(conn->pull_buf); } conn->pull_buf = wpabuf_dup(in_data); if (conn->pull_buf == NULL) return NULL; conn->pull_buf_offset = wpabuf_head(conn->pull_buf); } ret = gnutls_handshake(conn->session); if (ret < 0) { switch (ret) { case GNUTLS_E_AGAIN: if (global->server && conn->established && conn->push_buf == NULL) { /* Need to return something to trigger * completion of EAP-TLS. */ conn->push_buf = wpabuf_alloc(0); } break; case GNUTLS_E_FATAL_ALERT_RECEIVED: wpa_printf(MSG_DEBUG, "%s - received fatal '%s' alert", __func__, gnutls_alert_get_name( gnutls_alert_get(conn->session))); conn->read_alerts++; /* continue */ default: wpa_printf(MSG_DEBUG, "%s - gnutls_handshake failed " "-> %s", __func__, gnutls_strerror(ret)); conn->failed++; } } else { size_t size; gnutls_alert_description_t err; if (conn->verify_peer && tls_connection_verify_peer(conn, &err)) { wpa_printf(MSG_INFO, "TLS: Peer certificate chain " "failed validation"); conn->failed++; gnutls_alert_send(conn->session, GNUTLS_AL_FATAL, err); goto out; } wpa_printf(MSG_DEBUG, "TLS: Handshake completed successfully"); conn->established = 1; if (conn->push_buf == NULL) { /* Need to return something to get final TLS ACK. */ conn->push_buf = wpabuf_alloc(0); } gnutls_session_get_data(conn->session, NULL, &size); if (global->session_data == NULL || global->session_data_size < size) { os_free(global->session_data); global->session_data = os_malloc(size); } if (global->session_data) { global->session_data_size = size; gnutls_session_get_data(conn->session, global->session_data, &global->session_data_size); } if (conn->pull_buf && appl_data) *appl_data = gnutls_get_appl_data(conn); } out: out_data = conn->push_buf; conn->push_buf = NULL; return out_data; } struct wpabuf * tls_connection_server_handshake(void *tls_ctx, struct tls_connection *conn, const struct wpabuf *in_data, struct wpabuf **appl_data) { return tls_connection_handshake(tls_ctx, conn, in_data, appl_data); } struct wpabuf * tls_connection_encrypt(void *tls_ctx, struct tls_connection *conn, const struct wpabuf *in_data) { ssize_t res; struct wpabuf *buf; res = gnutls_record_send(conn->session, wpabuf_head(in_data), wpabuf_len(in_data)); if (res < 0) { wpa_printf(MSG_INFO, "%s: Encryption failed: %s", __func__, gnutls_strerror(res)); return NULL; } buf = conn->push_buf; conn->push_buf = NULL; return buf; } struct wpabuf * tls_connection_decrypt(void *tls_ctx, struct tls_connection *conn, const struct wpabuf *in_data) { ssize_t res; struct wpabuf *out; if (conn->pull_buf) { wpa_printf(MSG_DEBUG, "%s - %lu bytes remaining in " "pull_buf", __func__, (unsigned long) wpabuf_len(conn->pull_buf)); wpabuf_free(conn->pull_buf); } conn->pull_buf = wpabuf_dup(in_data); if (conn->pull_buf == NULL) return NULL; conn->pull_buf_offset = wpabuf_head(conn->pull_buf); /* * Even though we try to disable TLS compression, it is possible that * this cannot be done with all TLS libraries. Add extra buffer space * to handle the possibility of the decrypted data being longer than * input data. */ out = wpabuf_alloc((wpabuf_len(in_data) + 500) * 3); if (out == NULL) return NULL; res = gnutls_record_recv(conn->session, wpabuf_mhead(out), wpabuf_size(out)); if (res < 0) { wpa_printf(MSG_DEBUG, "%s - gnutls_record_recv failed: %d " "(%s)", __func__, (int) res, gnutls_strerror(res)); wpabuf_free(out); return NULL; } wpabuf_put(out, res); return out; } int tls_connection_resumed(void *ssl_ctx, struct tls_connection *conn) { if (conn == NULL) return 0; return gnutls_session_is_resumed(conn->session); } int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn, u8 *ciphers) { /* TODO */ return -1; } int tls_get_cipher(void *ssl_ctx, struct tls_connection *conn, char *buf, size_t buflen) { /* TODO */ buf[0] = '\0'; return 0; } int tls_connection_enable_workaround(void *ssl_ctx, struct tls_connection *conn) { gnutls_record_disable_padding(conn->session); return 0; } int tls_connection_client_hello_ext(void *ssl_ctx, struct tls_connection *conn, int ext_type, const u8 *data, size_t data_len) { /* TODO */ return -1; } int tls_connection_get_failed(void *ssl_ctx, struct tls_connection *conn) { if (conn == NULL) return -1; return conn->failed; } int tls_connection_get_read_alerts(void *ssl_ctx, struct tls_connection *conn) { if (conn == NULL) return -1; return conn->read_alerts; } int tls_connection_get_write_alerts(void *ssl_ctx, struct tls_connection *conn) { if (conn == NULL) return -1; return conn->write_alerts; } int tls_connection_get_keyblock_size(void *tls_ctx, struct tls_connection *conn) { /* TODO */ return -1; } unsigned int tls_capabilities(void *tls_ctx) { return 0; } int tls_connection_set_session_ticket_cb(void *tls_ctx, struct tls_connection *conn, tls_session_ticket_cb cb, void *ctx) { return -1; } wpa-2.1/src/crypto/tls_internal.c000066400000000000000000000347021227414666700171410ustar00rootroot00000000000000/* * TLS interface functions and an internal TLS implementation * Copyright (c) 2004-2011, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. * * This file interface functions for hostapd/wpa_supplicant to use the * integrated TLSv1 implementation. */ #include "includes.h" #include "common.h" #include "tls.h" #include "tls/tlsv1_client.h" #include "tls/tlsv1_server.h" static int tls_ref_count = 0; struct tls_global { int server; struct tlsv1_credentials *server_cred; int check_crl; }; struct tls_connection { struct tlsv1_client *client; struct tlsv1_server *server; }; void * tls_init(const struct tls_config *conf) { struct tls_global *global; if (tls_ref_count == 0) { #ifdef CONFIG_TLS_INTERNAL_CLIENT if (tlsv1_client_global_init()) return NULL; #endif /* CONFIG_TLS_INTERNAL_CLIENT */ #ifdef CONFIG_TLS_INTERNAL_SERVER if (tlsv1_server_global_init()) return NULL; #endif /* CONFIG_TLS_INTERNAL_SERVER */ } tls_ref_count++; global = os_zalloc(sizeof(*global)); if (global == NULL) return NULL; return global; } void tls_deinit(void *ssl_ctx) { struct tls_global *global = ssl_ctx; tls_ref_count--; if (tls_ref_count == 0) { #ifdef CONFIG_TLS_INTERNAL_CLIENT tlsv1_client_global_deinit(); #endif /* CONFIG_TLS_INTERNAL_CLIENT */ #ifdef CONFIG_TLS_INTERNAL_SERVER tlsv1_cred_free(global->server_cred); tlsv1_server_global_deinit(); #endif /* CONFIG_TLS_INTERNAL_SERVER */ } os_free(global); } int tls_get_errors(void *tls_ctx) { return 0; } struct tls_connection * tls_connection_init(void *tls_ctx) { struct tls_connection *conn; struct tls_global *global = tls_ctx; conn = os_zalloc(sizeof(*conn)); if (conn == NULL) return NULL; #ifdef CONFIG_TLS_INTERNAL_CLIENT if (!global->server) { conn->client = tlsv1_client_init(); if (conn->client == NULL) { os_free(conn); return NULL; } } #endif /* CONFIG_TLS_INTERNAL_CLIENT */ #ifdef CONFIG_TLS_INTERNAL_SERVER if (global->server) { conn->server = tlsv1_server_init(global->server_cred); if (conn->server == NULL) { os_free(conn); return NULL; } } #endif /* CONFIG_TLS_INTERNAL_SERVER */ return conn; } void tls_connection_deinit(void *tls_ctx, struct tls_connection *conn) { if (conn == NULL) return; #ifdef CONFIG_TLS_INTERNAL_CLIENT if (conn->client) tlsv1_client_deinit(conn->client); #endif /* CONFIG_TLS_INTERNAL_CLIENT */ #ifdef CONFIG_TLS_INTERNAL_SERVER if (conn->server) tlsv1_server_deinit(conn->server); #endif /* CONFIG_TLS_INTERNAL_SERVER */ os_free(conn); } int tls_connection_established(void *tls_ctx, struct tls_connection *conn) { #ifdef CONFIG_TLS_INTERNAL_CLIENT if (conn->client) return tlsv1_client_established(conn->client); #endif /* CONFIG_TLS_INTERNAL_CLIENT */ #ifdef CONFIG_TLS_INTERNAL_SERVER if (conn->server) return tlsv1_server_established(conn->server); #endif /* CONFIG_TLS_INTERNAL_SERVER */ return 0; } int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn) { #ifdef CONFIG_TLS_INTERNAL_CLIENT if (conn->client) return tlsv1_client_shutdown(conn->client); #endif /* CONFIG_TLS_INTERNAL_CLIENT */ #ifdef CONFIG_TLS_INTERNAL_SERVER if (conn->server) return tlsv1_server_shutdown(conn->server); #endif /* CONFIG_TLS_INTERNAL_SERVER */ return -1; } int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, const struct tls_connection_params *params) { #ifdef CONFIG_TLS_INTERNAL_CLIENT struct tlsv1_credentials *cred; if (conn->client == NULL) return -1; cred = tlsv1_cred_alloc(); if (cred == NULL) return -1; if (tlsv1_set_ca_cert(cred, params->ca_cert, params->ca_cert_blob, params->ca_cert_blob_len, params->ca_path)) { wpa_printf(MSG_INFO, "TLS: Failed to configure trusted CA " "certificates"); tlsv1_cred_free(cred); return -1; } if (tlsv1_set_cert(cred, params->client_cert, params->client_cert_blob, params->client_cert_blob_len)) { wpa_printf(MSG_INFO, "TLS: Failed to configure client " "certificate"); tlsv1_cred_free(cred); return -1; } if (tlsv1_set_private_key(cred, params->private_key, params->private_key_passwd, params->private_key_blob, params->private_key_blob_len)) { wpa_printf(MSG_INFO, "TLS: Failed to load private key"); tlsv1_cred_free(cred); return -1; } if (tlsv1_set_dhparams(cred, params->dh_file, params->dh_blob, params->dh_blob_len)) { wpa_printf(MSG_INFO, "TLS: Failed to load DH parameters"); tlsv1_cred_free(cred); return -1; } if (tlsv1_client_set_cred(conn->client, cred) < 0) { tlsv1_cred_free(cred); return -1; } tlsv1_client_set_time_checks( conn->client, !(params->flags & TLS_CONN_DISABLE_TIME_CHECKS)); return 0; #else /* CONFIG_TLS_INTERNAL_CLIENT */ return -1; #endif /* CONFIG_TLS_INTERNAL_CLIENT */ } int tls_global_set_params(void *tls_ctx, const struct tls_connection_params *params) { #ifdef CONFIG_TLS_INTERNAL_SERVER struct tls_global *global = tls_ctx; struct tlsv1_credentials *cred; /* Currently, global parameters are only set when running in server * mode. */ global->server = 1; tlsv1_cred_free(global->server_cred); global->server_cred = cred = tlsv1_cred_alloc(); if (cred == NULL) return -1; if (tlsv1_set_ca_cert(cred, params->ca_cert, params->ca_cert_blob, params->ca_cert_blob_len, params->ca_path)) { wpa_printf(MSG_INFO, "TLS: Failed to configure trusted CA " "certificates"); return -1; } if (tlsv1_set_cert(cred, params->client_cert, params->client_cert_blob, params->client_cert_blob_len)) { wpa_printf(MSG_INFO, "TLS: Failed to configure server " "certificate"); return -1; } if (tlsv1_set_private_key(cred, params->private_key, params->private_key_passwd, params->private_key_blob, params->private_key_blob_len)) { wpa_printf(MSG_INFO, "TLS: Failed to load private key"); return -1; } if (tlsv1_set_dhparams(cred, params->dh_file, params->dh_blob, params->dh_blob_len)) { wpa_printf(MSG_INFO, "TLS: Failed to load DH parameters"); return -1; } return 0; #else /* CONFIG_TLS_INTERNAL_SERVER */ return -1; #endif /* CONFIG_TLS_INTERNAL_SERVER */ } int tls_global_set_verify(void *tls_ctx, int check_crl) { struct tls_global *global = tls_ctx; global->check_crl = check_crl; return 0; } int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn, int verify_peer) { #ifdef CONFIG_TLS_INTERNAL_SERVER if (conn->server) return tlsv1_server_set_verify(conn->server, verify_peer); #endif /* CONFIG_TLS_INTERNAL_SERVER */ return -1; } int tls_connection_get_keys(void *tls_ctx, struct tls_connection *conn, struct tls_keys *keys) { #ifdef CONFIG_TLS_INTERNAL_CLIENT if (conn->client) return tlsv1_client_get_keys(conn->client, keys); #endif /* CONFIG_TLS_INTERNAL_CLIENT */ #ifdef CONFIG_TLS_INTERNAL_SERVER if (conn->server) return tlsv1_server_get_keys(conn->server, keys); #endif /* CONFIG_TLS_INTERNAL_SERVER */ return -1; } int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, const char *label, int server_random_first, u8 *out, size_t out_len) { #ifdef CONFIG_TLS_INTERNAL_CLIENT if (conn->client) { return tlsv1_client_prf(conn->client, label, server_random_first, out, out_len); } #endif /* CONFIG_TLS_INTERNAL_CLIENT */ #ifdef CONFIG_TLS_INTERNAL_SERVER if (conn->server) { return tlsv1_server_prf(conn->server, label, server_random_first, out, out_len); } #endif /* CONFIG_TLS_INTERNAL_SERVER */ return -1; } struct wpabuf * tls_connection_handshake(void *tls_ctx, struct tls_connection *conn, const struct wpabuf *in_data, struct wpabuf **appl_data) { return tls_connection_handshake2(tls_ctx, conn, in_data, appl_data, NULL); } struct wpabuf * tls_connection_handshake2(void *tls_ctx, struct tls_connection *conn, const struct wpabuf *in_data, struct wpabuf **appl_data, int *need_more_data) { #ifdef CONFIG_TLS_INTERNAL_CLIENT u8 *res, *ad; size_t res_len, ad_len; struct wpabuf *out; if (conn->client == NULL) return NULL; ad = NULL; res = tlsv1_client_handshake(conn->client, in_data ? wpabuf_head(in_data) : NULL, in_data ? wpabuf_len(in_data) : 0, &res_len, &ad, &ad_len, need_more_data); if (res == NULL) return NULL; out = wpabuf_alloc_ext_data(res, res_len); if (out == NULL) { os_free(res); os_free(ad); return NULL; } if (appl_data) { if (ad) { *appl_data = wpabuf_alloc_ext_data(ad, ad_len); if (*appl_data == NULL) os_free(ad); } else *appl_data = NULL; } else os_free(ad); return out; #else /* CONFIG_TLS_INTERNAL_CLIENT */ return NULL; #endif /* CONFIG_TLS_INTERNAL_CLIENT */ } struct wpabuf * tls_connection_server_handshake(void *tls_ctx, struct tls_connection *conn, const struct wpabuf *in_data, struct wpabuf **appl_data) { #ifdef CONFIG_TLS_INTERNAL_SERVER u8 *res; size_t res_len; struct wpabuf *out; if (conn->server == NULL) return NULL; if (appl_data) *appl_data = NULL; res = tlsv1_server_handshake(conn->server, wpabuf_head(in_data), wpabuf_len(in_data), &res_len); if (res == NULL && tlsv1_server_established(conn->server)) return wpabuf_alloc(0); if (res == NULL) return NULL; out = wpabuf_alloc_ext_data(res, res_len); if (out == NULL) { os_free(res); return NULL; } return out; #else /* CONFIG_TLS_INTERNAL_SERVER */ return NULL; #endif /* CONFIG_TLS_INTERNAL_SERVER */ } struct wpabuf * tls_connection_encrypt(void *tls_ctx, struct tls_connection *conn, const struct wpabuf *in_data) { #ifdef CONFIG_TLS_INTERNAL_CLIENT if (conn->client) { struct wpabuf *buf; int res; buf = wpabuf_alloc(wpabuf_len(in_data) + 300); if (buf == NULL) return NULL; res = tlsv1_client_encrypt(conn->client, wpabuf_head(in_data), wpabuf_len(in_data), wpabuf_mhead(buf), wpabuf_size(buf)); if (res < 0) { wpabuf_free(buf); return NULL; } wpabuf_put(buf, res); return buf; } #endif /* CONFIG_TLS_INTERNAL_CLIENT */ #ifdef CONFIG_TLS_INTERNAL_SERVER if (conn->server) { struct wpabuf *buf; int res; buf = wpabuf_alloc(wpabuf_len(in_data) + 300); if (buf == NULL) return NULL; res = tlsv1_server_encrypt(conn->server, wpabuf_head(in_data), wpabuf_len(in_data), wpabuf_mhead(buf), wpabuf_size(buf)); if (res < 0) { wpabuf_free(buf); return NULL; } wpabuf_put(buf, res); return buf; } #endif /* CONFIG_TLS_INTERNAL_SERVER */ return NULL; } struct wpabuf * tls_connection_decrypt(void *tls_ctx, struct tls_connection *conn, const struct wpabuf *in_data) { return tls_connection_decrypt2(tls_ctx, conn, in_data, NULL); } struct wpabuf * tls_connection_decrypt2(void *tls_ctx, struct tls_connection *conn, const struct wpabuf *in_data, int *need_more_data) { if (need_more_data) *need_more_data = 0; #ifdef CONFIG_TLS_INTERNAL_CLIENT if (conn->client) { return tlsv1_client_decrypt(conn->client, wpabuf_head(in_data), wpabuf_len(in_data), need_more_data); } #endif /* CONFIG_TLS_INTERNAL_CLIENT */ #ifdef CONFIG_TLS_INTERNAL_SERVER if (conn->server) { struct wpabuf *buf; int res; buf = wpabuf_alloc((wpabuf_len(in_data) + 500) * 3); if (buf == NULL) return NULL; res = tlsv1_server_decrypt(conn->server, wpabuf_head(in_data), wpabuf_len(in_data), wpabuf_mhead(buf), wpabuf_size(buf)); if (res < 0) { wpabuf_free(buf); return NULL; } wpabuf_put(buf, res); return buf; } #endif /* CONFIG_TLS_INTERNAL_SERVER */ return NULL; } int tls_connection_resumed(void *tls_ctx, struct tls_connection *conn) { #ifdef CONFIG_TLS_INTERNAL_CLIENT if (conn->client) return tlsv1_client_resumed(conn->client); #endif /* CONFIG_TLS_INTERNAL_CLIENT */ #ifdef CONFIG_TLS_INTERNAL_SERVER if (conn->server) return tlsv1_server_resumed(conn->server); #endif /* CONFIG_TLS_INTERNAL_SERVER */ return -1; } int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn, u8 *ciphers) { #ifdef CONFIG_TLS_INTERNAL_CLIENT if (conn->client) return tlsv1_client_set_cipher_list(conn->client, ciphers); #endif /* CONFIG_TLS_INTERNAL_CLIENT */ #ifdef CONFIG_TLS_INTERNAL_SERVER if (conn->server) return tlsv1_server_set_cipher_list(conn->server, ciphers); #endif /* CONFIG_TLS_INTERNAL_SERVER */ return -1; } int tls_get_cipher(void *tls_ctx, struct tls_connection *conn, char *buf, size_t buflen) { if (conn == NULL) return -1; #ifdef CONFIG_TLS_INTERNAL_CLIENT if (conn->client) return tlsv1_client_get_cipher(conn->client, buf, buflen); #endif /* CONFIG_TLS_INTERNAL_CLIENT */ #ifdef CONFIG_TLS_INTERNAL_SERVER if (conn->server) return tlsv1_server_get_cipher(conn->server, buf, buflen); #endif /* CONFIG_TLS_INTERNAL_SERVER */ return -1; } int tls_connection_enable_workaround(void *tls_ctx, struct tls_connection *conn) { return -1; } int tls_connection_client_hello_ext(void *tls_ctx, struct tls_connection *conn, int ext_type, const u8 *data, size_t data_len) { #ifdef CONFIG_TLS_INTERNAL_CLIENT if (conn->client) { return tlsv1_client_hello_ext(conn->client, ext_type, data, data_len); } #endif /* CONFIG_TLS_INTERNAL_CLIENT */ return -1; } int tls_connection_get_failed(void *tls_ctx, struct tls_connection *conn) { return 0; } int tls_connection_get_read_alerts(void *tls_ctx, struct tls_connection *conn) { return 0; } int tls_connection_get_write_alerts(void *tls_ctx, struct tls_connection *conn) { return 0; } int tls_connection_get_keyblock_size(void *tls_ctx, struct tls_connection *conn) { #ifdef CONFIG_TLS_INTERNAL_CLIENT if (conn->client) return tlsv1_client_get_keyblock_size(conn->client); #endif /* CONFIG_TLS_INTERNAL_CLIENT */ #ifdef CONFIG_TLS_INTERNAL_SERVER if (conn->server) return tlsv1_server_get_keyblock_size(conn->server); #endif /* CONFIG_TLS_INTERNAL_SERVER */ return -1; } unsigned int tls_capabilities(void *tls_ctx) { return 0; } int tls_connection_set_session_ticket_cb(void *tls_ctx, struct tls_connection *conn, tls_session_ticket_cb cb, void *ctx) { #ifdef CONFIG_TLS_INTERNAL_CLIENT if (conn->client) { tlsv1_client_set_session_ticket_cb(conn->client, cb, ctx); return 0; } #endif /* CONFIG_TLS_INTERNAL_CLIENT */ #ifdef CONFIG_TLS_INTERNAL_SERVER if (conn->server) { tlsv1_server_set_session_ticket_cb(conn->server, cb, ctx); return 0; } #endif /* CONFIG_TLS_INTERNAL_SERVER */ return -1; } wpa-2.1/src/crypto/tls_none.c000066400000000000000000000062431227414666700162630ustar00rootroot00000000000000/* * SSL/TLS interface functions for no TLS case * 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 "tls.h" void * tls_init(const struct tls_config *conf) { return (void *) 1; } void tls_deinit(void *ssl_ctx) { } int tls_get_errors(void *tls_ctx) { return 0; } struct tls_connection * tls_connection_init(void *tls_ctx) { return NULL; } void tls_connection_deinit(void *tls_ctx, struct tls_connection *conn) { } int tls_connection_established(void *tls_ctx, struct tls_connection *conn) { return -1; } int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn) { return -1; } int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, const struct tls_connection_params *params) { return -1; } int tls_global_set_params(void *tls_ctx, const struct tls_connection_params *params) { return -1; } int tls_global_set_verify(void *tls_ctx, int check_crl) { return -1; } int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn, int verify_peer) { return -1; } int tls_connection_get_keys(void *tls_ctx, struct tls_connection *conn, struct tls_keys *keys) { return -1; } int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, const char *label, int server_random_first, u8 *out, size_t out_len) { return -1; } struct wpabuf * tls_connection_handshake(void *tls_ctx, struct tls_connection *conn, const struct wpabuf *in_data, struct wpabuf **appl_data) { return NULL; } struct wpabuf * tls_connection_server_handshake(void *tls_ctx, struct tls_connection *conn, const struct wpabuf *in_data, struct wpabuf **appl_data) { return NULL; } struct wpabuf * tls_connection_encrypt(void *tls_ctx, struct tls_connection *conn, const struct wpabuf *in_data) { return NULL; } struct wpabuf * tls_connection_decrypt(void *tls_ctx, struct tls_connection *conn, const struct wpabuf *in_data) { return NULL; } int tls_connection_resumed(void *tls_ctx, struct tls_connection *conn) { return 0; } int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn, u8 *ciphers) { return -1; } int tls_get_cipher(void *tls_ctx, struct tls_connection *conn, char *buf, size_t buflen) { return -1; } int tls_connection_enable_workaround(void *tls_ctx, struct tls_connection *conn) { return -1; } int tls_connection_client_hello_ext(void *tls_ctx, struct tls_connection *conn, int ext_type, const u8 *data, size_t data_len) { return -1; } int tls_connection_get_failed(void *tls_ctx, struct tls_connection *conn) { return 0; } int tls_connection_get_read_alerts(void *tls_ctx, struct tls_connection *conn) { return 0; } int tls_connection_get_write_alerts(void *tls_ctx, struct tls_connection *conn) { return 0; } int tls_connection_get_keyblock_size(void *tls_ctx, struct tls_connection *conn) { return -1; } unsigned int tls_capabilities(void *tls_ctx) { return 0; } wpa-2.1/src/crypto/tls_nss.c000066400000000000000000000346561227414666700161400ustar00rootroot00000000000000/* * SSL/TLS interface functions for NSS * Copyright (c) 2009, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "includes.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "common.h" #include "tls.h" static int tls_nss_ref_count = 0; static PRDescIdentity nss_layer_id; struct tls_connection { PRFileDesc *fd; int established; int verify_peer; u8 *push_buf, *pull_buf, *pull_buf_offset; size_t push_buf_len, pull_buf_len; }; static PRStatus nss_io_close(PRFileDesc *fd) { wpa_printf(MSG_DEBUG, "NSS: I/O close"); return PR_SUCCESS; } static PRInt32 nss_io_read(PRFileDesc *fd, void *buf, PRInt32 amount) { wpa_printf(MSG_DEBUG, "NSS: I/O read(%d)", amount); return PR_FAILURE; } static PRInt32 nss_io_write(PRFileDesc *fd, const void *buf, PRInt32 amount) { wpa_printf(MSG_DEBUG, "NSS: I/O write(%d)", amount); return PR_FAILURE; } static PRInt32 nss_io_writev(PRFileDesc *fd, const PRIOVec *iov, PRInt32 iov_size, PRIntervalTime timeout) { wpa_printf(MSG_DEBUG, "NSS: I/O writev(%d)", iov_size); return PR_FAILURE; } static PRInt32 nss_io_recv(PRFileDesc *fd, void *buf, PRInt32 amount, PRIntn flags, PRIntervalTime timeout) { struct tls_connection *conn = (struct tls_connection *) fd->secret; u8 *end; wpa_printf(MSG_DEBUG, "NSS: I/O recv(%d)", amount); if (conn->pull_buf == NULL) { wpa_printf(MSG_DEBUG, "NSS: No data available to be read yet"); return PR_FAILURE; } end = conn->pull_buf + conn->pull_buf_len; if (end - conn->pull_buf_offset < amount) amount = end - conn->pull_buf_offset; os_memcpy(buf, conn->pull_buf_offset, amount); conn->pull_buf_offset += amount; if (conn->pull_buf_offset == end) { wpa_printf(MSG_DEBUG, "%s - pull_buf consumed", __func__); os_free(conn->pull_buf); conn->pull_buf = conn->pull_buf_offset = NULL; conn->pull_buf_len = 0; } else { wpa_printf(MSG_DEBUG, "%s - %lu bytes remaining in pull_buf", __func__, (unsigned long) (end - conn->pull_buf_offset)); } return amount; } static PRInt32 nss_io_send(PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags, PRIntervalTime timeout) { struct tls_connection *conn = (struct tls_connection *) fd->secret; u8 *nbuf; wpa_printf(MSG_DEBUG, "NSS: I/O %s", __func__); wpa_hexdump(MSG_MSGDUMP, "NSS: I/O send data", buf, amount); nbuf = os_realloc(conn->push_buf, conn->push_buf_len + amount); if (nbuf == NULL) { wpa_printf(MSG_ERROR, "NSS: Failed to allocate memory for the " "data to be sent"); return PR_FAILURE; } os_memcpy(nbuf + conn->push_buf_len, buf, amount); conn->push_buf = nbuf; conn->push_buf_len += amount; return amount; } static PRInt32 nss_io_recvfrom(PRFileDesc *fd, void *buf, PRInt32 amount, PRIntn flags, PRNetAddr *addr, PRIntervalTime timeout) { wpa_printf(MSG_DEBUG, "NSS: I/O %s", __func__); return PR_FAILURE; } static PRInt32 nss_io_sendto(PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags, const PRNetAddr *addr, PRIntervalTime timeout) { wpa_printf(MSG_DEBUG, "NSS: I/O %s", __func__); return PR_FAILURE; } static PRStatus nss_io_getpeername(PRFileDesc *fd, PRNetAddr *addr) { wpa_printf(MSG_DEBUG, "NSS: I/O getpeername"); /* * It Looks like NSS only supports IPv4 and IPv6 TCP sockets. Provide a * fake IPv4 address to work around this even though we are not really * using TCP. */ os_memset(addr, 0, sizeof(*addr)); addr->inet.family = PR_AF_INET; return PR_SUCCESS; } static PRStatus nss_io_getsocketoption(PRFileDesc *fd, PRSocketOptionData *data) { switch (data->option) { case PR_SockOpt_Nonblocking: wpa_printf(MSG_DEBUG, "NSS: I/O getsocketoption(Nonblocking)"); data->value.non_blocking = PR_TRUE; return PR_SUCCESS; default: wpa_printf(MSG_DEBUG, "NSS: I/O getsocketoption(%d)", data->option); return PR_FAILURE; } } static const PRIOMethods nss_io = { PR_DESC_LAYERED, nss_io_close, nss_io_read, nss_io_write, NULL /* available */, NULL /* available64 */, NULL /* fsync */, NULL /* fseek */, NULL /* fseek64 */, NULL /* fileinfo */, NULL /* fileinfo64 */, nss_io_writev, NULL /* connect */, NULL /* accept */, NULL /* bind */, NULL /* listen */, NULL /* shutdown */, nss_io_recv, nss_io_send, nss_io_recvfrom, nss_io_sendto, NULL /* poll */, NULL /* acceptread */, NULL /* transmitfile */, NULL /* getsockname */, nss_io_getpeername, NULL /* reserved_fn_6 */, NULL /* reserved_fn_5 */, nss_io_getsocketoption, NULL /* setsocketoption */, NULL /* sendfile */, NULL /* connectcontinue */, NULL /* reserved_fn_3 */, NULL /* reserved_fn_2 */, NULL /* reserved_fn_1 */, NULL /* reserved_fn_0 */ }; static char * nss_password_cb(PK11SlotInfo *slot, PRBool retry, void *arg) { wpa_printf(MSG_ERROR, "NSS: TODO - %s", __func__); return NULL; } void * tls_init(const struct tls_config *conf) { char *dir; tls_nss_ref_count++; if (tls_nss_ref_count > 1) return (void *) 1; PR_Init(PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1); nss_layer_id = PR_GetUniqueIdentity("wpa_supplicant"); PK11_SetPasswordFunc(nss_password_cb); dir = getenv("SSL_DIR"); if (dir) { if (NSS_Init(dir) != SECSuccess) { wpa_printf(MSG_ERROR, "NSS: NSS_Init(cert_dir=%s) " "failed", dir); return NULL; } } else { if (NSS_NoDB_Init(NULL) != SECSuccess) { wpa_printf(MSG_ERROR, "NSS: NSS_NoDB_Init(NULL) " "failed"); return NULL; } } if (SSL_OptionSetDefault(SSL_V2_COMPATIBLE_HELLO, PR_FALSE) != SECSuccess || SSL_OptionSetDefault(SSL_ENABLE_SSL3, PR_FALSE) != SECSuccess || SSL_OptionSetDefault(SSL_ENABLE_SSL2, PR_FALSE) != SECSuccess || SSL_OptionSetDefault(SSL_ENABLE_TLS, PR_TRUE) != SECSuccess) { wpa_printf(MSG_ERROR, "NSS: SSL_OptionSetDefault failed"); return NULL; } if (NSS_SetDomesticPolicy() != SECSuccess) { wpa_printf(MSG_ERROR, "NSS: NSS_SetDomesticPolicy() failed"); return NULL; } return (void *) 1; } void tls_deinit(void *ssl_ctx) { tls_nss_ref_count--; if (tls_nss_ref_count == 0) { if (NSS_Shutdown() != SECSuccess) wpa_printf(MSG_ERROR, "NSS: NSS_Shutdown() failed"); } } int tls_get_errors(void *tls_ctx) { return 0; } static SECStatus nss_bad_cert_cb(void *arg, PRFileDesc *fd) { struct tls_connection *conn = arg; SECStatus res = SECSuccess; PRErrorCode err; CERTCertificate *cert; char *subject, *issuer; err = PR_GetError(); if (IS_SEC_ERROR(err)) wpa_printf(MSG_DEBUG, "NSS: Bad Server Certificate (sec err " "%d)", err - SEC_ERROR_BASE); else wpa_printf(MSG_DEBUG, "NSS: Bad Server Certificate (err %d)", err); cert = SSL_PeerCertificate(fd); subject = CERT_NameToAscii(&cert->subject); issuer = CERT_NameToAscii(&cert->issuer); wpa_printf(MSG_DEBUG, "NSS: Peer certificate subject='%s' issuer='%s'", subject, issuer); CERT_DestroyCertificate(cert); PR_Free(subject); PR_Free(issuer); if (conn->verify_peer) res = SECFailure; return res; } static void nss_handshake_cb(PRFileDesc *fd, void *client_data) { struct tls_connection *conn = client_data; wpa_printf(MSG_DEBUG, "NSS: Handshake completed"); conn->established = 1; } struct tls_connection * tls_connection_init(void *tls_ctx) { struct tls_connection *conn; conn = os_zalloc(sizeof(*conn)); if (conn == NULL) return NULL; conn->fd = PR_CreateIOLayerStub(nss_layer_id, &nss_io); if (conn->fd == NULL) { os_free(conn); return NULL; } conn->fd->secret = (void *) conn; conn->fd = SSL_ImportFD(NULL, conn->fd); if (conn->fd == NULL) { os_free(conn); return NULL; } if (SSL_OptionSet(conn->fd, SSL_SECURITY, PR_TRUE) != SECSuccess || SSL_OptionSet(conn->fd, SSL_HANDSHAKE_AS_CLIENT, PR_TRUE) != SECSuccess || SSL_OptionSet(conn->fd, SSL_HANDSHAKE_AS_SERVER, PR_FALSE) != SECSuccess || SSL_OptionSet(conn->fd, SSL_ENABLE_TLS, PR_TRUE) != SECSuccess || SSL_BadCertHook(conn->fd, nss_bad_cert_cb, conn) != SECSuccess || SSL_HandshakeCallback(conn->fd, nss_handshake_cb, conn) != SECSuccess) { wpa_printf(MSG_ERROR, "NSS: Failed to set options"); PR_Close(conn->fd); os_free(conn); return NULL; } SSL_ResetHandshake(conn->fd, PR_FALSE); return conn; } void tls_connection_deinit(void *tls_ctx, struct tls_connection *conn) { PR_Close(conn->fd); os_free(conn->push_buf); os_free(conn->pull_buf); os_free(conn); } int tls_connection_established(void *tls_ctx, struct tls_connection *conn) { return conn->established; } int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn) { return -1; } int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, const struct tls_connection_params *params) { wpa_printf(MSG_ERROR, "NSS: TODO - %s", __func__); return 0; } int tls_global_set_params(void *tls_ctx, const struct tls_connection_params *params) { return -1; } int tls_global_set_verify(void *tls_ctx, int check_crl) { return -1; } int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn, int verify_peer) { conn->verify_peer = verify_peer; return 0; } int tls_connection_get_keys(void *tls_ctx, struct tls_connection *conn, struct tls_keys *keys) { /* NSS does not export master secret or client/server random. */ return -1; } int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, const char *label, int server_random_first, u8 *out, size_t out_len) { if (conn == NULL || server_random_first) { wpa_printf(MSG_INFO, "NSS: Unsupported PRF request " "(server_random_first=%d)", server_random_first); return -1; } if (SSL_ExportKeyingMaterial(conn->fd, label, NULL, 0, out, out_len) != SECSuccess) { wpa_printf(MSG_INFO, "NSS: Failed to use TLS extractor " "(label='%s' out_len=%d", label, (int) out_len); return -1; } return 0; } struct wpabuf * tls_connection_handshake(void *tls_ctx, struct tls_connection *conn, const struct wpabuf *in_data, struct wpabuf **appl_data) { struct wpabuf *out_data; wpa_printf(MSG_DEBUG, "NSS: handshake: in_len=%u", in_data ? (unsigned int) wpabuf_len(in_data) : 0); if (appl_data) *appl_data = NULL; if (in_data && wpabuf_len(in_data) > 0) { if (conn->pull_buf) { wpa_printf(MSG_DEBUG, "%s - %lu bytes remaining in " "pull_buf", __func__, (unsigned long) conn->pull_buf_len); os_free(conn->pull_buf); } conn->pull_buf = os_malloc(wpabuf_len(in_data)); if (conn->pull_buf == NULL) return NULL; os_memcpy(conn->pull_buf, wpabuf_head(in_data), wpabuf_len(in_data)); conn->pull_buf_offset = conn->pull_buf; conn->pull_buf_len = wpabuf_len(in_data); } SSL_ForceHandshake(conn->fd); if (conn->established && conn->push_buf == NULL) { /* Need to return something to get final TLS ACK. */ conn->push_buf = os_malloc(1); } if (conn->push_buf == NULL) return NULL; out_data = wpabuf_alloc_ext_data(conn->push_buf, conn->push_buf_len); if (out_data == NULL) os_free(conn->push_buf); conn->push_buf = NULL; conn->push_buf_len = 0; return out_data; } struct wpabuf * tls_connection_server_handshake(void *tls_ctx, struct tls_connection *conn, const struct wpabuf *in_data, struct wpabuf **appl_data) { return NULL; } struct wpabuf * tls_connection_encrypt(void *tls_ctx, struct tls_connection *conn, const struct wpabuf *in_data) { PRInt32 res; struct wpabuf *buf; wpa_printf(MSG_DEBUG, "NSS: encrypt %d bytes", (int) wpabuf_len(in_data)); res = PR_Send(conn->fd, wpabuf_head(in_data), wpabuf_len(in_data), 0, 0); if (res < 0) { wpa_printf(MSG_ERROR, "NSS: Encryption failed"); return NULL; } if (conn->push_buf == NULL) return NULL; buf = wpabuf_alloc_ext_data(conn->push_buf, conn->push_buf_len); if (buf == NULL) os_free(conn->push_buf); conn->push_buf = NULL; conn->push_buf_len = 0; return buf; } struct wpabuf * tls_connection_decrypt(void *tls_ctx, struct tls_connection *conn, const struct wpabuf *in_data) { PRInt32 res; struct wpabuf *out; wpa_printf(MSG_DEBUG, "NSS: decrypt %d bytes", (int) wpabuf_len(in_data)); if (conn->pull_buf) { wpa_printf(MSG_DEBUG, "%s - %lu bytes remaining in " "pull_buf", __func__, (unsigned long) conn->pull_buf_len); os_free(conn->pull_buf); } conn->pull_buf = os_malloc(wpabuf_len(in_data)); if (conn->pull_buf == NULL) return NULL; os_memcpy(conn->pull_buf, wpabuf_head(in_data), wpabuf_len(in_data)); conn->pull_buf_offset = conn->pull_buf; conn->pull_buf_len = wpabuf_len(in_data); /* * Even though we try to disable TLS compression, it is possible that * this cannot be done with all TLS libraries. Add extra buffer space * to handle the possibility of the decrypted data being longer than * input data. */ out = wpabuf_alloc((wpabuf_len(in_data) + 500) * 3); if (out == NULL) return NULL; res = PR_Recv(conn->fd, wpabuf_mhead(out), wpabuf_size(out), 0, 0); wpa_printf(MSG_DEBUG, "NSS: PR_Recv: %d", res); if (res < 0) { wpabuf_free(out); return NULL; } wpabuf_put(out, res); return out; } int tls_connection_resumed(void *tls_ctx, struct tls_connection *conn) { return 0; } int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn, u8 *ciphers) { return -1; } int tls_get_cipher(void *tls_ctx, struct tls_connection *conn, char *buf, size_t buflen) { return -1; } int tls_connection_enable_workaround(void *tls_ctx, struct tls_connection *conn) { return -1; } int tls_connection_client_hello_ext(void *tls_ctx, struct tls_connection *conn, int ext_type, const u8 *data, size_t data_len) { return -1; } int tls_connection_get_failed(void *tls_ctx, struct tls_connection *conn) { return 0; } int tls_connection_get_read_alerts(void *tls_ctx, struct tls_connection *conn) { return 0; } int tls_connection_get_write_alerts(void *tls_ctx, struct tls_connection *conn) { return 0; } int tls_connection_get_keyblock_size(void *tls_ctx, struct tls_connection *conn) { return -1; } unsigned int tls_capabilities(void *tls_ctx) { return 0; } int tls_connection_set_session_ticket_cb(void *tls_ctx, struct tls_connection *conn, tls_session_ticket_cb cb, void *ctx) { return -1; } wpa-2.1/src/crypto/tls_openssl.c000066400000000000000000002525671227414666700170230ustar00rootroot00000000000000/* * SSL/TLS interface functions for OpenSSL * Copyright (c) 2004-2013, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "includes.h" #ifndef CONFIG_SMARTCARD #ifndef OPENSSL_NO_ENGINE #ifndef ANDROID #define OPENSSL_NO_ENGINE #endif #endif #endif #include #include #include #include #ifndef OPENSSL_NO_ENGINE #include #endif /* OPENSSL_NO_ENGINE */ #include "common.h" #include "crypto.h" #include "tls.h" #if OPENSSL_VERSION_NUMBER >= 0x0090800fL #define OPENSSL_d2i_TYPE const unsigned char ** #else #define OPENSSL_d2i_TYPE unsigned char ** #endif #if defined(SSL_CTX_get_app_data) && defined(SSL_CTX_set_app_data) #define OPENSSL_SUPPORTS_CTX_APP_DATA #endif #ifdef SSL_F_SSL_SET_SESSION_TICKET_EXT #ifdef SSL_OP_NO_TICKET /* * Session ticket override patch was merged into OpenSSL 0.9.9 tree on * 2008-11-15. This version uses a bit different API compared to the old patch. */ #define CONFIG_OPENSSL_TICKET_OVERRIDE #endif #endif #ifdef SSL_set_tlsext_status_type #ifndef OPENSSL_NO_TLSEXT #define HAVE_OCSP #include #endif /* OPENSSL_NO_TLSEXT */ #endif /* SSL_set_tlsext_status_type */ #ifdef ANDROID #include #include static BIO * BIO_from_keystore(const char *key) { BIO *bio = NULL; uint8_t *value = NULL; int length = keystore_get(key, strlen(key), &value); if (length != -1 && (bio = BIO_new(BIO_s_mem())) != NULL) BIO_write(bio, value, length); free(value); return bio; } #endif /* ANDROID */ static int tls_openssl_ref_count = 0; struct tls_context { void (*event_cb)(void *ctx, enum tls_event ev, union tls_event_data *data); void *cb_ctx; int cert_in_cb; char *ocsp_stapling_response; }; static struct tls_context *tls_global = NULL; struct tls_connection { struct tls_context *context; SSL *ssl; BIO *ssl_in, *ssl_out; #ifndef OPENSSL_NO_ENGINE ENGINE *engine; /* functional reference to the engine */ EVP_PKEY *private_key; /* the private key if using engine */ #endif /* OPENSSL_NO_ENGINE */ char *subject_match, *altsubject_match, *suffix_match; int read_alerts, write_alerts, failed; tls_session_ticket_cb session_ticket_cb; void *session_ticket_cb_ctx; /* SessionTicket received from OpenSSL hello_extension_cb (server) */ u8 *session_ticket; size_t session_ticket_len; unsigned int ca_cert_verify:1; unsigned int cert_probe:1; unsigned int server_cert_only:1; unsigned int server:1; u8 srv_cert_hash[32]; unsigned int flags; X509 *peer_cert; X509 *peer_issuer; X509 *peer_issuer_issuer; }; static struct tls_context * tls_context_new(const struct tls_config *conf) { struct tls_context *context = os_zalloc(sizeof(*context)); if (context == NULL) return NULL; if (conf) { context->event_cb = conf->event_cb; context->cb_ctx = conf->cb_ctx; context->cert_in_cb = conf->cert_in_cb; } return context; } #ifdef CONFIG_NO_STDOUT_DEBUG static void _tls_show_errors(void) { unsigned long err; while ((err = ERR_get_error())) { /* Just ignore the errors, since stdout is disabled */ } } #define tls_show_errors(l, f, t) _tls_show_errors() #else /* CONFIG_NO_STDOUT_DEBUG */ static void tls_show_errors(int level, const char *func, const char *txt) { unsigned long err; wpa_printf(level, "OpenSSL: %s - %s %s", func, txt, ERR_error_string(ERR_get_error(), NULL)); while ((err = ERR_get_error())) { wpa_printf(MSG_INFO, "OpenSSL: pending error: %s", ERR_error_string(err, NULL)); } } #endif /* CONFIG_NO_STDOUT_DEBUG */ #ifdef CONFIG_NATIVE_WINDOWS /* Windows CryptoAPI and access to certificate stores */ #include #ifdef __MINGW32_VERSION /* * MinGW does not yet include all the needed definitions for CryptoAPI, so * define here whatever extra is needed. */ #define CERT_SYSTEM_STORE_CURRENT_USER (1 << 16) #define CERT_STORE_READONLY_FLAG 0x00008000 #define CERT_STORE_OPEN_EXISTING_FLAG 0x00004000 #endif /* __MINGW32_VERSION */ struct cryptoapi_rsa_data { const CERT_CONTEXT *cert; HCRYPTPROV crypt_prov; DWORD key_spec; BOOL free_crypt_prov; }; static void cryptoapi_error(const char *msg) { wpa_printf(MSG_INFO, "CryptoAPI: %s; err=%u", msg, (unsigned int) GetLastError()); } static int cryptoapi_rsa_pub_enc(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, int padding) { wpa_printf(MSG_DEBUG, "%s - not implemented", __func__); return 0; } static int cryptoapi_rsa_pub_dec(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, int padding) { wpa_printf(MSG_DEBUG, "%s - not implemented", __func__); return 0; } static int cryptoapi_rsa_priv_enc(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, int padding) { struct cryptoapi_rsa_data *priv = (struct cryptoapi_rsa_data *) rsa->meth->app_data; HCRYPTHASH hash; DWORD hash_size, len, i; unsigned char *buf = NULL; int ret = 0; if (priv == NULL) { RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT, ERR_R_PASSED_NULL_PARAMETER); return 0; } if (padding != RSA_PKCS1_PADDING) { RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT, RSA_R_UNKNOWN_PADDING_TYPE); return 0; } if (flen != 16 /* MD5 */ + 20 /* SHA-1 */) { wpa_printf(MSG_INFO, "%s - only MD5-SHA1 hash supported", __func__); RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT, RSA_R_INVALID_MESSAGE_LENGTH); return 0; } if (!CryptCreateHash(priv->crypt_prov, CALG_SSL3_SHAMD5, 0, 0, &hash)) { cryptoapi_error("CryptCreateHash failed"); return 0; } len = sizeof(hash_size); if (!CryptGetHashParam(hash, HP_HASHSIZE, (BYTE *) &hash_size, &len, 0)) { cryptoapi_error("CryptGetHashParam failed"); goto err; } if ((int) hash_size != flen) { wpa_printf(MSG_INFO, "CryptoAPI: Invalid hash size (%u != %d)", (unsigned) hash_size, flen); RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT, RSA_R_INVALID_MESSAGE_LENGTH); goto err; } if (!CryptSetHashParam(hash, HP_HASHVAL, (BYTE * ) from, 0)) { cryptoapi_error("CryptSetHashParam failed"); goto err; } len = RSA_size(rsa); buf = os_malloc(len); if (buf == NULL) { RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT, ERR_R_MALLOC_FAILURE); goto err; } if (!CryptSignHash(hash, priv->key_spec, NULL, 0, buf, &len)) { cryptoapi_error("CryptSignHash failed"); goto err; } for (i = 0; i < len; i++) to[i] = buf[len - i - 1]; ret = len; err: os_free(buf); CryptDestroyHash(hash); return ret; } static int cryptoapi_rsa_priv_dec(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, int padding) { wpa_printf(MSG_DEBUG, "%s - not implemented", __func__); return 0; } static void cryptoapi_free_data(struct cryptoapi_rsa_data *priv) { if (priv == NULL) return; if (priv->crypt_prov && priv->free_crypt_prov) CryptReleaseContext(priv->crypt_prov, 0); if (priv->cert) CertFreeCertificateContext(priv->cert); os_free(priv); } static int cryptoapi_finish(RSA *rsa) { cryptoapi_free_data((struct cryptoapi_rsa_data *) rsa->meth->app_data); os_free((void *) rsa->meth); rsa->meth = NULL; return 1; } static const CERT_CONTEXT * cryptoapi_find_cert(const char *name, DWORD store) { HCERTSTORE cs; const CERT_CONTEXT *ret = NULL; cs = CertOpenStore((LPCSTR) CERT_STORE_PROV_SYSTEM, 0, 0, store | CERT_STORE_OPEN_EXISTING_FLAG | CERT_STORE_READONLY_FLAG, L"MY"); if (cs == NULL) { cryptoapi_error("Failed to open 'My system store'"); return NULL; } if (strncmp(name, "cert://", 7) == 0) { unsigned short wbuf[255]; MultiByteToWideChar(CP_ACP, 0, name + 7, -1, wbuf, 255); ret = CertFindCertificateInStore(cs, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_FIND_SUBJECT_STR, wbuf, NULL); } else if (strncmp(name, "hash://", 7) == 0) { CRYPT_HASH_BLOB blob; int len; const char *hash = name + 7; unsigned char *buf; len = os_strlen(hash) / 2; buf = os_malloc(len); if (buf && hexstr2bin(hash, buf, len) == 0) { blob.cbData = len; blob.pbData = buf; ret = CertFindCertificateInStore(cs, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_FIND_HASH, &blob, NULL); } os_free(buf); } CertCloseStore(cs, 0); return ret; } static int tls_cryptoapi_cert(SSL *ssl, const char *name) { X509 *cert = NULL; RSA *rsa = NULL, *pub_rsa; struct cryptoapi_rsa_data *priv; RSA_METHOD *rsa_meth; if (name == NULL || (strncmp(name, "cert://", 7) != 0 && strncmp(name, "hash://", 7) != 0)) return -1; priv = os_zalloc(sizeof(*priv)); rsa_meth = os_zalloc(sizeof(*rsa_meth)); if (priv == NULL || rsa_meth == NULL) { wpa_printf(MSG_WARNING, "CryptoAPI: Failed to allocate memory " "for CryptoAPI RSA method"); os_free(priv); os_free(rsa_meth); return -1; } priv->cert = cryptoapi_find_cert(name, CERT_SYSTEM_STORE_CURRENT_USER); if (priv->cert == NULL) { priv->cert = cryptoapi_find_cert( name, CERT_SYSTEM_STORE_LOCAL_MACHINE); } if (priv->cert == NULL) { wpa_printf(MSG_INFO, "CryptoAPI: Could not find certificate " "'%s'", name); goto err; } cert = d2i_X509(NULL, (OPENSSL_d2i_TYPE) &priv->cert->pbCertEncoded, priv->cert->cbCertEncoded); if (cert == NULL) { wpa_printf(MSG_INFO, "CryptoAPI: Could not process X509 DER " "encoding"); goto err; } if (!CryptAcquireCertificatePrivateKey(priv->cert, CRYPT_ACQUIRE_COMPARE_KEY_FLAG, NULL, &priv->crypt_prov, &priv->key_spec, &priv->free_crypt_prov)) { cryptoapi_error("Failed to acquire a private key for the " "certificate"); goto err; } rsa_meth->name = "Microsoft CryptoAPI RSA Method"; rsa_meth->rsa_pub_enc = cryptoapi_rsa_pub_enc; rsa_meth->rsa_pub_dec = cryptoapi_rsa_pub_dec; rsa_meth->rsa_priv_enc = cryptoapi_rsa_priv_enc; rsa_meth->rsa_priv_dec = cryptoapi_rsa_priv_dec; rsa_meth->finish = cryptoapi_finish; rsa_meth->flags = RSA_METHOD_FLAG_NO_CHECK; rsa_meth->app_data = (char *) priv; rsa = RSA_new(); if (rsa == NULL) { SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE, ERR_R_MALLOC_FAILURE); goto err; } if (!SSL_use_certificate(ssl, cert)) { RSA_free(rsa); rsa = NULL; goto err; } pub_rsa = cert->cert_info->key->pkey->pkey.rsa; X509_free(cert); cert = NULL; rsa->n = BN_dup(pub_rsa->n); rsa->e = BN_dup(pub_rsa->e); if (!RSA_set_method(rsa, rsa_meth)) goto err; if (!SSL_use_RSAPrivateKey(ssl, rsa)) goto err; RSA_free(rsa); return 0; err: if (cert) X509_free(cert); if (rsa) RSA_free(rsa); else { os_free(rsa_meth); cryptoapi_free_data(priv); } return -1; } static int tls_cryptoapi_ca_cert(SSL_CTX *ssl_ctx, SSL *ssl, const char *name) { HCERTSTORE cs; PCCERT_CONTEXT ctx = NULL; X509 *cert; char buf[128]; const char *store; #ifdef UNICODE WCHAR *wstore; #endif /* UNICODE */ if (name == NULL || strncmp(name, "cert_store://", 13) != 0) return -1; store = name + 13; #ifdef UNICODE wstore = os_malloc((os_strlen(store) + 1) * sizeof(WCHAR)); if (wstore == NULL) return -1; wsprintf(wstore, L"%S", store); cs = CertOpenSystemStore(0, wstore); os_free(wstore); #else /* UNICODE */ cs = CertOpenSystemStore(0, store); #endif /* UNICODE */ if (cs == NULL) { wpa_printf(MSG_DEBUG, "%s: failed to open system cert store " "'%s': error=%d", __func__, store, (int) GetLastError()); return -1; } while ((ctx = CertEnumCertificatesInStore(cs, ctx))) { cert = d2i_X509(NULL, (OPENSSL_d2i_TYPE) &ctx->pbCertEncoded, ctx->cbCertEncoded); if (cert == NULL) { wpa_printf(MSG_INFO, "CryptoAPI: Could not process " "X509 DER encoding for CA cert"); continue; } X509_NAME_oneline(X509_get_subject_name(cert), buf, sizeof(buf)); wpa_printf(MSG_DEBUG, "OpenSSL: Loaded CA certificate for " "system certificate store: subject='%s'", buf); if (!X509_STORE_add_cert(ssl_ctx->cert_store, cert)) { tls_show_errors(MSG_WARNING, __func__, "Failed to add ca_cert to OpenSSL " "certificate store"); } X509_free(cert); } if (!CertCloseStore(cs, 0)) { wpa_printf(MSG_DEBUG, "%s: failed to close system cert store " "'%s': error=%d", __func__, name + 13, (int) GetLastError()); } return 0; } #else /* CONFIG_NATIVE_WINDOWS */ static int tls_cryptoapi_cert(SSL *ssl, const char *name) { return -1; } #endif /* CONFIG_NATIVE_WINDOWS */ static void ssl_info_cb(const SSL *ssl, int where, int ret) { const char *str; int w; wpa_printf(MSG_DEBUG, "SSL: (where=0x%x ret=0x%x)", where, ret); w = where & ~SSL_ST_MASK; if (w & SSL_ST_CONNECT) str = "SSL_connect"; else if (w & SSL_ST_ACCEPT) str = "SSL_accept"; else str = "undefined"; if (where & SSL_CB_LOOP) { wpa_printf(MSG_DEBUG, "SSL: %s:%s", str, SSL_state_string_long(ssl)); } else if (where & SSL_CB_ALERT) { struct tls_connection *conn = SSL_get_app_data((SSL *) ssl); wpa_printf(MSG_INFO, "SSL: SSL3 alert: %s:%s:%s", where & SSL_CB_READ ? "read (remote end reported an error)" : "write (local SSL3 detected an error)", SSL_alert_type_string_long(ret), SSL_alert_desc_string_long(ret)); if ((ret >> 8) == SSL3_AL_FATAL) { if (where & SSL_CB_READ) conn->read_alerts++; else conn->write_alerts++; } if (conn->context->event_cb != NULL) { union tls_event_data ev; struct tls_context *context = conn->context; os_memset(&ev, 0, sizeof(ev)); ev.alert.is_local = !(where & SSL_CB_READ); ev.alert.type = SSL_alert_type_string_long(ret); ev.alert.description = SSL_alert_desc_string_long(ret); context->event_cb(context->cb_ctx, TLS_ALERT, &ev); } } else if (where & SSL_CB_EXIT && ret <= 0) { wpa_printf(MSG_DEBUG, "SSL: %s:%s in %s", str, ret == 0 ? "failed" : "error", SSL_state_string_long(ssl)); } } #ifndef OPENSSL_NO_ENGINE /** * tls_engine_load_dynamic_generic - load any openssl engine * @pre: an array of commands and values that load an engine initialized * in the engine specific function * @post: an array of commands and values that initialize an already loaded * engine (or %NULL if not required) * @id: the engine id of the engine to load (only required if post is not %NULL * * This function is a generic function that loads any openssl engine. * * Returns: 0 on success, -1 on failure */ static int tls_engine_load_dynamic_generic(const char *pre[], const char *post[], const char *id) { ENGINE *engine; const char *dynamic_id = "dynamic"; engine = ENGINE_by_id(id); if (engine) { ENGINE_free(engine); wpa_printf(MSG_DEBUG, "ENGINE: engine '%s' is already " "available", id); return 0; } ERR_clear_error(); engine = ENGINE_by_id(dynamic_id); if (engine == NULL) { wpa_printf(MSG_INFO, "ENGINE: Can't find engine %s [%s]", dynamic_id, ERR_error_string(ERR_get_error(), NULL)); return -1; } /* Perform the pre commands. This will load the engine. */ while (pre && pre[0]) { wpa_printf(MSG_DEBUG, "ENGINE: '%s' '%s'", pre[0], pre[1]); if (ENGINE_ctrl_cmd_string(engine, pre[0], pre[1], 0) == 0) { wpa_printf(MSG_INFO, "ENGINE: ctrl cmd_string failed: " "%s %s [%s]", pre[0], pre[1], ERR_error_string(ERR_get_error(), NULL)); ENGINE_free(engine); return -1; } pre += 2; } /* * Free the reference to the "dynamic" engine. The loaded engine can * now be looked up using ENGINE_by_id(). */ ENGINE_free(engine); engine = ENGINE_by_id(id); if (engine == NULL) { wpa_printf(MSG_INFO, "ENGINE: Can't find engine %s [%s]", id, ERR_error_string(ERR_get_error(), NULL)); return -1; } while (post && post[0]) { wpa_printf(MSG_DEBUG, "ENGINE: '%s' '%s'", post[0], post[1]); if (ENGINE_ctrl_cmd_string(engine, post[0], post[1], 0) == 0) { wpa_printf(MSG_DEBUG, "ENGINE: ctrl cmd_string failed:" " %s %s [%s]", post[0], post[1], ERR_error_string(ERR_get_error(), NULL)); ENGINE_remove(engine); ENGINE_free(engine); return -1; } post += 2; } ENGINE_free(engine); return 0; } /** * tls_engine_load_dynamic_pkcs11 - load the pkcs11 engine provided by opensc * @pkcs11_so_path: pksc11_so_path from the configuration * @pcks11_module_path: pkcs11_module_path from the configuration */ static int tls_engine_load_dynamic_pkcs11(const char *pkcs11_so_path, const char *pkcs11_module_path) { char *engine_id = "pkcs11"; const char *pre_cmd[] = { "SO_PATH", NULL /* pkcs11_so_path */, "ID", NULL /* engine_id */, "LIST_ADD", "1", /* "NO_VCHECK", "1", */ "LOAD", NULL, NULL, NULL }; const char *post_cmd[] = { "MODULE_PATH", NULL /* pkcs11_module_path */, NULL, NULL }; if (!pkcs11_so_path || !pkcs11_module_path) return 0; pre_cmd[1] = pkcs11_so_path; pre_cmd[3] = engine_id; post_cmd[1] = pkcs11_module_path; wpa_printf(MSG_DEBUG, "ENGINE: Loading pkcs11 Engine from %s", pkcs11_so_path); return tls_engine_load_dynamic_generic(pre_cmd, post_cmd, engine_id); } /** * tls_engine_load_dynamic_opensc - load the opensc engine provided by opensc * @opensc_so_path: opensc_so_path from the configuration */ static int tls_engine_load_dynamic_opensc(const char *opensc_so_path) { char *engine_id = "opensc"; const char *pre_cmd[] = { "SO_PATH", NULL /* opensc_so_path */, "ID", NULL /* engine_id */, "LIST_ADD", "1", "LOAD", NULL, NULL, NULL }; if (!opensc_so_path) return 0; pre_cmd[1] = opensc_so_path; pre_cmd[3] = engine_id; wpa_printf(MSG_DEBUG, "ENGINE: Loading OpenSC Engine from %s", opensc_so_path); return tls_engine_load_dynamic_generic(pre_cmd, NULL, engine_id); } #endif /* OPENSSL_NO_ENGINE */ void * tls_init(const struct tls_config *conf) { SSL_CTX *ssl; struct tls_context *context; if (tls_openssl_ref_count == 0) { tls_global = context = tls_context_new(conf); if (context == NULL) return NULL; #ifdef CONFIG_FIPS #ifdef OPENSSL_FIPS if (conf && conf->fips_mode) { if (!FIPS_mode_set(1)) { wpa_printf(MSG_ERROR, "Failed to enable FIPS " "mode"); ERR_load_crypto_strings(); ERR_print_errors_fp(stderr); os_free(tls_global); tls_global = NULL; return NULL; } else wpa_printf(MSG_INFO, "Running in FIPS mode"); } #else /* OPENSSL_FIPS */ if (conf && conf->fips_mode) { wpa_printf(MSG_ERROR, "FIPS mode requested, but not " "supported"); os_free(tls_global); tls_global = NULL; return NULL; } #endif /* OPENSSL_FIPS */ #endif /* CONFIG_FIPS */ SSL_load_error_strings(); SSL_library_init(); #if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) && !defined(OPENSSL_NO_SHA256) EVP_add_digest(EVP_sha256()); #endif /* OPENSSL_NO_SHA256 */ /* TODO: if /dev/urandom is available, PRNG is seeded * automatically. If this is not the case, random data should * be added here. */ #ifdef PKCS12_FUNCS #ifndef OPENSSL_NO_RC2 /* * 40-bit RC2 is commonly used in PKCS#12 files, so enable it. * This is enabled by PKCS12_PBE_add() in OpenSSL 0.9.8 * versions, but it looks like OpenSSL 1.0.0 does not do that * anymore. */ EVP_add_cipher(EVP_rc2_40_cbc()); #endif /* OPENSSL_NO_RC2 */ PKCS12_PBE_add(); #endif /* PKCS12_FUNCS */ } else { context = tls_global; #ifdef OPENSSL_SUPPORTS_CTX_APP_DATA /* Newer OpenSSL can store app-data per-SSL */ context = tls_context_new(conf); if (context == NULL) return NULL; #endif /* OPENSSL_SUPPORTS_CTX_APP_DATA */ } tls_openssl_ref_count++; ssl = SSL_CTX_new(TLSv1_method()); if (ssl == NULL) { tls_openssl_ref_count--; #ifdef OPENSSL_SUPPORTS_CTX_APP_DATA if (context != tls_global) os_free(context); #endif /* OPENSSL_SUPPORTS_CTX_APP_DATA */ if (tls_openssl_ref_count == 0) { os_free(tls_global); tls_global = NULL; } return NULL; } SSL_CTX_set_info_callback(ssl, ssl_info_cb); #ifdef OPENSSL_SUPPORTS_CTX_APP_DATA SSL_CTX_set_app_data(ssl, context); #endif /* OPENSSL_SUPPORTS_CTX_APP_DATA */ #ifndef OPENSSL_NO_ENGINE if (conf && (conf->opensc_engine_path || conf->pkcs11_engine_path || conf->pkcs11_module_path)) { wpa_printf(MSG_DEBUG, "ENGINE: Loading dynamic engine"); ERR_load_ENGINE_strings(); ENGINE_load_dynamic(); if (tls_engine_load_dynamic_opensc(conf->opensc_engine_path) || tls_engine_load_dynamic_pkcs11(conf->pkcs11_engine_path, conf->pkcs11_module_path)) { tls_deinit(ssl); return NULL; } } #endif /* OPENSSL_NO_ENGINE */ return ssl; } void tls_deinit(void *ssl_ctx) { SSL_CTX *ssl = ssl_ctx; #ifdef OPENSSL_SUPPORTS_CTX_APP_DATA struct tls_context *context = SSL_CTX_get_app_data(ssl); if (context != tls_global) os_free(context); #endif /* OPENSSL_SUPPORTS_CTX_APP_DATA */ SSL_CTX_free(ssl); tls_openssl_ref_count--; if (tls_openssl_ref_count == 0) { #ifndef OPENSSL_NO_ENGINE ENGINE_cleanup(); #endif /* OPENSSL_NO_ENGINE */ CRYPTO_cleanup_all_ex_data(); ERR_remove_state(0); ERR_free_strings(); EVP_cleanup(); os_free(tls_global->ocsp_stapling_response); tls_global->ocsp_stapling_response = NULL; os_free(tls_global); tls_global = NULL; } } static int tls_engine_init(struct tls_connection *conn, const char *engine_id, const char *pin, const char *key_id, const char *cert_id, const char *ca_cert_id) { #ifndef OPENSSL_NO_ENGINE int ret = -1; if (engine_id == NULL) { wpa_printf(MSG_ERROR, "ENGINE: Engine ID not set"); return -1; } #ifndef ANDROID if (pin == NULL) { wpa_printf(MSG_ERROR, "ENGINE: Smartcard PIN not set"); return -1; } #endif if (key_id == NULL) { wpa_printf(MSG_ERROR, "ENGINE: Key Id not set"); return -1; } ERR_clear_error(); #ifdef ANDROID ENGINE_load_dynamic(); #endif conn->engine = ENGINE_by_id(engine_id); if (!conn->engine) { wpa_printf(MSG_ERROR, "ENGINE: engine %s not available [%s]", engine_id, ERR_error_string(ERR_get_error(), NULL)); goto err; } if (ENGINE_init(conn->engine) != 1) { wpa_printf(MSG_ERROR, "ENGINE: engine init failed " "(engine: %s) [%s]", engine_id, ERR_error_string(ERR_get_error(), NULL)); goto err; } wpa_printf(MSG_DEBUG, "ENGINE: engine initialized"); #ifndef ANDROID if (ENGINE_ctrl_cmd_string(conn->engine, "PIN", pin, 0) == 0) { wpa_printf(MSG_ERROR, "ENGINE: cannot set pin [%s]", ERR_error_string(ERR_get_error(), NULL)); goto err; } #endif /* load private key first in-case PIN is required for cert */ conn->private_key = ENGINE_load_private_key(conn->engine, key_id, NULL, NULL); if (!conn->private_key) { wpa_printf(MSG_ERROR, "ENGINE: cannot load private key with id" " '%s' [%s]", key_id, ERR_error_string(ERR_get_error(), NULL)); ret = TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED; goto err; } /* handle a certificate and/or CA certificate */ if (cert_id || ca_cert_id) { const char *cmd_name = "LOAD_CERT_CTRL"; /* test if the engine supports a LOAD_CERT_CTRL */ if (!ENGINE_ctrl(conn->engine, ENGINE_CTRL_GET_CMD_FROM_NAME, 0, (void *)cmd_name, NULL)) { wpa_printf(MSG_ERROR, "ENGINE: engine does not support" " loading certificates"); ret = TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED; goto err; } } return 0; err: if (conn->engine) { ENGINE_free(conn->engine); conn->engine = NULL; } if (conn->private_key) { EVP_PKEY_free(conn->private_key); conn->private_key = NULL; } return ret; #else /* OPENSSL_NO_ENGINE */ return 0; #endif /* OPENSSL_NO_ENGINE */ } static void tls_engine_deinit(struct tls_connection *conn) { #ifndef OPENSSL_NO_ENGINE wpa_printf(MSG_DEBUG, "ENGINE: engine deinit"); if (conn->private_key) { EVP_PKEY_free(conn->private_key); conn->private_key = NULL; } if (conn->engine) { ENGINE_finish(conn->engine); conn->engine = NULL; } #endif /* OPENSSL_NO_ENGINE */ } int tls_get_errors(void *ssl_ctx) { int count = 0; unsigned long err; while ((err = ERR_get_error())) { wpa_printf(MSG_INFO, "TLS - SSL error: %s", ERR_error_string(err, NULL)); count++; } return count; } struct tls_connection * tls_connection_init(void *ssl_ctx) { SSL_CTX *ssl = ssl_ctx; struct tls_connection *conn; long options; struct tls_context *context = tls_global; #ifdef OPENSSL_SUPPORTS_CTX_APP_DATA context = SSL_CTX_get_app_data(ssl); #endif /* OPENSSL_SUPPORTS_CTX_APP_DATA */ conn = os_zalloc(sizeof(*conn)); if (conn == NULL) return NULL; conn->ssl = SSL_new(ssl); if (conn->ssl == NULL) { tls_show_errors(MSG_INFO, __func__, "Failed to initialize new SSL connection"); os_free(conn); return NULL; } conn->context = context; SSL_set_app_data(conn->ssl, conn); options = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_SINGLE_DH_USE; #ifdef SSL_OP_NO_COMPRESSION options |= SSL_OP_NO_COMPRESSION; #endif /* SSL_OP_NO_COMPRESSION */ SSL_set_options(conn->ssl, options); conn->ssl_in = BIO_new(BIO_s_mem()); if (!conn->ssl_in) { tls_show_errors(MSG_INFO, __func__, "Failed to create a new BIO for ssl_in"); SSL_free(conn->ssl); os_free(conn); return NULL; } conn->ssl_out = BIO_new(BIO_s_mem()); if (!conn->ssl_out) { tls_show_errors(MSG_INFO, __func__, "Failed to create a new BIO for ssl_out"); SSL_free(conn->ssl); BIO_free(conn->ssl_in); os_free(conn); return NULL; } SSL_set_bio(conn->ssl, conn->ssl_in, conn->ssl_out); return conn; } void tls_connection_deinit(void *ssl_ctx, struct tls_connection *conn) { if (conn == NULL) return; SSL_free(conn->ssl); tls_engine_deinit(conn); os_free(conn->subject_match); os_free(conn->altsubject_match); os_free(conn->suffix_match); os_free(conn->session_ticket); os_free(conn); } int tls_connection_established(void *ssl_ctx, struct tls_connection *conn) { return conn ? SSL_is_init_finished(conn->ssl) : 0; } int tls_connection_shutdown(void *ssl_ctx, struct tls_connection *conn) { if (conn == NULL) return -1; /* Shutdown previous TLS connection without notifying the peer * because the connection was already terminated in practice * and "close notify" shutdown alert would confuse AS. */ SSL_set_quiet_shutdown(conn->ssl, 1); SSL_shutdown(conn->ssl); return 0; } static int tls_match_altsubject_component(X509 *cert, int type, const char *value, size_t len) { GENERAL_NAME *gen; void *ext; int i, found = 0; ext = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL); for (i = 0; ext && i < sk_GENERAL_NAME_num(ext); i++) { gen = sk_GENERAL_NAME_value(ext, i); if (gen->type != type) continue; if (os_strlen((char *) gen->d.ia5->data) == len && os_memcmp(value, gen->d.ia5->data, len) == 0) found++; } return found; } static int tls_match_altsubject(X509 *cert, const char *match) { int type; const char *pos, *end; size_t len; pos = match; do { if (os_strncmp(pos, "EMAIL:", 6) == 0) { type = GEN_EMAIL; pos += 6; } else if (os_strncmp(pos, "DNS:", 4) == 0) { type = GEN_DNS; pos += 4; } else if (os_strncmp(pos, "URI:", 4) == 0) { type = GEN_URI; pos += 4; } else { wpa_printf(MSG_INFO, "TLS: Invalid altSubjectName " "match '%s'", pos); return 0; } end = os_strchr(pos, ';'); while (end) { if (os_strncmp(end + 1, "EMAIL:", 6) == 0 || os_strncmp(end + 1, "DNS:", 4) == 0 || os_strncmp(end + 1, "URI:", 4) == 0) break; end = os_strchr(end + 1, ';'); } if (end) len = end - pos; else len = os_strlen(pos); if (tls_match_altsubject_component(cert, type, pos, len) > 0) return 1; pos = end + 1; } while (end); return 0; } #ifndef CONFIG_NATIVE_WINDOWS static int domain_suffix_match(const u8 *val, size_t len, const char *match) { size_t i, match_len; /* Check for embedded nuls that could mess up suffix matching */ for (i = 0; i < len; i++) { if (val[i] == '\0') { wpa_printf(MSG_DEBUG, "TLS: Embedded null in a string - reject"); return 0; } } match_len = os_strlen(match); if (match_len > len) return 0; if (os_strncasecmp((const char *) val + len - match_len, match, match_len) != 0) return 0; /* no match */ if (match_len == len) return 1; /* exact match */ if (val[len - match_len - 1] == '.') return 1; /* full label match completes suffix match */ wpa_printf(MSG_DEBUG, "TLS: Reject due to incomplete label match"); return 0; } #endif /* CONFIG_NATIVE_WINDOWS */ static int tls_match_suffix(X509 *cert, const char *match) { #ifdef CONFIG_NATIVE_WINDOWS /* wincrypt.h has conflicting X509_NAME definition */ return -1; #else /* CONFIG_NATIVE_WINDOWS */ GENERAL_NAME *gen; void *ext; int i; int dns_name = 0; X509_NAME *name; wpa_printf(MSG_DEBUG, "TLS: Match domain against suffix %s", match); ext = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL); for (i = 0; ext && i < sk_GENERAL_NAME_num(ext); i++) { gen = sk_GENERAL_NAME_value(ext, i); if (gen->type != GEN_DNS) continue; dns_name++; wpa_hexdump_ascii(MSG_DEBUG, "TLS: Certificate dNSName", gen->d.dNSName->data, gen->d.dNSName->length); if (domain_suffix_match(gen->d.dNSName->data, gen->d.dNSName->length, match) == 1) { wpa_printf(MSG_DEBUG, "TLS: Suffix match in dNSName found"); return 1; } } if (dns_name) { wpa_printf(MSG_DEBUG, "TLS: None of the dNSName(s) matched"); return 0; } name = X509_get_subject_name(cert); i = -1; for (;;) { X509_NAME_ENTRY *e; ASN1_STRING *cn; i = X509_NAME_get_index_by_NID(name, NID_commonName, i); if (i == -1) break; e = X509_NAME_get_entry(name, i); if (e == NULL) continue; cn = X509_NAME_ENTRY_get_data(e); if (cn == NULL) continue; wpa_hexdump_ascii(MSG_DEBUG, "TLS: Certificate commonName", cn->data, cn->length); if (domain_suffix_match(cn->data, cn->length, match) == 1) { wpa_printf(MSG_DEBUG, "TLS: Suffix match in commonName found"); return 1; } } wpa_printf(MSG_DEBUG, "TLS: No CommonName suffix match found"); return 0; #endif /* CONFIG_NATIVE_WINDOWS */ } static enum tls_fail_reason openssl_tls_fail_reason(int err) { switch (err) { case X509_V_ERR_CERT_REVOKED: return TLS_FAIL_REVOKED; case X509_V_ERR_CERT_NOT_YET_VALID: case X509_V_ERR_CRL_NOT_YET_VALID: return TLS_FAIL_NOT_YET_VALID; case X509_V_ERR_CERT_HAS_EXPIRED: case X509_V_ERR_CRL_HAS_EXPIRED: return TLS_FAIL_EXPIRED; case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: case X509_V_ERR_UNABLE_TO_GET_CRL: case X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER: case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN: case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE: case X509_V_ERR_CERT_CHAIN_TOO_LONG: case X509_V_ERR_PATH_LENGTH_EXCEEDED: case X509_V_ERR_INVALID_CA: return TLS_FAIL_UNTRUSTED; case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE: case X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE: case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY: case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD: case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD: case X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD: case X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD: case X509_V_ERR_CERT_UNTRUSTED: case X509_V_ERR_CERT_REJECTED: return TLS_FAIL_BAD_CERTIFICATE; default: return TLS_FAIL_UNSPECIFIED; } } static struct wpabuf * get_x509_cert(X509 *cert) { struct wpabuf *buf; u8 *tmp; int cert_len = i2d_X509(cert, NULL); if (cert_len <= 0) return NULL; buf = wpabuf_alloc(cert_len); if (buf == NULL) return NULL; tmp = wpabuf_put(buf, cert_len); i2d_X509(cert, &tmp); return buf; } static void openssl_tls_fail_event(struct tls_connection *conn, X509 *err_cert, int err, int depth, const char *subject, const char *err_str, enum tls_fail_reason reason) { union tls_event_data ev; struct wpabuf *cert = NULL; struct tls_context *context = conn->context; if (context->event_cb == NULL) return; cert = get_x509_cert(err_cert); os_memset(&ev, 0, sizeof(ev)); ev.cert_fail.reason = reason != TLS_FAIL_UNSPECIFIED ? reason : openssl_tls_fail_reason(err); ev.cert_fail.depth = depth; ev.cert_fail.subject = subject; ev.cert_fail.reason_txt = err_str; ev.cert_fail.cert = cert; context->event_cb(context->cb_ctx, TLS_CERT_CHAIN_FAILURE, &ev); wpabuf_free(cert); } static void openssl_tls_cert_event(struct tls_connection *conn, X509 *err_cert, int depth, const char *subject) { struct wpabuf *cert = NULL; union tls_event_data ev; struct tls_context *context = conn->context; #ifdef CONFIG_SHA256 u8 hash[32]; #endif /* CONFIG_SHA256 */ if (context->event_cb == NULL) return; os_memset(&ev, 0, sizeof(ev)); if (conn->cert_probe || context->cert_in_cb) { cert = get_x509_cert(err_cert); ev.peer_cert.cert = cert; } #ifdef CONFIG_SHA256 if (cert) { const u8 *addr[1]; size_t len[1]; addr[0] = wpabuf_head(cert); len[0] = wpabuf_len(cert); if (sha256_vector(1, addr, len, hash) == 0) { ev.peer_cert.hash = hash; ev.peer_cert.hash_len = sizeof(hash); } } #endif /* CONFIG_SHA256 */ ev.peer_cert.depth = depth; ev.peer_cert.subject = subject; context->event_cb(context->cb_ctx, TLS_PEER_CERTIFICATE, &ev); wpabuf_free(cert); } static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) { char buf[256]; X509 *err_cert; int err, depth; SSL *ssl; struct tls_connection *conn; struct tls_context *context; char *match, *altmatch, *suffix_match; const char *err_str; err_cert = X509_STORE_CTX_get_current_cert(x509_ctx); err = X509_STORE_CTX_get_error(x509_ctx); depth = X509_STORE_CTX_get_error_depth(x509_ctx); ssl = X509_STORE_CTX_get_ex_data(x509_ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); X509_NAME_oneline(X509_get_subject_name(err_cert), buf, sizeof(buf)); conn = SSL_get_app_data(ssl); if (conn == NULL) return 0; if (depth == 0) conn->peer_cert = err_cert; else if (depth == 1) conn->peer_issuer = err_cert; else if (depth == 2) conn->peer_issuer_issuer = err_cert; context = conn->context; match = conn->subject_match; altmatch = conn->altsubject_match; suffix_match = conn->suffix_match; if (!preverify_ok && !conn->ca_cert_verify) preverify_ok = 1; if (!preverify_ok && depth > 0 && conn->server_cert_only) preverify_ok = 1; if (!preverify_ok && (conn->flags & TLS_CONN_DISABLE_TIME_CHECKS) && (err == X509_V_ERR_CERT_HAS_EXPIRED || err == X509_V_ERR_CERT_NOT_YET_VALID)) { wpa_printf(MSG_DEBUG, "OpenSSL: Ignore certificate validity " "time mismatch"); preverify_ok = 1; } err_str = X509_verify_cert_error_string(err); #ifdef CONFIG_SHA256 if (preverify_ok && depth == 0 && conn->server_cert_only) { struct wpabuf *cert; cert = get_x509_cert(err_cert); if (!cert) { wpa_printf(MSG_DEBUG, "OpenSSL: Could not fetch " "server certificate data"); preverify_ok = 0; } else { u8 hash[32]; const u8 *addr[1]; size_t len[1]; addr[0] = wpabuf_head(cert); len[0] = wpabuf_len(cert); if (sha256_vector(1, addr, len, hash) < 0 || os_memcmp(conn->srv_cert_hash, hash, 32) != 0) { err_str = "Server certificate mismatch"; err = X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN; preverify_ok = 0; } wpabuf_free(cert); } } #endif /* CONFIG_SHA256 */ if (!preverify_ok) { wpa_printf(MSG_WARNING, "TLS: Certificate verification failed," " error %d (%s) depth %d for '%s'", err, err_str, depth, buf); openssl_tls_fail_event(conn, err_cert, err, depth, buf, err_str, TLS_FAIL_UNSPECIFIED); return preverify_ok; } wpa_printf(MSG_DEBUG, "TLS: tls_verify_cb - preverify_ok=%d " "err=%d (%s) ca_cert_verify=%d depth=%d buf='%s'", preverify_ok, err, err_str, conn->ca_cert_verify, depth, buf); if (depth == 0 && match && os_strstr(buf, match) == NULL) { wpa_printf(MSG_WARNING, "TLS: Subject '%s' did not " "match with '%s'", buf, match); preverify_ok = 0; openssl_tls_fail_event(conn, err_cert, err, depth, buf, "Subject mismatch", TLS_FAIL_SUBJECT_MISMATCH); } else if (depth == 0 && altmatch && !tls_match_altsubject(err_cert, altmatch)) { wpa_printf(MSG_WARNING, "TLS: altSubjectName match " "'%s' not found", altmatch); preverify_ok = 0; openssl_tls_fail_event(conn, err_cert, err, depth, buf, "AltSubject mismatch", TLS_FAIL_ALTSUBJECT_MISMATCH); } else if (depth == 0 && suffix_match && !tls_match_suffix(err_cert, suffix_match)) { wpa_printf(MSG_WARNING, "TLS: Domain suffix match '%s' not found", suffix_match); preverify_ok = 0; openssl_tls_fail_event(conn, err_cert, err, depth, buf, "Domain suffix mismatch", TLS_FAIL_DOMAIN_SUFFIX_MISMATCH); } else openssl_tls_cert_event(conn, err_cert, depth, buf); if (conn->cert_probe && preverify_ok && depth == 0) { wpa_printf(MSG_DEBUG, "OpenSSL: Reject server certificate " "on probe-only run"); preverify_ok = 0; openssl_tls_fail_event(conn, err_cert, err, depth, buf, "Server certificate chain probe", TLS_FAIL_SERVER_CHAIN_PROBE); } if (!conn->server && err_cert && preverify_ok && depth == 0 && (err_cert->ex_flags & EXFLAG_XKUSAGE) && (err_cert->ex_xkusage & XKU_SSL_CLIENT)) { wpa_printf(MSG_WARNING, "TLS: Server used client certificate"); openssl_tls_fail_event(conn, err_cert, err, depth, buf, "Server used client certificate", TLS_FAIL_SERVER_USED_CLIENT_CERT); preverify_ok = 0; } if (preverify_ok && context->event_cb != NULL) context->event_cb(context->cb_ctx, TLS_CERT_CHAIN_SUCCESS, NULL); return preverify_ok; } #ifndef OPENSSL_NO_STDIO static int tls_load_ca_der(void *_ssl_ctx, const char *ca_cert) { SSL_CTX *ssl_ctx = _ssl_ctx; X509_LOOKUP *lookup; int ret = 0; lookup = X509_STORE_add_lookup(ssl_ctx->cert_store, X509_LOOKUP_file()); if (lookup == NULL) { tls_show_errors(MSG_WARNING, __func__, "Failed add lookup for X509 store"); return -1; } if (!X509_LOOKUP_load_file(lookup, ca_cert, X509_FILETYPE_ASN1)) { unsigned long err = ERR_peek_error(); tls_show_errors(MSG_WARNING, __func__, "Failed load CA in DER format"); if (ERR_GET_LIB(err) == ERR_LIB_X509 && ERR_GET_REASON(err) == X509_R_CERT_ALREADY_IN_HASH_TABLE) { wpa_printf(MSG_DEBUG, "OpenSSL: %s - ignoring " "cert already in hash table error", __func__); } else ret = -1; } return ret; } #endif /* OPENSSL_NO_STDIO */ static int tls_connection_ca_cert(void *_ssl_ctx, struct tls_connection *conn, const char *ca_cert, const u8 *ca_cert_blob, size_t ca_cert_blob_len, const char *ca_path) { SSL_CTX *ssl_ctx = _ssl_ctx; /* * Remove previously configured trusted CA certificates before adding * new ones. */ X509_STORE_free(ssl_ctx->cert_store); ssl_ctx->cert_store = X509_STORE_new(); if (ssl_ctx->cert_store == NULL) { wpa_printf(MSG_DEBUG, "OpenSSL: %s - failed to allocate new " "certificate store", __func__); return -1; } SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb); conn->ca_cert_verify = 1; if (ca_cert && os_strncmp(ca_cert, "probe://", 8) == 0) { wpa_printf(MSG_DEBUG, "OpenSSL: Probe for server certificate " "chain"); conn->cert_probe = 1; conn->ca_cert_verify = 0; return 0; } if (ca_cert && os_strncmp(ca_cert, "hash://", 7) == 0) { #ifdef CONFIG_SHA256 const char *pos = ca_cert + 7; if (os_strncmp(pos, "server/sha256/", 14) != 0) { wpa_printf(MSG_DEBUG, "OpenSSL: Unsupported ca_cert " "hash value '%s'", ca_cert); return -1; } pos += 14; if (os_strlen(pos) != 32 * 2) { wpa_printf(MSG_DEBUG, "OpenSSL: Unexpected SHA256 " "hash length in ca_cert '%s'", ca_cert); return -1; } if (hexstr2bin(pos, conn->srv_cert_hash, 32) < 0) { wpa_printf(MSG_DEBUG, "OpenSSL: Invalid SHA256 hash " "value in ca_cert '%s'", ca_cert); return -1; } conn->server_cert_only = 1; wpa_printf(MSG_DEBUG, "OpenSSL: Checking only server " "certificate match"); return 0; #else /* CONFIG_SHA256 */ wpa_printf(MSG_INFO, "No SHA256 included in the build - " "cannot validate server certificate hash"); return -1; #endif /* CONFIG_SHA256 */ } if (ca_cert_blob) { X509 *cert = d2i_X509(NULL, (OPENSSL_d2i_TYPE) &ca_cert_blob, ca_cert_blob_len); if (cert == NULL) { tls_show_errors(MSG_WARNING, __func__, "Failed to parse ca_cert_blob"); return -1; } if (!X509_STORE_add_cert(ssl_ctx->cert_store, cert)) { unsigned long err = ERR_peek_error(); tls_show_errors(MSG_WARNING, __func__, "Failed to add ca_cert_blob to " "certificate store"); if (ERR_GET_LIB(err) == ERR_LIB_X509 && ERR_GET_REASON(err) == X509_R_CERT_ALREADY_IN_HASH_TABLE) { wpa_printf(MSG_DEBUG, "OpenSSL: %s - ignoring " "cert already in hash table error", __func__); } else { X509_free(cert); return -1; } } X509_free(cert); wpa_printf(MSG_DEBUG, "OpenSSL: %s - added ca_cert_blob " "to certificate store", __func__); return 0; } #ifdef ANDROID if (ca_cert && os_strncmp("keystore://", ca_cert, 11) == 0) { BIO *bio = BIO_from_keystore(&ca_cert[11]); STACK_OF(X509_INFO) *stack = NULL; int i; if (bio) { stack = PEM_X509_INFO_read_bio(bio, NULL, NULL, NULL); BIO_free(bio); } if (!stack) return -1; for (i = 0; i < sk_X509_INFO_num(stack); ++i) { X509_INFO *info = sk_X509_INFO_value(stack, i); if (info->x509) { X509_STORE_add_cert(ssl_ctx->cert_store, info->x509); } if (info->crl) { X509_STORE_add_crl(ssl_ctx->cert_store, info->crl); } } sk_X509_INFO_pop_free(stack, X509_INFO_free); SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb); return 0; } #endif /* ANDROID */ #ifdef CONFIG_NATIVE_WINDOWS if (ca_cert && tls_cryptoapi_ca_cert(ssl_ctx, conn->ssl, ca_cert) == 0) { wpa_printf(MSG_DEBUG, "OpenSSL: Added CA certificates from " "system certificate store"); return 0; } #endif /* CONFIG_NATIVE_WINDOWS */ if (ca_cert || ca_path) { #ifndef OPENSSL_NO_STDIO if (SSL_CTX_load_verify_locations(ssl_ctx, ca_cert, ca_path) != 1) { tls_show_errors(MSG_WARNING, __func__, "Failed to load root certificates"); if (ca_cert && tls_load_ca_der(ssl_ctx, ca_cert) == 0) { wpa_printf(MSG_DEBUG, "OpenSSL: %s - loaded " "DER format CA certificate", __func__); } else return -1; } else { wpa_printf(MSG_DEBUG, "TLS: Trusted root " "certificate(s) loaded"); tls_get_errors(ssl_ctx); } #else /* OPENSSL_NO_STDIO */ wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO", __func__); return -1; #endif /* OPENSSL_NO_STDIO */ } else { /* No ca_cert configured - do not try to verify server * certificate */ conn->ca_cert_verify = 0; } return 0; } static int tls_global_ca_cert(SSL_CTX *ssl_ctx, const char *ca_cert) { if (ca_cert) { if (SSL_CTX_load_verify_locations(ssl_ctx, ca_cert, NULL) != 1) { tls_show_errors(MSG_WARNING, __func__, "Failed to load root certificates"); return -1; } wpa_printf(MSG_DEBUG, "TLS: Trusted root " "certificate(s) loaded"); #ifndef OPENSSL_NO_STDIO /* Add the same CAs to the client certificate requests */ SSL_CTX_set_client_CA_list(ssl_ctx, SSL_load_client_CA_file(ca_cert)); #endif /* OPENSSL_NO_STDIO */ } return 0; } int tls_global_set_verify(void *ssl_ctx, int check_crl) { int flags; if (check_crl) { X509_STORE *cs = SSL_CTX_get_cert_store(ssl_ctx); if (cs == NULL) { tls_show_errors(MSG_INFO, __func__, "Failed to get " "certificate store when enabling " "check_crl"); return -1; } flags = X509_V_FLAG_CRL_CHECK; if (check_crl == 2) flags |= X509_V_FLAG_CRL_CHECK_ALL; X509_STORE_set_flags(cs, flags); } return 0; } static int tls_connection_set_subject_match(struct tls_connection *conn, const char *subject_match, const char *altsubject_match, const char *suffix_match) { os_free(conn->subject_match); conn->subject_match = NULL; if (subject_match) { conn->subject_match = os_strdup(subject_match); if (conn->subject_match == NULL) return -1; } os_free(conn->altsubject_match); conn->altsubject_match = NULL; if (altsubject_match) { conn->altsubject_match = os_strdup(altsubject_match); if (conn->altsubject_match == NULL) return -1; } os_free(conn->suffix_match); conn->suffix_match = NULL; if (suffix_match) { conn->suffix_match = os_strdup(suffix_match); if (conn->suffix_match == NULL) return -1; } return 0; } int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn, int verify_peer) { static int counter = 0; if (conn == NULL) return -1; if (verify_peer) { conn->ca_cert_verify = 1; SSL_set_verify(conn->ssl, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT | SSL_VERIFY_CLIENT_ONCE, tls_verify_cb); } else { conn->ca_cert_verify = 0; SSL_set_verify(conn->ssl, SSL_VERIFY_NONE, NULL); } SSL_set_accept_state(conn->ssl); /* * Set session id context in order to avoid fatal errors when client * tries to resume a session. However, set the context to a unique * value in order to effectively disable session resumption for now * since not all areas of the server code are ready for it (e.g., * EAP-TTLS needs special handling for Phase 2 after abbreviated TLS * handshake). */ counter++; SSL_set_session_id_context(conn->ssl, (const unsigned char *) &counter, sizeof(counter)); return 0; } static int tls_connection_client_cert(struct tls_connection *conn, const char *client_cert, const u8 *client_cert_blob, size_t client_cert_blob_len) { if (client_cert == NULL && client_cert_blob == NULL) return 0; if (client_cert_blob && SSL_use_certificate_ASN1(conn->ssl, (u8 *) client_cert_blob, client_cert_blob_len) == 1) { wpa_printf(MSG_DEBUG, "OpenSSL: SSL_use_certificate_ASN1 --> " "OK"); return 0; } else if (client_cert_blob) { tls_show_errors(MSG_DEBUG, __func__, "SSL_use_certificate_ASN1 failed"); } if (client_cert == NULL) return -1; #ifdef ANDROID if (os_strncmp("keystore://", client_cert, 11) == 0) { BIO *bio = BIO_from_keystore(&client_cert[11]); X509 *x509 = NULL; int ret = -1; if (bio) { x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL); BIO_free(bio); } if (x509) { if (SSL_use_certificate(conn->ssl, x509) == 1) ret = 0; X509_free(x509); } return ret; } #endif /* ANDROID */ #ifndef OPENSSL_NO_STDIO if (SSL_use_certificate_file(conn->ssl, client_cert, SSL_FILETYPE_ASN1) == 1) { wpa_printf(MSG_DEBUG, "OpenSSL: SSL_use_certificate_file (DER)" " --> OK"); return 0; } if (SSL_use_certificate_file(conn->ssl, client_cert, SSL_FILETYPE_PEM) == 1) { ERR_clear_error(); wpa_printf(MSG_DEBUG, "OpenSSL: SSL_use_certificate_file (PEM)" " --> OK"); return 0; } tls_show_errors(MSG_DEBUG, __func__, "SSL_use_certificate_file failed"); #else /* OPENSSL_NO_STDIO */ wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO", __func__); #endif /* OPENSSL_NO_STDIO */ return -1; } static int tls_global_client_cert(SSL_CTX *ssl_ctx, const char *client_cert) { #ifndef OPENSSL_NO_STDIO if (client_cert == NULL) return 0; if (SSL_CTX_use_certificate_file(ssl_ctx, client_cert, SSL_FILETYPE_ASN1) != 1 && SSL_CTX_use_certificate_chain_file(ssl_ctx, client_cert) != 1 && SSL_CTX_use_certificate_file(ssl_ctx, client_cert, SSL_FILETYPE_PEM) != 1) { tls_show_errors(MSG_INFO, __func__, "Failed to load client certificate"); return -1; } return 0; #else /* OPENSSL_NO_STDIO */ if (client_cert == NULL) return 0; wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO", __func__); return -1; #endif /* OPENSSL_NO_STDIO */ } static int tls_passwd_cb(char *buf, int size, int rwflag, void *password) { if (password == NULL) { return 0; } os_strlcpy(buf, (char *) password, size); return os_strlen(buf); } #ifdef PKCS12_FUNCS static int tls_parse_pkcs12(SSL_CTX *ssl_ctx, SSL *ssl, PKCS12 *p12, const char *passwd) { EVP_PKEY *pkey; X509 *cert; STACK_OF(X509) *certs; int res = 0; char buf[256]; pkey = NULL; cert = NULL; certs = NULL; if (!PKCS12_parse(p12, passwd, &pkey, &cert, &certs)) { tls_show_errors(MSG_DEBUG, __func__, "Failed to parse PKCS12 file"); PKCS12_free(p12); return -1; } wpa_printf(MSG_DEBUG, "TLS: Successfully parsed PKCS12 data"); if (cert) { X509_NAME_oneline(X509_get_subject_name(cert), buf, sizeof(buf)); wpa_printf(MSG_DEBUG, "TLS: Got certificate from PKCS12: " "subject='%s'", buf); if (ssl) { if (SSL_use_certificate(ssl, cert) != 1) res = -1; } else { if (SSL_CTX_use_certificate(ssl_ctx, cert) != 1) res = -1; } X509_free(cert); } if (pkey) { wpa_printf(MSG_DEBUG, "TLS: Got private key from PKCS12"); if (ssl) { if (SSL_use_PrivateKey(ssl, pkey) != 1) res = -1; } else { if (SSL_CTX_use_PrivateKey(ssl_ctx, pkey) != 1) res = -1; } EVP_PKEY_free(pkey); } if (certs) { while ((cert = sk_X509_pop(certs)) != NULL) { X509_NAME_oneline(X509_get_subject_name(cert), buf, sizeof(buf)); wpa_printf(MSG_DEBUG, "TLS: additional certificate" " from PKCS12: subject='%s'", buf); /* * There is no SSL equivalent for the chain cert - so * always add it to the context... */ if (SSL_CTX_add_extra_chain_cert(ssl_ctx, cert) != 1) { res = -1; break; } } sk_X509_free(certs); } PKCS12_free(p12); if (res < 0) tls_get_errors(ssl_ctx); return res; } #endif /* PKCS12_FUNCS */ static int tls_read_pkcs12(SSL_CTX *ssl_ctx, SSL *ssl, const char *private_key, const char *passwd) { #ifdef PKCS12_FUNCS FILE *f; PKCS12 *p12; f = fopen(private_key, "rb"); if (f == NULL) return -1; p12 = d2i_PKCS12_fp(f, NULL); fclose(f); if (p12 == NULL) { tls_show_errors(MSG_INFO, __func__, "Failed to use PKCS#12 file"); return -1; } return tls_parse_pkcs12(ssl_ctx, ssl, p12, passwd); #else /* PKCS12_FUNCS */ wpa_printf(MSG_INFO, "TLS: PKCS12 support disabled - cannot read " "p12/pfx files"); return -1; #endif /* PKCS12_FUNCS */ } static int tls_read_pkcs12_blob(SSL_CTX *ssl_ctx, SSL *ssl, const u8 *blob, size_t len, const char *passwd) { #ifdef PKCS12_FUNCS PKCS12 *p12; p12 = d2i_PKCS12(NULL, (OPENSSL_d2i_TYPE) &blob, len); if (p12 == NULL) { tls_show_errors(MSG_INFO, __func__, "Failed to use PKCS#12 blob"); return -1; } return tls_parse_pkcs12(ssl_ctx, ssl, p12, passwd); #else /* PKCS12_FUNCS */ wpa_printf(MSG_INFO, "TLS: PKCS12 support disabled - cannot parse " "p12/pfx blobs"); return -1; #endif /* PKCS12_FUNCS */ } #ifndef OPENSSL_NO_ENGINE static int tls_engine_get_cert(struct tls_connection *conn, const char *cert_id, X509 **cert) { /* this runs after the private key is loaded so no PIN is required */ struct { const char *cert_id; X509 *cert; } params; params.cert_id = cert_id; params.cert = NULL; if (!ENGINE_ctrl_cmd(conn->engine, "LOAD_CERT_CTRL", 0, ¶ms, NULL, 1)) { wpa_printf(MSG_ERROR, "ENGINE: cannot load client cert with id" " '%s' [%s]", cert_id, ERR_error_string(ERR_get_error(), NULL)); return TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED; } if (!params.cert) { wpa_printf(MSG_ERROR, "ENGINE: did not properly cert with id" " '%s'", cert_id); return TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED; } *cert = params.cert; return 0; } #endif /* OPENSSL_NO_ENGINE */ static int tls_connection_engine_client_cert(struct tls_connection *conn, const char *cert_id) { #ifndef OPENSSL_NO_ENGINE X509 *cert; if (tls_engine_get_cert(conn, cert_id, &cert)) return -1; if (!SSL_use_certificate(conn->ssl, cert)) { tls_show_errors(MSG_ERROR, __func__, "SSL_use_certificate failed"); X509_free(cert); return -1; } X509_free(cert); wpa_printf(MSG_DEBUG, "ENGINE: SSL_use_certificate --> " "OK"); return 0; #else /* OPENSSL_NO_ENGINE */ return -1; #endif /* OPENSSL_NO_ENGINE */ } static int tls_connection_engine_ca_cert(void *_ssl_ctx, struct tls_connection *conn, const char *ca_cert_id) { #ifndef OPENSSL_NO_ENGINE X509 *cert; SSL_CTX *ssl_ctx = _ssl_ctx; if (tls_engine_get_cert(conn, ca_cert_id, &cert)) return -1; /* start off the same as tls_connection_ca_cert */ X509_STORE_free(ssl_ctx->cert_store); ssl_ctx->cert_store = X509_STORE_new(); if (ssl_ctx->cert_store == NULL) { wpa_printf(MSG_DEBUG, "OpenSSL: %s - failed to allocate new " "certificate store", __func__); X509_free(cert); return -1; } if (!X509_STORE_add_cert(ssl_ctx->cert_store, cert)) { unsigned long err = ERR_peek_error(); tls_show_errors(MSG_WARNING, __func__, "Failed to add CA certificate from engine " "to certificate store"); if (ERR_GET_LIB(err) == ERR_LIB_X509 && ERR_GET_REASON(err) == X509_R_CERT_ALREADY_IN_HASH_TABLE) { wpa_printf(MSG_DEBUG, "OpenSSL: %s - ignoring cert" " already in hash table error", __func__); } else { X509_free(cert); return -1; } } X509_free(cert); wpa_printf(MSG_DEBUG, "OpenSSL: %s - added CA certificate from engine " "to certificate store", __func__); SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb); conn->ca_cert_verify = 1; return 0; #else /* OPENSSL_NO_ENGINE */ return -1; #endif /* OPENSSL_NO_ENGINE */ } static int tls_connection_engine_private_key(struct tls_connection *conn) { #ifndef OPENSSL_NO_ENGINE if (SSL_use_PrivateKey(conn->ssl, conn->private_key) != 1) { tls_show_errors(MSG_ERROR, __func__, "ENGINE: cannot use private key for TLS"); return -1; } if (!SSL_check_private_key(conn->ssl)) { tls_show_errors(MSG_INFO, __func__, "Private key failed verification"); return -1; } return 0; #else /* OPENSSL_NO_ENGINE */ wpa_printf(MSG_ERROR, "SSL: Configuration uses engine, but " "engine support was not compiled in"); return -1; #endif /* OPENSSL_NO_ENGINE */ } static int tls_connection_private_key(void *_ssl_ctx, struct tls_connection *conn, const char *private_key, const char *private_key_passwd, const u8 *private_key_blob, size_t private_key_blob_len) { SSL_CTX *ssl_ctx = _ssl_ctx; char *passwd; int ok; if (private_key == NULL && private_key_blob == NULL) return 0; if (private_key_passwd) { passwd = os_strdup(private_key_passwd); if (passwd == NULL) return -1; } else passwd = NULL; SSL_CTX_set_default_passwd_cb(ssl_ctx, tls_passwd_cb); SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, passwd); ok = 0; while (private_key_blob) { if (SSL_use_PrivateKey_ASN1(EVP_PKEY_RSA, conn->ssl, (u8 *) private_key_blob, private_key_blob_len) == 1) { wpa_printf(MSG_DEBUG, "OpenSSL: SSL_use_PrivateKey_" "ASN1(EVP_PKEY_RSA) --> OK"); ok = 1; break; } if (SSL_use_PrivateKey_ASN1(EVP_PKEY_DSA, conn->ssl, (u8 *) private_key_blob, private_key_blob_len) == 1) { wpa_printf(MSG_DEBUG, "OpenSSL: SSL_use_PrivateKey_" "ASN1(EVP_PKEY_DSA) --> OK"); ok = 1; break; } if (SSL_use_RSAPrivateKey_ASN1(conn->ssl, (u8 *) private_key_blob, private_key_blob_len) == 1) { wpa_printf(MSG_DEBUG, "OpenSSL: " "SSL_use_RSAPrivateKey_ASN1 --> OK"); ok = 1; break; } if (tls_read_pkcs12_blob(ssl_ctx, conn->ssl, private_key_blob, private_key_blob_len, passwd) == 0) { wpa_printf(MSG_DEBUG, "OpenSSL: PKCS#12 as blob --> " "OK"); ok = 1; break; } break; } while (!ok && private_key) { #ifndef OPENSSL_NO_STDIO if (SSL_use_PrivateKey_file(conn->ssl, private_key, SSL_FILETYPE_ASN1) == 1) { wpa_printf(MSG_DEBUG, "OpenSSL: " "SSL_use_PrivateKey_File (DER) --> OK"); ok = 1; break; } if (SSL_use_PrivateKey_file(conn->ssl, private_key, SSL_FILETYPE_PEM) == 1) { wpa_printf(MSG_DEBUG, "OpenSSL: " "SSL_use_PrivateKey_File (PEM) --> OK"); ok = 1; break; } #else /* OPENSSL_NO_STDIO */ wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO", __func__); #endif /* OPENSSL_NO_STDIO */ if (tls_read_pkcs12(ssl_ctx, conn->ssl, private_key, passwd) == 0) { wpa_printf(MSG_DEBUG, "OpenSSL: Reading PKCS#12 file " "--> OK"); ok = 1; break; } if (tls_cryptoapi_cert(conn->ssl, private_key) == 0) { wpa_printf(MSG_DEBUG, "OpenSSL: Using CryptoAPI to " "access certificate store --> OK"); ok = 1; break; } break; } if (!ok) { tls_show_errors(MSG_INFO, __func__, "Failed to load private key"); os_free(passwd); return -1; } ERR_clear_error(); SSL_CTX_set_default_passwd_cb(ssl_ctx, NULL); os_free(passwd); if (!SSL_check_private_key(conn->ssl)) { tls_show_errors(MSG_INFO, __func__, "Private key failed " "verification"); return -1; } wpa_printf(MSG_DEBUG, "SSL: Private key loaded successfully"); return 0; } static int tls_global_private_key(SSL_CTX *ssl_ctx, const char *private_key, const char *private_key_passwd) { char *passwd; if (private_key == NULL) return 0; if (private_key_passwd) { passwd = os_strdup(private_key_passwd); if (passwd == NULL) return -1; } else passwd = NULL; SSL_CTX_set_default_passwd_cb(ssl_ctx, tls_passwd_cb); SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, passwd); if ( #ifndef OPENSSL_NO_STDIO SSL_CTX_use_PrivateKey_file(ssl_ctx, private_key, SSL_FILETYPE_ASN1) != 1 && SSL_CTX_use_PrivateKey_file(ssl_ctx, private_key, SSL_FILETYPE_PEM) != 1 && #endif /* OPENSSL_NO_STDIO */ tls_read_pkcs12(ssl_ctx, NULL, private_key, passwd)) { tls_show_errors(MSG_INFO, __func__, "Failed to load private key"); os_free(passwd); ERR_clear_error(); return -1; } os_free(passwd); ERR_clear_error(); SSL_CTX_set_default_passwd_cb(ssl_ctx, NULL); if (!SSL_CTX_check_private_key(ssl_ctx)) { tls_show_errors(MSG_INFO, __func__, "Private key failed verification"); return -1; } return 0; } static int tls_connection_dh(struct tls_connection *conn, const char *dh_file) { #ifdef OPENSSL_NO_DH if (dh_file == NULL) return 0; wpa_printf(MSG_ERROR, "TLS: openssl does not include DH support, but " "dh_file specified"); return -1; #else /* OPENSSL_NO_DH */ DH *dh; BIO *bio; /* TODO: add support for dh_blob */ if (dh_file == NULL) return 0; if (conn == NULL) return -1; bio = BIO_new_file(dh_file, "r"); if (bio == NULL) { wpa_printf(MSG_INFO, "TLS: Failed to open DH file '%s': %s", dh_file, ERR_error_string(ERR_get_error(), NULL)); return -1; } dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL); BIO_free(bio); #ifndef OPENSSL_NO_DSA while (dh == NULL) { DSA *dsa; wpa_printf(MSG_DEBUG, "TLS: Failed to parse DH file '%s': %s -" " trying to parse as DSA params", dh_file, ERR_error_string(ERR_get_error(), NULL)); bio = BIO_new_file(dh_file, "r"); if (bio == NULL) break; dsa = PEM_read_bio_DSAparams(bio, NULL, NULL, NULL); BIO_free(bio); if (!dsa) { wpa_printf(MSG_DEBUG, "TLS: Failed to parse DSA file " "'%s': %s", dh_file, ERR_error_string(ERR_get_error(), NULL)); break; } wpa_printf(MSG_DEBUG, "TLS: DH file in DSA param format"); dh = DSA_dup_DH(dsa); DSA_free(dsa); if (dh == NULL) { wpa_printf(MSG_INFO, "TLS: Failed to convert DSA " "params into DH params"); break; } break; } #endif /* !OPENSSL_NO_DSA */ if (dh == NULL) { wpa_printf(MSG_INFO, "TLS: Failed to read/parse DH/DSA file " "'%s'", dh_file); return -1; } if (SSL_set_tmp_dh(conn->ssl, dh) != 1) { wpa_printf(MSG_INFO, "TLS: Failed to set DH params from '%s': " "%s", dh_file, ERR_error_string(ERR_get_error(), NULL)); DH_free(dh); return -1; } DH_free(dh); return 0; #endif /* OPENSSL_NO_DH */ } static int tls_global_dh(SSL_CTX *ssl_ctx, const char *dh_file) { #ifdef OPENSSL_NO_DH if (dh_file == NULL) return 0; wpa_printf(MSG_ERROR, "TLS: openssl does not include DH support, but " "dh_file specified"); return -1; #else /* OPENSSL_NO_DH */ DH *dh; BIO *bio; /* TODO: add support for dh_blob */ if (dh_file == NULL) return 0; if (ssl_ctx == NULL) return -1; bio = BIO_new_file(dh_file, "r"); if (bio == NULL) { wpa_printf(MSG_INFO, "TLS: Failed to open DH file '%s': %s", dh_file, ERR_error_string(ERR_get_error(), NULL)); return -1; } dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL); BIO_free(bio); #ifndef OPENSSL_NO_DSA while (dh == NULL) { DSA *dsa; wpa_printf(MSG_DEBUG, "TLS: Failed to parse DH file '%s': %s -" " trying to parse as DSA params", dh_file, ERR_error_string(ERR_get_error(), NULL)); bio = BIO_new_file(dh_file, "r"); if (bio == NULL) break; dsa = PEM_read_bio_DSAparams(bio, NULL, NULL, NULL); BIO_free(bio); if (!dsa) { wpa_printf(MSG_DEBUG, "TLS: Failed to parse DSA file " "'%s': %s", dh_file, ERR_error_string(ERR_get_error(), NULL)); break; } wpa_printf(MSG_DEBUG, "TLS: DH file in DSA param format"); dh = DSA_dup_DH(dsa); DSA_free(dsa); if (dh == NULL) { wpa_printf(MSG_INFO, "TLS: Failed to convert DSA " "params into DH params"); break; } break; } #endif /* !OPENSSL_NO_DSA */ if (dh == NULL) { wpa_printf(MSG_INFO, "TLS: Failed to read/parse DH/DSA file " "'%s'", dh_file); return -1; } if (SSL_CTX_set_tmp_dh(ssl_ctx, dh) != 1) { wpa_printf(MSG_INFO, "TLS: Failed to set DH params from '%s': " "%s", dh_file, ERR_error_string(ERR_get_error(), NULL)); DH_free(dh); return -1; } DH_free(dh); return 0; #endif /* OPENSSL_NO_DH */ } int tls_connection_get_keys(void *ssl_ctx, struct tls_connection *conn, struct tls_keys *keys) { #ifdef CONFIG_FIPS wpa_printf(MSG_ERROR, "OpenSSL: TLS keys cannot be exported in FIPS " "mode"); return -1; #else /* CONFIG_FIPS */ SSL *ssl; if (conn == NULL || keys == NULL) return -1; ssl = conn->ssl; if (ssl == NULL || ssl->s3 == NULL || ssl->session == NULL) return -1; os_memset(keys, 0, sizeof(*keys)); keys->master_key = ssl->session->master_key; keys->master_key_len = ssl->session->master_key_length; keys->client_random = ssl->s3->client_random; keys->client_random_len = SSL3_RANDOM_SIZE; keys->server_random = ssl->s3->server_random; keys->server_random_len = SSL3_RANDOM_SIZE; return 0; #endif /* CONFIG_FIPS */ } int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, const char *label, int server_random_first, u8 *out, size_t out_len) { #if OPENSSL_VERSION_NUMBER >= 0x10001000L SSL *ssl; if (conn == NULL) return -1; if (server_random_first) return -1; ssl = conn->ssl; if (SSL_export_keying_material(ssl, out, out_len, label, os_strlen(label), NULL, 0, 0) == 1) { wpa_printf(MSG_DEBUG, "OpenSSL: Using internal PRF"); return 0; } #endif return -1; } static struct wpabuf * openssl_handshake(struct tls_connection *conn, const struct wpabuf *in_data, int server) { int res; struct wpabuf *out_data; conn->server = !!server; /* * Give TLS handshake data from the server (if available) to OpenSSL * for processing. */ if (in_data && BIO_write(conn->ssl_in, wpabuf_head(in_data), wpabuf_len(in_data)) < 0) { tls_show_errors(MSG_INFO, __func__, "Handshake failed - BIO_write"); return NULL; } /* Initiate TLS handshake or continue the existing handshake */ if (server) res = SSL_accept(conn->ssl); else res = SSL_connect(conn->ssl); if (res != 1) { int err = SSL_get_error(conn->ssl, res); if (err == SSL_ERROR_WANT_READ) wpa_printf(MSG_DEBUG, "SSL: SSL_connect - want " "more data"); else if (err == SSL_ERROR_WANT_WRITE) wpa_printf(MSG_DEBUG, "SSL: SSL_connect - want to " "write"); else { tls_show_errors(MSG_INFO, __func__, "SSL_connect"); conn->failed++; } } /* Get the TLS handshake data to be sent to the server */ res = BIO_ctrl_pending(conn->ssl_out); wpa_printf(MSG_DEBUG, "SSL: %d bytes pending from ssl_out", res); out_data = wpabuf_alloc(res); if (out_data == NULL) { wpa_printf(MSG_DEBUG, "SSL: Failed to allocate memory for " "handshake output (%d bytes)", res); if (BIO_reset(conn->ssl_out) < 0) { tls_show_errors(MSG_INFO, __func__, "BIO_reset failed"); } return NULL; } res = res == 0 ? 0 : BIO_read(conn->ssl_out, wpabuf_mhead(out_data), res); if (res < 0) { tls_show_errors(MSG_INFO, __func__, "Handshake failed - BIO_read"); if (BIO_reset(conn->ssl_out) < 0) { tls_show_errors(MSG_INFO, __func__, "BIO_reset failed"); } wpabuf_free(out_data); return NULL; } wpabuf_put(out_data, res); return out_data; } static struct wpabuf * openssl_get_appl_data(struct tls_connection *conn, size_t max_len) { struct wpabuf *appl_data; int res; appl_data = wpabuf_alloc(max_len + 100); if (appl_data == NULL) return NULL; res = SSL_read(conn->ssl, wpabuf_mhead(appl_data), wpabuf_size(appl_data)); if (res < 0) { int err = SSL_get_error(conn->ssl, res); if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) { wpa_printf(MSG_DEBUG, "SSL: No Application Data " "included"); } else { tls_show_errors(MSG_INFO, __func__, "Failed to read possible " "Application Data"); } wpabuf_free(appl_data); return NULL; } wpabuf_put(appl_data, res); wpa_hexdump_buf_key(MSG_MSGDUMP, "SSL: Application Data in Finished " "message", appl_data); return appl_data; } static struct wpabuf * openssl_connection_handshake(struct tls_connection *conn, const struct wpabuf *in_data, struct wpabuf **appl_data, int server) { struct wpabuf *out_data; if (appl_data) *appl_data = NULL; out_data = openssl_handshake(conn, in_data, server); if (out_data == NULL) return NULL; if (SSL_is_init_finished(conn->ssl) && appl_data && in_data) *appl_data = openssl_get_appl_data(conn, wpabuf_len(in_data)); return out_data; } struct wpabuf * tls_connection_handshake(void *ssl_ctx, struct tls_connection *conn, const struct wpabuf *in_data, struct wpabuf **appl_data) { return openssl_connection_handshake(conn, in_data, appl_data, 0); } struct wpabuf * tls_connection_server_handshake(void *tls_ctx, struct tls_connection *conn, const struct wpabuf *in_data, struct wpabuf **appl_data) { return openssl_connection_handshake(conn, in_data, appl_data, 1); } struct wpabuf * tls_connection_encrypt(void *tls_ctx, struct tls_connection *conn, const struct wpabuf *in_data) { int res; struct wpabuf *buf; if (conn == NULL) return NULL; /* Give plaintext data for OpenSSL to encrypt into the TLS tunnel. */ if ((res = BIO_reset(conn->ssl_in)) < 0 || (res = BIO_reset(conn->ssl_out)) < 0) { tls_show_errors(MSG_INFO, __func__, "BIO_reset failed"); return NULL; } res = SSL_write(conn->ssl, wpabuf_head(in_data), wpabuf_len(in_data)); if (res < 0) { tls_show_errors(MSG_INFO, __func__, "Encryption failed - SSL_write"); return NULL; } /* Read encrypted data to be sent to the server */ buf = wpabuf_alloc(wpabuf_len(in_data) + 300); if (buf == NULL) return NULL; res = BIO_read(conn->ssl_out, wpabuf_mhead(buf), wpabuf_size(buf)); if (res < 0) { tls_show_errors(MSG_INFO, __func__, "Encryption failed - BIO_read"); wpabuf_free(buf); return NULL; } wpabuf_put(buf, res); return buf; } struct wpabuf * tls_connection_decrypt(void *tls_ctx, struct tls_connection *conn, const struct wpabuf *in_data) { int res; struct wpabuf *buf; /* Give encrypted data from TLS tunnel for OpenSSL to decrypt. */ res = BIO_write(conn->ssl_in, wpabuf_head(in_data), wpabuf_len(in_data)); if (res < 0) { tls_show_errors(MSG_INFO, __func__, "Decryption failed - BIO_write"); return NULL; } if (BIO_reset(conn->ssl_out) < 0) { tls_show_errors(MSG_INFO, __func__, "BIO_reset failed"); return NULL; } /* Read decrypted data for further processing */ /* * Even though we try to disable TLS compression, it is possible that * this cannot be done with all TLS libraries. Add extra buffer space * to handle the possibility of the decrypted data being longer than * input data. */ buf = wpabuf_alloc((wpabuf_len(in_data) + 500) * 3); if (buf == NULL) return NULL; res = SSL_read(conn->ssl, wpabuf_mhead(buf), wpabuf_size(buf)); if (res < 0) { tls_show_errors(MSG_INFO, __func__, "Decryption failed - SSL_read"); wpabuf_free(buf); return NULL; } wpabuf_put(buf, res); return buf; } int tls_connection_resumed(void *ssl_ctx, struct tls_connection *conn) { return conn ? conn->ssl->hit : 0; } int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn, u8 *ciphers) { char buf[100], *pos, *end; u8 *c; int ret; if (conn == NULL || conn->ssl == NULL || ciphers == NULL) return -1; buf[0] = '\0'; pos = buf; end = pos + sizeof(buf); c = ciphers; while (*c != TLS_CIPHER_NONE) { const char *suite; switch (*c) { case TLS_CIPHER_RC4_SHA: suite = "RC4-SHA"; break; case TLS_CIPHER_AES128_SHA: suite = "AES128-SHA"; break; case TLS_CIPHER_RSA_DHE_AES128_SHA: suite = "DHE-RSA-AES128-SHA"; break; case TLS_CIPHER_ANON_DH_AES128_SHA: suite = "ADH-AES128-SHA"; break; default: wpa_printf(MSG_DEBUG, "TLS: Unsupported " "cipher selection: %d", *c); return -1; } ret = os_snprintf(pos, end - pos, ":%s", suite); if (ret < 0 || ret >= end - pos) break; pos += ret; c++; } wpa_printf(MSG_DEBUG, "OpenSSL: cipher suites: %s", buf + 1); if (SSL_set_cipher_list(conn->ssl, buf + 1) != 1) { tls_show_errors(MSG_INFO, __func__, "Cipher suite configuration failed"); return -1; } return 0; } int tls_get_cipher(void *ssl_ctx, struct tls_connection *conn, char *buf, size_t buflen) { const char *name; if (conn == NULL || conn->ssl == NULL) return -1; name = SSL_get_cipher(conn->ssl); if (name == NULL) return -1; os_strlcpy(buf, name, buflen); return 0; } int tls_connection_enable_workaround(void *ssl_ctx, struct tls_connection *conn) { SSL_set_options(conn->ssl, SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS); return 0; } #if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST) /* ClientHello TLS extensions require a patch to openssl, so this function is * commented out unless explicitly needed for EAP-FAST in order to be able to * build this file with unmodified openssl. */ int tls_connection_client_hello_ext(void *ssl_ctx, struct tls_connection *conn, int ext_type, const u8 *data, size_t data_len) { if (conn == NULL || conn->ssl == NULL || ext_type != 35) return -1; #ifdef CONFIG_OPENSSL_TICKET_OVERRIDE if (SSL_set_session_ticket_ext(conn->ssl, (void *) data, data_len) != 1) return -1; #else /* CONFIG_OPENSSL_TICKET_OVERRIDE */ if (SSL_set_hello_extension(conn->ssl, ext_type, (void *) data, data_len) != 1) return -1; #endif /* CONFIG_OPENSSL_TICKET_OVERRIDE */ return 0; } #endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */ int tls_connection_get_failed(void *ssl_ctx, struct tls_connection *conn) { if (conn == NULL) return -1; return conn->failed; } int tls_connection_get_read_alerts(void *ssl_ctx, struct tls_connection *conn) { if (conn == NULL) return -1; return conn->read_alerts; } int tls_connection_get_write_alerts(void *ssl_ctx, struct tls_connection *conn) { if (conn == NULL) return -1; return conn->write_alerts; } #ifdef HAVE_OCSP static void ocsp_debug_print_resp(OCSP_RESPONSE *rsp) { #ifndef CONFIG_NO_STDOUT_DEBUG BIO *out; size_t rlen; char *txt; int res; if (wpa_debug_level > MSG_DEBUG) return; out = BIO_new(BIO_s_mem()); if (!out) return; OCSP_RESPONSE_print(out, rsp, 0); rlen = BIO_ctrl_pending(out); txt = os_malloc(rlen + 1); if (!txt) { BIO_free(out); return; } res = BIO_read(out, txt, rlen); if (res > 0) { txt[res] = '\0'; wpa_printf(MSG_DEBUG, "OpenSSL: OCSP Response\n%s", txt); } os_free(txt); BIO_free(out); #endif /* CONFIG_NO_STDOUT_DEBUG */ } static int ocsp_resp_cb(SSL *s, void *arg) { struct tls_connection *conn = arg; const unsigned char *p; int len, status, reason; OCSP_RESPONSE *rsp; OCSP_BASICRESP *basic; OCSP_CERTID *id; ASN1_GENERALIZEDTIME *produced_at, *this_update, *next_update; X509_STORE *store; STACK_OF(X509) *certs = NULL; len = SSL_get_tlsext_status_ocsp_resp(s, &p); if (!p) { wpa_printf(MSG_DEBUG, "OpenSSL: No OCSP response received"); return (conn->flags & TLS_CONN_REQUIRE_OCSP) ? 0 : 1; } wpa_hexdump(MSG_DEBUG, "OpenSSL: OCSP response", p, len); rsp = d2i_OCSP_RESPONSE(NULL, &p, len); if (!rsp) { wpa_printf(MSG_INFO, "OpenSSL: Failed to parse OCSP response"); return 0; } ocsp_debug_print_resp(rsp); status = OCSP_response_status(rsp); if (status != OCSP_RESPONSE_STATUS_SUCCESSFUL) { wpa_printf(MSG_INFO, "OpenSSL: OCSP responder error %d (%s)", status, OCSP_response_status_str(status)); return 0; } basic = OCSP_response_get1_basic(rsp); if (!basic) { wpa_printf(MSG_INFO, "OpenSSL: Could not find BasicOCSPResponse"); return 0; } store = SSL_CTX_get_cert_store(s->ctx); if (conn->peer_issuer) { wpa_printf(MSG_DEBUG, "OpenSSL: Add issuer"); X509_print_fp(stdout, conn->peer_issuer); if (X509_STORE_add_cert(store, conn->peer_issuer) != 1) { tls_show_errors(MSG_INFO, __func__, "OpenSSL: Could not add issuer to certificate store\n"); } certs = sk_X509_new_null(); if (certs) { X509 *cert; cert = X509_dup(conn->peer_issuer); if (cert && !sk_X509_push(certs, cert)) { tls_show_errors( MSG_INFO, __func__, "OpenSSL: Could not add issuer to OCSP responder trust store\n"); X509_free(cert); sk_X509_free(certs); certs = NULL; } if (conn->peer_issuer_issuer) { cert = X509_dup(conn->peer_issuer_issuer); if (cert && !sk_X509_push(certs, cert)) { tls_show_errors( MSG_INFO, __func__, "OpenSSL: Could not add issuer to OCSP responder trust store\n"); X509_free(cert); } } } } status = OCSP_basic_verify(basic, certs, store, OCSP_TRUSTOTHER); sk_X509_pop_free(certs, X509_free); if (status <= 0) { tls_show_errors(MSG_INFO, __func__, "OpenSSL: OCSP response failed verification"); OCSP_BASICRESP_free(basic); OCSP_RESPONSE_free(rsp); return 0; } wpa_printf(MSG_DEBUG, "OpenSSL: OCSP response verification succeeded"); if (!conn->peer_cert) { wpa_printf(MSG_DEBUG, "OpenSSL: Peer certificate not available for OCSP status check"); OCSP_BASICRESP_free(basic); OCSP_RESPONSE_free(rsp); return 0; } if (!conn->peer_issuer) { wpa_printf(MSG_DEBUG, "OpenSSL: Peer issuer certificate not available for OCSP status check"); OCSP_BASICRESP_free(basic); OCSP_RESPONSE_free(rsp); return 0; } id = OCSP_cert_to_id(NULL, conn->peer_cert, conn->peer_issuer); if (!id) { wpa_printf(MSG_DEBUG, "OpenSSL: Could not create OCSP certificate identifier"); OCSP_BASICRESP_free(basic); OCSP_RESPONSE_free(rsp); return 0; } if (!OCSP_resp_find_status(basic, id, &status, &reason, &produced_at, &this_update, &next_update)) { wpa_printf(MSG_INFO, "OpenSSL: Could not find current server certificate from OCSP response%s", (conn->flags & TLS_CONN_REQUIRE_OCSP) ? "" : " (OCSP not required)"); OCSP_BASICRESP_free(basic); OCSP_RESPONSE_free(rsp); return (conn->flags & TLS_CONN_REQUIRE_OCSP) ? 0 : 1; } if (!OCSP_check_validity(this_update, next_update, 5 * 60, -1)) { tls_show_errors(MSG_INFO, __func__, "OpenSSL: OCSP status times invalid"); OCSP_BASICRESP_free(basic); OCSP_RESPONSE_free(rsp); return 0; } OCSP_BASICRESP_free(basic); OCSP_RESPONSE_free(rsp); wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status for server certificate: %s", OCSP_cert_status_str(status)); if (status == V_OCSP_CERTSTATUS_GOOD) return 1; if (status == V_OCSP_CERTSTATUS_REVOKED) return 0; if (conn->flags & TLS_CONN_REQUIRE_OCSP) { wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status unknown, but OCSP required"); return 0; } wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status unknown, but OCSP was not required, so allow connection to continue"); return 1; } static int ocsp_status_cb(SSL *s, void *arg) { char *tmp; char *resp; size_t len; if (tls_global->ocsp_stapling_response == NULL) { wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status callback - no response configured"); return SSL_TLSEXT_ERR_OK; } resp = os_readfile(tls_global->ocsp_stapling_response, &len); if (resp == NULL) { wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status callback - could not read response file"); /* TODO: Build OCSPResponse with responseStatus = internalError */ return SSL_TLSEXT_ERR_OK; } wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status callback - send cached response"); tmp = OPENSSL_malloc(len); if (tmp == NULL) { os_free(resp); return SSL_TLSEXT_ERR_ALERT_FATAL; } os_memcpy(tmp, resp, len); os_free(resp); SSL_set_tlsext_status_ocsp_resp(s, tmp, len); return SSL_TLSEXT_ERR_OK; } #endif /* HAVE_OCSP */ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, const struct tls_connection_params *params) { int ret; unsigned long err; if (conn == NULL) return -1; while ((err = ERR_get_error())) { wpa_printf(MSG_INFO, "%s: Clearing pending SSL error: %s", __func__, ERR_error_string(err, NULL)); } if (params->engine) { wpa_printf(MSG_DEBUG, "SSL: Initializing TLS engine"); ret = tls_engine_init(conn, params->engine_id, params->pin, params->key_id, params->cert_id, params->ca_cert_id); if (ret) return ret; } if (tls_connection_set_subject_match(conn, params->subject_match, params->altsubject_match, params->suffix_match)) return -1; if (params->engine && params->ca_cert_id) { if (tls_connection_engine_ca_cert(tls_ctx, conn, params->ca_cert_id)) return TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED; } else if (tls_connection_ca_cert(tls_ctx, conn, params->ca_cert, params->ca_cert_blob, params->ca_cert_blob_len, params->ca_path)) return -1; if (params->engine && params->cert_id) { if (tls_connection_engine_client_cert(conn, params->cert_id)) return TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED; } else if (tls_connection_client_cert(conn, params->client_cert, params->client_cert_blob, params->client_cert_blob_len)) return -1; if (params->engine && params->key_id) { wpa_printf(MSG_DEBUG, "TLS: Using private key from engine"); if (tls_connection_engine_private_key(conn)) return TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED; } else if (tls_connection_private_key(tls_ctx, conn, params->private_key, params->private_key_passwd, params->private_key_blob, params->private_key_blob_len)) { wpa_printf(MSG_INFO, "TLS: Failed to load private key '%s'", params->private_key); return -1; } if (tls_connection_dh(conn, params->dh_file)) { wpa_printf(MSG_INFO, "TLS: Failed to load DH file '%s'", params->dh_file); return -1; } #ifdef SSL_OP_NO_TICKET if (params->flags & TLS_CONN_DISABLE_SESSION_TICKET) SSL_set_options(conn->ssl, SSL_OP_NO_TICKET); #ifdef SSL_clear_options else SSL_clear_options(conn->ssl, SSL_OP_NO_TICKET); #endif /* SSL_clear_options */ #endif /* SSL_OP_NO_TICKET */ #ifdef HAVE_OCSP if (params->flags & TLS_CONN_REQUEST_OCSP) { SSL_CTX *ssl_ctx = tls_ctx; SSL_set_tlsext_status_type(conn->ssl, TLSEXT_STATUSTYPE_ocsp); SSL_CTX_set_tlsext_status_cb(ssl_ctx, ocsp_resp_cb); SSL_CTX_set_tlsext_status_arg(ssl_ctx, conn); } #endif /* HAVE_OCSP */ conn->flags = params->flags; tls_get_errors(tls_ctx); return 0; } int tls_global_set_params(void *tls_ctx, const struct tls_connection_params *params) { SSL_CTX *ssl_ctx = tls_ctx; unsigned long err; while ((err = ERR_get_error())) { wpa_printf(MSG_INFO, "%s: Clearing pending SSL error: %s", __func__, ERR_error_string(err, NULL)); } if (tls_global_ca_cert(ssl_ctx, params->ca_cert)) return -1; if (tls_global_client_cert(ssl_ctx, params->client_cert)) return -1; if (tls_global_private_key(ssl_ctx, params->private_key, params->private_key_passwd)) return -1; if (tls_global_dh(ssl_ctx, params->dh_file)) { wpa_printf(MSG_INFO, "TLS: Failed to load DH file '%s'", params->dh_file); return -1; } #ifdef SSL_OP_NO_TICKET if (params->flags & TLS_CONN_DISABLE_SESSION_TICKET) SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TICKET); #ifdef SSL_CTX_clear_options else SSL_CTX_clear_options(ssl_ctx, SSL_OP_NO_TICKET); #endif /* SSL_clear_options */ #endif /* SSL_OP_NO_TICKET */ #ifdef HAVE_OCSP SSL_CTX_set_tlsext_status_cb(ssl_ctx, ocsp_status_cb); SSL_CTX_set_tlsext_status_arg(ssl_ctx, ssl_ctx); os_free(tls_global->ocsp_stapling_response); if (params->ocsp_stapling_response) tls_global->ocsp_stapling_response = os_strdup(params->ocsp_stapling_response); else tls_global->ocsp_stapling_response = NULL; #endif /* HAVE_OCSP */ return 0; } int tls_connection_get_keyblock_size(void *tls_ctx, struct tls_connection *conn) { const EVP_CIPHER *c; const EVP_MD *h; int md_size; if (conn == NULL || conn->ssl == NULL || conn->ssl->enc_read_ctx == NULL || conn->ssl->enc_read_ctx->cipher == NULL || conn->ssl->read_hash == NULL) return -1; c = conn->ssl->enc_read_ctx->cipher; #if OPENSSL_VERSION_NUMBER >= 0x00909000L h = EVP_MD_CTX_md(conn->ssl->read_hash); #else h = conn->ssl->read_hash; #endif if (h) md_size = EVP_MD_size(h); #if OPENSSL_VERSION_NUMBER >= 0x10000000L else if (conn->ssl->s3) md_size = conn->ssl->s3->tmp.new_mac_secret_size; #endif else return -1; wpa_printf(MSG_DEBUG, "OpenSSL: keyblock size: key_len=%d MD_size=%d " "IV_len=%d", EVP_CIPHER_key_length(c), md_size, EVP_CIPHER_iv_length(c)); return 2 * (EVP_CIPHER_key_length(c) + md_size + EVP_CIPHER_iv_length(c)); } unsigned int tls_capabilities(void *tls_ctx) { return 0; } #if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST) /* Pre-shared secred requires a patch to openssl, so this function is * commented out unless explicitly needed for EAP-FAST in order to be able to * build this file with unmodified openssl. */ static int tls_sess_sec_cb(SSL *s, void *secret, int *secret_len, STACK_OF(SSL_CIPHER) *peer_ciphers, SSL_CIPHER **cipher, void *arg) { struct tls_connection *conn = arg; int ret; if (conn == NULL || conn->session_ticket_cb == NULL) return 0; ret = conn->session_ticket_cb(conn->session_ticket_cb_ctx, conn->session_ticket, conn->session_ticket_len, s->s3->client_random, s->s3->server_random, secret); os_free(conn->session_ticket); conn->session_ticket = NULL; if (ret <= 0) return 0; *secret_len = SSL_MAX_MASTER_KEY_LENGTH; return 1; } #ifdef CONFIG_OPENSSL_TICKET_OVERRIDE static int tls_session_ticket_ext_cb(SSL *s, const unsigned char *data, int len, void *arg) { struct tls_connection *conn = arg; if (conn == NULL || conn->session_ticket_cb == NULL) return 0; wpa_printf(MSG_DEBUG, "OpenSSL: %s: length=%d", __func__, len); os_free(conn->session_ticket); conn->session_ticket = NULL; wpa_hexdump(MSG_DEBUG, "OpenSSL: ClientHello SessionTicket " "extension", data, len); conn->session_ticket = os_malloc(len); if (conn->session_ticket == NULL) return 0; os_memcpy(conn->session_ticket, data, len); conn->session_ticket_len = len; return 1; } #else /* CONFIG_OPENSSL_TICKET_OVERRIDE */ #ifdef SSL_OP_NO_TICKET static void tls_hello_ext_cb(SSL *s, int client_server, int type, unsigned char *data, int len, void *arg) { struct tls_connection *conn = arg; if (conn == NULL || conn->session_ticket_cb == NULL) return; wpa_printf(MSG_DEBUG, "OpenSSL: %s: type=%d length=%d", __func__, type, len); if (type == TLSEXT_TYPE_session_ticket && !client_server) { os_free(conn->session_ticket); conn->session_ticket = NULL; wpa_hexdump(MSG_DEBUG, "OpenSSL: ClientHello SessionTicket " "extension", data, len); conn->session_ticket = os_malloc(len); if (conn->session_ticket == NULL) return; os_memcpy(conn->session_ticket, data, len); conn->session_ticket_len = len; } } #else /* SSL_OP_NO_TICKET */ static int tls_hello_ext_cb(SSL *s, TLS_EXTENSION *ext, void *arg) { struct tls_connection *conn = arg; if (conn == NULL || conn->session_ticket_cb == NULL) return 0; wpa_printf(MSG_DEBUG, "OpenSSL: %s: type=%d length=%d", __func__, ext->type, ext->length); os_free(conn->session_ticket); conn->session_ticket = NULL; if (ext->type == 35) { wpa_hexdump(MSG_DEBUG, "OpenSSL: ClientHello SessionTicket " "extension", ext->data, ext->length); conn->session_ticket = os_malloc(ext->length); if (conn->session_ticket == NULL) return SSL_AD_INTERNAL_ERROR; os_memcpy(conn->session_ticket, ext->data, ext->length); conn->session_ticket_len = ext->length; } return 0; } #endif /* SSL_OP_NO_TICKET */ #endif /* CONFIG_OPENSSL_TICKET_OVERRIDE */ #endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */ int tls_connection_set_session_ticket_cb(void *tls_ctx, struct tls_connection *conn, tls_session_ticket_cb cb, void *ctx) { #if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST) conn->session_ticket_cb = cb; conn->session_ticket_cb_ctx = ctx; if (cb) { if (SSL_set_session_secret_cb(conn->ssl, tls_sess_sec_cb, conn) != 1) return -1; #ifdef CONFIG_OPENSSL_TICKET_OVERRIDE SSL_set_session_ticket_ext_cb(conn->ssl, tls_session_ticket_ext_cb, conn); #else /* CONFIG_OPENSSL_TICKET_OVERRIDE */ #ifdef SSL_OP_NO_TICKET SSL_set_tlsext_debug_callback(conn->ssl, tls_hello_ext_cb); SSL_set_tlsext_debug_arg(conn->ssl, conn); #else /* SSL_OP_NO_TICKET */ if (SSL_set_hello_extension_cb(conn->ssl, tls_hello_ext_cb, conn) != 1) return -1; #endif /* SSL_OP_NO_TICKET */ #endif /* CONFIG_OPENSSL_TICKET_OVERRIDE */ } else { if (SSL_set_session_secret_cb(conn->ssl, NULL, NULL) != 1) return -1; #ifdef CONFIG_OPENSSL_TICKET_OVERRIDE SSL_set_session_ticket_ext_cb(conn->ssl, NULL, NULL); #else /* CONFIG_OPENSSL_TICKET_OVERRIDE */ #ifdef SSL_OP_NO_TICKET SSL_set_tlsext_debug_callback(conn->ssl, NULL); SSL_set_tlsext_debug_arg(conn->ssl, conn); #else /* SSL_OP_NO_TICKET */ if (SSL_set_hello_extension_cb(conn->ssl, NULL, NULL) != 1) return -1; #endif /* SSL_OP_NO_TICKET */ #endif /* CONFIG_OPENSSL_TICKET_OVERRIDE */ } return 0; #else /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */ return -1; #endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */ } wpa-2.1/src/crypto/tls_schannel.c000066400000000000000000000452101227414666700171140ustar00rootroot00000000000000/* * SSL/TLS interface functions for Microsoft Schannel * Copyright (c) 2005-2009, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ /* * FIX: Go through all SSPI functions and verify what needs to be freed * FIX: session resumption * TODO: add support for server cert chain validation * TODO: add support for CA cert validation * TODO: add support for EAP-TLS (client cert/key conf) */ #include "includes.h" #include #include #include #define SECURITY_WIN32 #include #include #include "common.h" #include "tls.h" struct tls_global { HMODULE hsecurity; PSecurityFunctionTable sspi; HCERTSTORE my_cert_store; }; struct tls_connection { int established, start; int failed, read_alerts, write_alerts; SCHANNEL_CRED schannel_cred; CredHandle creds; CtxtHandle context; u8 eap_tls_prf[128]; int eap_tls_prf_set; }; static int schannel_load_lib(struct tls_global *global) { INIT_SECURITY_INTERFACE pInitSecurityInterface; global->hsecurity = LoadLibrary(TEXT("Secur32.dll")); if (global->hsecurity == NULL) { wpa_printf(MSG_ERROR, "%s: Could not load Secur32.dll - 0x%x", __func__, (unsigned int) GetLastError()); return -1; } pInitSecurityInterface = (INIT_SECURITY_INTERFACE) GetProcAddress( global->hsecurity, "InitSecurityInterfaceA"); if (pInitSecurityInterface == NULL) { wpa_printf(MSG_ERROR, "%s: Could not find " "InitSecurityInterfaceA from Secur32.dll", __func__); FreeLibrary(global->hsecurity); global->hsecurity = NULL; return -1; } global->sspi = pInitSecurityInterface(); if (global->sspi == NULL) { wpa_printf(MSG_ERROR, "%s: Could not read security " "interface - 0x%x", __func__, (unsigned int) GetLastError()); FreeLibrary(global->hsecurity); global->hsecurity = NULL; return -1; } return 0; } void * tls_init(const struct tls_config *conf) { struct tls_global *global; global = os_zalloc(sizeof(*global)); if (global == NULL) return NULL; if (schannel_load_lib(global)) { os_free(global); return NULL; } return global; } void tls_deinit(void *ssl_ctx) { struct tls_global *global = ssl_ctx; if (global->my_cert_store) CertCloseStore(global->my_cert_store, 0); FreeLibrary(global->hsecurity); os_free(global); } int tls_get_errors(void *ssl_ctx) { return 0; } struct tls_connection * tls_connection_init(void *ssl_ctx) { struct tls_connection *conn; conn = os_zalloc(sizeof(*conn)); if (conn == NULL) return NULL; conn->start = 1; return conn; } void tls_connection_deinit(void *ssl_ctx, struct tls_connection *conn) { if (conn == NULL) return; os_free(conn); } int tls_connection_established(void *ssl_ctx, struct tls_connection *conn) { return conn ? conn->established : 0; } int tls_connection_shutdown(void *ssl_ctx, struct tls_connection *conn) { struct tls_global *global = ssl_ctx; if (conn == NULL) return -1; conn->eap_tls_prf_set = 0; conn->established = conn->failed = 0; conn->read_alerts = conn->write_alerts = 0; global->sspi->DeleteSecurityContext(&conn->context); /* FIX: what else needs to be reseted? */ return 0; } int tls_global_set_params(void *tls_ctx, const struct tls_connection_params *params) { return -1; } int tls_global_set_verify(void *ssl_ctx, int check_crl) { return -1; } int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn, int verify_peer) { return -1; } int tls_connection_get_keys(void *ssl_ctx, struct tls_connection *conn, struct tls_keys *keys) { /* Schannel does not export master secret or client/server random. */ return -1; } int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, const char *label, int server_random_first, u8 *out, size_t out_len) { /* * Cannot get master_key from Schannel, but EapKeyBlock can be used to * generate session keys for EAP-TLS and EAP-PEAPv0. EAP-PEAPv2 and * EAP-TTLS cannot use this, though, since they are using different * labels. The only option could be to implement TLSv1 completely here * and just use Schannel or CryptoAPI for low-level crypto * functionality.. */ if (conn == NULL || !conn->eap_tls_prf_set || server_random_first || os_strcmp(label, "client EAP encryption") != 0 || out_len > sizeof(conn->eap_tls_prf)) return -1; os_memcpy(out, conn->eap_tls_prf, out_len); return 0; } static struct wpabuf * tls_conn_hs_clienthello(struct tls_global *global, struct tls_connection *conn) { DWORD sspi_flags, sspi_flags_out; SecBufferDesc outbuf; SecBuffer outbufs[1]; SECURITY_STATUS status; TimeStamp ts_expiry; sspi_flags = ISC_REQ_REPLAY_DETECT | ISC_REQ_CONFIDENTIALITY | ISC_RET_EXTENDED_ERROR | ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_MANUAL_CRED_VALIDATION; wpa_printf(MSG_DEBUG, "%s: Generating ClientHello", __func__); outbufs[0].pvBuffer = NULL; outbufs[0].BufferType = SECBUFFER_TOKEN; outbufs[0].cbBuffer = 0; outbuf.cBuffers = 1; outbuf.pBuffers = outbufs; outbuf.ulVersion = SECBUFFER_VERSION; #ifdef UNICODE status = global->sspi->InitializeSecurityContextW( &conn->creds, NULL, NULL /* server name */, sspi_flags, 0, SECURITY_NATIVE_DREP, NULL, 0, &conn->context, &outbuf, &sspi_flags_out, &ts_expiry); #else /* UNICODE */ status = global->sspi->InitializeSecurityContextA( &conn->creds, NULL, NULL /* server name */, sspi_flags, 0, SECURITY_NATIVE_DREP, NULL, 0, &conn->context, &outbuf, &sspi_flags_out, &ts_expiry); #endif /* UNICODE */ if (status != SEC_I_CONTINUE_NEEDED) { wpa_printf(MSG_ERROR, "%s: InitializeSecurityContextA " "failed - 0x%x", __func__, (unsigned int) status); return NULL; } if (outbufs[0].cbBuffer != 0 && outbufs[0].pvBuffer) { struct wpabuf *buf; wpa_hexdump(MSG_MSGDUMP, "SChannel - ClientHello", outbufs[0].pvBuffer, outbufs[0].cbBuffer); conn->start = 0; buf = wpabuf_alloc_copy(outbufs[0].pvBuffer, outbufs[0].cbBuffer); if (buf == NULL) return NULL; global->sspi->FreeContextBuffer(outbufs[0].pvBuffer); return buf; } wpa_printf(MSG_ERROR, "SChannel: Failed to generate ClientHello"); return NULL; } #ifndef SECPKG_ATTR_EAP_KEY_BLOCK #define SECPKG_ATTR_EAP_KEY_BLOCK 0x5b typedef struct _SecPkgContext_EapKeyBlock { BYTE rgbKeys[128]; BYTE rgbIVs[64]; } SecPkgContext_EapKeyBlock, *PSecPkgContext_EapKeyBlock; #endif /* !SECPKG_ATTR_EAP_KEY_BLOCK */ static int tls_get_eap(struct tls_global *global, struct tls_connection *conn) { SECURITY_STATUS status; SecPkgContext_EapKeyBlock kb; /* Note: Windows NT and Windows Me/98/95 do not support getting * EapKeyBlock */ status = global->sspi->QueryContextAttributes( &conn->context, SECPKG_ATTR_EAP_KEY_BLOCK, &kb); if (status != SEC_E_OK) { wpa_printf(MSG_DEBUG, "%s: QueryContextAttributes(" "SECPKG_ATTR_EAP_KEY_BLOCK) failed (%d)", __func__, (int) status); return -1; } wpa_hexdump_key(MSG_MSGDUMP, "Schannel - EapKeyBlock - rgbKeys", kb.rgbKeys, sizeof(kb.rgbKeys)); wpa_hexdump_key(MSG_MSGDUMP, "Schannel - EapKeyBlock - rgbIVs", kb.rgbIVs, sizeof(kb.rgbIVs)); os_memcpy(conn->eap_tls_prf, kb.rgbKeys, sizeof(kb.rgbKeys)); conn->eap_tls_prf_set = 1; return 0; } struct wpabuf * tls_connection_handshake(void *tls_ctx, struct tls_connection *conn, const struct wpabuf *in_data, struct wpabuf **appl_data) { struct tls_global *global = tls_ctx; DWORD sspi_flags, sspi_flags_out; SecBufferDesc inbuf, outbuf; SecBuffer inbufs[2], outbufs[1]; SECURITY_STATUS status; TimeStamp ts_expiry; struct wpabuf *out_buf = NULL; if (appl_data) *appl_data = NULL; if (conn->start) return tls_conn_hs_clienthello(global, conn); wpa_printf(MSG_DEBUG, "SChannel: %d bytes handshake data to process", (int) wpabuf_len(in_data)); sspi_flags = ISC_REQ_REPLAY_DETECT | ISC_REQ_CONFIDENTIALITY | ISC_RET_EXTENDED_ERROR | ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_MANUAL_CRED_VALIDATION; /* Input buffer for Schannel */ inbufs[0].pvBuffer = (u8 *) wpabuf_head(in_data); inbufs[0].cbBuffer = wpabuf_len(in_data); inbufs[0].BufferType = SECBUFFER_TOKEN; /* Place for leftover data from Schannel */ inbufs[1].pvBuffer = NULL; inbufs[1].cbBuffer = 0; inbufs[1].BufferType = SECBUFFER_EMPTY; inbuf.cBuffers = 2; inbuf.pBuffers = inbufs; inbuf.ulVersion = SECBUFFER_VERSION; /* Output buffer for Schannel */ outbufs[0].pvBuffer = NULL; outbufs[0].cbBuffer = 0; outbufs[0].BufferType = SECBUFFER_TOKEN; outbuf.cBuffers = 1; outbuf.pBuffers = outbufs; outbuf.ulVersion = SECBUFFER_VERSION; #ifdef UNICODE status = global->sspi->InitializeSecurityContextW( &conn->creds, &conn->context, NULL, sspi_flags, 0, SECURITY_NATIVE_DREP, &inbuf, 0, NULL, &outbuf, &sspi_flags_out, &ts_expiry); #else /* UNICODE */ status = global->sspi->InitializeSecurityContextA( &conn->creds, &conn->context, NULL, sspi_flags, 0, SECURITY_NATIVE_DREP, &inbuf, 0, NULL, &outbuf, &sspi_flags_out, &ts_expiry); #endif /* UNICODE */ wpa_printf(MSG_MSGDUMP, "Schannel: InitializeSecurityContext -> " "status=%d inlen[0]=%d intype[0]=%d inlen[1]=%d " "intype[1]=%d outlen[0]=%d", (int) status, (int) inbufs[0].cbBuffer, (int) inbufs[0].BufferType, (int) inbufs[1].cbBuffer, (int) inbufs[1].BufferType, (int) outbufs[0].cbBuffer); if (status == SEC_E_OK || status == SEC_I_CONTINUE_NEEDED || (FAILED(status) && (sspi_flags_out & ISC_RET_EXTENDED_ERROR))) { if (outbufs[0].cbBuffer != 0 && outbufs[0].pvBuffer) { wpa_hexdump(MSG_MSGDUMP, "SChannel - output", outbufs[0].pvBuffer, outbufs[0].cbBuffer); out_buf = wpabuf_alloc_copy(outbufs[0].pvBuffer, outbufs[0].cbBuffer); global->sspi->FreeContextBuffer(outbufs[0].pvBuffer); outbufs[0].pvBuffer = NULL; if (out_buf == NULL) return NULL; } } switch (status) { case SEC_E_INCOMPLETE_MESSAGE: wpa_printf(MSG_DEBUG, "Schannel: SEC_E_INCOMPLETE_MESSAGE"); break; case SEC_I_CONTINUE_NEEDED: wpa_printf(MSG_DEBUG, "Schannel: SEC_I_CONTINUE_NEEDED"); break; case SEC_E_OK: /* TODO: verify server certificate chain */ wpa_printf(MSG_DEBUG, "Schannel: SEC_E_OK - Handshake " "completed successfully"); conn->established = 1; tls_get_eap(global, conn); /* Need to return something to get final TLS ACK. */ if (out_buf == NULL) out_buf = wpabuf_alloc(0); if (inbufs[1].BufferType == SECBUFFER_EXTRA) { wpa_hexdump(MSG_MSGDUMP, "SChannel - Encrypted " "application data", inbufs[1].pvBuffer, inbufs[1].cbBuffer); if (appl_data) { *appl_data = wpabuf_alloc_copy( outbufs[1].pvBuffer, outbufs[1].cbBuffer); } global->sspi->FreeContextBuffer(inbufs[1].pvBuffer); inbufs[1].pvBuffer = NULL; } break; case SEC_I_INCOMPLETE_CREDENTIALS: wpa_printf(MSG_DEBUG, "Schannel: SEC_I_INCOMPLETE_CREDENTIALS"); break; case SEC_E_WRONG_PRINCIPAL: wpa_printf(MSG_DEBUG, "Schannel: SEC_E_WRONG_PRINCIPAL"); break; case SEC_E_INTERNAL_ERROR: wpa_printf(MSG_DEBUG, "Schannel: SEC_E_INTERNAL_ERROR"); break; } if (FAILED(status)) { wpa_printf(MSG_DEBUG, "Schannel: Handshake failed " "(out_buf=%p)", out_buf); conn->failed++; global->sspi->DeleteSecurityContext(&conn->context); return out_buf; } if (inbufs[1].BufferType == SECBUFFER_EXTRA) { /* TODO: Can this happen? What to do with this data? */ wpa_hexdump(MSG_MSGDUMP, "SChannel - Leftover data", inbufs[1].pvBuffer, inbufs[1].cbBuffer); global->sspi->FreeContextBuffer(inbufs[1].pvBuffer); inbufs[1].pvBuffer = NULL; } return out_buf; } struct wpabuf * tls_connection_server_handshake(void *tls_ctx, struct tls_connection *conn, const struct wpabuf *in_data, struct wpabuf **appl_data) { return NULL; } struct wpabuf * tls_connection_encrypt(void *tls_ctx, struct tls_connection *conn, const struct wpabuf *in_data) { struct tls_global *global = tls_ctx; SECURITY_STATUS status; SecBufferDesc buf; SecBuffer bufs[4]; SecPkgContext_StreamSizes sizes; int i; struct wpabuf *out; status = global->sspi->QueryContextAttributes(&conn->context, SECPKG_ATTR_STREAM_SIZES, &sizes); if (status != SEC_E_OK) { wpa_printf(MSG_DEBUG, "%s: QueryContextAttributes failed", __func__); return NULL; } wpa_printf(MSG_DEBUG, "%s: Stream sizes: header=%u trailer=%u", __func__, (unsigned int) sizes.cbHeader, (unsigned int) sizes.cbTrailer); out = wpabuf_alloc(sizes.cbHeader + wpabuf_len(in_data) + sizes.cbTrailer); os_memset(&bufs, 0, sizeof(bufs)); bufs[0].pvBuffer = wpabuf_put(out, sizes.cbHeader); bufs[0].cbBuffer = sizes.cbHeader; bufs[0].BufferType = SECBUFFER_STREAM_HEADER; bufs[1].pvBuffer = wpabuf_put(out, 0); wpabuf_put_buf(out, in_data); bufs[1].cbBuffer = wpabuf_len(in_data); bufs[1].BufferType = SECBUFFER_DATA; bufs[2].pvBuffer = wpabuf_put(out, sizes.cbTrailer); bufs[2].cbBuffer = sizes.cbTrailer; bufs[2].BufferType = SECBUFFER_STREAM_TRAILER; buf.ulVersion = SECBUFFER_VERSION; buf.cBuffers = 3; buf.pBuffers = bufs; status = global->sspi->EncryptMessage(&conn->context, 0, &buf, 0); wpa_printf(MSG_MSGDUMP, "Schannel: EncryptMessage -> " "status=%d len[0]=%d type[0]=%d len[1]=%d type[1]=%d " "len[2]=%d type[2]=%d", (int) status, (int) bufs[0].cbBuffer, (int) bufs[0].BufferType, (int) bufs[1].cbBuffer, (int) bufs[1].BufferType, (int) bufs[2].cbBuffer, (int) bufs[2].BufferType); wpa_printf(MSG_MSGDUMP, "Schannel: EncryptMessage pointers: " "out_data=%p bufs %p %p %p", wpabuf_head(out), bufs[0].pvBuffer, bufs[1].pvBuffer, bufs[2].pvBuffer); for (i = 0; i < 3; i++) { if (bufs[i].pvBuffer && bufs[i].BufferType != SECBUFFER_EMPTY) { wpa_hexdump(MSG_MSGDUMP, "SChannel: bufs", bufs[i].pvBuffer, bufs[i].cbBuffer); } } if (status == SEC_E_OK) { wpa_printf(MSG_DEBUG, "%s: SEC_E_OK", __func__); wpa_hexdump_buf_key(MSG_MSGDUMP, "Schannel: Encrypted data " "from EncryptMessage", out); return out; } wpa_printf(MSG_DEBUG, "%s: Failed - status=%d", __func__, (int) status); wpabuf_free(out); return NULL; } struct wpabuf * tls_connection_decrypt(void *tls_ctx, struct tls_connection *conn, const struct wpabuf *in_data) { struct tls_global *global = tls_ctx; SECURITY_STATUS status; SecBufferDesc buf; SecBuffer bufs[4]; int i; struct wpabuf *out, *tmp; wpa_hexdump_buf(MSG_MSGDUMP, "Schannel: Encrypted data to DecryptMessage", in_data); os_memset(&bufs, 0, sizeof(bufs)); tmp = wpabuf_dup(in_data); if (tmp == NULL) return NULL; bufs[0].pvBuffer = wpabuf_mhead(tmp); bufs[0].cbBuffer = wpabuf_len(in_data); bufs[0].BufferType = SECBUFFER_DATA; bufs[1].BufferType = SECBUFFER_EMPTY; bufs[2].BufferType = SECBUFFER_EMPTY; bufs[3].BufferType = SECBUFFER_EMPTY; buf.ulVersion = SECBUFFER_VERSION; buf.cBuffers = 4; buf.pBuffers = bufs; status = global->sspi->DecryptMessage(&conn->context, &buf, 0, NULL); wpa_printf(MSG_MSGDUMP, "Schannel: DecryptMessage -> " "status=%d len[0]=%d type[0]=%d len[1]=%d type[1]=%d " "len[2]=%d type[2]=%d len[3]=%d type[3]=%d", (int) status, (int) bufs[0].cbBuffer, (int) bufs[0].BufferType, (int) bufs[1].cbBuffer, (int) bufs[1].BufferType, (int) bufs[2].cbBuffer, (int) bufs[2].BufferType, (int) bufs[3].cbBuffer, (int) bufs[3].BufferType); wpa_printf(MSG_MSGDUMP, "Schannel: DecryptMessage pointers: " "out_data=%p bufs %p %p %p %p", wpabuf_head(tmp), bufs[0].pvBuffer, bufs[1].pvBuffer, bufs[2].pvBuffer, bufs[3].pvBuffer); switch (status) { case SEC_E_INCOMPLETE_MESSAGE: wpa_printf(MSG_DEBUG, "%s: SEC_E_INCOMPLETE_MESSAGE", __func__); break; case SEC_E_OK: wpa_printf(MSG_DEBUG, "%s: SEC_E_OK", __func__); for (i = 0; i < 4; i++) { if (bufs[i].BufferType == SECBUFFER_DATA) break; } if (i == 4) { wpa_printf(MSG_DEBUG, "%s: No output data from " "DecryptMessage", __func__); wpabuf_free(tmp); return NULL; } wpa_hexdump_key(MSG_MSGDUMP, "Schannel: Decrypted data from " "DecryptMessage", bufs[i].pvBuffer, bufs[i].cbBuffer); out = wpabuf_alloc_copy(bufs[i].pvBuffer, bufs[i].cbBuffer); wpabuf_free(tmp); return out; } wpa_printf(MSG_DEBUG, "%s: Failed - status=%d", __func__, (int) status); wpabuf_free(tmp); return NULL; } int tls_connection_resumed(void *ssl_ctx, struct tls_connection *conn) { return 0; } int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn, u8 *ciphers) { return -1; } int tls_get_cipher(void *ssl_ctx, struct tls_connection *conn, char *buf, size_t buflen) { return -1; } int tls_connection_enable_workaround(void *ssl_ctx, struct tls_connection *conn) { return 0; } int tls_connection_client_hello_ext(void *ssl_ctx, struct tls_connection *conn, int ext_type, const u8 *data, size_t data_len) { return -1; } int tls_connection_get_failed(void *ssl_ctx, struct tls_connection *conn) { if (conn == NULL) return -1; return conn->failed; } int tls_connection_get_read_alerts(void *ssl_ctx, struct tls_connection *conn) { if (conn == NULL) return -1; return conn->read_alerts; } int tls_connection_get_write_alerts(void *ssl_ctx, struct tls_connection *conn) { if (conn == NULL) return -1; return conn->write_alerts; } int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, const struct tls_connection_params *params) { struct tls_global *global = tls_ctx; ALG_ID algs[1]; SECURITY_STATUS status; TimeStamp ts_expiry; if (conn == NULL) return -1; if (global->my_cert_store == NULL && (global->my_cert_store = CertOpenSystemStore(0, TEXT("MY"))) == NULL) { wpa_printf(MSG_ERROR, "%s: CertOpenSystemStore failed - 0x%x", __func__, (unsigned int) GetLastError()); return -1; } os_memset(&conn->schannel_cred, 0, sizeof(conn->schannel_cred)); conn->schannel_cred.dwVersion = SCHANNEL_CRED_VERSION; conn->schannel_cred.grbitEnabledProtocols = SP_PROT_TLS1; algs[0] = CALG_RSA_KEYX; conn->schannel_cred.cSupportedAlgs = 1; conn->schannel_cred.palgSupportedAlgs = algs; conn->schannel_cred.dwFlags |= SCH_CRED_NO_DEFAULT_CREDS; #ifdef UNICODE status = global->sspi->AcquireCredentialsHandleW( NULL, UNISP_NAME_W, SECPKG_CRED_OUTBOUND, NULL, &conn->schannel_cred, NULL, NULL, &conn->creds, &ts_expiry); #else /* UNICODE */ status = global->sspi->AcquireCredentialsHandleA( NULL, UNISP_NAME_A, SECPKG_CRED_OUTBOUND, NULL, &conn->schannel_cred, NULL, NULL, &conn->creds, &ts_expiry); #endif /* UNICODE */ if (status != SEC_E_OK) { wpa_printf(MSG_DEBUG, "%s: AcquireCredentialsHandleA failed - " "0x%x", __func__, (unsigned int) status); return -1; } return 0; } unsigned int tls_capabilities(void *tls_ctx) { return 0; } wpa-2.1/src/drivers/000077500000000000000000000000001227414666700144275ustar00rootroot00000000000000wpa-2.1/src/drivers/.gitignore000066400000000000000000000000431227414666700164140ustar00rootroot00000000000000build.wpa_supplicant build.hostapd wpa-2.1/src/drivers/Makefile000066400000000000000000000002361227414666700160700ustar00rootroot00000000000000all: @echo Nothing to be made. clean: rm -f *~ *.o *.d *.gcno *.gcda *.gcov rm -f build.wpa_supplicant build.hostapd install: @echo Nothing to be made. wpa-2.1/src/drivers/android_drv.h000066400000000000000000000037761227414666700171100ustar00rootroot00000000000000/* * Android driver interface * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef ANDROID_DRV_H #define ANDROID_DRV_H #define WPA_EVENT_DRIVER_STATE "CTRL-EVENT-DRIVER-STATE " #define MAX_SSID_LEN 32 #define MAX_DRV_CMD_SIZE 248 #define DRV_NUMBER_SEQUENTIAL_ERRORS 4 #define WEXT_PNOSETUP_HEADER "PNOSETUP " #define WEXT_PNOSETUP_HEADER_SIZE 9 #define WEXT_PNO_TLV_PREFIX 'S' #define WEXT_PNO_TLV_VERSION '1' #define WEXT_PNO_TLV_SUBVERSION '2' #define WEXT_PNO_TLV_RESERVED '0' #define WEXT_PNO_VERSION_SIZE 4 #define WEXT_PNO_AMOUNT 16 #define WEXT_PNO_SSID_SECTION 'S' /* SSID header size is SSID section type above + SSID length */ #define WEXT_PNO_SSID_HEADER_SIZE 2 #define WEXT_PNO_SCAN_INTERVAL_SECTION 'T' #define WEXT_PNO_SCAN_INTERVAL_LENGTH 2 #define WEXT_PNO_SCAN_INTERVAL 30 /* Scan interval size is scan interval section type + scan interval length * above */ #define WEXT_PNO_SCAN_INTERVAL_SIZE (1 + WEXT_PNO_SCAN_INTERVAL_LENGTH) #define WEXT_PNO_REPEAT_SECTION 'R' #define WEXT_PNO_REPEAT_LENGTH 1 #define WEXT_PNO_REPEAT 4 /* Repeat section size is Repeat section type + Repeat value length above */ #define WEXT_PNO_REPEAT_SIZE (1 + WEXT_PNO_REPEAT_LENGTH) #define WEXT_PNO_MAX_REPEAT_SECTION 'M' #define WEXT_PNO_MAX_REPEAT_LENGTH 1 #define WEXT_PNO_MAX_REPEAT 3 /* Max Repeat section size is Max Repeat section type + Max Repeat value length * above */ #define WEXT_PNO_MAX_REPEAT_SIZE (1 + WEXT_PNO_MAX_REPEAT_LENGTH) /* This corresponds to the size of all sections expect SSIDs */ #define WEXT_PNO_NONSSID_SECTIONS_SIZE \ (WEXT_PNO_SCAN_INTERVAL_SIZE + WEXT_PNO_REPEAT_SIZE + WEXT_PNO_MAX_REPEAT_SIZE) /* PNO Max command size is total of header, version, ssid and other sections + * Null termination */ #define WEXT_PNO_MAX_COMMAND_SIZE \ (WEXT_PNOSETUP_HEADER_SIZE + WEXT_PNO_VERSION_SIZE \ + WEXT_PNO_AMOUNT * (WEXT_PNO_SSID_HEADER_SIZE + MAX_SSID_LEN) \ + WEXT_PNO_NONSSID_SECTIONS_SIZE + 1) #endif /* ANDROID_DRV_H */ wpa-2.1/src/drivers/driver.h000066400000000000000000003477561227414666700161210ustar00rootroot00000000000000/* * Driver interface definition * Copyright (c) 2003-2014, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. * * This file defines a driver interface used by both %wpa_supplicant and * hostapd. The first part of the file defines data structures used in various * driver operations. This is followed by the struct wpa_driver_ops that each * driver wrapper will beed to define with callback functions for requesting * driver operations. After this, there are definitions for driver event * reporting with wpa_supplicant_event() and some convenience helper functions * that can be used to report events. */ #ifndef DRIVER_H #define DRIVER_H #define WPA_SUPPLICANT_DRIVER_VERSION 4 #include "common/defs.h" #include "utils/list.h" #define HOSTAPD_CHAN_DISABLED 0x00000001 #define HOSTAPD_CHAN_PASSIVE_SCAN 0x00000002 #define HOSTAPD_CHAN_NO_IBSS 0x00000004 #define HOSTAPD_CHAN_RADAR 0x00000008 #define HOSTAPD_CHAN_HT40PLUS 0x00000010 #define HOSTAPD_CHAN_HT40MINUS 0x00000020 #define HOSTAPD_CHAN_HT40 0x00000040 #define HOSTAPD_CHAN_SURVEY_LIST_INITIALIZED 0x00000080 #define HOSTAPD_CHAN_DFS_UNKNOWN 0x00000000 #define HOSTAPD_CHAN_DFS_USABLE 0x00000100 #define HOSTAPD_CHAN_DFS_UNAVAILABLE 0x00000200 #define HOSTAPD_CHAN_DFS_AVAILABLE 0x00000300 #define HOSTAPD_CHAN_DFS_MASK 0x00000300 #define HOSTAPD_CHAN_VHT_10_70 0x00000800 #define HOSTAPD_CHAN_VHT_30_50 0x00001000 #define HOSTAPD_CHAN_VHT_50_30 0x00002000 #define HOSTAPD_CHAN_VHT_70_10 0x00004000 enum reg_change_initiator { REGDOM_SET_BY_CORE, REGDOM_SET_BY_USER, REGDOM_SET_BY_DRIVER, REGDOM_SET_BY_COUNTRY_IE, REGDOM_BEACON_HINT, }; /** * struct hostapd_channel_data - Channel information */ struct hostapd_channel_data { /** * chan - Channel number (IEEE 802.11) */ short chan; /** * freq - Frequency in MHz */ int freq; /** * flag - Channel flags (HOSTAPD_CHAN_*) */ int flag; /** * max_tx_power - Regulatory transmit power limit in dBm */ u8 max_tx_power; /* * survey_list - Linked list of surveys */ struct dl_list survey_list; /** * min_nf - Minimum observed noise floor, in dBm, based on all * surveyed channel data */ s8 min_nf; #ifdef CONFIG_ACS /** * interference_factor - Computed interference factor on this * channel (used internally in src/ap/acs.c; driver wrappers do not * need to set this) */ long double interference_factor; #endif /* CONFIG_ACS */ }; #define HOSTAPD_MODE_FLAG_HT_INFO_KNOWN BIT(0) #define HOSTAPD_MODE_FLAG_VHT_INFO_KNOWN BIT(1) /** * struct hostapd_hw_modes - Supported hardware mode information */ struct hostapd_hw_modes { /** * mode - Hardware mode */ enum hostapd_hw_mode mode; /** * num_channels - Number of entries in the channels array */ int num_channels; /** * channels - Array of supported channels */ struct hostapd_channel_data *channels; /** * num_rates - Number of entries in the rates array */ int num_rates; /** * rates - Array of supported rates in 100 kbps units */ int *rates; /** * ht_capab - HT (IEEE 802.11n) capabilities */ u16 ht_capab; /** * mcs_set - MCS (IEEE 802.11n) rate parameters */ u8 mcs_set[16]; /** * a_mpdu_params - A-MPDU (IEEE 802.11n) parameters */ u8 a_mpdu_params; /** * vht_capab - VHT (IEEE 802.11ac) capabilities */ u32 vht_capab; /** * vht_mcs_set - VHT MCS (IEEE 802.11ac) rate parameters */ u8 vht_mcs_set[8]; unsigned int flags; /* HOSTAPD_MODE_FLAG_* */ }; #define IEEE80211_MODE_INFRA 0 #define IEEE80211_MODE_IBSS 1 #define IEEE80211_MODE_AP 2 #define IEEE80211_CAP_ESS 0x0001 #define IEEE80211_CAP_IBSS 0x0002 #define IEEE80211_CAP_PRIVACY 0x0010 /* DMG (60 GHz) IEEE 802.11ad */ /* type - bits 0..1 */ #define IEEE80211_CAP_DMG_MASK 0x0003 #define IEEE80211_CAP_DMG_IBSS 0x0001 /* Tx by: STA */ #define IEEE80211_CAP_DMG_PBSS 0x0002 /* Tx by: PCP */ #define IEEE80211_CAP_DMG_AP 0x0003 /* Tx by: AP */ #define WPA_SCAN_QUAL_INVALID BIT(0) #define WPA_SCAN_NOISE_INVALID BIT(1) #define WPA_SCAN_LEVEL_INVALID BIT(2) #define WPA_SCAN_LEVEL_DBM BIT(3) #define WPA_SCAN_AUTHENTICATED BIT(4) #define WPA_SCAN_ASSOCIATED BIT(5) /** * struct wpa_scan_res - Scan result for an BSS/IBSS * @flags: information flags about the BSS/IBSS (WPA_SCAN_*) * @bssid: BSSID * @freq: frequency of the channel in MHz (e.g., 2412 = channel 1) * @beacon_int: beacon interval in TUs (host byte order) * @caps: capability information field in host byte order * @qual: signal quality * @noise: noise level * @level: signal level * @tsf: Timestamp * @age: Age of the information in milliseconds (i.e., how many milliseconds * ago the last Beacon or Probe Response frame was received) * @ie_len: length of the following IE field in octets * @beacon_ie_len: length of the following Beacon IE field in octets * * This structure is used as a generic format for scan results from the * driver. Each driver interface implementation is responsible for converting * the driver or OS specific scan results into this format. * * If the driver does not support reporting all IEs, the IE data structure is * constructed of the IEs that are available. This field will also need to * include SSID in IE format. All drivers are encouraged to be extended to * report all IEs to make it easier to support future additions. */ struct wpa_scan_res { unsigned int flags; u8 bssid[ETH_ALEN]; int freq; u16 beacon_int; u16 caps; int qual; int noise; int level; u64 tsf; unsigned int age; size_t ie_len; size_t beacon_ie_len; /* * Followed by ie_len octets of IEs from Probe Response frame (or if * the driver does not indicate source of IEs, these may also be from * Beacon frame). After the first set of IEs, another set of IEs may * follow (with beacon_ie_len octets of data) if the driver provides * both IE sets. */ }; /** * struct wpa_scan_results - Scan results * @res: Array of pointers to allocated variable length scan result entries * @num: Number of entries in the scan result array * @fetch_time: Time when the results were fetched from the driver */ struct wpa_scan_results { struct wpa_scan_res **res; size_t num; struct os_reltime fetch_time; }; /** * struct wpa_interface_info - Network interface information * @next: Pointer to the next interface or NULL if this is the last one * @ifname: Interface name that can be used with init() or init2() * @desc: Human readable adapter description (e.g., vendor/model) or NULL if * not available * @drv_name: struct wpa_driver_ops::name (note: unlike other strings, this one * is not an allocated copy, i.e., get_interfaces() caller will not free * this) */ struct wpa_interface_info { struct wpa_interface_info *next; char *ifname; char *desc; const char *drv_name; }; #define WPAS_MAX_SCAN_SSIDS 16 /** * struct wpa_driver_scan_params - Scan parameters * Data for struct wpa_driver_ops::scan2(). */ struct wpa_driver_scan_params { /** * ssids - SSIDs to scan for */ struct wpa_driver_scan_ssid { /** * ssid - specific SSID to scan for (ProbeReq) * %NULL or zero-length SSID is used to indicate active scan * with wildcard SSID. */ const u8 *ssid; /** * ssid_len: Length of the SSID in octets */ size_t ssid_len; } ssids[WPAS_MAX_SCAN_SSIDS]; /** * num_ssids - Number of entries in ssids array * Zero indicates a request for a passive scan. */ size_t num_ssids; /** * extra_ies - Extra IE(s) to add into Probe Request or %NULL */ const u8 *extra_ies; /** * extra_ies_len - Length of extra_ies in octets */ size_t extra_ies_len; /** * freqs - Array of frequencies to scan or %NULL for all frequencies * * The frequency is set in MHz. The array is zero-terminated. */ int *freqs; /** * filter_ssids - Filter for reporting SSIDs * * This optional parameter can be used to request the driver wrapper to * filter scan results to include only the specified SSIDs. %NULL * indicates that no filtering is to be done. This can be used to * reduce memory needs for scan results in environments that have large * number of APs with different SSIDs. * * The driver wrapper is allowed to take this allocated buffer into its * own use by setting the pointer to %NULL. In that case, the driver * wrapper is responsible for freeing the buffer with os_free() once it * is not needed anymore. */ struct wpa_driver_scan_filter { u8 ssid[32]; size_t ssid_len; } *filter_ssids; /** * num_filter_ssids - Number of entries in filter_ssids array */ size_t num_filter_ssids; /** * filter_rssi - Filter by RSSI * * The driver may filter scan results in firmware to reduce host * wakeups and thereby save power. Specify the RSSI threshold in s32 * dBm. */ s32 filter_rssi; /** * p2p_probe - Used to disable CCK (802.11b) rates for P2P probes * * When set, the driver is expected to remove rates 1, 2, 5.5, and 11 * Mbps from the support rates element(s) in the Probe Request frames * and not to transmit the frames at any of those rates. */ u8 p2p_probe; /** * only_new_results - Request driver to report only new results * * This is used to request the driver to report only BSSes that have * been detected after this scan request has been started, i.e., to * flush old cached BSS entries. */ int only_new_results; /* * NOTE: Whenever adding new parameters here, please make sure * wpa_scan_clone_params() and wpa_scan_free_params() get updated with * matching changes. */ }; /** * struct wpa_driver_auth_params - Authentication parameters * Data for struct wpa_driver_ops::authenticate(). */ struct wpa_driver_auth_params { int freq; const u8 *bssid; const u8 *ssid; size_t ssid_len; int auth_alg; const u8 *ie; size_t ie_len; const u8 *wep_key[4]; size_t wep_key_len[4]; int wep_tx_keyidx; int local_state_change; /** * p2p - Whether this connection is a P2P group */ int p2p; const u8 *sae_data; size_t sae_data_len; }; enum wps_mode { WPS_MODE_NONE /* no WPS provisioning being used */, WPS_MODE_OPEN /* WPS provisioning with AP that is in open mode */, WPS_MODE_PRIVACY /* WPS provisioning with AP that is using protection */ }; /** * struct wpa_driver_associate_params - Association parameters * Data for struct wpa_driver_ops::associate(). */ struct wpa_driver_associate_params { /** * bssid - BSSID of the selected AP * This can be %NULL, if ap_scan=2 mode is used and the driver is * responsible for selecting with which BSS to associate. */ const u8 *bssid; /** * ssid - The selected SSID */ const u8 *ssid; /** * ssid_len - Length of the SSID (1..32) */ size_t ssid_len; /** * freq - Frequency of the channel the selected AP is using * Frequency that the selected AP is using (in MHz as * reported in the scan results) */ int freq; /** * bg_scan_period - Background scan period in seconds, 0 to disable * background scan, or -1 to indicate no change to default driver * configuration */ int bg_scan_period; /** * wpa_ie - WPA information element for (Re)Association Request * WPA information element to be included in (Re)Association * Request (including information element id and length). Use * of this WPA IE is optional. If the driver generates the WPA * IE, it can use pairwise_suite, group_suite, and * key_mgmt_suite to select proper algorithms. In this case, * the driver has to notify wpa_supplicant about the used WPA * IE by generating an event that the interface code will * convert into EVENT_ASSOCINFO data (see below). * * When using WPA2/IEEE 802.11i, wpa_ie is used for RSN IE * instead. The driver can determine which version is used by * looking at the first byte of the IE (0xdd for WPA, 0x30 for * WPA2/RSN). * * When using WPS, wpa_ie is used for WPS IE instead of WPA/RSN IE. */ const u8 *wpa_ie; /** * wpa_ie_len - length of the wpa_ie */ size_t wpa_ie_len; /** * wpa_proto - Bitfield of WPA_PROTO_* values to indicate WPA/WPA2 */ unsigned int wpa_proto; /** * pairwise_suite - Selected pairwise cipher suite (WPA_CIPHER_*) * * This is usually ignored if @wpa_ie is used. */ unsigned int pairwise_suite; /** * group_suite - Selected group cipher suite (WPA_CIPHER_*) * * This is usually ignored if @wpa_ie is used. */ unsigned int group_suite; /** * key_mgmt_suite - Selected key management suite (WPA_KEY_MGMT_*) * * This is usually ignored if @wpa_ie is used. */ unsigned int key_mgmt_suite; /** * auth_alg - Allowed authentication algorithms * Bit field of WPA_AUTH_ALG_* */ int auth_alg; /** * mode - Operation mode (infra/ibss) IEEE80211_MODE_* */ int mode; /** * wep_key - WEP keys for static WEP configuration */ const u8 *wep_key[4]; /** * wep_key_len - WEP key length for static WEP configuration */ size_t wep_key_len[4]; /** * wep_tx_keyidx - WEP TX key index for static WEP configuration */ int wep_tx_keyidx; /** * mgmt_frame_protection - IEEE 802.11w management frame protection */ enum mfp_options mgmt_frame_protection; /** * ft_ies - IEEE 802.11r / FT information elements * If the supplicant is using IEEE 802.11r (FT) and has the needed keys * for fast transition, this parameter is set to include the IEs that * are to be sent in the next FT Authentication Request message. * update_ft_ies() handler is called to update the IEs for further * FT messages in the sequence. * * The driver should use these IEs only if the target AP is advertising * the same mobility domain as the one included in the MDIE here. * * In ap_scan=2 mode, the driver can use these IEs when moving to a new * AP after the initial association. These IEs can only be used if the * target AP is advertising support for FT and is using the same MDIE * and SSID as the current AP. * * The driver is responsible for reporting the FT IEs received from the * AP's response using wpa_supplicant_event() with EVENT_FT_RESPONSE * type. update_ft_ies() handler will then be called with the FT IEs to * include in the next frame in the authentication sequence. */ const u8 *ft_ies; /** * ft_ies_len - Length of ft_ies in bytes */ size_t ft_ies_len; /** * ft_md - FT Mobility domain (6 octets) (also included inside ft_ies) * * This value is provided to allow the driver interface easier access * to the current mobility domain. This value is set to %NULL if no * mobility domain is currently active. */ const u8 *ft_md; /** * passphrase - RSN passphrase for PSK * * This value is made available only for WPA/WPA2-Personal (PSK) and * only for drivers that set WPA_DRIVER_FLAGS_4WAY_HANDSHAKE. This is * the 8..63 character ASCII passphrase, if available. Please note that * this can be %NULL if passphrase was not used to generate the PSK. In * that case, the psk field must be used to fetch the PSK. */ const char *passphrase; /** * psk - RSN PSK (alternative for passphrase for PSK) * * This value is made available only for WPA/WPA2-Personal (PSK) and * only for drivers that set WPA_DRIVER_FLAGS_4WAY_HANDSHAKE. This is * the 32-octet (256-bit) PSK, if available. The driver wrapper should * be prepared to handle %NULL value as an error. */ const u8 *psk; /** * drop_unencrypted - Enable/disable unencrypted frame filtering * * Configure the driver to drop all non-EAPOL frames (both receive and * transmit paths). Unencrypted EAPOL frames (ethertype 0x888e) must * still be allowed for key negotiation. */ int drop_unencrypted; /** * prev_bssid - Previously used BSSID in this ESS * * When not %NULL, this is a request to use reassociation instead of * association. */ const u8 *prev_bssid; /** * wps - WPS mode * * If the driver needs to do special configuration for WPS association, * this variable provides more information on what type of association * is being requested. Most drivers should not need ot use this. */ enum wps_mode wps; /** * p2p - Whether this connection is a P2P group */ int p2p; /** * uapsd - UAPSD parameters for the network * -1 = do not change defaults * AP mode: 1 = enabled, 0 = disabled * STA mode: bits 0..3 UAPSD enabled for VO,VI,BK,BE */ int uapsd; /** * fixed_bssid - Whether to force this BSSID in IBSS mode * 1 = Fix this BSSID and prevent merges. * 0 = Do not fix BSSID. */ int fixed_bssid; /** * disable_ht - Disable HT (IEEE 802.11n) for this connection */ int disable_ht; /** * HT Capabilities over-rides. Only bits set in the mask will be used, * and not all values are used by the kernel anyway. Currently, MCS, * MPDU and MSDU fields are used. */ const u8 *htcaps; /* struct ieee80211_ht_capabilities * */ const u8 *htcaps_mask; /* struct ieee80211_ht_capabilities * */ #ifdef CONFIG_VHT_OVERRIDES /** * disable_vht - Disable VHT for this connection */ int disable_vht; /** * VHT capability overrides. */ const struct ieee80211_vht_capabilities *vhtcaps; const struct ieee80211_vht_capabilities *vhtcaps_mask; #endif /* CONFIG_VHT_OVERRIDES */ }; enum hide_ssid { NO_SSID_HIDING, HIDDEN_SSID_ZERO_LEN, HIDDEN_SSID_ZERO_CONTENTS }; struct wpa_driver_ap_params { /** * head - Beacon head from IEEE 802.11 header to IEs before TIM IE */ u8 *head; /** * head_len - Length of the head buffer in octets */ size_t head_len; /** * tail - Beacon tail following TIM IE */ u8 *tail; /** * tail_len - Length of the tail buffer in octets */ size_t tail_len; /** * dtim_period - DTIM period */ int dtim_period; /** * beacon_int - Beacon interval */ int beacon_int; /** * basic_rates: -1 terminated array of basic rates in 100 kbps * * This parameter can be used to set a specific basic rate set for the * BSS. If %NULL, default basic rate set is used. */ int *basic_rates; /** * proberesp - Probe Response template * * This is used by drivers that reply to Probe Requests internally in * AP mode and require the full Probe Response template. */ u8 *proberesp; /** * proberesp_len - Length of the proberesp buffer in octets */ size_t proberesp_len; /** * ssid - The SSID to use in Beacon/Probe Response frames */ const u8 *ssid; /** * ssid_len - Length of the SSID (1..32) */ size_t ssid_len; /** * hide_ssid - Whether to hide the SSID */ enum hide_ssid hide_ssid; /** * pairwise_ciphers - WPA_CIPHER_* bitfield */ unsigned int pairwise_ciphers; /** * group_cipher - WPA_CIPHER_* */ unsigned int group_cipher; /** * key_mgmt_suites - WPA_KEY_MGMT_* bitfield */ unsigned int key_mgmt_suites; /** * auth_algs - WPA_AUTH_ALG_* bitfield */ unsigned int auth_algs; /** * wpa_version - WPA_PROTO_* bitfield */ unsigned int wpa_version; /** * privacy - Whether privacy is used in the BSS */ int privacy; /** * beacon_ies - WPS/P2P IE(s) for Beacon frames * * This is used to add IEs like WPS IE and P2P IE by drivers that do * not use the full Beacon template. */ const struct wpabuf *beacon_ies; /** * proberesp_ies - P2P/WPS IE(s) for Probe Response frames * * This is used to add IEs like WPS IE and P2P IE by drivers that * reply to Probe Request frames internally. */ const struct wpabuf *proberesp_ies; /** * assocresp_ies - WPS IE(s) for (Re)Association Response frames * * This is used to add IEs like WPS IE by drivers that reply to * (Re)Association Request frames internally. */ const struct wpabuf *assocresp_ies; /** * isolate - Whether to isolate frames between associated stations * * If this is non-zero, the AP is requested to disable forwarding of * frames between associated stations. */ int isolate; /** * cts_protect - Whether CTS protection is enabled */ int cts_protect; /** * preamble - Whether short preamble is enabled */ int preamble; /** * short_slot_time - Whether short slot time is enabled * * 0 = short slot time disable, 1 = short slot time enabled, -1 = do * not set (e.g., when 802.11g mode is not in use) */ int short_slot_time; /** * ht_opmode - HT operation mode or -1 if HT not in use */ int ht_opmode; /** * interworking - Whether Interworking is enabled */ int interworking; /** * hessid - Homogeneous ESS identifier or %NULL if not set */ const u8 *hessid; /** * access_network_type - Access Network Type (0..15) * * This is used for filtering Probe Request frames when Interworking is * enabled. */ u8 access_network_type; /** * ap_max_inactivity - Timeout in seconds to detect STA's inactivity * * This is used by driver which advertises this capability. */ int ap_max_inactivity; /** * disable_dgaf - Whether group-addressed frames are disabled */ int disable_dgaf; }; /** * struct wpa_driver_capa - Driver capability information */ struct wpa_driver_capa { #define WPA_DRIVER_CAPA_KEY_MGMT_WPA 0x00000001 #define WPA_DRIVER_CAPA_KEY_MGMT_WPA2 0x00000002 #define WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK 0x00000004 #define WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK 0x00000008 #define WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE 0x00000010 #define WPA_DRIVER_CAPA_KEY_MGMT_FT 0x00000020 #define WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK 0x00000040 #define WPA_DRIVER_CAPA_KEY_MGMT_WAPI_PSK 0x00000080 unsigned int key_mgmt; #define WPA_DRIVER_CAPA_ENC_WEP40 0x00000001 #define WPA_DRIVER_CAPA_ENC_WEP104 0x00000002 #define WPA_DRIVER_CAPA_ENC_TKIP 0x00000004 #define WPA_DRIVER_CAPA_ENC_CCMP 0x00000008 #define WPA_DRIVER_CAPA_ENC_WEP128 0x00000010 #define WPA_DRIVER_CAPA_ENC_GCMP 0x00000020 #define WPA_DRIVER_CAPA_ENC_GCMP_256 0x00000040 #define WPA_DRIVER_CAPA_ENC_CCMP_256 0x00000080 #define WPA_DRIVER_CAPA_ENC_BIP 0x00000100 #define WPA_DRIVER_CAPA_ENC_BIP_GMAC_128 0x00000200 #define WPA_DRIVER_CAPA_ENC_BIP_GMAC_256 0x00000400 #define WPA_DRIVER_CAPA_ENC_BIP_CMAC_256 0x00000800 unsigned int enc; #define WPA_DRIVER_AUTH_OPEN 0x00000001 #define WPA_DRIVER_AUTH_SHARED 0x00000002 #define WPA_DRIVER_AUTH_LEAP 0x00000004 unsigned int auth; /* Driver generated WPA/RSN IE */ #define WPA_DRIVER_FLAGS_DRIVER_IE 0x00000001 /* Driver needs static WEP key setup after association command */ #define WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC 0x00000002 /* unused: 0x00000004 */ /* Driver takes care of RSN 4-way handshake internally; PMK is configured with * struct wpa_driver_ops::set_key using alg = WPA_ALG_PMK */ #define WPA_DRIVER_FLAGS_4WAY_HANDSHAKE 0x00000008 #define WPA_DRIVER_FLAGS_WIRED 0x00000010 /* Driver provides separate commands for authentication and association (SME in * wpa_supplicant). */ #define WPA_DRIVER_FLAGS_SME 0x00000020 /* Driver supports AP mode */ #define WPA_DRIVER_FLAGS_AP 0x00000040 /* Driver needs static WEP key setup after association has been completed */ #define WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE 0x00000080 /* unused: 0x00000100 */ /* Driver supports concurrent P2P operations */ #define WPA_DRIVER_FLAGS_P2P_CONCURRENT 0x00000200 /* * Driver uses the initial interface as a dedicated management interface, i.e., * it cannot be used for P2P group operations or non-P2P purposes. */ #define WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE 0x00000400 /* This interface is P2P capable (P2P GO or P2P Client) */ #define WPA_DRIVER_FLAGS_P2P_CAPABLE 0x00000800 /* unused: 0x00001000 */ /* * Driver uses the initial interface for P2P management interface and non-P2P * purposes (e.g., connect to infra AP), but this interface cannot be used for * P2P group operations. */ #define WPA_DRIVER_FLAGS_P2P_MGMT_AND_NON_P2P 0x00002000 /* * Driver is known to use sane error codes, i.e., when it indicates that * something (e.g., association) fails, there was indeed a failure and the * operation does not end up getting completed successfully later. */ #define WPA_DRIVER_FLAGS_SANE_ERROR_CODES 0x00004000 /* Driver supports off-channel TX */ #define WPA_DRIVER_FLAGS_OFFCHANNEL_TX 0x00008000 /* Driver indicates TX status events for EAPOL Data frames */ #define WPA_DRIVER_FLAGS_EAPOL_TX_STATUS 0x00010000 /* Driver indicates TX status events for Deauth/Disassoc frames */ #define WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS 0x00020000 /* Driver supports roaming (BSS selection) in firmware */ #define WPA_DRIVER_FLAGS_BSS_SELECTION 0x00040000 /* Driver supports operating as a TDLS peer */ #define WPA_DRIVER_FLAGS_TDLS_SUPPORT 0x00080000 /* Driver requires external TDLS setup/teardown/discovery */ #define WPA_DRIVER_FLAGS_TDLS_EXTERNAL_SETUP 0x00100000 /* Driver indicates support for Probe Response offloading in AP mode */ #define WPA_DRIVER_FLAGS_PROBE_RESP_OFFLOAD 0x00200000 /* Driver supports U-APSD in AP mode */ #define WPA_DRIVER_FLAGS_AP_UAPSD 0x00400000 /* Driver supports inactivity timer in AP mode */ #define WPA_DRIVER_FLAGS_INACTIVITY_TIMER 0x00800000 /* Driver expects user space implementation of MLME in AP mode */ #define WPA_DRIVER_FLAGS_AP_MLME 0x01000000 /* Driver supports SAE with user space SME */ #define WPA_DRIVER_FLAGS_SAE 0x02000000 /* Driver makes use of OBSS scan mechanism in wpa_supplicant */ #define WPA_DRIVER_FLAGS_OBSS_SCAN 0x04000000 /* Driver supports IBSS (Ad-hoc) mode */ #define WPA_DRIVER_FLAGS_IBSS 0x08000000 /* Driver supports radar detection */ #define WPA_DRIVER_FLAGS_RADAR 0x10000000 /* Driver supports a dedicated interface for P2P Device */ #define WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE 0x20000000 /* Driver supports QoS Mapping */ #define WPA_DRIVER_FLAGS_QOS_MAPPING 0x40000000 /* Driver supports CSA in AP mode */ #define WPA_DRIVER_FLAGS_AP_CSA 0x80000000 unsigned int flags; int max_scan_ssids; int max_sched_scan_ssids; int sched_scan_supported; int max_match_sets; /** * max_remain_on_chan - Maximum remain-on-channel duration in msec */ unsigned int max_remain_on_chan; /** * max_stations - Maximum number of associated stations the driver * supports in AP mode */ unsigned int max_stations; /** * probe_resp_offloads - Bitmap of supported protocols by the driver * for Probe Response offloading. */ /* Driver Probe Response offloading support for WPS ver. 1 */ #define WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS 0x00000001 /* Driver Probe Response offloading support for WPS ver. 2 */ #define WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS2 0x00000002 /* Driver Probe Response offloading support for P2P */ #define WPA_DRIVER_PROBE_RESP_OFFLOAD_P2P 0x00000004 /* Driver Probe Response offloading support for IEEE 802.11u (Interworking) */ #define WPA_DRIVER_PROBE_RESP_OFFLOAD_INTERWORKING 0x00000008 unsigned int probe_resp_offloads; unsigned int max_acl_mac_addrs; /** * Number of supported concurrent channels */ unsigned int num_multichan_concurrent; /** * extended_capa - extended capabilities in driver/device * * Must be allocated and freed by driver and the pointers must be * valid for the lifetime of the driver, i.e., freed in deinit() */ const u8 *extended_capa, *extended_capa_mask; unsigned int extended_capa_len; }; struct hostapd_data; struct hostap_sta_driver_data { unsigned long rx_packets, tx_packets, rx_bytes, tx_bytes; unsigned long current_tx_rate; unsigned long inactive_msec; unsigned long flags; unsigned long num_ps_buf_frames; unsigned long tx_retry_failed; unsigned long tx_retry_count; int last_rssi; int last_ack_rssi; }; struct hostapd_sta_add_params { const u8 *addr; u16 aid; u16 capability; const u8 *supp_rates; size_t supp_rates_len; u16 listen_interval; const struct ieee80211_ht_capabilities *ht_capabilities; const struct ieee80211_vht_capabilities *vht_capabilities; u32 flags; /* bitmask of WPA_STA_* flags */ int set; /* Set STA parameters instead of add */ u8 qosinfo; const u8 *ext_capab; size_t ext_capab_len; const u8 *supp_channels; size_t supp_channels_len; const u8 *supp_oper_classes; size_t supp_oper_classes_len; }; struct hostapd_freq_params { int mode; int freq; int channel; /* for HT */ int ht_enabled; int sec_channel_offset; /* 0 = HT40 disabled, -1 = HT40 enabled, * secondary channel below primary, 1 = HT40 * enabled, secondary channel above primary */ /* for VHT */ int vht_enabled; /* valid for both HT and VHT, center_freq2 is non-zero * only for bandwidth 80 and an 80+80 channel */ int center_freq1, center_freq2; int bandwidth; }; struct mac_address { u8 addr[ETH_ALEN]; }; struct hostapd_acl_params { u8 acl_policy; unsigned int num_mac_acl; struct mac_address mac_acl[0]; }; enum wpa_driver_if_type { /** * WPA_IF_STATION - Station mode interface */ WPA_IF_STATION, /** * WPA_IF_AP_VLAN - AP mode VLAN interface * * This interface shares its address and Beacon frame with the main * BSS. */ WPA_IF_AP_VLAN, /** * WPA_IF_AP_BSS - AP mode BSS interface * * This interface has its own address and Beacon frame. */ WPA_IF_AP_BSS, /** * WPA_IF_P2P_GO - P2P Group Owner */ WPA_IF_P2P_GO, /** * WPA_IF_P2P_CLIENT - P2P Client */ WPA_IF_P2P_CLIENT, /** * WPA_IF_P2P_GROUP - P2P Group interface (will become either * WPA_IF_P2P_GO or WPA_IF_P2P_CLIENT, but the role is not yet known) */ WPA_IF_P2P_GROUP, /** * WPA_IF_P2P_DEVICE - P2P Device interface is used to indentify the * abstracted P2P Device function in the driver */ WPA_IF_P2P_DEVICE }; struct wpa_init_params { void *global_priv; const u8 *bssid; const char *ifname; const u8 *ssid; size_t ssid_len; const char *test_socket; int use_pae_group_addr; char **bridge; size_t num_bridge; u8 *own_addr; /* buffer for writing own MAC address */ }; struct wpa_bss_params { /** Interface name (for multi-SSID/VLAN support) */ const char *ifname; /** Whether IEEE 802.1X or WPA/WPA2 is enabled */ int enabled; int wpa; int ieee802_1x; int wpa_group; int wpa_pairwise; int wpa_key_mgmt; int rsn_preauth; enum mfp_options ieee80211w; }; #define WPA_STA_AUTHORIZED BIT(0) #define WPA_STA_WMM BIT(1) #define WPA_STA_SHORT_PREAMBLE BIT(2) #define WPA_STA_MFP BIT(3) #define WPA_STA_TDLS_PEER BIT(4) enum tdls_oper { TDLS_DISCOVERY_REQ, TDLS_SETUP, TDLS_TEARDOWN, TDLS_ENABLE_LINK, TDLS_DISABLE_LINK, TDLS_ENABLE, TDLS_DISABLE }; enum wnm_oper { WNM_SLEEP_ENTER_CONFIRM, WNM_SLEEP_ENTER_FAIL, WNM_SLEEP_EXIT_CONFIRM, WNM_SLEEP_EXIT_FAIL, WNM_SLEEP_TFS_REQ_IE_ADD, /* STA requests driver to add TFS req IE */ WNM_SLEEP_TFS_REQ_IE_NONE, /* STA requests empty TFS req IE */ WNM_SLEEP_TFS_REQ_IE_SET, /* AP requests driver to set TFS req IE for * a STA */ WNM_SLEEP_TFS_RESP_IE_ADD, /* AP requests driver to add TFS resp IE * for a STA */ WNM_SLEEP_TFS_RESP_IE_NONE, /* AP requests empty TFS resp IE */ WNM_SLEEP_TFS_RESP_IE_SET, /* AP requests driver to set TFS resp IE * for a STA */ WNM_SLEEP_TFS_IE_DEL /* AP delete the TFS IE */ }; /* enum chan_width - Channel width definitions */ enum chan_width { CHAN_WIDTH_20_NOHT, CHAN_WIDTH_20, CHAN_WIDTH_40, CHAN_WIDTH_80, CHAN_WIDTH_80P80, CHAN_WIDTH_160, CHAN_WIDTH_UNKNOWN }; /** * struct wpa_signal_info - Information about channel signal quality */ struct wpa_signal_info { u32 frequency; int above_threshold; int current_signal; int avg_signal; int current_noise; int current_txrate; enum chan_width chanwidth; int center_frq1; int center_frq2; }; /** * struct beacon_data - Beacon data * @head: Head portion of Beacon frame (before TIM IE) * @tail: Tail portion of Beacon frame (after TIM IE) * @beacon_ies: Extra information element(s) to add into Beacon frames or %NULL * @proberesp_ies: Extra information element(s) to add into Probe Response * frames or %NULL * @assocresp_ies: Extra information element(s) to add into (Re)Association * Response frames or %NULL * @probe_resp: Probe Response frame template * @head_len: Length of @head * @tail_len: Length of @tail * @beacon_ies_len: Length of beacon_ies in octets * @proberesp_ies_len: Length of proberesp_ies in octets * @proberesp_ies_len: Length of proberesp_ies in octets * @probe_resp_len: Length of probe response template (@probe_resp) */ struct beacon_data { u8 *head, *tail; u8 *beacon_ies; u8 *proberesp_ies; u8 *assocresp_ies; u8 *probe_resp; size_t head_len, tail_len; size_t beacon_ies_len; size_t proberesp_ies_len; size_t assocresp_ies_len; size_t probe_resp_len; }; /** * struct csa_settings - Settings for channel switch command * @cs_count: Count in Beacon frames (TBTT) to perform the switch * @block_tx: 1 - block transmission for CSA period * @freq_params: Next channel frequency parameter * @beacon_csa: Beacon/probe resp/asooc resp info for CSA period * @beacon_after: Next beacon/probe resp/asooc resp info * @counter_offset_beacon: Offset to the count field in beacon's tail * @counter_offset_presp: Offset to the count field in probe resp. */ struct csa_settings { u8 cs_count; u8 block_tx; struct hostapd_freq_params freq_params; struct beacon_data beacon_csa; struct beacon_data beacon_after; u16 counter_offset_beacon; u16 counter_offset_presp; }; /** * struct wpa_driver_ops - Driver interface API definition * * This structure defines the API that each driver interface needs to implement * for core wpa_supplicant code. All driver specific functionality is captured * in this wrapper. */ struct wpa_driver_ops { /** Name of the driver interface */ const char *name; /** One line description of the driver interface */ const char *desc; /** * get_bssid - Get the current BSSID * @priv: private driver interface data * @bssid: buffer for BSSID (ETH_ALEN = 6 bytes) * * Returns: 0 on success, -1 on failure * * Query kernel driver for the current BSSID and copy it to bssid. * Setting bssid to 00:00:00:00:00:00 is recommended if the STA is not * associated. */ int (*get_bssid)(void *priv, u8 *bssid); /** * get_ssid - Get the current SSID * @priv: private driver interface data * @ssid: buffer for SSID (at least 32 bytes) * * Returns: Length of the SSID on success, -1 on failure * * Query kernel driver for the current SSID and copy it to ssid. * Returning zero is recommended if the STA is not associated. * * Note: SSID is an array of octets, i.e., it is not nul terminated and * can, at least in theory, contain control characters (including nul) * and as such, should be processed as binary data, not a printable * string. */ int (*get_ssid)(void *priv, u8 *ssid); /** * set_key - Configure encryption key * @ifname: Interface name (for multi-SSID/VLAN support) * @priv: private driver interface data * @alg: encryption algorithm (%WPA_ALG_NONE, %WPA_ALG_WEP, * %WPA_ALG_TKIP, %WPA_ALG_CCMP, %WPA_ALG_IGTK, %WPA_ALG_PMK, * %WPA_ALG_GCMP, %WPA_ALG_GCMP_256, %WPA_ALG_CCMP_256, * %WPA_ALG_BIP_GMAC_128, %WPA_ALG_BIP_GMAC_256, * %WPA_ALG_BIP_CMAC_256); * %WPA_ALG_NONE clears the key. * @addr: Address of the peer STA (BSSID of the current AP when setting * pairwise key in station mode), ff:ff:ff:ff:ff:ff for * broadcast keys, %NULL for default keys that are used both for * broadcast and unicast; when clearing keys, %NULL is used to * indicate that both the broadcast-only and default key of the * specified key index is to be cleared * @key_idx: key index (0..3), usually 0 for unicast keys; 0..4095 for * IGTK * @set_tx: configure this key as the default Tx key (only used when * driver does not support separate unicast/individual key * @seq: sequence number/packet number, seq_len octets, the next * packet number to be used for in replay protection; configured * for Rx keys (in most cases, this is only used with broadcast * keys and set to zero for unicast keys); %NULL if not set * @seq_len: length of the seq, depends on the algorithm: * TKIP: 6 octets, CCMP/GCMP: 6 octets, IGTK: 6 octets * @key: key buffer; TKIP: 16-byte temporal key, 8-byte Tx Mic key, * 8-byte Rx Mic Key * @key_len: length of the key buffer in octets (WEP: 5 or 13, * TKIP: 32, CCMP/GCMP: 16, IGTK: 16) * * Returns: 0 on success, -1 on failure * * Configure the given key for the kernel driver. If the driver * supports separate individual keys (4 default keys + 1 individual), * addr can be used to determine whether the key is default or * individual. If only 4 keys are supported, the default key with key * index 0 is used as the individual key. STA must be configured to use * it as the default Tx key (set_tx is set) and accept Rx for all the * key indexes. In most cases, WPA uses only key indexes 1 and 2 for * broadcast keys, so key index 0 is available for this kind of * configuration. * * Please note that TKIP keys include separate TX and RX MIC keys and * some drivers may expect them in different order than wpa_supplicant * is using. If the TX/RX keys are swapped, all TKIP encrypted packets * will trigger Michael MIC errors. This can be fixed by changing the * order of MIC keys by swapping te bytes 16..23 and 24..31 of the key * in driver_*.c set_key() implementation, see driver_ndis.c for an * example on how this can be done. */ int (*set_key)(const char *ifname, void *priv, enum wpa_alg alg, const u8 *addr, int key_idx, int set_tx, const u8 *seq, size_t seq_len, const u8 *key, size_t key_len); /** * init - Initialize driver interface * @ctx: context to be used when calling wpa_supplicant functions, * e.g., wpa_supplicant_event() * @ifname: interface name, e.g., wlan0 * * Returns: Pointer to private data, %NULL on failure * * Initialize driver interface, including event processing for kernel * driver events (e.g., associated, scan results, Michael MIC failure). * This function can allocate a private configuration data area for * @ctx, file descriptor, interface name, etc. information that may be * needed in future driver operations. If this is not used, non-NULL * value will need to be returned because %NULL is used to indicate * failure. The returned value will be used as 'void *priv' data for * all other driver_ops functions. * * The main event loop (eloop.c) of wpa_supplicant can be used to * register callback for read sockets (eloop_register_read_sock()). * * See below for more information about events and * wpa_supplicant_event() function. */ void * (*init)(void *ctx, const char *ifname); /** * deinit - Deinitialize driver interface * @priv: private driver interface data from init() * * Shut down driver interface and processing of driver events. Free * private data buffer if one was allocated in init() handler. */ void (*deinit)(void *priv); /** * set_param - Set driver configuration parameters * @priv: private driver interface data from init() * @param: driver specific configuration parameters * * Returns: 0 on success, -1 on failure * * Optional handler for notifying driver interface about configuration * parameters (driver_param). */ int (*set_param)(void *priv, const char *param); /** * set_countermeasures - Enable/disable TKIP countermeasures * @priv: private driver interface data * @enabled: 1 = countermeasures enabled, 0 = disabled * * Returns: 0 on success, -1 on failure * * Configure TKIP countermeasures. When these are enabled, the driver * should drop all received and queued frames that are using TKIP. */ int (*set_countermeasures)(void *priv, int enabled); /** * deauthenticate - Request driver to deauthenticate * @priv: private driver interface data * @addr: peer address (BSSID of the AP) * @reason_code: 16-bit reason code to be sent in the deauthentication * frame * * Returns: 0 on success, -1 on failure */ int (*deauthenticate)(void *priv, const u8 *addr, int reason_code); /** * associate - Request driver to associate * @priv: private driver interface data * @params: association parameters * * Returns: 0 on success, -1 on failure */ int (*associate)(void *priv, struct wpa_driver_associate_params *params); /** * add_pmkid - Add PMKSA cache entry to the driver * @priv: private driver interface data * @bssid: BSSID for the PMKSA cache entry * @pmkid: PMKID for the PMKSA cache entry * * Returns: 0 on success, -1 on failure * * This function is called when a new PMK is received, as a result of * either normal authentication or RSN pre-authentication. * * If the driver generates RSN IE, i.e., it does not use wpa_ie in * associate(), add_pmkid() can be used to add new PMKSA cache entries * in the driver. If the driver uses wpa_ie from wpa_supplicant, this * driver_ops function does not need to be implemented. Likewise, if * the driver does not support WPA, this function is not needed. */ int (*add_pmkid)(void *priv, const u8 *bssid, const u8 *pmkid); /** * remove_pmkid - Remove PMKSA cache entry to the driver * @priv: private driver interface data * @bssid: BSSID for the PMKSA cache entry * @pmkid: PMKID for the PMKSA cache entry * * Returns: 0 on success, -1 on failure * * This function is called when the supplicant drops a PMKSA cache * entry for any reason. * * If the driver generates RSN IE, i.e., it does not use wpa_ie in * associate(), remove_pmkid() can be used to synchronize PMKSA caches * between the driver and wpa_supplicant. If the driver uses wpa_ie * from wpa_supplicant, this driver_ops function does not need to be * implemented. Likewise, if the driver does not support WPA, this * function is not needed. */ int (*remove_pmkid)(void *priv, const u8 *bssid, const u8 *pmkid); /** * flush_pmkid - Flush PMKSA cache * @priv: private driver interface data * * Returns: 0 on success, -1 on failure * * This function is called when the supplicant drops all PMKSA cache * entries for any reason. * * If the driver generates RSN IE, i.e., it does not use wpa_ie in * associate(), remove_pmkid() can be used to synchronize PMKSA caches * between the driver and wpa_supplicant. If the driver uses wpa_ie * from wpa_supplicant, this driver_ops function does not need to be * implemented. Likewise, if the driver does not support WPA, this * function is not needed. */ int (*flush_pmkid)(void *priv); /** * get_capa - Get driver capabilities * @priv: private driver interface data * * Returns: 0 on success, -1 on failure * * Get driver/firmware/hardware capabilities. */ int (*get_capa)(void *priv, struct wpa_driver_capa *capa); /** * poll - Poll driver for association information * @priv: private driver interface data * * This is an option callback that can be used when the driver does not * provide event mechanism for association events. This is called when * receiving WPA EAPOL-Key messages that require association * information. The driver interface is supposed to generate associnfo * event before returning from this callback function. In addition, the * driver interface should generate an association event after having * sent out associnfo. */ void (*poll)(void *priv); /** * get_ifname - Get interface name * @priv: private driver interface data * * Returns: Pointer to the interface name. This can differ from the * interface name used in init() call. Init() is called first. * * This optional function can be used to allow the driver interface to * replace the interface name with something else, e.g., based on an * interface mapping from a more descriptive name. */ const char * (*get_ifname)(void *priv); /** * get_mac_addr - Get own MAC address * @priv: private driver interface data * * Returns: Pointer to own MAC address or %NULL on failure * * This optional function can be used to get the own MAC address of the * device from the driver interface code. This is only needed if the * l2_packet implementation for the OS does not provide easy access to * a MAC address. */ const u8 * (*get_mac_addr)(void *priv); /** * send_eapol - Optional function for sending EAPOL packets * @priv: private driver interface data * @dest: Destination MAC address * @proto: Ethertype * @data: EAPOL packet starting with IEEE 802.1X header * @data_len: Size of the EAPOL packet * * Returns: 0 on success, -1 on failure * * This optional function can be used to override l2_packet operations * with driver specific functionality. If this function pointer is set, * l2_packet module is not used at all and the driver interface code is * responsible for receiving and sending all EAPOL packets. The * received EAPOL packets are sent to core code with EVENT_EAPOL_RX * event. The driver interface is required to implement get_mac_addr() * handler if send_eapol() is used. */ int (*send_eapol)(void *priv, const u8 *dest, u16 proto, const u8 *data, size_t data_len); /** * set_operstate - Sets device operating state to DORMANT or UP * @priv: private driver interface data * @state: 0 = dormant, 1 = up * Returns: 0 on success, -1 on failure * * This is an optional function that can be used on operating systems * that support a concept of controlling network device state from user * space applications. This function, if set, gets called with * state = 1 when authentication has been completed and with state = 0 * when connection is lost. */ int (*set_operstate)(void *priv, int state); /** * mlme_setprotection - MLME-SETPROTECTION.request primitive * @priv: Private driver interface data * @addr: Address of the station for which to set protection (may be * %NULL for group keys) * @protect_type: MLME_SETPROTECTION_PROTECT_TYPE_* * @key_type: MLME_SETPROTECTION_KEY_TYPE_* * Returns: 0 on success, -1 on failure * * This is an optional function that can be used to set the driver to * require protection for Tx and/or Rx frames. This uses the layer * interface defined in IEEE 802.11i-2004 clause 10.3.22.1 * (MLME-SETPROTECTION.request). Many drivers do not use explicit * set protection operation; instead, they set protection implicitly * based on configured keys. */ int (*mlme_setprotection)(void *priv, const u8 *addr, int protect_type, int key_type); /** * get_hw_feature_data - Get hardware support data (channels and rates) * @priv: Private driver interface data * @num_modes: Variable for returning the number of returned modes * flags: Variable for returning hardware feature flags * Returns: Pointer to allocated hardware data on success or %NULL on * failure. Caller is responsible for freeing this. */ struct hostapd_hw_modes * (*get_hw_feature_data)(void *priv, u16 *num_modes, u16 *flags); /** * send_mlme - Send management frame from MLME * @priv: Private driver interface data * @data: IEEE 802.11 management frame with IEEE 802.11 header * @data_len: Size of the management frame * @noack: Do not wait for this frame to be acked (disable retries) * Returns: 0 on success, -1 on failure */ int (*send_mlme)(void *priv, const u8 *data, size_t data_len, int noack); /** * update_ft_ies - Update FT (IEEE 802.11r) IEs * @priv: Private driver interface data * @md: Mobility domain (2 octets) (also included inside ies) * @ies: FT IEs (MDIE, FTIE, ...) or %NULL to remove IEs * @ies_len: Length of FT IEs in bytes * Returns: 0 on success, -1 on failure * * The supplicant uses this callback to let the driver know that keying * material for FT is available and that the driver can use the * provided IEs in the next message in FT authentication sequence. * * This function is only needed for driver that support IEEE 802.11r * (Fast BSS Transition). */ int (*update_ft_ies)(void *priv, const u8 *md, const u8 *ies, size_t ies_len); /** * send_ft_action - Send FT Action frame (IEEE 802.11r) * @priv: Private driver interface data * @action: Action field value * @target_ap: Target AP address * @ies: FT IEs (MDIE, FTIE, ...) (FT Request action frame body) * @ies_len: Length of FT IEs in bytes * Returns: 0 on success, -1 on failure * * The supplicant uses this callback to request the driver to transmit * an FT Action frame (action category 6) for over-the-DS fast BSS * transition. */ int (*send_ft_action)(void *priv, u8 action, const u8 *target_ap, const u8 *ies, size_t ies_len); /** * get_scan_results2 - Fetch the latest scan results * @priv: private driver interface data * * Returns: Allocated buffer of scan results (caller is responsible for * freeing the data structure) on success, NULL on failure */ struct wpa_scan_results * (*get_scan_results2)(void *priv); /** * set_country - Set country * @priv: Private driver interface data * @alpha2: country to which to switch to * Returns: 0 on success, -1 on failure * * This function is for drivers which support some form * of setting a regulatory domain. */ int (*set_country)(void *priv, const char *alpha2); /** * get_country - Get country * @priv: Private driver interface data * @alpha2: Buffer for returning country code (at least 3 octets) * Returns: 0 on success, -1 on failure */ int (*get_country)(void *priv, char *alpha2); /** * global_init - Global driver initialization * Returns: Pointer to private data (global), %NULL on failure * * This optional function is called to initialize the driver wrapper * for global data, i.e., data that applies to all interfaces. If this * function is implemented, global_deinit() will also need to be * implemented to free the private data. The driver will also likely * use init2() function instead of init() to get the pointer to global * data available to per-interface initializer. */ void * (*global_init)(void); /** * global_deinit - Global driver deinitialization * @priv: private driver global data from global_init() * * Terminate any global driver related functionality and free the * global data structure. */ void (*global_deinit)(void *priv); /** * init2 - Initialize driver interface (with global data) * @ctx: context to be used when calling wpa_supplicant functions, * e.g., wpa_supplicant_event() * @ifname: interface name, e.g., wlan0 * @global_priv: private driver global data from global_init() * Returns: Pointer to private data, %NULL on failure * * This function can be used instead of init() if the driver wrapper * uses global data. */ void * (*init2)(void *ctx, const char *ifname, void *global_priv); /** * get_interfaces - Get information about available interfaces * @global_priv: private driver global data from global_init() * Returns: Allocated buffer of interface information (caller is * responsible for freeing the data structure) on success, NULL on * failure */ struct wpa_interface_info * (*get_interfaces)(void *global_priv); /** * scan2 - Request the driver to initiate scan * @priv: private driver interface data * @params: Scan parameters * * Returns: 0 on success, -1 on failure * * Once the scan results are ready, the driver should report scan * results event for wpa_supplicant which will eventually request the * results with wpa_driver_get_scan_results2(). */ int (*scan2)(void *priv, struct wpa_driver_scan_params *params); /** * authenticate - Request driver to authenticate * @priv: private driver interface data * @params: authentication parameters * Returns: 0 on success, -1 on failure * * This is an optional function that can be used with drivers that * support separate authentication and association steps, i.e., when * wpa_supplicant can act as the SME. If not implemented, associate() * function is expected to take care of IEEE 802.11 authentication, * too. */ int (*authenticate)(void *priv, struct wpa_driver_auth_params *params); /** * set_ap - Set Beacon and Probe Response information for AP mode * @priv: Private driver interface data * @params: Parameters to use in AP mode * * This function is used to configure Beacon template and/or extra IEs * to add for Beacon and Probe Response frames for the driver in * AP mode. The driver is responsible for building the full Beacon * frame by concatenating the head part with TIM IE generated by the * driver/firmware and finishing with the tail part. Depending on the * driver architectue, this can be done either by using the full * template or the set of additional IEs (e.g., WPS and P2P IE). * Similarly, Probe Response processing depends on the driver design. * If the driver (or firmware) takes care of replying to Probe Request * frames, the extra IEs provided here needs to be added to the Probe * Response frames. * * Returns: 0 on success, -1 on failure */ int (*set_ap)(void *priv, struct wpa_driver_ap_params *params); /** * set_acl - Set ACL in AP mode * @priv: Private driver interface data * @params: Parameters to configure ACL * Returns: 0 on success, -1 on failure * * This is used only for the drivers which support MAC address ACL. */ int (*set_acl)(void *priv, struct hostapd_acl_params *params); /** * hapd_init - Initialize driver interface (hostapd only) * @hapd: Pointer to hostapd context * @params: Configuration for the driver wrapper * Returns: Pointer to private data, %NULL on failure * * This function is used instead of init() or init2() when the driver * wrapper is used with hostapd. */ void * (*hapd_init)(struct hostapd_data *hapd, struct wpa_init_params *params); /** * hapd_deinit - Deinitialize driver interface (hostapd only) * @priv: Private driver interface data from hapd_init() */ void (*hapd_deinit)(void *priv); /** * set_ieee8021x - Enable/disable IEEE 802.1X support (AP only) * @priv: Private driver interface data * @params: BSS parameters * Returns: 0 on success, -1 on failure * * This is an optional function to configure the kernel driver to * enable/disable IEEE 802.1X support and set WPA/WPA2 parameters. This * can be left undefined (set to %NULL) if IEEE 802.1X support is * always enabled and the driver uses set_ap() to set WPA/RSN IE * for Beacon frames. * * DEPRECATED - use set_ap() instead */ int (*set_ieee8021x)(void *priv, struct wpa_bss_params *params); /** * set_privacy - Enable/disable privacy (AP only) * @priv: Private driver interface data * @enabled: 1 = privacy enabled, 0 = disabled * Returns: 0 on success, -1 on failure * * This is an optional function to configure privacy field in the * kernel driver for Beacon frames. This can be left undefined (set to * %NULL) if the driver uses the Beacon template from set_ap(). * * DEPRECATED - use set_ap() instead */ int (*set_privacy)(void *priv, int enabled); /** * get_seqnum - Fetch the current TSC/packet number (AP only) * @ifname: The interface name (main or virtual) * @priv: Private driver interface data * @addr: MAC address of the station or %NULL for group keys * @idx: Key index * @seq: Buffer for returning the latest used TSC/packet number * Returns: 0 on success, -1 on failure * * This function is used to fetch the last used TSC/packet number for * a TKIP, CCMP, GCMP, or BIP/IGTK key. It is mainly used with group * keys, so there is no strict requirement on implementing support for * unicast keys (i.e., addr != %NULL). */ int (*get_seqnum)(const char *ifname, void *priv, const u8 *addr, int idx, u8 *seq); /** * flush - Flush all association stations (AP only) * @priv: Private driver interface data * Returns: 0 on success, -1 on failure * * This function requests the driver to disassociate all associated * stations. This function does not need to be implemented if the * driver does not process association frames internally. */ int (*flush)(void *priv); /** * set_generic_elem - Add IEs into Beacon/Probe Response frames (AP) * @priv: Private driver interface data * @elem: Information elements * @elem_len: Length of the elem buffer in octets * Returns: 0 on success, -1 on failure * * This is an optional function to add information elements in the * kernel driver for Beacon and Probe Response frames. This can be left * undefined (set to %NULL) if the driver uses the Beacon template from * set_ap(). * * DEPRECATED - use set_ap() instead */ int (*set_generic_elem)(void *priv, const u8 *elem, size_t elem_len); /** * read_sta_data - Fetch station data * @priv: Private driver interface data * @data: Buffer for returning station information * @addr: MAC address of the station * Returns: 0 on success, -1 on failure */ int (*read_sta_data)(void *priv, struct hostap_sta_driver_data *data, const u8 *addr); /** * hapd_send_eapol - Send an EAPOL packet (AP only) * @priv: private driver interface data * @addr: Destination MAC address * @data: EAPOL packet starting with IEEE 802.1X header * @data_len: Length of the EAPOL packet in octets * @encrypt: Whether the frame should be encrypted * @own_addr: Source MAC address * @flags: WPA_STA_* flags for the destination station * * Returns: 0 on success, -1 on failure */ int (*hapd_send_eapol)(void *priv, const u8 *addr, const u8 *data, size_t data_len, int encrypt, const u8 *own_addr, u32 flags); /** * sta_deauth - Deauthenticate a station (AP only) * @priv: Private driver interface data * @own_addr: Source address and BSSID for the Deauthentication frame * @addr: MAC address of the station to deauthenticate * @reason: Reason code for the Deauthentiation frame * Returns: 0 on success, -1 on failure * * This function requests a specific station to be deauthenticated and * a Deauthentication frame to be sent to it. */ int (*sta_deauth)(void *priv, const u8 *own_addr, const u8 *addr, int reason); /** * sta_disassoc - Disassociate a station (AP only) * @priv: Private driver interface data * @own_addr: Source address and BSSID for the Disassociation frame * @addr: MAC address of the station to disassociate * @reason: Reason code for the Disassociation frame * Returns: 0 on success, -1 on failure * * This function requests a specific station to be disassociated and * a Disassociation frame to be sent to it. */ int (*sta_disassoc)(void *priv, const u8 *own_addr, const u8 *addr, int reason); /** * sta_remove - Remove a station entry (AP only) * @priv: Private driver interface data * @addr: MAC address of the station to be removed * Returns: 0 on success, -1 on failure */ int (*sta_remove)(void *priv, const u8 *addr); /** * hapd_get_ssid - Get the current SSID (AP only) * @priv: Private driver interface data * @buf: Buffer for returning the SSID * @len: Maximum length of the buffer * Returns: Length of the SSID on success, -1 on failure * * This function need not be implemented if the driver uses Beacon * template from set_ap() and does not reply to Probe Request frames. */ int (*hapd_get_ssid)(void *priv, u8 *buf, int len); /** * hapd_set_ssid - Set SSID (AP only) * @priv: Private driver interface data * @buf: SSID * @len: Length of the SSID in octets * Returns: 0 on success, -1 on failure * * DEPRECATED - use set_ap() instead */ int (*hapd_set_ssid)(void *priv, const u8 *buf, int len); /** * hapd_set_countermeasures - Enable/disable TKIP countermeasures (AP) * @priv: Private driver interface data * @enabled: 1 = countermeasures enabled, 0 = disabled * Returns: 0 on success, -1 on failure * * This need not be implemented if the driver does not take care of * association processing. */ int (*hapd_set_countermeasures)(void *priv, int enabled); /** * sta_add - Add a station entry * @priv: Private driver interface data * @params: Station parameters * Returns: 0 on success, -1 on failure * * This function is used to add a station entry to the driver once the * station has completed association. This is only used if the driver * does not take care of association processing. * * With TDLS, this function is also used to add or set (params->set 1) * TDLS peer entries. */ int (*sta_add)(void *priv, struct hostapd_sta_add_params *params); /** * get_inact_sec - Get station inactivity duration (AP only) * @priv: Private driver interface data * @addr: Station address * Returns: Number of seconds station has been inactive, -1 on failure */ int (*get_inact_sec)(void *priv, const u8 *addr); /** * sta_clear_stats - Clear station statistics (AP only) * @priv: Private driver interface data * @addr: Station address * Returns: 0 on success, -1 on failure */ int (*sta_clear_stats)(void *priv, const u8 *addr); /** * set_freq - Set channel/frequency (AP only) * @priv: Private driver interface data * @freq: Channel parameters * Returns: 0 on success, -1 on failure */ int (*set_freq)(void *priv, struct hostapd_freq_params *freq); /** * set_rts - Set RTS threshold * @priv: Private driver interface data * @rts: RTS threshold in octets * Returns: 0 on success, -1 on failure */ int (*set_rts)(void *priv, int rts); /** * set_frag - Set fragmentation threshold * @priv: Private driver interface data * @frag: Fragmentation threshold in octets * Returns: 0 on success, -1 on failure */ int (*set_frag)(void *priv, int frag); /** * sta_set_flags - Set station flags (AP only) * @priv: Private driver interface data * @addr: Station address * @total_flags: Bitmap of all WPA_STA_* flags currently set * @flags_or: Bitmap of WPA_STA_* flags to add * @flags_and: Bitmap of WPA_STA_* flags to us as a mask * Returns: 0 on success, -1 on failure */ int (*sta_set_flags)(void *priv, const u8 *addr, int total_flags, int flags_or, int flags_and); /** * set_tx_queue_params - Set TX queue parameters * @priv: Private driver interface data * @queue: Queue number (0 = VO, 1 = VI, 2 = BE, 3 = BK) * @aifs: AIFS * @cw_min: cwMin * @cw_max: cwMax * @burst_time: Maximum length for bursting in 0.1 msec units */ int (*set_tx_queue_params)(void *priv, int queue, int aifs, int cw_min, int cw_max, int burst_time); /** * if_add - Add a virtual interface * @priv: Private driver interface data * @type: Interface type * @ifname: Interface name for the new virtual interface * @addr: Local address to use for the interface or %NULL to use the * parent interface address * @bss_ctx: BSS context for %WPA_IF_AP_BSS interfaces * @drv_priv: Pointer for overwriting the driver context or %NULL if * not allowed (applies only to %WPA_IF_AP_BSS type) * @force_ifname: Buffer for returning an interface name that the * driver ended up using if it differs from the requested ifname * @if_addr: Buffer for returning the allocated interface address * (this may differ from the requested addr if the driver cannot * change interface address) * @bridge: Bridge interface to use or %NULL if no bridge configured * @use_existing: Whether to allow existing interface to be used * Returns: 0 on success, -1 on failure */ int (*if_add)(void *priv, enum wpa_driver_if_type type, const char *ifname, const u8 *addr, void *bss_ctx, void **drv_priv, char *force_ifname, u8 *if_addr, const char *bridge, int use_existing); /** * if_remove - Remove a virtual interface * @priv: Private driver interface data * @type: Interface type * @ifname: Interface name of the virtual interface to be removed * Returns: 0 on success, -1 on failure */ int (*if_remove)(void *priv, enum wpa_driver_if_type type, const char *ifname); /** * set_sta_vlan - Bind a station into a specific interface (AP only) * @priv: Private driver interface data * @ifname: Interface (main or virtual BSS or VLAN) * @addr: MAC address of the associated station * @vlan_id: VLAN ID * Returns: 0 on success, -1 on failure * * This function is used to bind a station to a specific virtual * interface. It is only used if when virtual interfaces are supported, * e.g., to assign stations to different VLAN interfaces based on * information from a RADIUS server. This allows separate broadcast * domains to be used with a single BSS. */ int (*set_sta_vlan)(void *priv, const u8 *addr, const char *ifname, int vlan_id); /** * commit - Optional commit changes handler (AP only) * @priv: driver private data * Returns: 0 on success, -1 on failure * * This optional handler function can be registered if the driver * interface implementation needs to commit changes (e.g., by setting * network interface up) at the end of initial configuration. If set, * this handler will be called after initial setup has been completed. */ int (*commit)(void *priv); /** * send_ether - Send an ethernet packet (AP only) * @priv: private driver interface data * @dst: Destination MAC address * @src: Source MAC address * @proto: Ethertype * @data: EAPOL packet starting with IEEE 802.1X header * @data_len: Length of the EAPOL packet in octets * Returns: 0 on success, -1 on failure */ int (*send_ether)(void *priv, const u8 *dst, const u8 *src, u16 proto, const u8 *data, size_t data_len); /** * set_radius_acl_auth - Notification of RADIUS ACL change * @priv: Private driver interface data * @mac: MAC address of the station * @accepted: Whether the station was accepted * @session_timeout: Session timeout for the station * Returns: 0 on success, -1 on failure */ int (*set_radius_acl_auth)(void *priv, const u8 *mac, int accepted, u32 session_timeout); /** * set_radius_acl_expire - Notification of RADIUS ACL expiration * @priv: Private driver interface data * @mac: MAC address of the station * Returns: 0 on success, -1 on failure */ int (*set_radius_acl_expire)(void *priv, const u8 *mac); /** * set_ap_wps_ie - Add WPS IE(s) into Beacon/Probe Response frames (AP) * @priv: Private driver interface data * @beacon: WPS IE(s) for Beacon frames or %NULL to remove extra IE(s) * @proberesp: WPS IE(s) for Probe Response frames or %NULL to remove * extra IE(s) * @assocresp: WPS IE(s) for (Re)Association Response frames or %NULL * to remove extra IE(s) * Returns: 0 on success, -1 on failure * * This is an optional function to add WPS IE in the kernel driver for * Beacon and Probe Response frames. This can be left undefined (set * to %NULL) if the driver uses the Beacon template from set_ap() * and does not process Probe Request frames. If the driver takes care * of (Re)Association frame processing, the assocresp buffer includes * WPS IE(s) that need to be added to (Re)Association Response frames * whenever a (Re)Association Request frame indicated use of WPS. * * This will also be used to add P2P IE(s) into Beacon/Probe Response * frames when operating as a GO. The driver is responsible for adding * timing related attributes (e.g., NoA) in addition to the IEs * included here by appending them after these buffers. This call is * also used to provide Probe Response IEs for P2P Listen state * operations for drivers that generate the Probe Response frames * internally. * * DEPRECATED - use set_ap() instead */ int (*set_ap_wps_ie)(void *priv, const struct wpabuf *beacon, const struct wpabuf *proberesp, const struct wpabuf *assocresp); /** * set_supp_port - Set IEEE 802.1X Supplicant Port status * @priv: Private driver interface data * @authorized: Whether the port is authorized * Returns: 0 on success, -1 on failure */ int (*set_supp_port)(void *priv, int authorized); /** * set_wds_sta - Bind a station into a 4-address WDS (AP only) * @priv: Private driver interface data * @addr: MAC address of the associated station * @aid: Association ID * @val: 1 = bind to 4-address WDS; 0 = unbind * @bridge_ifname: Bridge interface to use for the WDS station or %NULL * to indicate that bridge is not to be used * @ifname_wds: Buffer to return the interface name for the new WDS * station or %NULL to indicate name is not returned. * Returns: 0 on success, -1 on failure */ int (*set_wds_sta)(void *priv, const u8 *addr, int aid, int val, const char *bridge_ifname, char *ifname_wds); /** * send_action - Transmit an Action frame * @priv: Private driver interface data * @freq: Frequency (in MHz) of the channel * @wait: Time to wait off-channel for a response (in ms), or zero * @dst: Destination MAC address (Address 1) * @src: Source MAC address (Address 2) * @bssid: BSSID (Address 3) * @data: Frame body * @data_len: data length in octets @ @no_cck: Whether CCK rates must not be used to transmit this frame * Returns: 0 on success, -1 on failure * * This command can be used to request the driver to transmit an action * frame to the specified destination. * * If the %WPA_DRIVER_FLAGS_OFFCHANNEL_TX flag is set, the frame will * be transmitted on the given channel and the device will wait for a * response on that channel for the given wait time. * * If the flag is not set, the wait time will be ignored. In this case, * if a remain-on-channel duration is in progress, the frame must be * transmitted on that channel; alternatively the frame may be sent on * the current operational channel (if in associated state in station * mode or while operating as an AP.) */ int (*send_action)(void *priv, unsigned int freq, unsigned int wait, const u8 *dst, const u8 *src, const u8 *bssid, const u8 *data, size_t data_len, int no_cck); /** * send_action_cancel_wait - Cancel action frame TX wait * @priv: Private driver interface data * * This command cancels the wait time associated with sending an action * frame. It is only available when %WPA_DRIVER_FLAGS_OFFCHANNEL_TX is * set in the driver flags. */ void (*send_action_cancel_wait)(void *priv); /** * remain_on_channel - Remain awake on a channel * @priv: Private driver interface data * @freq: Frequency (in MHz) of the channel * @duration: Duration in milliseconds * Returns: 0 on success, -1 on failure * * This command is used to request the driver to remain awake on the * specified channel for the specified duration and report received * Action frames with EVENT_RX_MGMT events. Optionally, received * Probe Request frames may also be requested to be reported by calling * probe_req_report(). These will be reported with EVENT_RX_PROBE_REQ. * * The driver may not be at the requested channel when this function * returns, i.e., the return code is only indicating whether the * request was accepted. The caller will need to wait until the * EVENT_REMAIN_ON_CHANNEL event indicates that the driver has * completed the channel change. This may take some time due to other * need for the radio and the caller should be prepared to timing out * its wait since there are no guarantees on when this request can be * executed. */ int (*remain_on_channel)(void *priv, unsigned int freq, unsigned int duration); /** * cancel_remain_on_channel - Cancel remain-on-channel operation * @priv: Private driver interface data * * This command can be used to cancel a remain-on-channel operation * before its originally requested duration has passed. This could be * used, e.g., when remain_on_channel() is used to request extra time * to receive a response to an Action frame and the response is * received when there is still unneeded time remaining on the * remain-on-channel operation. */ int (*cancel_remain_on_channel)(void *priv); /** * probe_req_report - Request Probe Request frames to be indicated * @priv: Private driver interface data * @report: Whether to report received Probe Request frames * Returns: 0 on success, -1 on failure (or if not supported) * * This command can be used to request the driver to indicate when * Probe Request frames are received with EVENT_RX_PROBE_REQ events. * Since this operation may require extra resources, e.g., due to less * optimal hardware/firmware RX filtering, many drivers may disable * Probe Request reporting at least in station mode. This command is * used to notify the driver when the Probe Request frames need to be * reported, e.g., during remain-on-channel operations. */ int (*probe_req_report)(void *priv, int report); /** * deinit_ap - Deinitialize AP mode * @priv: Private driver interface data * Returns: 0 on success, -1 on failure (or if not supported) * * This optional function can be used to disable AP mode related * configuration. If the interface was not dynamically added, * change the driver mode to station mode to allow normal station * operations like scanning to be completed. */ int (*deinit_ap)(void *priv); /** * deinit_p2p_cli - Deinitialize P2P client mode * @priv: Private driver interface data * Returns: 0 on success, -1 on failure (or if not supported) * * This optional function can be used to disable P2P client mode. If the * interface was not dynamically added, change the interface type back * to station mode. */ int (*deinit_p2p_cli)(void *priv); /** * suspend - Notification on system suspend/hibernate event * @priv: Private driver interface data */ void (*suspend)(void *priv); /** * resume - Notification on system resume/thaw event * @priv: Private driver interface data */ void (*resume)(void *priv); /** * signal_monitor - Set signal monitoring parameters * @priv: Private driver interface data * @threshold: Threshold value for signal change events; 0 = disabled * @hysteresis: Minimum change in signal strength before indicating a * new event * Returns: 0 on success, -1 on failure (or if not supported) * * This function can be used to configure monitoring of signal strength * with the current AP. Whenever signal strength drops below the * %threshold value or increases above it, EVENT_SIGNAL_CHANGE event * should be generated assuming the signal strength has changed at * least %hysteresis from the previously indicated signal change event. */ int (*signal_monitor)(void *priv, int threshold, int hysteresis); /** * send_frame - Send IEEE 802.11 frame (testing use only) * @priv: Private driver interface data * @data: IEEE 802.11 frame with IEEE 802.11 header * @data_len: Size of the frame * @encrypt: Whether to encrypt the frame (if keys are set) * Returns: 0 on success, -1 on failure * * This function is only used for debugging purposes and is not * required to be implemented for normal operations. */ int (*send_frame)(void *priv, const u8 *data, size_t data_len, int encrypt); /** * shared_freq - Get operating frequency of shared interface(s) * @priv: Private driver interface data * Returns: Operating frequency in MHz, 0 if no shared operation in * use, or -1 on failure * * This command can be used to request the current operating frequency * of any virtual interface that shares the same radio to provide * information for channel selection for other virtual interfaces. */ int (*shared_freq)(void *priv); /** * get_noa - Get current Notice of Absence attribute payload * @priv: Private driver interface data * @buf: Buffer for returning NoA * @buf_len: Buffer length in octets * Returns: Number of octets used in buf, 0 to indicate no NoA is being * advertized, or -1 on failure * * This function is used to fetch the current Notice of Absence * attribute value from GO. */ int (*get_noa)(void *priv, u8 *buf, size_t buf_len); /** * set_noa - Set Notice of Absence parameters for GO (testing) * @priv: Private driver interface data * @count: Count * @start: Start time in ms from next TBTT * @duration: Duration in ms * Returns: 0 on success or -1 on failure * * This function is used to set Notice of Absence parameters for GO. It * is used only for testing. To disable NoA, all parameters are set to * 0. */ int (*set_noa)(void *priv, u8 count, int start, int duration); /** * set_p2p_powersave - Set P2P power save options * @priv: Private driver interface data * @legacy_ps: 0 = disable, 1 = enable, 2 = maximum PS, -1 = no change * @opp_ps: 0 = disable, 1 = enable, -1 = no change * @ctwindow: 0.. = change (msec), -1 = no change * Returns: 0 on success or -1 on failure */ int (*set_p2p_powersave)(void *priv, int legacy_ps, int opp_ps, int ctwindow); /** * ampdu - Enable/disable aggregation * @priv: Private driver interface data * @ampdu: 1/0 = enable/disable A-MPDU aggregation * Returns: 0 on success or -1 on failure */ int (*ampdu)(void *priv, int ampdu); /** * get_radio_name - Get physical radio name for the device * @priv: Private driver interface data * Returns: Radio name or %NULL if not known * * The returned data must not be modified by the caller. It is assumed * that any interface that has the same radio name as another is * sharing the same physical radio. This information can be used to * share scan results etc. information between the virtual interfaces * to speed up various operations. */ const char * (*get_radio_name)(void *priv); /** * send_tdls_mgmt - for sending TDLS management packets * @priv: private driver interface data * @dst: Destination (peer) MAC address * @action_code: TDLS action code for the mssage * @dialog_token: Dialog Token to use in the message (if needed) * @status_code: Status Code or Reason Code to use (if needed) * @buf: TDLS IEs to add to the message * @len: Length of buf in octets * Returns: 0 on success, negative (<0) on failure * * This optional function can be used to send packet to driver which is * responsible for receiving and sending all TDLS packets. */ int (*send_tdls_mgmt)(void *priv, const u8 *dst, u8 action_code, u8 dialog_token, u16 status_code, const u8 *buf, size_t len); /** * tdls_oper - Ask the driver to perform high-level TDLS operations * @priv: Private driver interface data * @oper: TDLS high-level operation. See %enum tdls_oper * @peer: Destination (peer) MAC address * Returns: 0 on success, negative (<0) on failure * * This optional function can be used to send high-level TDLS commands * to the driver. */ int (*tdls_oper)(void *priv, enum tdls_oper oper, const u8 *peer); /** * wnm_oper - Notify driver of the WNM frame reception * @priv: Private driver interface data * @oper: WNM operation. See %enum wnm_oper * @peer: Destination (peer) MAC address * @buf: Buffer for the driver to fill in (for getting IE) * @buf_len: Return the len of buf * Returns: 0 on success, negative (<0) on failure */ int (*wnm_oper)(void *priv, enum wnm_oper oper, const u8 *peer, u8 *buf, u16 *buf_len); /** * set_qos_map - Set QoS Map * @priv: Private driver interface data * @qos_map_set: QoS Map * @qos_map_set_len: Length of QoS Map */ int (*set_qos_map)(void *priv, const u8 *qos_map_set, u8 qos_map_set_len); /** * signal_poll - Get current connection information * @priv: Private driver interface data * @signal_info: Connection info structure */ int (*signal_poll)(void *priv, struct wpa_signal_info *signal_info); /** * set_authmode - Set authentication algorithm(s) for static WEP * @priv: Private driver interface data * @authmode: 1=Open System, 2=Shared Key, 3=both * Returns: 0 on success, -1 on failure * * This function can be used to set authentication algorithms for AP * mode when static WEP is used. If the driver uses user space MLME/SME * implementation, there is no need to implement this function. * * DEPRECATED - use set_ap() instead */ int (*set_authmode)(void *priv, int authmode); #ifdef ANDROID /** * driver_cmd - Execute driver-specific command * @priv: Private driver interface data * @cmd: Command to execute * @buf: Return buffer * @buf_len: Buffer length * Returns: 0 on success, -1 on failure */ int (*driver_cmd)(void *priv, char *cmd, char *buf, size_t buf_len); #endif /* ANDROID */ /** * set_rekey_info - Set rekey information * @priv: Private driver interface data * @kek: Current KEK * @kck: Current KCK * @replay_ctr: Current EAPOL-Key Replay Counter * * This optional function can be used to provide information for the * driver/firmware to process EAPOL-Key frames in Group Key Handshake * while the host (including wpa_supplicant) is sleeping. */ void (*set_rekey_info)(void *priv, const u8 *kek, const u8 *kck, const u8 *replay_ctr); /** * sta_assoc - Station association indication * @priv: Private driver interface data * @own_addr: Source address and BSSID for association frame * @addr: MAC address of the station to associate * @reassoc: flag to indicate re-association * @status: association response status code * @ie: assoc response ie buffer * @len: ie buffer length * Returns: 0 on success, -1 on failure * * This function indicates the driver to send (Re)Association * Response frame to the station. */ int (*sta_assoc)(void *priv, const u8 *own_addr, const u8 *addr, int reassoc, u16 status, const u8 *ie, size_t len); /** * sta_auth - Station authentication indication * @priv: Private driver interface data * @own_addr: Source address and BSSID for authentication frame * @addr: MAC address of the station to associate * @seq: authentication sequence number * @status: authentication response status code * @ie: authentication frame ie buffer * @len: ie buffer length * * This function indicates the driver to send Authentication frame * to the station. */ int (*sta_auth)(void *priv, const u8 *own_addr, const u8 *addr, u16 seq, u16 status, const u8 *ie, size_t len); /** * add_tspec - Add traffic stream * @priv: Private driver interface data * @addr: MAC address of the station to associate * @tspec_ie: tspec ie buffer * @tspec_ielen: tspec ie length * Returns: 0 on success, -1 on failure * * This function adds the traffic steam for the station * and fills the medium_time in tspec_ie. */ int (*add_tspec)(void *priv, const u8 *addr, u8 *tspec_ie, size_t tspec_ielen); /** * add_sta_node - Add a station node in the driver * @priv: Private driver interface data * @addr: MAC address of the station to add * @auth_alg: authentication algorithm used by the station * Returns: 0 on success, -1 on failure * * This function adds the station node in the driver, when * the station gets added by FT-over-DS. */ int (*add_sta_node)(void *priv, const u8 *addr, u16 auth_alg); /** * sched_scan - Request the driver to initiate scheduled scan * @priv: Private driver interface data * @params: Scan parameters * @interval: Interval between scan cycles in milliseconds * Returns: 0 on success, -1 on failure * * This operation should be used for scheduled scan offload to * the hardware. Every time scan results are available, the * driver should report scan results event for wpa_supplicant * which will eventually request the results with * wpa_driver_get_scan_results2(). This operation is optional * and if not provided or if it returns -1, we fall back to * normal host-scheduled scans. */ int (*sched_scan)(void *priv, struct wpa_driver_scan_params *params, u32 interval); /** * stop_sched_scan - Request the driver to stop a scheduled scan * @priv: Private driver interface data * Returns: 0 on success, -1 on failure * * This should cause the scheduled scan to be stopped and * results should stop being sent. Must be supported if * sched_scan is supported. */ int (*stop_sched_scan)(void *priv); /** * poll_client - Probe (null data or such) the given station * @priv: Private driver interface data * @own_addr: MAC address of sending interface * @addr: MAC address of the station to probe * @qos: Indicates whether station is QoS station * * This function is used to verify whether an associated station is * still present. This function does not need to be implemented if the * driver provides such inactivity polling mechanism. */ void (*poll_client)(void *priv, const u8 *own_addr, const u8 *addr, int qos); /** * radio_disable - Disable/enable radio * @priv: Private driver interface data * @disabled: 1=disable 0=enable radio * Returns: 0 on success, -1 on failure * * This optional command is for testing purposes. It can be used to * disable the radio on a testbed device to simulate out-of-radio-range * conditions. */ int (*radio_disable)(void *priv, int disabled); /** * switch_channel - Announce channel switch and migrate the GO to the * given frequency * @priv: Private driver interface data * @settings: Settings for CSA period and new channel * Returns: 0 on success, -1 on failure * * This function is used to move the GO to the legacy STA channel to * avoid frequency conflict in single channel concurrency. */ int (*switch_channel)(void *priv, struct csa_settings *settings); /** * start_dfs_cac - Listen for radar interference on the channel * @priv: Private driver interface data * @freq: Channel parameters * Returns: 0 on success, -1 on failure */ int (*start_dfs_cac)(void *priv, struct hostapd_freq_params *freq); /** * stop_ap - Removes beacon from AP * @priv: Private driver interface data * Returns: 0 on success, -1 on failure (or if not supported) * * This optional function can be used to disable AP mode related * configuration. Unlike deinit_ap, it does not change to station * mode. */ int (*stop_ap)(void *priv); /** * get_survey - Retrieve survey data * @priv: Private driver interface data * @freq: If set, survey data for the specified frequency is only * being requested. If not set, all survey data is requested. * Returns: 0 on success, -1 on failure * * Use this to retrieve: * * - the observed channel noise floor * - the amount of time we have spent on the channel * - the amount of time during which we have spent on the channel that * the radio has determined the medium is busy and we cannot * transmit * - the amount of time we have spent receiving data * - the amount of time we have spent transmitting data * * This data can be used for spectrum heuristics. One example is * Automatic Channel Selection (ACS). The channel survey data is * kept on a linked list on the channel data, one entry is added * for each survey. The min_nf of the channel is updated for each * survey. */ int (*get_survey)(void *priv, unsigned int freq); /** * status - Get driver interface status information * @priv: Private driver interface data * @buf: Buffer for printing tou the status information * @buflen: Maximum length of the buffer * Returns: Length of written status information or -1 on failure */ int (*status)(void *priv, char *buf, size_t buflen); }; /** * enum wpa_event_type - Event type for wpa_supplicant_event() calls */ enum wpa_event_type { /** * EVENT_ASSOC - Association completed * * This event needs to be delivered when the driver completes IEEE * 802.11 association or reassociation successfully. * wpa_driver_ops::get_bssid() is expected to provide the current BSSID * after this event has been generated. In addition, optional * EVENT_ASSOCINFO may be generated just before EVENT_ASSOC to provide * more information about the association. If the driver interface gets * both of these events at the same time, it can also include the * assoc_info data in EVENT_ASSOC call. */ EVENT_ASSOC, /** * EVENT_DISASSOC - Association lost * * This event should be called when association is lost either due to * receiving deauthenticate or disassociate frame from the AP or when * sending either of these frames to the current AP. If the driver * supports separate deauthentication event, EVENT_DISASSOC should only * be used for disassociation and EVENT_DEAUTH for deauthentication. * In AP mode, union wpa_event_data::disassoc_info is required. */ EVENT_DISASSOC, /** * EVENT_MICHAEL_MIC_FAILURE - Michael MIC (TKIP) detected * * This event must be delivered when a Michael MIC error is detected by * the local driver. Additional data for event processing is * provided with union wpa_event_data::michael_mic_failure. This * information is used to request new encyption key and to initiate * TKIP countermeasures if needed. */ EVENT_MICHAEL_MIC_FAILURE, /** * EVENT_SCAN_RESULTS - Scan results available * * This event must be called whenever scan results are available to be * fetched with struct wpa_driver_ops::get_scan_results(). This event * is expected to be used some time after struct wpa_driver_ops::scan() * is called. If the driver provides an unsolicited event when the scan * has been completed, this event can be used to trigger * EVENT_SCAN_RESULTS call. If such event is not available from the * driver, the driver wrapper code is expected to use a registered * timeout to generate EVENT_SCAN_RESULTS call after the time that the * scan is expected to be completed. Optional information about * completed scan can be provided with union wpa_event_data::scan_info. */ EVENT_SCAN_RESULTS, /** * EVENT_ASSOCINFO - Report optional extra information for association * * This event can be used to report extra association information for * EVENT_ASSOC processing. This extra information includes IEs from * association frames and Beacon/Probe Response frames in union * wpa_event_data::assoc_info. EVENT_ASSOCINFO must be send just before * EVENT_ASSOC. Alternatively, the driver interface can include * assoc_info data in the EVENT_ASSOC call if it has all the * information available at the same point. */ EVENT_ASSOCINFO, /** * EVENT_INTERFACE_STATUS - Report interface status changes * * This optional event can be used to report changes in interface * status (interface added/removed) using union * wpa_event_data::interface_status. This can be used to trigger * wpa_supplicant to stop and re-start processing for the interface, * e.g., when a cardbus card is ejected/inserted. */ EVENT_INTERFACE_STATUS, /** * EVENT_PMKID_CANDIDATE - Report a candidate AP for pre-authentication * * This event can be used to inform wpa_supplicant about candidates for * RSN (WPA2) pre-authentication. If wpa_supplicant is not responsible * for scan request (ap_scan=2 mode), this event is required for * pre-authentication. If wpa_supplicant is performing scan request * (ap_scan=1), this event is optional since scan results can be used * to add pre-authentication candidates. union * wpa_event_data::pmkid_candidate is used to report the BSSID of the * candidate and priority of the candidate, e.g., based on the signal * strength, in order to try to pre-authenticate first with candidates * that are most likely targets for re-association. * * EVENT_PMKID_CANDIDATE can be called whenever the driver has updates * on the candidate list. In addition, it can be called for the current * AP and APs that have existing PMKSA cache entries. wpa_supplicant * will automatically skip pre-authentication in cases where a valid * PMKSA exists. When more than one candidate exists, this event should * be generated once for each candidate. * * Driver will be notified about successful pre-authentication with * struct wpa_driver_ops::add_pmkid() calls. */ EVENT_PMKID_CANDIDATE, /** * EVENT_STKSTART - Request STK handshake (MLME-STKSTART.request) * * This event can be used to inform wpa_supplicant about desire to set * up secure direct link connection between two stations as defined in * IEEE 802.11e with a new PeerKey mechanism that replaced the original * STAKey negotiation. The caller will need to set peer address for the * event. */ EVENT_STKSTART, /** * EVENT_TDLS - Request TDLS operation * * This event can be used to request a TDLS operation to be performed. */ EVENT_TDLS, /** * EVENT_FT_RESPONSE - Report FT (IEEE 802.11r) response IEs * * The driver is expected to report the received FT IEs from * FT authentication sequence from the AP. The FT IEs are included in * the extra information in union wpa_event_data::ft_ies. */ EVENT_FT_RESPONSE, /** * EVENT_IBSS_RSN_START - Request RSN authentication in IBSS * * The driver can use this event to inform wpa_supplicant about a STA * in an IBSS with which protected frames could be exchanged. This * event starts RSN authentication with the other STA to authenticate * the STA and set up encryption keys with it. */ EVENT_IBSS_RSN_START, /** * EVENT_AUTH - Authentication result * * This event should be called when authentication attempt has been * completed. This is only used if the driver supports separate * authentication step (struct wpa_driver_ops::authenticate). * Information about authentication result is included in * union wpa_event_data::auth. */ EVENT_AUTH, /** * EVENT_DEAUTH - Authentication lost * * This event should be called when authentication is lost either due * to receiving deauthenticate frame from the AP or when sending that * frame to the current AP. * In AP mode, union wpa_event_data::deauth_info is required. */ EVENT_DEAUTH, /** * EVENT_ASSOC_REJECT - Association rejected * * This event should be called when (re)association attempt has been * rejected by the AP. Information about the association response is * included in union wpa_event_data::assoc_reject. */ EVENT_ASSOC_REJECT, /** * EVENT_AUTH_TIMED_OUT - Authentication timed out */ EVENT_AUTH_TIMED_OUT, /** * EVENT_ASSOC_TIMED_OUT - Association timed out */ EVENT_ASSOC_TIMED_OUT, /** * EVENT_FT_RRB_RX - FT (IEEE 802.11r) RRB frame received */ EVENT_FT_RRB_RX, /** * EVENT_WPS_BUTTON_PUSHED - Report hardware push button press for WPS */ EVENT_WPS_BUTTON_PUSHED, /** * EVENT_TX_STATUS - Report TX status */ EVENT_TX_STATUS, /** * EVENT_RX_FROM_UNKNOWN - Report RX from unknown STA */ EVENT_RX_FROM_UNKNOWN, /** * EVENT_RX_MGMT - Report RX of a management frame */ EVENT_RX_MGMT, /** * EVENT_REMAIN_ON_CHANNEL - Remain-on-channel duration started * * This event is used to indicate when the driver has started the * requested remain-on-channel duration. Information about the * operation is included in union wpa_event_data::remain_on_channel. */ EVENT_REMAIN_ON_CHANNEL, /** * EVENT_CANCEL_REMAIN_ON_CHANNEL - Remain-on-channel timed out * * This event is used to indicate when the driver has completed * remain-on-channel duration, i.e., may noot be available on the * requested channel anymore. Information about the * operation is included in union wpa_event_data::remain_on_channel. */ EVENT_CANCEL_REMAIN_ON_CHANNEL, /** * EVENT_MLME_RX - Report reception of frame for MLME (test use only) * * This event is used only by driver_test.c and userspace MLME. */ EVENT_MLME_RX, /** * EVENT_RX_PROBE_REQ - Indicate received Probe Request frame * * This event is used to indicate when a Probe Request frame has been * received. Information about the received frame is included in * union wpa_event_data::rx_probe_req. The driver is required to report * these events only after successfully completed probe_req_report() * commands to request the events (i.e., report parameter is non-zero) * in station mode. In AP mode, Probe Request frames should always be * reported. */ EVENT_RX_PROBE_REQ, /** * EVENT_NEW_STA - New wired device noticed * * This event is used to indicate that a new device has been detected * in a network that does not use association-like functionality (i.e., * mainly wired Ethernet). This can be used to start EAPOL * authenticator when receiving a frame from a device. The address of * the device is included in union wpa_event_data::new_sta. */ EVENT_NEW_STA, /** * EVENT_EAPOL_RX - Report received EAPOL frame * * When in AP mode with hostapd, this event is required to be used to * deliver the receive EAPOL frames from the driver. With * %wpa_supplicant, this event is used only if the send_eapol() handler * is used to override the use of l2_packet for EAPOL frame TX. */ EVENT_EAPOL_RX, /** * EVENT_SIGNAL_CHANGE - Indicate change in signal strength * * This event is used to indicate changes in the signal strength * observed in frames received from the current AP if signal strength * monitoring has been enabled with signal_monitor(). */ EVENT_SIGNAL_CHANGE, /** * EVENT_INTERFACE_ENABLED - Notify that interface was enabled * * This event is used to indicate that the interface was enabled after * having been previously disabled, e.g., due to rfkill. */ EVENT_INTERFACE_ENABLED, /** * EVENT_INTERFACE_DISABLED - Notify that interface was disabled * * This event is used to indicate that the interface was disabled, * e.g., due to rfkill. */ EVENT_INTERFACE_DISABLED, /** * EVENT_CHANNEL_LIST_CHANGED - Channel list changed * * This event is used to indicate that the channel list has changed, * e.g., because of a regulatory domain change triggered by scan * results including an AP advertising a country code. */ EVENT_CHANNEL_LIST_CHANGED, /** * EVENT_INTERFACE_UNAVAILABLE - Notify that interface is unavailable * * This event is used to indicate that the driver cannot maintain this * interface in its operation mode anymore. The most likely use for * this is to indicate that AP mode operation is not available due to * operating channel would need to be changed to a DFS channel when * the driver does not support radar detection and another virtual * interfaces caused the operating channel to change. Other similar * resource conflicts could also trigger this for station mode * interfaces. */ EVENT_INTERFACE_UNAVAILABLE, /** * EVENT_BEST_CHANNEL * * Driver generates this event whenever it detects a better channel * (e.g., based on RSSI or channel use). This information can be used * to improve channel selection for a new AP/P2P group. */ EVENT_BEST_CHANNEL, /** * EVENT_UNPROT_DEAUTH - Unprotected Deauthentication frame received * * This event should be called when a Deauthentication frame is dropped * due to it not being protected (MFP/IEEE 802.11w). * union wpa_event_data::unprot_deauth is required to provide more * details of the frame. */ EVENT_UNPROT_DEAUTH, /** * EVENT_UNPROT_DISASSOC - Unprotected Disassociation frame received * * This event should be called when a Disassociation frame is dropped * due to it not being protected (MFP/IEEE 802.11w). * union wpa_event_data::unprot_disassoc is required to provide more * details of the frame. */ EVENT_UNPROT_DISASSOC, /** * EVENT_STATION_LOW_ACK * * Driver generates this event whenever it detected that a particular * station was lost. Detection can be through massive transmission * failures for example. */ EVENT_STATION_LOW_ACK, /** * EVENT_IBSS_PEER_LOST - IBSS peer not reachable anymore */ EVENT_IBSS_PEER_LOST, /** * EVENT_DRIVER_GTK_REKEY - Device/driver did GTK rekey * * This event carries the new replay counter to notify wpa_supplicant * of the current EAPOL-Key Replay Counter in case the driver/firmware * completed Group Key Handshake while the host (including * wpa_supplicant was sleeping). */ EVENT_DRIVER_GTK_REKEY, /** * EVENT_SCHED_SCAN_STOPPED - Scheduled scan was stopped */ EVENT_SCHED_SCAN_STOPPED, /** * EVENT_DRIVER_CLIENT_POLL_OK - Station responded to poll * * This event indicates that the station responded to the poll * initiated with @poll_client. */ EVENT_DRIVER_CLIENT_POLL_OK, /** * EVENT_EAPOL_TX_STATUS - notify of EAPOL TX status */ EVENT_EAPOL_TX_STATUS, /** * EVENT_CH_SWITCH - AP or GO decided to switch channels * * Described in wpa_event_data.ch_switch * */ EVENT_CH_SWITCH, /** * EVENT_WNM - Request WNM operation * * This event can be used to request a WNM operation to be performed. */ EVENT_WNM, /** * EVENT_CONNECT_FAILED_REASON - Connection failure reason in AP mode * * This event indicates that the driver reported a connection failure * with the specified client (for example, max client reached, etc.) in * AP mode. */ EVENT_CONNECT_FAILED_REASON, /** * EVENT_RADAR_DETECTED - Notify of radar detection * * A radar has been detected on the supplied frequency, hostapd should * react accordingly (e.g., change channel). */ EVENT_DFS_RADAR_DETECTED, /** * EVENT_CAC_FINISHED - Notify that channel availability check has been completed * * After a successful CAC, the channel can be marked clear and used. */ EVENT_DFS_CAC_FINISHED, /** * EVENT_CAC_ABORTED - Notify that channel availability check has been aborted * * The CAC was not successful, and the channel remains in the previous * state. This may happen due to a radar beeing detected or other * external influences. */ EVENT_DFS_CAC_ABORTED, /** * EVENT_DFS_CAC_NOP_FINISHED - Notify that non-occupancy period is over * * The channel which was previously unavailable is now available again. */ EVENT_DFS_NOP_FINISHED, /** * EVENT_SURVEY - Received survey data * * This event gets triggered when a driver query is issued for survey * data and the requested data becomes available. The returned data is * stored in struct survey_results. The results provide at most one * survey entry for each frequency and at minimum will provide one * survey entry for one frequency. The survey data can be os_malloc()'d * and then os_free()'d, so the event callback must only copy data. */ EVENT_SURVEY, /** * EVENT_SCAN_STARTED - Scan started * * This indicates that driver has started a scan operation either based * on a request from wpa_supplicant/hostapd or from another application. * EVENT_SCAN_RESULTS is used to indicate when the scan has been * completed (either successfully or by getting cancelled). */ EVENT_SCAN_STARTED, /** * EVENT_AVOID_FREQUENCIES - Received avoid frequency range * * This event indicates a set of frequency ranges that should be avoided * to reduce issues due to interference or internal co-existence * information in the driver. */ EVENT_AVOID_FREQUENCIES }; /** * struct freq_survey - Channel survey info * * @ifidx: Interface index in which this survey was observed * @freq: Center of frequency of the surveyed channel * @nf: Channel noise floor in dBm * @channel_time: Amount of time in ms the radio spent on the channel * @channel_time_busy: Amount of time in ms the radio detected some signal * that indicated to the radio the channel was not clear * @channel_time_rx: Amount of time the radio spent receiving data * @channel_time_tx: Amount of time the radio spent transmitting data * @filled: bitmask indicating which fields have been reported, see * SURVEY_HAS_* defines. * @list: Internal list pointers */ struct freq_survey { u32 ifidx; unsigned int freq; s8 nf; u64 channel_time; u64 channel_time_busy; u64 channel_time_rx; u64 channel_time_tx; unsigned int filled; struct dl_list list; }; #define SURVEY_HAS_NF BIT(0) #define SURVEY_HAS_CHAN_TIME BIT(1) #define SURVEY_HAS_CHAN_TIME_BUSY BIT(2) #define SURVEY_HAS_CHAN_TIME_RX BIT(3) #define SURVEY_HAS_CHAN_TIME_TX BIT(4) /** * union wpa_event_data - Additional data for wpa_supplicant_event() calls */ union wpa_event_data { /** * struct assoc_info - Data for EVENT_ASSOC and EVENT_ASSOCINFO events * * This structure is optional for EVENT_ASSOC calls and required for * EVENT_ASSOCINFO calls. By using EVENT_ASSOC with this data, the * driver interface does not need to generate separate EVENT_ASSOCINFO * calls. */ struct assoc_info { /** * reassoc - Flag to indicate association or reassociation */ int reassoc; /** * req_ies - (Re)Association Request IEs * * If the driver generates WPA/RSN IE, this event data must be * returned for WPA handshake to have needed information. If * wpa_supplicant-generated WPA/RSN IE is used, this * information event is optional. * * This should start with the first IE (fixed fields before IEs * are not included). */ const u8 *req_ies; /** * req_ies_len - Length of req_ies in bytes */ size_t req_ies_len; /** * resp_ies - (Re)Association Response IEs * * Optional association data from the driver. This data is not * required WPA, but may be useful for some protocols and as * such, should be reported if this is available to the driver * interface. * * This should start with the first IE (fixed fields before IEs * are not included). */ const u8 *resp_ies; /** * resp_ies_len - Length of resp_ies in bytes */ size_t resp_ies_len; /** * beacon_ies - Beacon or Probe Response IEs * * Optional Beacon/ProbeResp data: IEs included in Beacon or * Probe Response frames from the current AP (i.e., the one * that the client just associated with). This information is * used to update WPA/RSN IE for the AP. If this field is not * set, the results from previous scan will be used. If no * data for the new AP is found, scan results will be requested * again (without scan request). At this point, the driver is * expected to provide WPA/RSN IE for the AP (if WPA/WPA2 is * used). * * This should start with the first IE (fixed fields before IEs * are not included). */ const u8 *beacon_ies; /** * beacon_ies_len - Length of beacon_ies */ size_t beacon_ies_len; /** * freq - Frequency of the operational channel in MHz */ unsigned int freq; /** * addr - Station address (for AP mode) */ const u8 *addr; } assoc_info; /** * struct disassoc_info - Data for EVENT_DISASSOC events */ struct disassoc_info { /** * addr - Station address (for AP mode) */ const u8 *addr; /** * reason_code - Reason Code (host byte order) used in * Deauthentication frame */ u16 reason_code; /** * ie - Optional IE(s) in Disassociation frame */ const u8 *ie; /** * ie_len - Length of ie buffer in octets */ size_t ie_len; /** * locally_generated - Whether the frame was locally generated */ int locally_generated; } disassoc_info; /** * struct deauth_info - Data for EVENT_DEAUTH events */ struct deauth_info { /** * addr - Station address (for AP mode) */ const u8 *addr; /** * reason_code - Reason Code (host byte order) used in * Deauthentication frame */ u16 reason_code; /** * ie - Optional IE(s) in Deauthentication frame */ const u8 *ie; /** * ie_len - Length of ie buffer in octets */ size_t ie_len; /** * locally_generated - Whether the frame was locally generated */ int locally_generated; } deauth_info; /** * struct michael_mic_failure - Data for EVENT_MICHAEL_MIC_FAILURE */ struct michael_mic_failure { int unicast; const u8 *src; } michael_mic_failure; /** * struct interface_status - Data for EVENT_INTERFACE_STATUS */ struct interface_status { char ifname[100]; enum { EVENT_INTERFACE_ADDED, EVENT_INTERFACE_REMOVED } ievent; } interface_status; /** * struct pmkid_candidate - Data for EVENT_PMKID_CANDIDATE */ struct pmkid_candidate { /** BSSID of the PMKID candidate */ u8 bssid[ETH_ALEN]; /** Smaller the index, higher the priority */ int index; /** Whether RSN IE includes pre-authenticate flag */ int preauth; } pmkid_candidate; /** * struct stkstart - Data for EVENT_STKSTART */ struct stkstart { u8 peer[ETH_ALEN]; } stkstart; /** * struct tdls - Data for EVENT_TDLS */ struct tdls { u8 peer[ETH_ALEN]; enum { TDLS_REQUEST_SETUP, TDLS_REQUEST_TEARDOWN } oper; u16 reason_code; /* for teardown */ } tdls; /** * struct wnm - Data for EVENT_WNM */ struct wnm { u8 addr[ETH_ALEN]; enum { WNM_OPER_SLEEP, } oper; enum { WNM_SLEEP_ENTER, WNM_SLEEP_EXIT } sleep_action; int sleep_intval; u16 reason_code; u8 *buf; u16 buf_len; } wnm; /** * struct ft_ies - FT information elements (EVENT_FT_RESPONSE) * * During FT (IEEE 802.11r) authentication sequence, the driver is * expected to use this event to report received FT IEs (MDIE, FTIE, * RSN IE, TIE, possible resource request) to the supplicant. The FT * IEs for the next message will be delivered through the * struct wpa_driver_ops::update_ft_ies() callback. */ struct ft_ies { const u8 *ies; size_t ies_len; int ft_action; u8 target_ap[ETH_ALEN]; /** Optional IE(s), e.g., WMM TSPEC(s), for RIC-Request */ const u8 *ric_ies; /** Length of ric_ies buffer in octets */ size_t ric_ies_len; } ft_ies; /** * struct ibss_rsn_start - Data for EVENT_IBSS_RSN_START */ struct ibss_rsn_start { u8 peer[ETH_ALEN]; } ibss_rsn_start; /** * struct auth_info - Data for EVENT_AUTH events */ struct auth_info { u8 peer[ETH_ALEN]; u8 bssid[ETH_ALEN]; u16 auth_type; u16 auth_transaction; u16 status_code; const u8 *ies; size_t ies_len; } auth; /** * struct assoc_reject - Data for EVENT_ASSOC_REJECT events */ struct assoc_reject { /** * bssid - BSSID of the AP that rejected association */ const u8 *bssid; /** * resp_ies - (Re)Association Response IEs * * Optional association data from the driver. This data is not * required WPA, but may be useful for some protocols and as * such, should be reported if this is available to the driver * interface. * * This should start with the first IE (fixed fields before IEs * are not included). */ const u8 *resp_ies; /** * resp_ies_len - Length of resp_ies in bytes */ size_t resp_ies_len; /** * status_code - Status Code from (Re)association Response */ u16 status_code; } assoc_reject; struct timeout_event { u8 addr[ETH_ALEN]; } timeout_event; /** * struct ft_rrb_rx - Data for EVENT_FT_RRB_RX events */ struct ft_rrb_rx { const u8 *src; const u8 *data; size_t data_len; } ft_rrb_rx; /** * struct tx_status - Data for EVENT_TX_STATUS events */ struct tx_status { u16 type; u16 stype; const u8 *dst; const u8 *data; size_t data_len; int ack; } tx_status; /** * struct rx_from_unknown - Data for EVENT_RX_FROM_UNKNOWN events */ struct rx_from_unknown { const u8 *bssid; const u8 *addr; int wds; } rx_from_unknown; /** * struct rx_mgmt - Data for EVENT_RX_MGMT events */ struct rx_mgmt { const u8 *frame; size_t frame_len; u32 datarate; /** * freq - Frequency (in MHz) on which the frame was received */ int freq; /** * ssi_signal - Signal strength in dBm (or 0 if not available) */ int ssi_signal; } rx_mgmt; /** * struct remain_on_channel - Data for EVENT_REMAIN_ON_CHANNEL events * * This is also used with EVENT_CANCEL_REMAIN_ON_CHANNEL events. */ struct remain_on_channel { /** * freq - Channel frequency in MHz */ unsigned int freq; /** * duration - Duration to remain on the channel in milliseconds */ unsigned int duration; } remain_on_channel; /** * struct scan_info - Optional data for EVENT_SCAN_RESULTS events * @aborted: Whether the scan was aborted * @freqs: Scanned frequencies in MHz (%NULL = all channels scanned) * @num_freqs: Number of entries in freqs array * @ssids: Scanned SSIDs (%NULL or zero-length SSID indicates wildcard * SSID) * @num_ssids: Number of entries in ssids array */ struct scan_info { int aborted; const int *freqs; size_t num_freqs; struct wpa_driver_scan_ssid ssids[WPAS_MAX_SCAN_SSIDS]; size_t num_ssids; } scan_info; /** * struct mlme_rx - Data for EVENT_MLME_RX events */ struct mlme_rx { const u8 *buf; size_t len; int freq; int channel; int ssi; } mlme_rx; /** * struct rx_probe_req - Data for EVENT_RX_PROBE_REQ events */ struct rx_probe_req { /** * sa - Source address of the received Probe Request frame */ const u8 *sa; /** * da - Destination address of the received Probe Request frame * or %NULL if not available */ const u8 *da; /** * bssid - BSSID of the received Probe Request frame or %NULL * if not available */ const u8 *bssid; /** * ie - IEs from the Probe Request body */ const u8 *ie; /** * ie_len - Length of ie buffer in octets */ size_t ie_len; /** * signal - signal strength in dBm (or 0 if not available) */ int ssi_signal; } rx_probe_req; /** * struct new_sta - Data for EVENT_NEW_STA events */ struct new_sta { const u8 *addr; } new_sta; /** * struct eapol_rx - Data for EVENT_EAPOL_RX events */ struct eapol_rx { const u8 *src; const u8 *data; size_t data_len; } eapol_rx; /** * signal_change - Data for EVENT_SIGNAL_CHANGE events */ struct wpa_signal_info signal_change; /** * struct best_channel - Data for EVENT_BEST_CHANNEL events * @freq_24: Best 2.4 GHz band channel frequency in MHz * @freq_5: Best 5 GHz band channel frequency in MHz * @freq_overall: Best channel frequency in MHz * * 0 can be used to indicate no preference in either band. */ struct best_channel { int freq_24; int freq_5; int freq_overall; } best_chan; struct unprot_deauth { const u8 *sa; const u8 *da; u16 reason_code; } unprot_deauth; struct unprot_disassoc { const u8 *sa; const u8 *da; u16 reason_code; } unprot_disassoc; /** * struct low_ack - Data for EVENT_STATION_LOW_ACK events * @addr: station address */ struct low_ack { u8 addr[ETH_ALEN]; } low_ack; /** * struct ibss_peer_lost - Data for EVENT_IBSS_PEER_LOST */ struct ibss_peer_lost { u8 peer[ETH_ALEN]; } ibss_peer_lost; /** * struct driver_gtk_rekey - Data for EVENT_DRIVER_GTK_REKEY */ struct driver_gtk_rekey { const u8 *bssid; const u8 *replay_ctr; } driver_gtk_rekey; /** * struct client_poll - Data for EVENT_DRIVER_CLIENT_POLL_OK events * @addr: station address */ struct client_poll { u8 addr[ETH_ALEN]; } client_poll; /** * struct eapol_tx_status * @dst: Original destination * @data: Data starting with IEEE 802.1X header (!) * @data_len: Length of data * @ack: Indicates ack or lost frame * * This corresponds to hapd_send_eapol if the frame sent * there isn't just reported as EVENT_TX_STATUS. */ struct eapol_tx_status { const u8 *dst; const u8 *data; int data_len; int ack; } eapol_tx_status; /** * struct ch_switch * @freq: Frequency of new channel in MHz * @ht_enabled: Whether this is an HT channel * @ch_offset: Secondary channel offset * @ch_width: Channel width * @cf1: Center frequency 1 * @cf2: Center frequency 2 */ struct ch_switch { int freq; int ht_enabled; int ch_offset; enum chan_width ch_width; int cf1; int cf2; } ch_switch; /** * struct connect_failed - Data for EVENT_CONNECT_FAILED_REASON * @addr: Remote client address * @code: Reason code for connection failure */ struct connect_failed_reason { u8 addr[ETH_ALEN]; enum { MAX_CLIENT_REACHED, BLOCKED_CLIENT } code; } connect_failed_reason; /** * struct dfs_event - Data for radar detected events * @freq: Frequency of the channel in MHz */ struct dfs_event { int freq; int ht_enabled; int chan_offset; enum chan_width chan_width; int cf1; int cf2; } dfs_event; /** * survey_results - Survey result data for EVENT_SURVEY * @freq_filter: Requested frequency survey filter, 0 if request * was for all survey data * @survey_list: Linked list of survey data */ struct survey_results { unsigned int freq_filter; struct dl_list survey_list; /* struct freq_survey */ } survey_results; /** * channel_list_changed - Data for EVENT_CHANNEL_LIST_CHANGED * @initiator: Initiator of the regulatory change */ struct channel_list_changed { enum reg_change_initiator initiator; } channel_list_changed; /** * freq_range - List of frequency ranges * * This is used as the data with EVENT_AVOID_FREQUENCIES. */ struct wpa_freq_range_list freq_range; }; /** * wpa_supplicant_event - Report a driver event for wpa_supplicant * @ctx: Context pointer (wpa_s); this is the ctx variable registered * with struct wpa_driver_ops::init() * @event: event type (defined above) * @data: possible extra data for the event * * Driver wrapper code should call this function whenever an event is received * from the driver. */ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, union wpa_event_data *data); /* * The following inline functions are provided for convenience to simplify * event indication for some of the common events. */ static inline void drv_event_assoc(void *ctx, const u8 *addr, const u8 *ie, size_t ielen, int reassoc) { union wpa_event_data event; os_memset(&event, 0, sizeof(event)); event.assoc_info.reassoc = reassoc; event.assoc_info.req_ies = ie; event.assoc_info.req_ies_len = ielen; event.assoc_info.addr = addr; wpa_supplicant_event(ctx, EVENT_ASSOC, &event); } static inline void drv_event_disassoc(void *ctx, const u8 *addr) { union wpa_event_data event; os_memset(&event, 0, sizeof(event)); event.disassoc_info.addr = addr; wpa_supplicant_event(ctx, EVENT_DISASSOC, &event); } static inline void drv_event_eapol_rx(void *ctx, const u8 *src, const u8 *data, size_t data_len) { union wpa_event_data event; os_memset(&event, 0, sizeof(event)); event.eapol_rx.src = src; event.eapol_rx.data = data; event.eapol_rx.data_len = data_len; wpa_supplicant_event(ctx, EVENT_EAPOL_RX, &event); } /* driver_common.c */ void wpa_scan_results_free(struct wpa_scan_results *res); /* Convert wpa_event_type to a string for logging */ const char * event_to_string(enum wpa_event_type event); /* NULL terminated array of linked in driver wrappers */ extern struct wpa_driver_ops *wpa_drivers[]; #endif /* DRIVER_H */ wpa-2.1/src/drivers/driver_atheros.c000066400000000000000000001700611227414666700176200ustar00rootroot00000000000000/* * hostapd / Driver interaction with Atheros driver * Copyright (c) 2004, Sam Leffler * Copyright (c) 2004, Video54 Technologies * Copyright (c) 2005-2007, Jouni Malinen * Copyright (c) 2009, Atheros Communications * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "includes.h" #include #include #include "common.h" #include "eloop.h" #include "common/ieee802_11_defs.h" #include "l2_packet/l2_packet.h" #include "p2p/p2p.h" #include "common.h" #ifndef _BYTE_ORDER #ifdef WORDS_BIGENDIAN #define _BYTE_ORDER _BIG_ENDIAN #else #define _BYTE_ORDER _LITTLE_ENDIAN #endif #endif /* _BYTE_ORDER */ /* * Note, the ATH_WPS_IE setting must match with the driver build.. If the * driver does not include this, the IEEE80211_IOCTL_GETWPAIE ioctl will fail. */ #define ATH_WPS_IE #include "ieee80211_external.h" #ifdef CONFIG_WPS #include #endif /* CONFIG_WPS */ #ifndef ETH_P_80211_RAW #define ETH_P_80211_RAW 0x0019 #endif #include "linux_wext.h" #include "driver.h" #include "eloop.h" #include "priv_netlink.h" #include "l2_packet/l2_packet.h" #include "common/ieee802_11_defs.h" #include "netlink.h" #include "linux_ioctl.h" struct atheros_driver_data { struct hostapd_data *hapd; /* back pointer */ char iface[IFNAMSIZ + 1]; int ifindex; struct l2_packet_data *sock_xmit; /* raw packet xmit socket */ struct l2_packet_data *sock_recv; /* raw packet recv socket */ int ioctl_sock; /* socket for ioctl() use */ struct netlink_data *netlink; int we_version; u8 acct_mac[ETH_ALEN]; struct hostap_sta_driver_data acct_data; struct l2_packet_data *sock_raw; /* raw 802.11 management frames */ struct wpabuf *wpa_ie; struct wpabuf *wps_beacon_ie; struct wpabuf *wps_probe_resp_ie; u8 own_addr[ETH_ALEN]; }; static int atheros_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, int reason_code); static int atheros_set_privacy(void *priv, int enabled); static const char * athr_get_ioctl_name(int op) { switch (op) { case IEEE80211_IOCTL_SETPARAM: return "SETPARAM"; case IEEE80211_IOCTL_GETPARAM: return "GETPARAM"; case IEEE80211_IOCTL_SETKEY: return "SETKEY"; case IEEE80211_IOCTL_SETWMMPARAMS: return "SETWMMPARAMS"; case IEEE80211_IOCTL_DELKEY: return "DELKEY"; case IEEE80211_IOCTL_GETWMMPARAMS: return "GETWMMPARAMS"; case IEEE80211_IOCTL_SETMLME: return "SETMLME"; case IEEE80211_IOCTL_GETCHANINFO: return "GETCHANINFO"; case IEEE80211_IOCTL_SETOPTIE: return "SETOPTIE"; case IEEE80211_IOCTL_GETOPTIE: return "GETOPTIE"; case IEEE80211_IOCTL_ADDMAC: return "ADDMAC"; case IEEE80211_IOCTL_DELMAC: return "DELMAC"; case IEEE80211_IOCTL_GETCHANLIST: return "GETCHANLIST"; case IEEE80211_IOCTL_SETCHANLIST: return "SETCHANLIST"; case IEEE80211_IOCTL_KICKMAC: return "KICKMAC"; case IEEE80211_IOCTL_CHANSWITCH: return "CHANSWITCH"; case IEEE80211_IOCTL_GETMODE: return "GETMODE"; case IEEE80211_IOCTL_SETMODE: return "SETMODE"; case IEEE80211_IOCTL_GET_APPIEBUF: return "GET_APPIEBUF"; case IEEE80211_IOCTL_SET_APPIEBUF: return "SET_APPIEBUF"; case IEEE80211_IOCTL_SET_ACPARAMS: return "SET_ACPARAMS"; case IEEE80211_IOCTL_FILTERFRAME: return "FILTERFRAME"; case IEEE80211_IOCTL_SET_RTPARAMS: return "SET_RTPARAMS"; case IEEE80211_IOCTL_SET_MEDENYENTRY: return "SET_MEDENYENTRY"; case IEEE80211_IOCTL_GET_MACADDR: return "GET_MACADDR"; case IEEE80211_IOCTL_SET_HBRPARAMS: return "SET_HBRPARAMS"; case IEEE80211_IOCTL_SET_RXTIMEOUT: return "SET_RXTIMEOUT"; case IEEE80211_IOCTL_STA_STATS: return "STA_STATS"; case IEEE80211_IOCTL_GETWPAIE: return "GETWPAIE"; default: return "??"; } } static const char * athr_get_param_name(int op) { switch (op) { case IEEE80211_IOC_MCASTCIPHER: return "MCASTCIPHER"; case IEEE80211_PARAM_MCASTKEYLEN: return "MCASTKEYLEN"; case IEEE80211_PARAM_UCASTCIPHERS: return "UCASTCIPHERS"; case IEEE80211_PARAM_KEYMGTALGS: return "KEYMGTALGS"; case IEEE80211_PARAM_RSNCAPS: return "RSNCAPS"; case IEEE80211_PARAM_WPA: return "WPA"; case IEEE80211_PARAM_AUTHMODE: return "AUTHMODE"; case IEEE80211_PARAM_PRIVACY: return "PRIVACY"; case IEEE80211_PARAM_COUNTERMEASURES: return "COUNTERMEASURES"; default: return "??"; } } static int set80211priv(struct atheros_driver_data *drv, int op, void *data, int len) { struct iwreq iwr; int do_inline = len < IFNAMSIZ; /* Certain ioctls must use the non-inlined method */ if (op == IEEE80211_IOCTL_SET_APPIEBUF || op == IEEE80211_IOCTL_FILTERFRAME) do_inline = 0; memset(&iwr, 0, sizeof(iwr)); os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); if (do_inline) { /* * Argument data fits inline; put it there. */ memcpy(iwr.u.name, data, len); } else { /* * Argument data too big for inline transfer; setup a * parameter block instead; the kernel will transfer * the data for the driver. */ iwr.u.data.pointer = data; iwr.u.data.length = len; } if (ioctl(drv->ioctl_sock, op, &iwr) < 0) { wpa_printf(MSG_DEBUG, "atheros: %s: %s: ioctl op=0x%x " "(%s) len=%d failed: %d (%s)", __func__, drv->iface, op, athr_get_ioctl_name(op), len, errno, strerror(errno)); return -1; } return 0; } static int set80211param(struct atheros_driver_data *drv, int op, int arg) { struct iwreq iwr; memset(&iwr, 0, sizeof(iwr)); os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); iwr.u.mode = op; memcpy(iwr.u.name+sizeof(__u32), &arg, sizeof(arg)); if (ioctl(drv->ioctl_sock, IEEE80211_IOCTL_SETPARAM, &iwr) < 0) { perror("ioctl[IEEE80211_IOCTL_SETPARAM]"); wpa_printf(MSG_DEBUG, "%s: %s: Failed to set parameter (op %d " "(%s) arg %d)", __func__, drv->iface, op, athr_get_param_name(op), arg); return -1; } return 0; } #ifndef CONFIG_NO_STDOUT_DEBUG static const char * ether_sprintf(const u8 *addr) { static char buf[sizeof(MACSTR)]; if (addr != NULL) snprintf(buf, sizeof(buf), MACSTR, MAC2STR(addr)); else snprintf(buf, sizeof(buf), MACSTR, 0,0,0,0,0,0); return buf; } #endif /* CONFIG_NO_STDOUT_DEBUG */ /* * Configure WPA parameters. */ static int atheros_configure_wpa(struct atheros_driver_data *drv, struct wpa_bss_params *params) { int v; switch (params->wpa_group) { case WPA_CIPHER_CCMP: v = IEEE80211_CIPHER_AES_CCM; break; case WPA_CIPHER_TKIP: v = IEEE80211_CIPHER_TKIP; break; case WPA_CIPHER_WEP104: v = IEEE80211_CIPHER_WEP; break; case WPA_CIPHER_WEP40: v = IEEE80211_CIPHER_WEP; break; case WPA_CIPHER_NONE: v = IEEE80211_CIPHER_NONE; break; default: wpa_printf(MSG_ERROR, "Unknown group key cipher %u", params->wpa_group); return -1; } wpa_printf(MSG_DEBUG, "%s: group key cipher=%d", __func__, v); if (set80211param(drv, IEEE80211_PARAM_MCASTCIPHER, v)) { printf("Unable to set group key cipher to %u\n", v); return -1; } if (v == IEEE80211_CIPHER_WEP) { /* key length is done only for specific ciphers */ v = (params->wpa_group == WPA_CIPHER_WEP104 ? 13 : 5); if (set80211param(drv, IEEE80211_PARAM_MCASTKEYLEN, v)) { printf("Unable to set group key length to %u\n", v); return -1; } } v = 0; if (params->wpa_pairwise & WPA_CIPHER_CCMP) v |= 1<wpa_pairwise & WPA_CIPHER_TKIP) v |= 1<wpa_pairwise & WPA_CIPHER_NONE) v |= 1<wpa_key_mgmt); if (set80211param(drv, IEEE80211_PARAM_KEYMGTALGS, params->wpa_key_mgmt)) { printf("Unable to set key management algorithms to 0x%x\n", params->wpa_key_mgmt); return -1; } v = 0; if (params->rsn_preauth) v |= BIT(0); #ifdef CONFIG_IEEE80211W if (params->ieee80211w != NO_MGMT_FRAME_PROTECTION) { v |= BIT(7); if (params->ieee80211w == MGMT_FRAME_PROTECTION_REQUIRED) v |= BIT(6); } #endif /* CONFIG_IEEE80211W */ wpa_printf(MSG_DEBUG, "%s: rsn capabilities=0x%x", __func__, v); if (set80211param(drv, IEEE80211_PARAM_RSNCAPS, v)) { printf("Unable to set RSN capabilities to 0x%x\n", v); return -1; } wpa_printf(MSG_DEBUG, "%s: enable WPA=0x%x", __func__, params->wpa); if (set80211param(drv, IEEE80211_PARAM_WPA, params->wpa)) { printf("Unable to set WPA to %u\n", params->wpa); return -1; } return 0; } static int atheros_set_ieee8021x(void *priv, struct wpa_bss_params *params) { struct atheros_driver_data *drv = priv; wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, params->enabled); if (!params->enabled) { /* XXX restore state */ if (set80211param(priv, IEEE80211_PARAM_AUTHMODE, IEEE80211_AUTH_AUTO) < 0) return -1; /* IEEE80211_AUTH_AUTO ends up enabling Privacy; clear that */ return atheros_set_privacy(drv, 0); } if (!params->wpa && !params->ieee802_1x) { wpa_printf(MSG_WARNING, "No 802.1X or WPA enabled!"); return -1; } if (params->wpa && atheros_configure_wpa(drv, params) != 0) { wpa_printf(MSG_WARNING, "Error configuring WPA state!"); return -1; } if (set80211param(priv, IEEE80211_PARAM_AUTHMODE, (params->wpa ? IEEE80211_AUTH_WPA : IEEE80211_AUTH_8021X))) { wpa_printf(MSG_WARNING, "Error enabling WPA/802.1X!"); return -1; } return 0; } static int atheros_set_privacy(void *priv, int enabled) { struct atheros_driver_data *drv = priv; wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled); return set80211param(drv, IEEE80211_PARAM_PRIVACY, enabled); } static int atheros_set_sta_authorized(void *priv, const u8 *addr, int authorized) { struct atheros_driver_data *drv = priv; struct ieee80211req_mlme mlme; int ret; wpa_printf(MSG_DEBUG, "%s: addr=%s authorized=%d", __func__, ether_sprintf(addr), authorized); if (authorized) mlme.im_op = IEEE80211_MLME_AUTHORIZE; else mlme.im_op = IEEE80211_MLME_UNAUTHORIZE; mlme.im_reason = 0; memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme)); if (ret < 0) { wpa_printf(MSG_DEBUG, "%s: Failed to %sauthorize STA " MACSTR, __func__, authorized ? "" : "un", MAC2STR(addr)); } return ret; } static int atheros_sta_set_flags(void *priv, const u8 *addr, int total_flags, int flags_or, int flags_and) { /* For now, only support setting Authorized flag */ if (flags_or & WPA_STA_AUTHORIZED) return atheros_set_sta_authorized(priv, addr, 1); if (!(flags_and & WPA_STA_AUTHORIZED)) return atheros_set_sta_authorized(priv, addr, 0); return 0; } static int atheros_del_key(void *priv, const u8 *addr, int key_idx) { struct atheros_driver_data *drv = priv; struct ieee80211req_del_key wk; int ret; wpa_printf(MSG_DEBUG, "%s: addr=%s key_idx=%d", __func__, ether_sprintf(addr), key_idx); memset(&wk, 0, sizeof(wk)); if (addr != NULL) { memcpy(wk.idk_macaddr, addr, IEEE80211_ADDR_LEN); wk.idk_keyix = (u8) IEEE80211_KEYIX_NONE; } else { wk.idk_keyix = key_idx; } ret = set80211priv(drv, IEEE80211_IOCTL_DELKEY, &wk, sizeof(wk)); if (ret < 0) { wpa_printf(MSG_DEBUG, "%s: Failed to delete key (addr %s" " key_idx %d)", __func__, ether_sprintf(addr), key_idx); } return ret; } static int atheros_set_key(const char *ifname, void *priv, enum wpa_alg alg, const u8 *addr, int key_idx, int set_tx, const u8 *seq, size_t seq_len, const u8 *key, size_t key_len) { struct atheros_driver_data *drv = priv; struct ieee80211req_key wk; u_int8_t cipher; int ret; if (alg == WPA_ALG_NONE) return atheros_del_key(drv, addr, key_idx); wpa_printf(MSG_DEBUG, "%s: alg=%d addr=%s key_idx=%d", __func__, alg, ether_sprintf(addr), key_idx); switch (alg) { case WPA_ALG_WEP: cipher = IEEE80211_CIPHER_WEP; break; case WPA_ALG_TKIP: cipher = IEEE80211_CIPHER_TKIP; break; case WPA_ALG_CCMP: cipher = IEEE80211_CIPHER_AES_CCM; break; #ifdef CONFIG_IEEE80211W case WPA_ALG_IGTK: cipher = IEEE80211_CIPHER_AES_CMAC; break; #endif /* CONFIG_IEEE80211W */ default: printf("%s: unknown/unsupported algorithm %d\n", __func__, alg); return -1; } if (key_len > sizeof(wk.ik_keydata)) { printf("%s: key length %lu too big\n", __func__, (unsigned long) key_len); return -3; } memset(&wk, 0, sizeof(wk)); wk.ik_type = cipher; wk.ik_flags = IEEE80211_KEY_RECV | IEEE80211_KEY_XMIT; if (addr == NULL || is_broadcast_ether_addr(addr)) { memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN); wk.ik_keyix = key_idx; if (set_tx) wk.ik_flags |= IEEE80211_KEY_DEFAULT; } else { memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN); wk.ik_keyix = IEEE80211_KEYIX_NONE; } wk.ik_keylen = key_len; memcpy(wk.ik_keydata, key, key_len); ret = set80211priv(drv, IEEE80211_IOCTL_SETKEY, &wk, sizeof(wk)); if (ret < 0) { wpa_printf(MSG_DEBUG, "%s: Failed to set key (addr %s" " key_idx %d alg %d key_len %lu set_tx %d)", __func__, ether_sprintf(wk.ik_macaddr), key_idx, alg, (unsigned long) key_len, set_tx); } return ret; } static int atheros_get_seqnum(const char *ifname, void *priv, const u8 *addr, int idx, u8 *seq) { struct atheros_driver_data *drv = priv; struct ieee80211req_key wk; wpa_printf(MSG_DEBUG, "%s: addr=%s idx=%d", __func__, ether_sprintf(addr), idx); memset(&wk, 0, sizeof(wk)); if (addr == NULL) memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN); else memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN); wk.ik_keyix = idx; if (set80211priv(drv, IEEE80211_IOCTL_GETKEY, &wk, sizeof(wk))) { wpa_printf(MSG_DEBUG, "%s: Failed to get encryption data " "(addr " MACSTR " key_idx %d)", __func__, MAC2STR(wk.ik_macaddr), idx); return -1; } #ifdef WORDS_BIGENDIAN { /* * wk.ik_keytsc is in host byte order (big endian), need to * swap it to match with the byte order used in WPA. */ int i; #ifndef WPA_KEY_RSC_LEN #define WPA_KEY_RSC_LEN 8 #endif u8 tmp[WPA_KEY_RSC_LEN]; memcpy(tmp, &wk.ik_keytsc, sizeof(wk.ik_keytsc)); for (i = 0; i < WPA_KEY_RSC_LEN; i++) { seq[i] = tmp[WPA_KEY_RSC_LEN - i - 1]; } } #else /* WORDS_BIGENDIAN */ memcpy(seq, &wk.ik_keytsc, sizeof(wk.ik_keytsc)); #endif /* WORDS_BIGENDIAN */ return 0; } static int atheros_flush(void *priv) { u8 allsta[IEEE80211_ADDR_LEN]; memset(allsta, 0xff, IEEE80211_ADDR_LEN); return atheros_sta_deauth(priv, NULL, allsta, IEEE80211_REASON_AUTH_LEAVE); } static int atheros_read_sta_driver_data(void *priv, struct hostap_sta_driver_data *data, const u8 *addr) { struct atheros_driver_data *drv = priv; struct ieee80211req_sta_stats stats; memset(data, 0, sizeof(*data)); /* * Fetch statistics for station from the system. */ memset(&stats, 0, sizeof(stats)); memcpy(stats.is_u.macaddr, addr, IEEE80211_ADDR_LEN); if (set80211priv(drv, IEEE80211_IOCTL_STA_STATS, &stats, sizeof(stats))) { wpa_printf(MSG_DEBUG, "%s: Failed to fetch STA stats (addr " MACSTR ")", __func__, MAC2STR(addr)); if (memcmp(addr, drv->acct_mac, ETH_ALEN) == 0) { memcpy(data, &drv->acct_data, sizeof(*data)); return 0; } printf("Failed to get station stats information element.\n"); return -1; } data->rx_packets = stats.is_stats.ns_rx_data; data->rx_bytes = stats.is_stats.ns_rx_bytes; data->tx_packets = stats.is_stats.ns_tx_data; data->tx_bytes = stats.is_stats.ns_tx_bytes; return 0; } static int atheros_sta_clear_stats(void *priv, const u8 *addr) { struct atheros_driver_data *drv = priv; struct ieee80211req_mlme mlme; int ret; wpa_printf(MSG_DEBUG, "%s: addr=%s", __func__, ether_sprintf(addr)); mlme.im_op = IEEE80211_MLME_CLEAR_STATS; memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme)); if (ret < 0) { wpa_printf(MSG_DEBUG, "%s: Failed to clear STA stats (addr " MACSTR ")", __func__, MAC2STR(addr)); } return ret; } static int atheros_set_opt_ie(void *priv, const u8 *ie, size_t ie_len) { struct atheros_driver_data *drv = priv; u8 buf[512]; struct ieee80211req_getset_appiebuf *app_ie; wpa_printf(MSG_DEBUG, "%s buflen = %lu", __func__, (unsigned long) ie_len); wpa_hexdump(MSG_DEBUG, "atheros: set_generic_elem", ie, ie_len); wpabuf_free(drv->wpa_ie); drv->wpa_ie = wpabuf_alloc_copy(ie, ie_len); app_ie = (struct ieee80211req_getset_appiebuf *) buf; os_memcpy(&(app_ie->app_buf[0]), ie, ie_len); app_ie->app_buflen = ie_len; app_ie->app_frmtype = IEEE80211_APPIE_FRAME_BEACON; /* append WPS IE for Beacon */ if (drv->wps_beacon_ie != NULL) { os_memcpy(&(app_ie->app_buf[ie_len]), wpabuf_head(drv->wps_beacon_ie), wpabuf_len(drv->wps_beacon_ie)); app_ie->app_buflen = ie_len + wpabuf_len(drv->wps_beacon_ie); } wpa_hexdump(MSG_DEBUG, "atheros: SET_APPIEBUF(Beacon)", app_ie->app_buf, app_ie->app_buflen); set80211priv(drv, IEEE80211_IOCTL_SET_APPIEBUF, app_ie, sizeof(struct ieee80211req_getset_appiebuf) + app_ie->app_buflen); /* append WPS IE for Probe Response */ app_ie->app_frmtype = IEEE80211_APPIE_FRAME_PROBE_RESP; if (drv->wps_probe_resp_ie != NULL) { os_memcpy(&(app_ie->app_buf[ie_len]), wpabuf_head(drv->wps_probe_resp_ie), wpabuf_len(drv->wps_probe_resp_ie)); app_ie->app_buflen = ie_len + wpabuf_len(drv->wps_probe_resp_ie); } else app_ie->app_buflen = ie_len; wpa_hexdump(MSG_DEBUG, "atheros: SET_APPIEBUF(ProbeResp)", app_ie->app_buf, app_ie->app_buflen); set80211priv(drv, IEEE80211_IOCTL_SET_APPIEBUF, app_ie, sizeof(struct ieee80211req_getset_appiebuf) + app_ie->app_buflen); return 0; } static int atheros_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, int reason_code) { struct atheros_driver_data *drv = priv; struct ieee80211req_mlme mlme; int ret; wpa_printf(MSG_DEBUG, "%s: addr=%s reason_code=%d", __func__, ether_sprintf(addr), reason_code); mlme.im_op = IEEE80211_MLME_DEAUTH; mlme.im_reason = reason_code; memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme)); if (ret < 0) { wpa_printf(MSG_DEBUG, "%s: Failed to deauth STA (addr " MACSTR " reason %d)", __func__, MAC2STR(addr), reason_code); } return ret; } static int atheros_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr, int reason_code) { struct atheros_driver_data *drv = priv; struct ieee80211req_mlme mlme; int ret; wpa_printf(MSG_DEBUG, "%s: addr=%s reason_code=%d", __func__, ether_sprintf(addr), reason_code); mlme.im_op = IEEE80211_MLME_DISASSOC; mlme.im_reason = reason_code; memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme)); if (ret < 0) { wpa_printf(MSG_DEBUG, "%s: Failed to disassoc STA (addr " MACSTR " reason %d)", __func__, MAC2STR(addr), reason_code); } return ret; } #ifdef CONFIG_WPS static void atheros_raw_recv_wps(void *ctx, const u8 *src_addr, const u8 *buf, size_t len) { struct atheros_driver_data *drv = ctx; const struct ieee80211_mgmt *mgmt; u16 fc; union wpa_event_data event; /* Send Probe Request information to WPS processing */ if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req)) return; mgmt = (const struct ieee80211_mgmt *) buf; fc = le_to_host16(mgmt->frame_control); if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT || WLAN_FC_GET_STYPE(fc) != WLAN_FC_STYPE_PROBE_REQ) return; os_memset(&event, 0, sizeof(event)); event.rx_probe_req.sa = mgmt->sa; event.rx_probe_req.da = mgmt->da; event.rx_probe_req.bssid = mgmt->bssid; event.rx_probe_req.ie = mgmt->u.probe_req.variable; event.rx_probe_req.ie_len = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req)); wpa_supplicant_event(drv->hapd, EVENT_RX_PROBE_REQ, &event); } #endif /* CONFIG_WPS */ #ifdef CONFIG_IEEE80211R static void atheros_raw_recv_11r(void *ctx, const u8 *src_addr, const u8 *buf, size_t len) { struct atheros_driver_data *drv = ctx; union wpa_event_data event; const struct ieee80211_mgmt *mgmt; u16 fc; u16 stype; int ielen; const u8 *iebuf; /* Do 11R processing for ASSOC/AUTH/FT ACTION frames */ if (len < IEEE80211_HDRLEN) return; mgmt = (const struct ieee80211_mgmt *) buf; fc = le_to_host16(mgmt->frame_control); if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT) return; stype = WLAN_FC_GET_STYPE(fc); wpa_printf(MSG_DEBUG, "%s: subtype 0x%x len %d", __func__, stype, (int) len); if (os_memcmp(drv->own_addr, mgmt->bssid, ETH_ALEN) != 0) { wpa_printf(MSG_DEBUG, "%s: BSSID does not match - ignore", __func__); return; } switch (stype) { case WLAN_FC_STYPE_ASSOC_REQ: if (len - IEEE80211_HDRLEN < sizeof(mgmt->u.assoc_req)) break; ielen = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.assoc_req)); iebuf = mgmt->u.assoc_req.variable; drv_event_assoc(drv->hapd, mgmt->sa, iebuf, ielen, 0); break; case WLAN_FC_STYPE_REASSOC_REQ: if (len - IEEE80211_HDRLEN < sizeof(mgmt->u.reassoc_req)) break; ielen = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.reassoc_req)); iebuf = mgmt->u.reassoc_req.variable; drv_event_assoc(drv->hapd, mgmt->sa, iebuf, ielen, 1); break; case WLAN_FC_STYPE_ACTION: os_memset(&event, 0, sizeof(event)); event.rx_mgmt.frame = buf; event.rx_mgmt.frame_len = len; wpa_supplicant_event(drv->hapd, EVENT_RX_MGMT, &event); break; case WLAN_FC_STYPE_AUTH: if (len - IEEE80211_HDRLEN < sizeof(mgmt->u.auth)) break; os_memset(&event, 0, sizeof(event)); os_memcpy(event.auth.peer, mgmt->sa, ETH_ALEN); os_memcpy(event.auth.bssid, mgmt->bssid, ETH_ALEN); event.auth.auth_type = le_to_host16(mgmt->u.auth.auth_alg); event.auth.status_code = le_to_host16(mgmt->u.auth.status_code); event.auth.auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction); event.auth.ies = mgmt->u.auth.variable; event.auth.ies_len = len - IEEE80211_HDRLEN - sizeof(mgmt->u.auth); wpa_supplicant_event(drv->hapd, EVENT_AUTH, &event); break; default: break; } } #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_HS20 static void atheros_raw_recv_hs20(void *ctx, const u8 *src_addr, const u8 *buf, size_t len) { struct atheros_driver_data *drv = ctx; const struct ieee80211_mgmt *mgmt; u16 fc; union wpa_event_data event; /* Send the Action frame for HS20 processing */ if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.action.category) + sizeof(mgmt->u.action.u.public_action)) return; mgmt = (const struct ieee80211_mgmt *) buf; fc = le_to_host16(mgmt->frame_control); if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT || WLAN_FC_GET_STYPE(fc) != WLAN_FC_STYPE_ACTION || mgmt->u.action.category != WLAN_ACTION_PUBLIC) return; wpa_printf(MSG_DEBUG, "%s:Received Public Action frame", __func__); os_memset(&event, 0, sizeof(event)); event.rx_mgmt.frame = (const u8 *) mgmt; event.rx_mgmt.frame_len = len; wpa_supplicant_event(drv->hapd, EVENT_RX_MGMT, &event); } #endif /* CONFIG_HS20 */ static int atheros_set_qos_map(void *ctx, const u8 *qos_map_set, u8 qos_map_set_len) { #ifdef CONFIG_ATHEROS_QOS_MAP struct atheros_driver_data *drv = ctx; struct ieee80211req_athdbg req; struct ieee80211_qos_map *qos_map = &req.data.qos_map; struct iwreq iwr; int i, up_start; if (qos_map_set_len < 16 || qos_map_set_len > 58 || qos_map_set_len & 1) { wpa_printf(MSG_ERROR, "Invalid QoS Map"); return -1; } else { memset(&req, 0, sizeof(struct ieee80211req_athdbg)); req.cmd = IEEE80211_DBGREQ_SETQOSMAPCONF; os_memset(&iwr, 0, sizeof(iwr)); os_strlcpy(iwr.ifr_name, drv->iface, sizeof(iwr.ifr_name)); iwr.u.data.pointer = (void *) &req; iwr.u.data.length = sizeof(struct ieee80211req_athdbg); } qos_map->valid = 1; qos_map->num_dscp_except = (qos_map_set_len - 16) / 2; if (qos_map->num_dscp_except) { for (i = 0; i < qos_map->num_dscp_except; i++) { qos_map->dscp_exception[i].dscp = qos_map_set[i * 2]; qos_map->dscp_exception[i].up = qos_map_set[i * 2 + 1]; } } up_start = qos_map_set_len - 16; for (i = 0; i < IEEE80211_MAX_QOS_UP_RANGE; i++) { qos_map->up[i].low = qos_map_set[up_start + (i * 2)]; qos_map->up[i].high = qos_map_set[up_start + (i * 2) + 1]; } if (ioctl(drv->ioctl_sock, IEEE80211_IOCTL_DBGREQ, &iwr) < 0) { perror("ioctl[IEEE80211_IOCTL_DBGREQ]"); wpa_printf(MSG_DEBUG, "%s: %s: Failed to set QoS Map", __func__, drv->iface); return -1; } #endif /* CONFIG_ATHEROS_QOS_MAP */ return 0; } #if defined(CONFIG_WNM) && !defined(CONFIG_IEEE80211R) static void atheros_raw_recv_11v(void *ctx, const u8 *src_addr, const u8 *buf, size_t len) { struct atheros_driver_data *drv = ctx; union wpa_event_data event; const struct ieee80211_mgmt *mgmt; u16 fc; u16 stype; /* Do 11R processing for WNM ACTION frames */ if (len < IEEE80211_HDRLEN) return; mgmt = (const struct ieee80211_mgmt *) buf; fc = le_to_host16(mgmt->frame_control); if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT) return; stype = WLAN_FC_GET_STYPE(fc); wpa_printf(MSG_DEBUG, "%s: subtype 0x%x len %d", __func__, stype, (int) len); if (os_memcmp(drv->own_addr, mgmt->bssid, ETH_ALEN) != 0) { wpa_printf(MSG_DEBUG, "%s: BSSID does not match - ignore", __func__); return; } switch (stype) { case WLAN_FC_STYPE_ACTION: os_memset(&event, 0, sizeof(event)); event.rx_mgmt.frame = buf; event.rx_mgmt.frame_len = len; wpa_supplicant_event(drv->hapd, EVENT_RX_MGMT, &event); break; default: break; } } #endif /* CONFIG_WNM */ #if defined(CONFIG_WPS) || defined(CONFIG_IEEE80211R) || defined(CONFIG_WNM) static void atheros_raw_receive(void *ctx, const u8 *src_addr, const u8 *buf, size_t len) { #ifdef CONFIG_WPS atheros_raw_recv_wps(ctx, src_addr, buf, len); #endif /* CONFIG_WPS */ #ifdef CONFIG_IEEE80211R atheros_raw_recv_11r(ctx, src_addr, buf, len); #endif /* CONFIG_IEEE80211R */ #if defined(CONFIG_WNM) && !defined(CONFIG_IEEE80211R) atheros_raw_recv_11v(ctx, src_addr, buf, len); #endif /* CONFIG_WNM */ #ifdef CONFIG_HS20 atheros_raw_recv_hs20(ctx, src_addr, buf, len); #endif /* CONFIG_HS20 */ } #endif /* CONFIG_WPS || CONFIG_IEEE80211R */ static int atheros_receive_pkt(struct atheros_driver_data *drv) { int ret = 0; struct ieee80211req_set_filter filt; wpa_printf(MSG_DEBUG, "%s Enter", __func__); filt.app_filterype = 0; #ifdef CONFIG_WPS filt.app_filterype |= IEEE80211_FILTER_TYPE_PROBE_REQ; #endif /* CONFIG_WPS */ #ifdef CONFIG_IEEE80211R filt.app_filterype |= (IEEE80211_FILTER_TYPE_ASSOC_REQ | IEEE80211_FILTER_TYPE_AUTH | IEEE80211_FILTER_TYPE_ACTION); #endif #ifdef CONFIG_WNM filt.app_filterype |= IEEE80211_FILTER_TYPE_ACTION; #endif /* CONFIG_WNM */ #ifdef CONFIG_HS20 filt.app_filterype |= IEEE80211_FILTER_TYPE_ACTION; #endif /* CONFIG_HS20 */ if (filt.app_filterype) { ret = set80211priv(drv, IEEE80211_IOCTL_FILTERFRAME, &filt, sizeof(struct ieee80211req_set_filter)); if (ret) return ret; } #if defined(CONFIG_WPS) || defined(CONFIG_IEEE80211R) drv->sock_raw = l2_packet_init(drv->iface, NULL, ETH_P_80211_RAW, atheros_raw_receive, drv, 1); if (drv->sock_raw == NULL) return -1; #endif /* CONFIG_WPS || CONFIG_IEEE80211R */ return ret; } static int atheros_reset_appfilter(struct atheros_driver_data *drv) { struct ieee80211req_set_filter filt; filt.app_filterype = 0; return set80211priv(drv, IEEE80211_IOCTL_FILTERFRAME, &filt, sizeof(struct ieee80211req_set_filter)); } #ifdef CONFIG_WPS static int atheros_set_wps_ie(void *priv, const u8 *ie, size_t len, u32 frametype) { struct atheros_driver_data *drv = priv; u8 buf[512]; struct ieee80211req_getset_appiebuf *beac_ie; wpa_printf(MSG_DEBUG, "%s buflen = %lu frametype=%u", __func__, (unsigned long) len, frametype); wpa_hexdump(MSG_DEBUG, "atheros: IE", ie, len); beac_ie = (struct ieee80211req_getset_appiebuf *) buf; beac_ie->app_frmtype = frametype; beac_ie->app_buflen = len; os_memcpy(&(beac_ie->app_buf[0]), ie, len); /* append the WPA/RSN IE if it is set already */ if (((frametype == IEEE80211_APPIE_FRAME_BEACON) || (frametype == IEEE80211_APPIE_FRAME_PROBE_RESP)) && (drv->wpa_ie != NULL)) { wpa_hexdump_buf(MSG_DEBUG, "atheros: Append WPA/RSN IE", drv->wpa_ie); os_memcpy(&(beac_ie->app_buf[len]), wpabuf_head(drv->wpa_ie), wpabuf_len(drv->wpa_ie)); beac_ie->app_buflen += wpabuf_len(drv->wpa_ie); } wpa_hexdump(MSG_DEBUG, "atheros: SET_APPIEBUF", beac_ie->app_buf, beac_ie->app_buflen); return set80211priv(drv, IEEE80211_IOCTL_SET_APPIEBUF, beac_ie, sizeof(struct ieee80211req_getset_appiebuf) + beac_ie->app_buflen); } static int atheros_set_ap_wps_ie(void *priv, const struct wpabuf *beacon, const struct wpabuf *proberesp, const struct wpabuf *assocresp) { struct atheros_driver_data *drv = priv; wpa_hexdump_buf(MSG_DEBUG, "atheros: set_ap_wps_ie - beacon", beacon); wpa_hexdump_buf(MSG_DEBUG, "atheros: set_ap_wps_ie - proberesp", proberesp); wpa_hexdump_buf(MSG_DEBUG, "atheros: set_ap_wps_ie - assocresp", assocresp); wpabuf_free(drv->wps_beacon_ie); drv->wps_beacon_ie = beacon ? wpabuf_dup(beacon) : NULL; wpabuf_free(drv->wps_probe_resp_ie); drv->wps_probe_resp_ie = proberesp ? wpabuf_dup(proberesp) : NULL; atheros_set_wps_ie(priv, assocresp ? wpabuf_head(assocresp) : NULL, assocresp ? wpabuf_len(assocresp) : 0, IEEE80211_APPIE_FRAME_ASSOC_RESP); if (atheros_set_wps_ie(priv, beacon ? wpabuf_head(beacon) : NULL, beacon ? wpabuf_len(beacon) : 0, IEEE80211_APPIE_FRAME_BEACON)) return -1; return atheros_set_wps_ie(priv, proberesp ? wpabuf_head(proberesp) : NULL, proberesp ? wpabuf_len(proberesp): 0, IEEE80211_APPIE_FRAME_PROBE_RESP); } #else /* CONFIG_WPS */ #define atheros_set_ap_wps_ie NULL #endif /* CONFIG_WPS */ #ifdef CONFIG_IEEE80211R static int atheros_sta_auth(void *priv, const u8 *own_addr, const u8 *addr, u16 seq, u16 status_code, const u8 *ie, size_t len) { struct atheros_driver_data *drv = priv; struct ieee80211req_mlme mlme; int ret; wpa_printf(MSG_DEBUG, "%s: addr=%s status_code=%d", __func__, ether_sprintf(addr), status_code); mlme.im_op = IEEE80211_MLME_AUTH; mlme.im_reason = status_code; mlme.im_seq = seq; os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); mlme.im_optie_len = len; if (len) { if (len < IEEE80211_MAX_OPT_IE) { os_memcpy(mlme.im_optie, ie, len); } else { wpa_printf(MSG_DEBUG, "%s: Not enough space to copy " "opt_ie STA (addr " MACSTR " reason %d, " "ie_len %d)", __func__, MAC2STR(addr), status_code, (int) len); return -1; } } ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme)); if (ret < 0) { wpa_printf(MSG_DEBUG, "%s: Failed to auth STA (addr " MACSTR " reason %d)", __func__, MAC2STR(addr), status_code); } return ret; } static int atheros_sta_assoc(void *priv, const u8 *own_addr, const u8 *addr, int reassoc, u16 status_code, const u8 *ie, size_t len) { struct atheros_driver_data *drv = priv; struct ieee80211req_mlme mlme; int ret; wpa_printf(MSG_DEBUG, "%s: addr=%s status_code=%d reassoc %d", __func__, ether_sprintf(addr), status_code, reassoc); if (reassoc) mlme.im_op = IEEE80211_MLME_REASSOC; else mlme.im_op = IEEE80211_MLME_ASSOC; mlme.im_reason = status_code; os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); mlme.im_optie_len = len; if (len) { if (len < IEEE80211_MAX_OPT_IE) { os_memcpy(mlme.im_optie, ie, len); } else { wpa_printf(MSG_DEBUG, "%s: Not enough space to copy " "opt_ie STA (addr " MACSTR " reason %d, " "ie_len %d)", __func__, MAC2STR(addr), status_code, (int) len); return -1; } } ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme)); if (ret < 0) { wpa_printf(MSG_DEBUG, "%s: Failed to assoc STA (addr " MACSTR " reason %d)", __func__, MAC2STR(addr), status_code); } return ret; } #endif /* CONFIG_IEEE80211R */ static void atheros_new_sta(struct atheros_driver_data *drv, u8 addr[IEEE80211_ADDR_LEN]) { struct hostapd_data *hapd = drv->hapd; struct ieee80211req_wpaie ie; int ielen = 0; u8 *iebuf = NULL; /* * Fetch negotiated WPA/RSN parameters from the system. */ memset(&ie, 0, sizeof(ie)); memcpy(ie.wpa_macaddr, addr, IEEE80211_ADDR_LEN); if (set80211priv(drv, IEEE80211_IOCTL_GETWPAIE, &ie, sizeof(ie))) { /* * See ATH_WPS_IE comment in the beginning of the file for a * possible cause for the failure.. */ wpa_printf(MSG_DEBUG, "%s: Failed to get WPA/RSN IE: %s", __func__, strerror(errno)); goto no_ie; } wpa_hexdump(MSG_MSGDUMP, "atheros req WPA IE", ie.wpa_ie, IEEE80211_MAX_OPT_IE); wpa_hexdump(MSG_MSGDUMP, "atheros req RSN IE", ie.rsn_ie, IEEE80211_MAX_OPT_IE); #ifdef ATH_WPS_IE wpa_hexdump(MSG_MSGDUMP, "atheros req WPS IE", ie.wps_ie, IEEE80211_MAX_OPT_IE); #endif /* ATH_WPS_IE */ iebuf = ie.wpa_ie; /* atheros seems to return some random data if WPA/RSN IE is not set. * Assume the IE was not included if the IE type is unknown. */ if (iebuf[0] != WLAN_EID_VENDOR_SPECIFIC) iebuf[1] = 0; if (iebuf[1] == 0 && ie.rsn_ie[1] > 0) { /* atheros-ng svn #1453 added rsn_ie. Use it, if wpa_ie was not * set. This is needed for WPA2. */ iebuf = ie.rsn_ie; if (iebuf[0] != WLAN_EID_RSN) iebuf[1] = 0; } ielen = iebuf[1]; #ifdef ATH_WPS_IE /* if WPS IE is present, preference is given to WPS */ if (ie.wps_ie && (ie.wps_ie[1] > 0 && (ie.wps_ie[0] == WLAN_EID_VENDOR_SPECIFIC))) { iebuf = ie.wps_ie; ielen = ie.wps_ie[1]; } #endif /* ATH_WPS_IE */ if (ielen == 0) iebuf = NULL; else ielen += 2; no_ie: drv_event_assoc(hapd, addr, iebuf, ielen, 0); if (memcmp(addr, drv->acct_mac, ETH_ALEN) == 0) { /* Cached accounting data is not valid anymore. */ memset(drv->acct_mac, 0, ETH_ALEN); memset(&drv->acct_data, 0, sizeof(drv->acct_data)); } } static void atheros_wireless_event_wireless_custom(struct atheros_driver_data *drv, char *custom, char *end) { wpa_printf(MSG_DEBUG, "Custom wireless event: '%s'", custom); if (strncmp(custom, "MLME-MICHAELMICFAILURE.indication", 33) == 0) { char *pos; u8 addr[ETH_ALEN]; pos = strstr(custom, "addr="); if (pos == NULL) { wpa_printf(MSG_DEBUG, "MLME-MICHAELMICFAILURE.indication " "without sender address ignored"); return; } pos += 5; if (hwaddr_aton(pos, addr) == 0) { union wpa_event_data data; os_memset(&data, 0, sizeof(data)); data.michael_mic_failure.unicast = 1; data.michael_mic_failure.src = addr; wpa_supplicant_event(drv->hapd, EVENT_MICHAEL_MIC_FAILURE, &data); } else { wpa_printf(MSG_DEBUG, "MLME-MICHAELMICFAILURE.indication " "with invalid MAC address"); } } else if (strncmp(custom, "STA-TRAFFIC-STAT", 16) == 0) { char *key, *value; u32 val; key = custom; while ((key = strchr(key, '\n')) != NULL) { key++; value = strchr(key, '='); if (value == NULL) continue; *value++ = '\0'; val = strtoul(value, NULL, 10); if (strcmp(key, "mac") == 0) hwaddr_aton(value, drv->acct_mac); else if (strcmp(key, "rx_packets") == 0) drv->acct_data.rx_packets = val; else if (strcmp(key, "tx_packets") == 0) drv->acct_data.tx_packets = val; else if (strcmp(key, "rx_bytes") == 0) drv->acct_data.rx_bytes = val; else if (strcmp(key, "tx_bytes") == 0) drv->acct_data.tx_bytes = val; key = value; } #ifdef CONFIG_WPS } else if (strncmp(custom, "PUSH-BUTTON.indication", 22) == 0) { /* Some atheros kernels send push button as a wireless event */ /* PROBLEM! this event is received for ALL BSSs ... * so all are enabled for WPS... ugh. */ wpa_supplicant_event(drv->hapd, EVENT_WPS_BUTTON_PUSHED, NULL); #endif /* CONFIG_WPS */ #if defined(CONFIG_WPS) || defined(CONFIG_IEEE80211R) || defined(CONFIG_HS20) #define MGMT_FRAM_TAG_SIZE 30 /* hardcoded in driver */ } else if (strncmp(custom, "Manage.prob_req ", 16) == 0) { /* * Atheros driver uses a hack to pass Probe Request frames as a * binary data in the custom wireless event. The old way (using * packet sniffing) didn't work when bridging. * Format: "Manage.prob_req " | zero padding | frame */ int len = atoi(custom + 16); if (len < 0 || custom + MGMT_FRAM_TAG_SIZE + len > end) { wpa_printf(MSG_DEBUG, "Invalid Manage.prob_req event " "length %d", len); return; } atheros_raw_receive(drv, NULL, (u8 *) custom + MGMT_FRAM_TAG_SIZE, len); } else if (strncmp(custom, "Manage.assoc_req ", 17) == 0) { /* Format: "Manage.assoc_req " | zero padding | * frame */ int len = atoi(custom + 17); if (len < 0 || custom + MGMT_FRAM_TAG_SIZE + len > end) { wpa_printf(MSG_DEBUG, "Invalid Manage.prob_req/" "assoc_req/auth event length %d", len); return; } atheros_raw_receive(drv, NULL, (u8 *) custom + MGMT_FRAM_TAG_SIZE, len); } else if (strncmp(custom, "Manage.action ", 14) == 0) { /* Format: "Manage.assoc_req " | zero padding | * frame */ int len = atoi(custom + 14); if (len < 0 || custom + MGMT_FRAM_TAG_SIZE + len > end) { wpa_printf(MSG_DEBUG, "Invalid Manage.prob_req/" "assoc_req/auth event length %d", len); return; } atheros_raw_receive(drv, NULL, (u8 *) custom + MGMT_FRAM_TAG_SIZE, len); } else if (strncmp(custom, "Manage.auth ", 12) == 0) { /* Format: "Manage.auth " | zero padding | frame */ int len = atoi(custom + 12); if (len < 0 || custom + MGMT_FRAM_TAG_SIZE + len > end) { wpa_printf(MSG_DEBUG, "Invalid Manage.prob_req/" "assoc_req/auth event length %d", len); return; } atheros_raw_receive(drv, NULL, (u8 *) custom + MGMT_FRAM_TAG_SIZE, len); #endif /* CONFIG_WPS or CONFIG_IEEE80211R */ } } /* * Handle size of data problem. WEXT only allows data of 256 bytes for custom * events, and p2p data can be much bigger. So the athr driver sends a small * event telling me to collect the big data with an ioctl. * On the first event, send all pending events to supplicant. */ static void fetch_pending_big_events(struct atheros_driver_data *drv) { union wpa_event_data event; const struct ieee80211_mgmt *mgmt; u8 tbuf[IW_PRIV_SIZE_MASK]; /* max size is 2047 bytes */ u16 fc, stype; struct iwreq iwr; size_t data_len; u32 freq, frame_type; while (1) { os_memset(&iwr, 0, sizeof(iwr)); os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); iwr.u.data.pointer = (void *) tbuf; iwr.u.data.length = sizeof(tbuf); iwr.u.data.flags = IEEE80211_IOC_P2P_FETCH_FRAME; if (ioctl(drv->ioctl_sock, IEEE80211_IOCTL_P2P_BIG_PARAM, &iwr) < 0) { if (errno == ENOSPC) { wpa_printf(MSG_DEBUG, "%s:%d exit", __func__, __LINE__); return; } wpa_printf(MSG_DEBUG, "athr: %s: P2P_BIG_PARAM[" "P2P_FETCH_FRAME] failed: %s", __func__, strerror(errno)); return; } data_len = iwr.u.data.length; wpa_hexdump(MSG_DEBUG, "athr: P2P_FETCH_FRAME data", (u8 *) tbuf, data_len); if (data_len < sizeof(freq) + sizeof(frame_type) + 24) { wpa_printf(MSG_DEBUG, "athr: frame too short"); continue; } os_memcpy(&freq, tbuf, sizeof(freq)); os_memcpy(&frame_type, &tbuf[sizeof(freq)], sizeof(frame_type)); mgmt = (void *) &tbuf[sizeof(freq) + sizeof(frame_type)]; data_len -= sizeof(freq) + sizeof(frame_type); if (frame_type == IEEE80211_EV_RX_MGMT) { fc = le_to_host16(mgmt->frame_control); stype = WLAN_FC_GET_STYPE(fc); wpa_printf(MSG_DEBUG, "athr: EV_RX_MGMT stype=%u " "freq=%u len=%u", stype, freq, (int) data_len); if (stype == WLAN_FC_STYPE_ACTION) { os_memset(&event, 0, sizeof(event)); event.rx_mgmt.frame = (const u8 *) mgmt; event.rx_mgmt.frame_len = data_len; wpa_supplicant_event(drv->hapd, EVENT_RX_MGMT, &event); continue; } } else { wpa_printf(MSG_DEBUG, "athr: %s unknown type %d", __func__, frame_type); continue; } } } static void atheros_wireless_event_atheros_custom(struct atheros_driver_data *drv, int opcode, char *buf, int len) { switch (opcode) { case IEEE80211_EV_RX_MGMT: wpa_printf(MSG_DEBUG, "WEXT: EV_RX_MGMT"); fetch_pending_big_events(drv); break; default: break; } } static void atheros_wireless_event_wireless(struct atheros_driver_data *drv, char *data, int len) { struct iw_event iwe_buf, *iwe = &iwe_buf; char *pos, *end, *custom, *buf; pos = data; end = data + len; while (pos + IW_EV_LCP_LEN <= end) { /* Event data may be unaligned, so make a local, aligned copy * before processing. */ memcpy(&iwe_buf, pos, IW_EV_LCP_LEN); wpa_printf(MSG_MSGDUMP, "Wireless event: cmd=0x%x len=%d", iwe->cmd, iwe->len); if (iwe->len <= IW_EV_LCP_LEN) return; custom = pos + IW_EV_POINT_LEN; if (drv->we_version > 18 && (iwe->cmd == IWEVMICHAELMICFAILURE || iwe->cmd == IWEVASSOCREQIE || iwe->cmd == IWEVCUSTOM)) { /* WE-19 removed the pointer from struct iw_point */ char *dpos = (char *) &iwe_buf.u.data.length; int dlen = dpos - (char *) &iwe_buf; memcpy(dpos, pos + IW_EV_LCP_LEN, sizeof(struct iw_event) - dlen); } else { memcpy(&iwe_buf, pos, sizeof(struct iw_event)); custom += IW_EV_POINT_OFF; } switch (iwe->cmd) { case IWEVEXPIRED: drv_event_disassoc(drv->hapd, (u8 *) iwe->u.addr.sa_data); break; case IWEVREGISTERED: atheros_new_sta(drv, (u8 *) iwe->u.addr.sa_data); break; case IWEVASSOCREQIE: /* Driver hack.. Use IWEVASSOCREQIE to bypass * IWEVCUSTOM size limitations. Need to handle this * just like IWEVCUSTOM. */ case IWEVCUSTOM: if (custom + iwe->u.data.length > end) return; buf = malloc(iwe->u.data.length + 1); if (buf == NULL) return; /* XXX */ memcpy(buf, custom, iwe->u.data.length); buf[iwe->u.data.length] = '\0'; if (iwe->u.data.flags != 0) { atheros_wireless_event_atheros_custom( drv, (int) iwe->u.data.flags, buf, len); } else { atheros_wireless_event_wireless_custom( drv, buf, buf + iwe->u.data.length); } free(buf); break; } pos += iwe->len; } } static void atheros_wireless_event_rtm_newlink(void *ctx, struct ifinfomsg *ifi, u8 *buf, size_t len) { struct atheros_driver_data *drv = ctx; int attrlen, rta_len; struct rtattr *attr; if (ifi->ifi_index != drv->ifindex) return; attrlen = len; attr = (struct rtattr *) buf; rta_len = RTA_ALIGN(sizeof(struct rtattr)); while (RTA_OK(attr, attrlen)) { if (attr->rta_type == IFLA_WIRELESS) { atheros_wireless_event_wireless( drv, ((char *) attr) + rta_len, attr->rta_len - rta_len); } attr = RTA_NEXT(attr, attrlen); } } static int atheros_get_we_version(struct atheros_driver_data *drv) { struct iw_range *range; struct iwreq iwr; int minlen; size_t buflen; drv->we_version = 0; /* * Use larger buffer than struct iw_range in order to allow the * structure to grow in the future. */ buflen = sizeof(struct iw_range) + 500; range = os_zalloc(buflen); if (range == NULL) return -1; memset(&iwr, 0, sizeof(iwr)); os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); iwr.u.data.pointer = (caddr_t) range; iwr.u.data.length = buflen; minlen = ((char *) &range->enc_capa) - (char *) range + sizeof(range->enc_capa); if (ioctl(drv->ioctl_sock, SIOCGIWRANGE, &iwr) < 0) { perror("ioctl[SIOCGIWRANGE]"); free(range); return -1; } else if (iwr.u.data.length >= minlen && range->we_version_compiled >= 18) { wpa_printf(MSG_DEBUG, "SIOCGIWRANGE: WE(compiled)=%d " "WE(source)=%d enc_capa=0x%x", range->we_version_compiled, range->we_version_source, range->enc_capa); drv->we_version = range->we_version_compiled; } os_free(range); return 0; } static int atheros_wireless_event_init(struct atheros_driver_data *drv) { struct netlink_config *cfg; atheros_get_we_version(drv); cfg = os_zalloc(sizeof(*cfg)); if (cfg == NULL) return -1; cfg->ctx = drv; cfg->newlink_cb = atheros_wireless_event_rtm_newlink; drv->netlink = netlink_init(cfg); if (drv->netlink == NULL) { os_free(cfg); return -1; } return 0; } static int atheros_send_eapol(void *priv, const u8 *addr, const u8 *data, size_t data_len, int encrypt, const u8 *own_addr, u32 flags) { struct atheros_driver_data *drv = priv; unsigned char buf[3000]; unsigned char *bp = buf; struct l2_ethhdr *eth; size_t len; int status; /* * Prepend the Ethernet header. If the caller left us * space at the front we could just insert it but since * we don't know we copy to a local buffer. Given the frequency * and size of frames this probably doesn't matter. */ len = data_len + sizeof(struct l2_ethhdr); if (len > sizeof(buf)) { bp = malloc(len); if (bp == NULL) { printf("EAPOL frame discarded, cannot malloc temp " "buffer of size %lu!\n", (unsigned long) len); return -1; } } eth = (struct l2_ethhdr *) bp; memcpy(eth->h_dest, addr, ETH_ALEN); memcpy(eth->h_source, own_addr, ETH_ALEN); eth->h_proto = host_to_be16(ETH_P_EAPOL); memcpy(eth+1, data, data_len); wpa_hexdump(MSG_MSGDUMP, "TX EAPOL", bp, len); status = l2_packet_send(drv->sock_xmit, addr, ETH_P_EAPOL, bp, len); if (bp != buf) free(bp); return status; } static void handle_read(void *ctx, const u8 *src_addr, const u8 *buf, size_t len) { struct atheros_driver_data *drv = ctx; drv_event_eapol_rx(drv->hapd, src_addr, buf + sizeof(struct l2_ethhdr), len - sizeof(struct l2_ethhdr)); } static void * atheros_init(struct hostapd_data *hapd, struct wpa_init_params *params) { struct atheros_driver_data *drv; struct ifreq ifr; struct iwreq iwr; char brname[IFNAMSIZ]; drv = os_zalloc(sizeof(struct atheros_driver_data)); if (drv == NULL) { printf("Could not allocate memory for atheros driver data\n"); return NULL; } drv->hapd = hapd; drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0); if (drv->ioctl_sock < 0) { perror("socket[PF_INET,SOCK_DGRAM]"); goto bad; } memcpy(drv->iface, params->ifname, sizeof(drv->iface)); memset(&ifr, 0, sizeof(ifr)); os_strlcpy(ifr.ifr_name, drv->iface, sizeof(ifr.ifr_name)); if (ioctl(drv->ioctl_sock, SIOCGIFINDEX, &ifr) != 0) { perror("ioctl(SIOCGIFINDEX)"); goto bad; } drv->ifindex = ifr.ifr_ifindex; drv->sock_xmit = l2_packet_init(drv->iface, NULL, ETH_P_EAPOL, handle_read, drv, 1); if (drv->sock_xmit == NULL) goto bad; if (l2_packet_get_own_addr(drv->sock_xmit, params->own_addr)) goto bad; os_memcpy(drv->own_addr, params->own_addr, ETH_ALEN); if (params->bridge[0]) { wpa_printf(MSG_DEBUG, "Configure bridge %s for EAPOL traffic.", params->bridge[0]); drv->sock_recv = l2_packet_init(params->bridge[0], NULL, ETH_P_EAPOL, handle_read, drv, 1); if (drv->sock_recv == NULL) goto bad; } else if (linux_br_get(brname, drv->iface) == 0) { wpa_printf(MSG_DEBUG, "Interface in bridge %s; configure for " "EAPOL receive", brname); drv->sock_recv = l2_packet_init(brname, NULL, ETH_P_EAPOL, handle_read, drv, 1); if (drv->sock_recv == NULL) goto bad; } else drv->sock_recv = drv->sock_xmit; memset(&iwr, 0, sizeof(iwr)); os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); iwr.u.mode = IW_MODE_MASTER; if (ioctl(drv->ioctl_sock, SIOCSIWMODE, &iwr) < 0) { perror("ioctl[SIOCSIWMODE]"); printf("Could not set interface to master mode!\n"); goto bad; } /* mark down during setup */ linux_set_iface_flags(drv->ioctl_sock, drv->iface, 0); atheros_set_privacy(drv, 0); /* default to no privacy */ if (atheros_receive_pkt(drv)) goto bad; if (atheros_wireless_event_init(drv)) goto bad; return drv; bad: atheros_reset_appfilter(drv); if (drv->sock_raw) l2_packet_deinit(drv->sock_raw); if (drv->sock_recv != NULL && drv->sock_recv != drv->sock_xmit) l2_packet_deinit(drv->sock_recv); if (drv->sock_xmit != NULL) l2_packet_deinit(drv->sock_xmit); if (drv->ioctl_sock >= 0) close(drv->ioctl_sock); if (drv != NULL) free(drv); return NULL; } static void atheros_deinit(void *priv) { struct atheros_driver_data *drv = priv; atheros_reset_appfilter(drv); netlink_deinit(drv->netlink); (void) linux_set_iface_flags(drv->ioctl_sock, drv->iface, 0); if (drv->ioctl_sock >= 0) close(drv->ioctl_sock); if (drv->sock_recv != NULL && drv->sock_recv != drv->sock_xmit) l2_packet_deinit(drv->sock_recv); if (drv->sock_xmit != NULL) l2_packet_deinit(drv->sock_xmit); if (drv->sock_raw) l2_packet_deinit(drv->sock_raw); wpabuf_free(drv->wpa_ie); wpabuf_free(drv->wps_beacon_ie); wpabuf_free(drv->wps_probe_resp_ie); free(drv); } static int atheros_set_ssid(void *priv, const u8 *buf, int len) { struct atheros_driver_data *drv = priv; struct iwreq iwr; memset(&iwr, 0, sizeof(iwr)); os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); iwr.u.essid.flags = 1; /* SSID active */ iwr.u.essid.pointer = (caddr_t) buf; iwr.u.essid.length = len + 1; if (ioctl(drv->ioctl_sock, SIOCSIWESSID, &iwr) < 0) { perror("ioctl[SIOCSIWESSID]"); printf("len=%d\n", len); return -1; } return 0; } static int atheros_get_ssid(void *priv, u8 *buf, int len) { struct atheros_driver_data *drv = priv; struct iwreq iwr; int ret = 0; memset(&iwr, 0, sizeof(iwr)); os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); iwr.u.essid.pointer = (caddr_t) buf; iwr.u.essid.length = (len > IW_ESSID_MAX_SIZE) ? IW_ESSID_MAX_SIZE : len; if (ioctl(drv->ioctl_sock, SIOCGIWESSID, &iwr) < 0) { perror("ioctl[SIOCGIWESSID]"); ret = -1; } else ret = iwr.u.essid.length; return ret; } static int atheros_set_countermeasures(void *priv, int enabled) { struct atheros_driver_data *drv = priv; wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled); return set80211param(drv, IEEE80211_PARAM_COUNTERMEASURES, enabled); } static int atheros_commit(void *priv) { struct atheros_driver_data *drv = priv; return linux_set_iface_flags(drv->ioctl_sock, drv->iface, 1); } static int atheros_set_authmode(void *priv, int auth_algs) { int authmode; if ((auth_algs & WPA_AUTH_ALG_OPEN) && (auth_algs & WPA_AUTH_ALG_SHARED)) authmode = IEEE80211_AUTH_AUTO; else if (auth_algs & WPA_AUTH_ALG_OPEN) authmode = IEEE80211_AUTH_OPEN; else if (auth_algs & WPA_AUTH_ALG_SHARED) authmode = IEEE80211_AUTH_SHARED; else return -1; return set80211param(priv, IEEE80211_PARAM_AUTHMODE, authmode); } static int atheros_set_ap(void *priv, struct wpa_driver_ap_params *params) { /* * TODO: Use this to replace set_authmode, set_privacy, set_ieee8021x, * set_generic_elem, and hapd_set_ssid. */ wpa_printf(MSG_DEBUG, "atheros: set_ap - pairwise_ciphers=0x%x " "group_cipher=0x%x key_mgmt_suites=0x%x auth_algs=0x%x " "wpa_version=0x%x privacy=%d interworking=%d", params->pairwise_ciphers, params->group_cipher, params->key_mgmt_suites, params->auth_algs, params->wpa_version, params->privacy, params->interworking); wpa_hexdump_ascii(MSG_DEBUG, "atheros: SSID", params->ssid, params->ssid_len); if (params->hessid) wpa_printf(MSG_DEBUG, "atheros: HESSID " MACSTR, MAC2STR(params->hessid)); wpa_hexdump_buf(MSG_DEBUG, "atheros: beacon_ies", params->beacon_ies); wpa_hexdump_buf(MSG_DEBUG, "atheros: proberesp_ies", params->proberesp_ies); wpa_hexdump_buf(MSG_DEBUG, "atheros: assocresp_ies", params->assocresp_ies); return 0; } #ifdef CONFIG_IEEE80211R static int atheros_send_mgmt(void *priv, const u8 *frm, size_t data_len, int noack) { struct atheros_driver_data *drv = priv; u8 buf[1510]; const struct ieee80211_mgmt *mgmt; struct ieee80211req_mgmtbuf *mgmt_frm; mgmt = (const struct ieee80211_mgmt *) frm; wpa_printf(MSG_DEBUG, "%s frmlen = %lu " MACSTR, __func__, (unsigned long) data_len, MAC2STR(mgmt->da)); mgmt_frm = (struct ieee80211req_mgmtbuf *) buf; memcpy(mgmt_frm->macaddr, (u8 *)mgmt->da, IEEE80211_ADDR_LEN); mgmt_frm->buflen = data_len; if (&mgmt_frm->buf[0] + data_len > buf + sizeof(buf)) { wpa_printf(MSG_INFO, "atheros: Too long frame for " "atheros_send_mgmt (%u)", (unsigned int) data_len); return -1; } os_memcpy(&mgmt_frm->buf[0], frm, data_len); return set80211priv(drv, IEEE80211_IOCTL_SEND_MGMT, mgmt_frm, sizeof(struct ieee80211req_mgmtbuf) + data_len); } static int atheros_add_tspec(void *priv, const u8 *addr, u8 *tspec_ie, size_t tspec_ielen) { struct atheros_driver_data *drv = priv; int retv; struct ieee80211req_res req; struct ieee80211req_res_addts *addts = &req.u.addts; wpa_printf(MSG_DEBUG, "%s", __func__); req.type = IEEE80211_RESREQ_ADDTS; os_memcpy(&req.macaddr[0], addr, IEEE80211_ADDR_LEN); os_memcpy(addts->tspecie, tspec_ie, tspec_ielen); retv = set80211priv(drv, IEEE80211_IOCTL_RES_REQ, &req, sizeof(struct ieee80211req_res)); if (retv < 0) { wpa_printf(MSG_DEBUG, "%s IEEE80211_IOCTL_RES_REQ FAILED " "retv = %d", __func__, retv); return -1; } os_memcpy(tspec_ie, addts->tspecie, tspec_ielen); return addts->status; } static int atheros_add_sta_node(void *priv, const u8 *addr, u16 auth_alg) { struct atheros_driver_data *drv = priv; struct ieee80211req_res req; struct ieee80211req_res_addnode *addnode = &req.u.addnode; wpa_printf(MSG_DEBUG, "%s", __func__); req.type = IEEE80211_RESREQ_ADDNODE; os_memcpy(&req.macaddr[0], addr, IEEE80211_ADDR_LEN); addnode->auth_alg = auth_alg; return set80211priv(drv, IEEE80211_IOCTL_RES_REQ, &req, sizeof(struct ieee80211req_res)); } #endif /* CONFIG_IEEE80211R */ /* Use only to set a big param, get will not work. */ static int set80211big(struct atheros_driver_data *drv, int op, const void *data, int len) { struct iwreq iwr; os_memset(&iwr, 0, sizeof(iwr)); os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); iwr.u.data.pointer = (void *) data; iwr.u.data.length = len; iwr.u.data.flags = op; wpa_printf(MSG_DEBUG, "%s: op=0x%x=%d (%s) len=0x%x", __func__, op, op, athr_get_param_name(op), len); if (ioctl(drv->ioctl_sock, IEEE80211_IOCTL_P2P_BIG_PARAM, &iwr) < 0) { wpa_printf(MSG_DEBUG, "%s: op=0x%x (%s) subop=0x%x=%d " "value=0x%x,0x%x failed: %d (%s)", __func__, op, athr_get_ioctl_name(op), iwr.u.mode, iwr.u.mode, iwr.u.data.length, iwr.u.data.flags, errno, strerror(errno)); return -1; } return 0; } static int atheros_send_action(void *priv, unsigned int freq, unsigned int wait, const u8 *dst, const u8 *src, const u8 *bssid, const u8 *data, size_t data_len, int no_cck) { struct atheros_driver_data *drv = priv; struct ieee80211_p2p_send_action *act; int res; act = os_zalloc(sizeof(*act) + data_len); if (act == NULL) return -1; act->freq = freq; os_memcpy(act->dst_addr, dst, ETH_ALEN); os_memcpy(act->src_addr, src, ETH_ALEN); os_memcpy(act->bssid, bssid, ETH_ALEN); os_memcpy(act + 1, data, data_len); wpa_printf(MSG_DEBUG, "%s: freq=%d, wait=%u, dst=" MACSTR ", src=" MACSTR ", bssid=" MACSTR, __func__, act->freq, wait, MAC2STR(act->dst_addr), MAC2STR(act->src_addr), MAC2STR(act->bssid)); wpa_hexdump(MSG_MSGDUMP, "athr: act", (u8 *) act, sizeof(*act)); wpa_hexdump(MSG_MSGDUMP, "athr: data", data, data_len); res = set80211big(drv, IEEE80211_IOC_P2P_SEND_ACTION, act, sizeof(*act) + data_len); os_free(act); return res; } #if defined(CONFIG_WNM) && defined(IEEE80211_APPIE_FRAME_WNM) static int athr_wnm_tfs(struct atheros_driver_data *drv, const u8* peer, u8 *ie, u16 *len, enum wnm_oper oper) { #define IEEE80211_APPIE_MAX 1024 /* max appie buffer size */ u8 buf[IEEE80211_APPIE_MAX]; struct ieee80211req_getset_appiebuf *tfs_ie; u16 val; wpa_printf(MSG_DEBUG, "atheros: ifname=%s, WNM TFS IE oper=%d " MACSTR, drv->iface, oper, MAC2STR(peer)); switch (oper) { case WNM_SLEEP_TFS_REQ_IE_SET: if (*len > IEEE80211_APPIE_MAX - sizeof(struct ieee80211req_getset_appiebuf)) { wpa_printf(MSG_DEBUG, "TFS Req IE(s) too large"); return -1; } tfs_ie = (struct ieee80211req_getset_appiebuf *) buf; tfs_ie->app_frmtype = IEEE80211_APPIE_FRAME_WNM; tfs_ie->app_buflen = ETH_ALEN + 2 + 2 + *len; /* Command header for driver */ os_memcpy(&(tfs_ie->app_buf[0]), peer, ETH_ALEN); val = oper; os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN, &val, 2); val = *len; os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN + 2, &val, 2); /* copy the ie */ os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN + 2 + 2, ie, *len); if (set80211priv(drv, IEEE80211_IOCTL_SET_APPIEBUF, tfs_ie, IEEE80211_APPIE_MAX)) { wpa_printf(MSG_DEBUG, "%s: Failed to set WNM TFS IE: " "%s", __func__, strerror(errno)); return -1; } break; case WNM_SLEEP_TFS_RESP_IE_ADD: tfs_ie = (struct ieee80211req_getset_appiebuf *) buf; tfs_ie->app_frmtype = IEEE80211_APPIE_FRAME_WNM; tfs_ie->app_buflen = IEEE80211_APPIE_MAX - sizeof(struct ieee80211req_getset_appiebuf); /* Command header for driver */ os_memcpy(&(tfs_ie->app_buf[0]), peer, ETH_ALEN); val = oper; os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN, &val, 2); val = 0; os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN + 2, &val, 2); if (set80211priv(drv, IEEE80211_IOCTL_GET_APPIEBUF, tfs_ie, IEEE80211_APPIE_MAX)) { wpa_printf(MSG_DEBUG, "%s: Failed to get WNM TFS IE: " "%s", __func__, strerror(errno)); return -1; } *len = tfs_ie->app_buflen; os_memcpy(ie, &(tfs_ie->app_buf[0]), *len); wpa_printf(MSG_DEBUG, "atheros: %c len=%d", tfs_ie->app_buf[0], *len); break; case WNM_SLEEP_TFS_RESP_IE_NONE: *len = 0; break; case WNM_SLEEP_TFS_IE_DEL: tfs_ie = (struct ieee80211req_getset_appiebuf *) buf; tfs_ie->app_frmtype = IEEE80211_APPIE_FRAME_WNM; tfs_ie->app_buflen = IEEE80211_APPIE_MAX - sizeof(struct ieee80211req_getset_appiebuf); /* Command header for driver */ os_memcpy(&(tfs_ie->app_buf[0]), peer, ETH_ALEN); val = oper; os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN, &val, 2); val = 0; os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN + 2, &val, 2); if (set80211priv(drv, IEEE80211_IOCTL_SET_APPIEBUF, tfs_ie, IEEE80211_APPIE_MAX)) { wpa_printf(MSG_DEBUG, "%s: Failed to set WNM TFS IE: " "%s", __func__, strerror(errno)); return -1; } break; default: wpa_printf(MSG_DEBUG, "Unsupported TFS oper %d", oper); break; } return 0; } static int atheros_wnm_sleep(struct atheros_driver_data *drv, const u8 *peer, enum wnm_oper oper) { u8 *data, *pos; size_t dlen; int ret; u16 val; wpa_printf(MSG_DEBUG, "atheros: WNM-Sleep Oper %d, " MACSTR, oper, MAC2STR(peer)); dlen = ETH_ALEN + 2 + 2; data = os_malloc(dlen); if (data == NULL) return -1; /* Command header for driver */ pos = data; os_memcpy(pos, peer, ETH_ALEN); pos += ETH_ALEN; val = oper; os_memcpy(pos, &val, 2); pos += 2; val = 0; os_memcpy(pos, &val, 2); ret = atheros_set_wps_ie(drv, data, dlen, IEEE80211_APPIE_FRAME_WNM); os_free(data); return ret; } static int atheros_wnm_oper(void *priv, enum wnm_oper oper, const u8 *peer, u8 *buf, u16 *buf_len) { struct atheros_driver_data *drv = priv; switch (oper) { case WNM_SLEEP_ENTER_CONFIRM: case WNM_SLEEP_ENTER_FAIL: case WNM_SLEEP_EXIT_CONFIRM: case WNM_SLEEP_EXIT_FAIL: return atheros_wnm_sleep(drv, peer, oper); case WNM_SLEEP_TFS_REQ_IE_SET: case WNM_SLEEP_TFS_RESP_IE_ADD: case WNM_SLEEP_TFS_RESP_IE_NONE: case WNM_SLEEP_TFS_IE_DEL: return athr_wnm_tfs(drv, peer, buf, buf_len, oper); default: wpa_printf(MSG_DEBUG, "atheros: Unsupported WNM operation %d", oper); return -1; } } #endif /* CONFIG_WNM && IEEE80211_APPIE_FRAME_WNM */ const struct wpa_driver_ops wpa_driver_atheros_ops = { .name = "atheros", .hapd_init = atheros_init, .hapd_deinit = atheros_deinit, .set_ieee8021x = atheros_set_ieee8021x, .set_privacy = atheros_set_privacy, .set_key = atheros_set_key, .get_seqnum = atheros_get_seqnum, .flush = atheros_flush, .set_generic_elem = atheros_set_opt_ie, .sta_set_flags = atheros_sta_set_flags, .read_sta_data = atheros_read_sta_driver_data, .hapd_send_eapol = atheros_send_eapol, .sta_disassoc = atheros_sta_disassoc, .sta_deauth = atheros_sta_deauth, .hapd_set_ssid = atheros_set_ssid, .hapd_get_ssid = atheros_get_ssid, .set_countermeasures = atheros_set_countermeasures, .sta_clear_stats = atheros_sta_clear_stats, .commit = atheros_commit, .set_ap_wps_ie = atheros_set_ap_wps_ie, .set_authmode = atheros_set_authmode, .set_ap = atheros_set_ap, #ifdef CONFIG_IEEE80211R .sta_assoc = atheros_sta_assoc, .sta_auth = atheros_sta_auth, .send_mlme = atheros_send_mgmt, .add_tspec = atheros_add_tspec, .add_sta_node = atheros_add_sta_node, #endif /* CONFIG_IEEE80211R */ .send_action = atheros_send_action, #if defined(CONFIG_WNM) && defined(IEEE80211_APPIE_FRAME_WNM) .wnm_oper = atheros_wnm_oper, #endif /* CONFIG_WNM && IEEE80211_APPIE_FRAME_WNM */ .set_qos_map = atheros_set_qos_map, }; wpa-2.1/src/drivers/driver_bsd.c000066400000000000000000001233151227414666700167230ustar00rootroot00000000000000/* * WPA Supplicant - driver interaction with BSD net80211 layer * Copyright (c) 2004, Sam Leffler * Copyright (c) 2004, 2Wire, Inc * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "includes.h" #include #include #include "common.h" #include "driver.h" #include "eloop.h" #include "common/ieee802_11_defs.h" #include "common/wpa_common.h" #include #include #ifdef __NetBSD__ #include #else #include #endif #include #ifdef __DragonFly__ #include #include #else /* __DragonFly__ */ #ifdef __GLIBC__ #include #endif /* __GLIBC__ */ #include #include #include #endif /* __DragonFly__ || __GLIBC__ */ #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) #include #endif #if __NetBSD__ #include #endif #include "l2_packet/l2_packet.h" struct bsd_driver_data { struct hostapd_data *hapd; /* back pointer */ int sock; /* open socket for 802.11 ioctls */ struct l2_packet_data *sock_xmit;/* raw packet xmit socket */ int route; /* routing socket for events */ char ifname[IFNAMSIZ+1]; /* interface name */ unsigned int ifindex; /* interface index */ void *ctx; struct wpa_driver_capa capa; /* driver capability */ int is_ap; /* Access point mode */ int prev_roaming; /* roaming state to restore on deinit */ int prev_privacy; /* privacy state to restore on deinit */ int prev_wpa; /* wpa state to restore on deinit */ enum ieee80211_opmode opmode; /* operation mode */ char *event_buf; size_t event_buf_len; }; /* Generic functions for hostapd and wpa_supplicant */ static int bsd_set80211(void *priv, int op, int val, const void *arg, int arg_len) { struct bsd_driver_data *drv = priv; struct ieee80211req ireq; os_memset(&ireq, 0, sizeof(ireq)); os_strlcpy(ireq.i_name, drv->ifname, sizeof(ireq.i_name)); ireq.i_type = op; ireq.i_val = val; ireq.i_data = (void *) arg; ireq.i_len = arg_len; if (ioctl(drv->sock, SIOCS80211, &ireq) < 0) { wpa_printf(MSG_ERROR, "ioctl[SIOCS80211, op=%u, val=%u, " "arg_len=%u]: %s", op, val, arg_len, strerror(errno)); return -1; } return 0; } static int bsd_get80211(void *priv, struct ieee80211req *ireq, int op, void *arg, int arg_len) { struct bsd_driver_data *drv = priv; os_memset(ireq, 0, sizeof(*ireq)); os_strlcpy(ireq->i_name, drv->ifname, sizeof(ireq->i_name)); ireq->i_type = op; ireq->i_len = arg_len; ireq->i_data = arg; if (ioctl(drv->sock, SIOCG80211, ireq) < 0) { wpa_printf(MSG_ERROR, "ioctl[SIOCS80211, op=%u, " "arg_len=%u]: %s", op, arg_len, strerror(errno)); return -1; } return 0; } static int get80211var(struct bsd_driver_data *drv, int op, void *arg, int arg_len) { struct ieee80211req ireq; if (bsd_get80211(drv, &ireq, op, arg, arg_len) < 0) return -1; return ireq.i_len; } static int set80211var(struct bsd_driver_data *drv, int op, const void *arg, int arg_len) { return bsd_set80211(drv, op, 0, arg, arg_len); } static int set80211param(struct bsd_driver_data *drv, int op, int arg) { return bsd_set80211(drv, op, arg, NULL, 0); } static int bsd_get_ssid(void *priv, u8 *ssid, int len) { struct bsd_driver_data *drv = priv; #ifdef SIOCG80211NWID struct ieee80211_nwid nwid; struct ifreq ifr; os_memset(&ifr, 0, sizeof(ifr)); os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name)); ifr.ifr_data = (void *)&nwid; if (ioctl(drv->sock, SIOCG80211NWID, &ifr) < 0 || nwid.i_len > IEEE80211_NWID_LEN) return -1; os_memcpy(ssid, nwid.i_nwid, nwid.i_len); return nwid.i_len; #else return get80211var(drv, IEEE80211_IOC_SSID, ssid, IEEE80211_NWID_LEN); #endif } static int bsd_set_ssid(void *priv, const u8 *ssid, int ssid_len) { struct bsd_driver_data *drv = priv; #ifdef SIOCS80211NWID struct ieee80211_nwid nwid; struct ifreq ifr; os_memcpy(nwid.i_nwid, ssid, ssid_len); nwid.i_len = ssid_len; os_memset(&ifr, 0, sizeof(ifr)); os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name)); ifr.ifr_data = (void *)&nwid; return ioctl(drv->sock, SIOCS80211NWID, &ifr); #else return set80211var(drv, IEEE80211_IOC_SSID, ssid, ssid_len); #endif } static int bsd_get_if_media(void *priv) { struct bsd_driver_data *drv = priv; struct ifmediareq ifmr; os_memset(&ifmr, 0, sizeof(ifmr)); os_strlcpy(ifmr.ifm_name, drv->ifname, sizeof(ifmr.ifm_name)); if (ioctl(drv->sock, SIOCGIFMEDIA, &ifmr) < 0) { wpa_printf(MSG_ERROR, "%s: SIOCGIFMEDIA %s", __func__, strerror(errno)); return -1; } return ifmr.ifm_current; } static int bsd_set_if_media(void *priv, int media) { struct bsd_driver_data *drv = priv; struct ifreq ifr; os_memset(&ifr, 0, sizeof(ifr)); os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name)); ifr.ifr_media = media; if (ioctl(drv->sock, SIOCSIFMEDIA, &ifr) < 0) { wpa_printf(MSG_ERROR, "%s: SIOCSIFMEDIA %s", __func__, strerror(errno)); return -1; } return 0; } static int bsd_set_mediaopt(void *priv, uint32_t mask, uint32_t mode) { int media = bsd_get_if_media(priv); if (media < 0) return -1; media &= ~mask; media |= mode; if (bsd_set_if_media(priv, media) < 0) return -1; return 0; } static int bsd_del_key(void *priv, const u8 *addr, int key_idx) { struct ieee80211req_del_key wk; os_memset(&wk, 0, sizeof(wk)); if (addr == NULL) { wpa_printf(MSG_DEBUG, "%s: key_idx=%d", __func__, key_idx); wk.idk_keyix = key_idx; } else { wpa_printf(MSG_DEBUG, "%s: addr=" MACSTR, __func__, MAC2STR(addr)); os_memcpy(wk.idk_macaddr, addr, IEEE80211_ADDR_LEN); wk.idk_keyix = (u_int8_t) IEEE80211_KEYIX_NONE; /* XXX */ } return set80211var(priv, IEEE80211_IOC_DELKEY, &wk, sizeof(wk)); } static int bsd_send_mlme_param(void *priv, const u8 op, const u16 reason, const u8 *addr) { struct ieee80211req_mlme mlme; os_memset(&mlme, 0, sizeof(mlme)); mlme.im_op = op; mlme.im_reason = reason; os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); return set80211var(priv, IEEE80211_IOC_MLME, &mlme, sizeof(mlme)); } static int bsd_ctrl_iface(void *priv, int enable) { struct bsd_driver_data *drv = priv; struct ifreq ifr; os_memset(&ifr, 0, sizeof(ifr)); os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name)); if (ioctl(drv->sock, SIOCGIFFLAGS, &ifr) < 0) { perror("ioctl[SIOCGIFFLAGS]"); return -1; } if (enable) { if (ifr.ifr_flags & IFF_UP) return 0; ifr.ifr_flags |= IFF_UP; } else { if (!(ifr.ifr_flags & IFF_UP)) return 0; ifr.ifr_flags &= ~IFF_UP; } if (ioctl(drv->sock, SIOCSIFFLAGS, &ifr) < 0) { perror("ioctl[SIOCSIFFLAGS]"); return -1; } return 0; } static int bsd_set_key(const char *ifname, void *priv, enum wpa_alg alg, const unsigned char *addr, int key_idx, int set_tx, const u8 *seq, size_t seq_len, const u8 *key, size_t key_len) { struct ieee80211req_key wk; #ifdef IEEE80211_KEY_NOREPLAY struct bsd_driver_data *drv = priv; #endif /* IEEE80211_KEY_NOREPLAY */ wpa_printf(MSG_DEBUG, "%s: alg=%d addr=%p key_idx=%d set_tx=%d " "seq_len=%zu key_len=%zu", __func__, alg, addr, key_idx, set_tx, seq_len, key_len); if (alg == WPA_ALG_NONE) { #ifndef HOSTAPD if (addr == NULL || is_broadcast_ether_addr(addr)) return bsd_del_key(priv, NULL, key_idx); else #endif /* HOSTAPD */ return bsd_del_key(priv, addr, key_idx); } os_memset(&wk, 0, sizeof(wk)); switch (alg) { case WPA_ALG_WEP: wk.ik_type = IEEE80211_CIPHER_WEP; break; case WPA_ALG_TKIP: wk.ik_type = IEEE80211_CIPHER_TKIP; break; case WPA_ALG_CCMP: wk.ik_type = IEEE80211_CIPHER_AES_CCM; break; default: wpa_printf(MSG_ERROR, "%s: unknown alg=%d", __func__, alg); return -1; } wk.ik_flags = IEEE80211_KEY_RECV; if (set_tx) wk.ik_flags |= IEEE80211_KEY_XMIT; if (addr == NULL) { os_memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN); wk.ik_keyix = key_idx; } else { os_memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN); /* * Deduce whether group/global or unicast key by checking * the address (yech). Note also that we can only mark global * keys default; doing this for a unicast key is an error. */ if (is_broadcast_ether_addr(addr)) { wk.ik_flags |= IEEE80211_KEY_GROUP; wk.ik_keyix = key_idx; } else { wk.ik_keyix = key_idx == 0 ? IEEE80211_KEYIX_NONE : key_idx; } } if (wk.ik_keyix != IEEE80211_KEYIX_NONE && set_tx) wk.ik_flags |= IEEE80211_KEY_DEFAULT; #ifndef HOSTAPD #ifdef IEEE80211_KEY_NOREPLAY /* * Ignore replay failures in IBSS and AHDEMO mode. */ if (drv->opmode == IEEE80211_M_IBSS || drv->opmode == IEEE80211_M_AHDEMO) wk.ik_flags |= IEEE80211_KEY_NOREPLAY; #endif /* IEEE80211_KEY_NOREPLAY */ #endif /* HOSTAPD */ wk.ik_keylen = key_len; if (seq) { #ifdef WORDS_BIGENDIAN /* * wk.ik_keyrsc is in host byte order (big endian), need to * swap it to match with the byte order used in WPA. */ int i; u8 *keyrsc = (u8 *) &wk.ik_keyrsc; for (i = 0; i < seq_len; i++) keyrsc[WPA_KEY_RSC_LEN - i - 1] = seq[i]; #else /* WORDS_BIGENDIAN */ os_memcpy(&wk.ik_keyrsc, seq, seq_len); #endif /* WORDS_BIGENDIAN */ } os_memcpy(wk.ik_keydata, key, key_len); return set80211var(priv, IEEE80211_IOC_WPAKEY, &wk, sizeof(wk)); } static int bsd_configure_wpa(void *priv, struct wpa_bss_params *params) { #ifndef IEEE80211_IOC_APPIE static const char *ciphernames[] = { "WEP", "TKIP", "AES-OCB", "AES-CCM", "CKIP", "NONE" }; int v; switch (params->wpa_group) { case WPA_CIPHER_CCMP: v = IEEE80211_CIPHER_AES_CCM; break; case WPA_CIPHER_TKIP: v = IEEE80211_CIPHER_TKIP; break; case WPA_CIPHER_WEP104: v = IEEE80211_CIPHER_WEP; break; case WPA_CIPHER_WEP40: v = IEEE80211_CIPHER_WEP; break; case WPA_CIPHER_NONE: v = IEEE80211_CIPHER_NONE; break; default: printf("Unknown group key cipher %u\n", params->wpa_group); return -1; } wpa_printf(MSG_DEBUG, "%s: group key cipher=%s (%u)", __func__, ciphernames[v], v); if (set80211param(priv, IEEE80211_IOC_MCASTCIPHER, v)) { printf("Unable to set group key cipher to %u (%s)\n", v, ciphernames[v]); return -1; } if (v == IEEE80211_CIPHER_WEP) { /* key length is done only for specific ciphers */ v = (params->wpa_group == WPA_CIPHER_WEP104 ? 13 : 5); if (set80211param(priv, IEEE80211_IOC_MCASTKEYLEN, v)) { printf("Unable to set group key length to %u\n", v); return -1; } } v = 0; if (params->wpa_pairwise & WPA_CIPHER_CCMP) v |= 1<wpa_pairwise & WPA_CIPHER_TKIP) v |= 1<wpa_pairwise & WPA_CIPHER_NONE) v |= 1<wpa_key_mgmt); if (set80211param(priv, IEEE80211_IOC_KEYMGTALGS, params->wpa_key_mgmt)) { printf("Unable to set key management algorithms to 0x%x\n", params->wpa_key_mgmt); return -1; } v = 0; if (params->rsn_preauth) v |= BIT(0); wpa_printf(MSG_DEBUG, "%s: rsn capabilities=0x%x", __func__, params->rsn_preauth); if (set80211param(priv, IEEE80211_IOC_RSNCAPS, v)) { printf("Unable to set RSN capabilities to 0x%x\n", v); return -1; } #endif /* IEEE80211_IOC_APPIE */ wpa_printf(MSG_DEBUG, "%s: enable WPA= 0x%x", __func__, params->wpa); if (set80211param(priv, IEEE80211_IOC_WPA, params->wpa)) { printf("Unable to set WPA to %u\n", params->wpa); return -1; } return 0; } static int bsd_set_ieee8021x(void *priv, struct wpa_bss_params *params) { wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, params->enabled); if (!params->enabled) { /* XXX restore state */ return set80211param(priv, IEEE80211_IOC_AUTHMODE, IEEE80211_AUTH_AUTO); } if (!params->wpa && !params->ieee802_1x) { wpa_printf(MSG_ERROR, "%s: No 802.1X or WPA enabled", __func__); return -1; } if (params->wpa && bsd_configure_wpa(priv, params) != 0) { wpa_printf(MSG_ERROR, "%s: Failed to configure WPA state", __func__); return -1; } if (set80211param(priv, IEEE80211_IOC_AUTHMODE, (params->wpa ? IEEE80211_AUTH_WPA : IEEE80211_AUTH_8021X))) { wpa_printf(MSG_ERROR, "%s: Failed to enable WPA/802.1X", __func__); return -1; } return bsd_ctrl_iface(priv, 1); } static void bsd_new_sta(void *priv, void *ctx, u8 addr[IEEE80211_ADDR_LEN]) { struct ieee80211req_wpaie ie; int ielen = 0; u8 *iebuf = NULL; /* * Fetch and validate any negotiated WPA/RSN parameters. */ memset(&ie, 0, sizeof(ie)); memcpy(ie.wpa_macaddr, addr, IEEE80211_ADDR_LEN); if (get80211var(priv, IEEE80211_IOC_WPAIE, &ie, sizeof(ie)) < 0) { printf("Failed to get WPA/RSN information element.\n"); goto no_ie; } iebuf = ie.wpa_ie; ielen = ie.wpa_ie[1]; if (ielen == 0) iebuf = NULL; else ielen += 2; no_ie: drv_event_assoc(ctx, addr, iebuf, ielen, 0); } static int bsd_send_eapol(void *priv, const u8 *addr, const u8 *data, size_t data_len, int encrypt, const u8 *own_addr, u32 flags) { struct bsd_driver_data *drv = priv; wpa_hexdump(MSG_MSGDUMP, "TX EAPOL", data, data_len); return l2_packet_send(drv->sock_xmit, addr, ETH_P_EAPOL, data, data_len); } static int bsd_set_freq(void *priv, struct hostapd_freq_params *freq) { struct bsd_driver_data *drv = priv; #ifdef SIOCS80211CHANNEL struct ieee80211chanreq creq; #endif /* SIOCS80211CHANNEL */ u32 mode; int channel = freq->channel; if (channel < 14) { mode = #ifdef CONFIG_IEEE80211N freq->ht_enabled ? IFM_IEEE80211_11NG : #endif /* CONFIG_IEEE80211N */ IFM_IEEE80211_11G; } else if (channel == 14) { mode = IFM_IEEE80211_11B; } else { mode = #ifdef CONFIG_IEEE80211N freq->ht_enabled ? IFM_IEEE80211_11NA : #endif /* CONFIG_IEEE80211N */ IFM_IEEE80211_11A; } if (bsd_set_mediaopt(drv, IFM_MMASK, mode) < 0) { wpa_printf(MSG_ERROR, "%s: failed to set modulation mode", __func__); return -1; } #ifdef SIOCS80211CHANNEL os_memset(&creq, 0, sizeof(creq)); os_strlcpy(creq.i_name, drv->ifname, sizeof(creq.i_name)); creq.i_channel = (u_int16_t)channel; return ioctl(drv->sock, SIOCS80211CHANNEL, &creq); #else /* SIOCS80211CHANNEL */ return set80211param(priv, IEEE80211_IOC_CHANNEL, channel); #endif /* SIOCS80211CHANNEL */ } static int bsd_set_opt_ie(void *priv, const u8 *ie, size_t ie_len) { #ifdef IEEE80211_IOC_APPIE wpa_printf(MSG_DEBUG, "%s: set WPA+RSN ie (len %lu)", __func__, (unsigned long)ie_len); return bsd_set80211(priv, IEEE80211_IOC_APPIE, IEEE80211_APPIE_WPA, ie, ie_len); #endif /* IEEE80211_IOC_APPIE */ return 0; } static size_t rtbuf_len(void) { size_t len; int mib[6] = {CTL_NET, AF_ROUTE, 0, AF_INET, NET_RT_DUMP, 0}; if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) { wpa_printf(MSG_WARNING, "%s failed: %s\n", __func__, strerror(errno)); len = 2048; } return len; } #ifdef HOSTAPD /* * Avoid conflicts with hostapd definitions by undefining couple of defines * from net80211 header files. */ #undef RSN_VERSION #undef WPA_VERSION #undef WPA_OUI_TYPE static int bsd_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, int reason_code); static const char * ether_sprintf(const u8 *addr) { static char buf[sizeof(MACSTR)]; if (addr != NULL) snprintf(buf, sizeof(buf), MACSTR, MAC2STR(addr)); else snprintf(buf, sizeof(buf), MACSTR, 0,0,0,0,0,0); return buf; } static int bsd_set_privacy(void *priv, int enabled) { wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled); return set80211param(priv, IEEE80211_IOC_PRIVACY, enabled); } static int bsd_get_seqnum(const char *ifname, void *priv, const u8 *addr, int idx, u8 *seq) { struct ieee80211req_key wk; wpa_printf(MSG_DEBUG, "%s: addr=%s idx=%d", __func__, ether_sprintf(addr), idx); memset(&wk, 0, sizeof(wk)); if (addr == NULL) memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN); else memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN); wk.ik_keyix = idx; if (get80211var(priv, IEEE80211_IOC_WPAKEY, &wk, sizeof(wk)) < 0) { printf("Failed to get encryption.\n"); return -1; } #ifdef WORDS_BIGENDIAN { /* * wk.ik_keytsc is in host byte order (big endian), need to * swap it to match with the byte order used in WPA. */ int i; u8 tmp[WPA_KEY_RSC_LEN]; memcpy(tmp, &wk.ik_keytsc, sizeof(wk.ik_keytsc)); for (i = 0; i < WPA_KEY_RSC_LEN; i++) { seq[i] = tmp[WPA_KEY_RSC_LEN - i - 1]; } } #else /* WORDS_BIGENDIAN */ memcpy(seq, &wk.ik_keytsc, sizeof(wk.ik_keytsc)); #endif /* WORDS_BIGENDIAN */ return 0; } static int bsd_flush(void *priv) { u8 allsta[IEEE80211_ADDR_LEN]; memset(allsta, 0xff, IEEE80211_ADDR_LEN); return bsd_sta_deauth(priv, NULL, allsta, IEEE80211_REASON_AUTH_LEAVE); } static int bsd_read_sta_driver_data(void *priv, struct hostap_sta_driver_data *data, const u8 *addr) { struct ieee80211req_sta_stats stats; memcpy(stats.is_u.macaddr, addr, IEEE80211_ADDR_LEN); if (get80211var(priv, IEEE80211_IOC_STA_STATS, &stats, sizeof(stats)) > 0) { /* XXX? do packets counts include non-data frames? */ data->rx_packets = stats.is_stats.ns_rx_data; data->rx_bytes = stats.is_stats.ns_rx_bytes; data->tx_packets = stats.is_stats.ns_tx_data; data->tx_bytes = stats.is_stats.ns_tx_bytes; } return 0; } static int bsd_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, int reason_code) { return bsd_send_mlme_param(priv, IEEE80211_MLME_DEAUTH, reason_code, addr); } static int bsd_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr, int reason_code) { return bsd_send_mlme_param(priv, IEEE80211_MLME_DISASSOC, reason_code, addr); } static void bsd_wireless_event_receive(int sock, void *ctx, void *sock_ctx) { struct bsd_driver_data *drv = ctx; struct if_announcemsghdr *ifan; struct rt_msghdr *rtm; struct ieee80211_michael_event *mic; struct ieee80211_join_event *join; struct ieee80211_leave_event *leave; int n; union wpa_event_data data; n = read(sock, drv->event_buf, drv->event_buf_len); if (n < 0) { if (errno != EINTR && errno != EAGAIN) wpa_printf(MSG_ERROR, "%s read() failed: %s\n", __func__, strerror(errno)); return; } rtm = (struct rt_msghdr *) drv->event_buf; if (rtm->rtm_version != RTM_VERSION) { wpa_printf(MSG_DEBUG, "Invalid routing message version=%d", rtm->rtm_version); return; } ifan = (struct if_announcemsghdr *) rtm; switch (rtm->rtm_type) { case RTM_IEEE80211: switch (ifan->ifan_what) { case RTM_IEEE80211_ASSOC: case RTM_IEEE80211_REASSOC: case RTM_IEEE80211_DISASSOC: case RTM_IEEE80211_SCAN: break; case RTM_IEEE80211_LEAVE: leave = (struct ieee80211_leave_event *) &ifan[1]; drv_event_disassoc(drv->hapd, leave->iev_addr); break; case RTM_IEEE80211_JOIN: #ifdef RTM_IEEE80211_REJOIN case RTM_IEEE80211_REJOIN: #endif join = (struct ieee80211_join_event *) &ifan[1]; bsd_new_sta(drv, drv->hapd, join->iev_addr); break; case RTM_IEEE80211_REPLAY: /* ignore */ break; case RTM_IEEE80211_MICHAEL: mic = (struct ieee80211_michael_event *) &ifan[1]; wpa_printf(MSG_DEBUG, "Michael MIC failure wireless event: " "keyix=%u src_addr=" MACSTR, mic->iev_keyix, MAC2STR(mic->iev_src)); os_memset(&data, 0, sizeof(data)); data.michael_mic_failure.unicast = 1; data.michael_mic_failure.src = mic->iev_src; wpa_supplicant_event(drv->hapd, EVENT_MICHAEL_MIC_FAILURE, &data); break; } break; } } static void handle_read(void *ctx, const u8 *src_addr, const u8 *buf, size_t len) { struct bsd_driver_data *drv = ctx; drv_event_eapol_rx(drv->hapd, src_addr, buf, len); } static void * bsd_init(struct hostapd_data *hapd, struct wpa_init_params *params) { struct bsd_driver_data *drv; drv = os_zalloc(sizeof(struct bsd_driver_data)); if (drv == NULL) { wpa_printf(MSG_ERROR, "Could not allocate memory for bsd driver data"); return NULL; } drv->event_buf_len = rtbuf_len(); drv->event_buf = os_malloc(drv->event_buf_len); if (drv->event_buf == NULL) { wpa_printf(MSG_ERROR, "%s: os_malloc() failed", __func__); goto bad; } drv->hapd = hapd; drv->sock = socket(PF_INET, SOCK_DGRAM, 0); if (drv->sock < 0) { perror("socket[PF_INET,SOCK_DGRAM]"); goto bad; } os_strlcpy(drv->ifname, params->ifname, sizeof(drv->ifname)); drv->sock_xmit = l2_packet_init(drv->ifname, NULL, ETH_P_EAPOL, handle_read, drv, 0); if (drv->sock_xmit == NULL) goto bad; if (l2_packet_get_own_addr(drv->sock_xmit, params->own_addr)) goto bad; /* mark down during setup */ if (bsd_ctrl_iface(drv, 0) < 0) goto bad; drv->route = socket(PF_ROUTE, SOCK_RAW, 0); if (drv->route < 0) { perror("socket(PF_ROUTE,SOCK_RAW)"); goto bad; } eloop_register_read_sock(drv->route, bsd_wireless_event_receive, drv, NULL); if (bsd_set_mediaopt(drv, IFM_OMASK, IFM_IEEE80211_HOSTAP) < 0) { wpa_printf(MSG_ERROR, "%s: failed to set operation mode", __func__); goto bad; } return drv; bad: if (drv->sock_xmit != NULL) l2_packet_deinit(drv->sock_xmit); if (drv->sock >= 0) close(drv->sock); os_free(drv->event_buf); if (drv != NULL) os_free(drv); return NULL; } static void bsd_deinit(void *priv) { struct bsd_driver_data *drv = priv; if (drv->route >= 0) { eloop_unregister_read_sock(drv->route); close(drv->route); } bsd_ctrl_iface(drv, 0); if (drv->sock >= 0) close(drv->sock); if (drv->sock_xmit != NULL) l2_packet_deinit(drv->sock_xmit); os_free(drv->event_buf); os_free(drv); } static int bsd_commit(void *priv) { return bsd_ctrl_iface(priv, 1); } static int bsd_set_sta_authorized(void *priv, const u8 *addr, int total_flags, int flags_or, int flags_and) { int authorized = -1; /* For now, only support setting Authorized flag */ if (flags_or & WPA_STA_AUTHORIZED) authorized = 1; if (!(flags_and & WPA_STA_AUTHORIZED)) authorized = 0; if (authorized < 0) return 0; return bsd_send_mlme_param(priv, authorized ? IEEE80211_MLME_AUTHORIZE : IEEE80211_MLME_UNAUTHORIZE, 0, addr); } #else /* HOSTAPD */ static int get80211param(struct bsd_driver_data *drv, int op) { struct ieee80211req ireq; if (bsd_get80211(drv, &ireq, op, NULL, 0) < 0) return -1; return ireq.i_val; } static int wpa_driver_bsd_get_bssid(void *priv, u8 *bssid) { struct bsd_driver_data *drv = priv; #ifdef SIOCG80211BSSID struct ieee80211_bssid bs; os_strlcpy(bs.i_name, drv->ifname, sizeof(bs.i_name)); if (ioctl(drv->sock, SIOCG80211BSSID, &bs) < 0) return -1; os_memcpy(bssid, bs.i_bssid, sizeof(bs.i_bssid)); return 0; #else return get80211var(drv, IEEE80211_IOC_BSSID, bssid, IEEE80211_ADDR_LEN) < 0 ? -1 : 0; #endif } static int wpa_driver_bsd_get_ssid(void *priv, u8 *ssid) { struct bsd_driver_data *drv = priv; return bsd_get_ssid(drv, ssid, 0); } static int wpa_driver_bsd_set_wpa_ie(struct bsd_driver_data *drv, const u8 *wpa_ie, size_t wpa_ie_len) { #ifdef IEEE80211_IOC_APPIE return bsd_set_opt_ie(drv, wpa_ie, wpa_ie_len); #else /* IEEE80211_IOC_APPIE */ return set80211var(drv, IEEE80211_IOC_OPTIE, wpa_ie, wpa_ie_len); #endif /* IEEE80211_IOC_APPIE */ } static int wpa_driver_bsd_set_wpa_internal(void *priv, int wpa, int privacy) { int ret = 0; wpa_printf(MSG_DEBUG, "%s: wpa=%d privacy=%d", __FUNCTION__, wpa, privacy); if (!wpa && wpa_driver_bsd_set_wpa_ie(priv, NULL, 0) < 0) ret = -1; if (set80211param(priv, IEEE80211_IOC_PRIVACY, privacy) < 0) ret = -1; if (set80211param(priv, IEEE80211_IOC_WPA, wpa) < 0) ret = -1; return ret; } static int wpa_driver_bsd_set_wpa(void *priv, int enabled) { wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled); return wpa_driver_bsd_set_wpa_internal(priv, enabled ? 3 : 0, enabled); } static int wpa_driver_bsd_set_countermeasures(void *priv, int enabled) { wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled); return set80211param(priv, IEEE80211_IOC_COUNTERMEASURES, enabled); } static int wpa_driver_bsd_set_drop_unencrypted(void *priv, int enabled) { wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled); return set80211param(priv, IEEE80211_IOC_DROPUNENCRYPTED, enabled); } static int wpa_driver_bsd_deauthenticate(void *priv, const u8 *addr, int reason_code) { return bsd_send_mlme_param(priv, IEEE80211_MLME_DEAUTH, reason_code, addr); } static int wpa_driver_bsd_set_auth_alg(void *priv, int auth_alg) { int authmode; if ((auth_alg & WPA_AUTH_ALG_OPEN) && (auth_alg & WPA_AUTH_ALG_SHARED)) authmode = IEEE80211_AUTH_AUTO; else if (auth_alg & WPA_AUTH_ALG_SHARED) authmode = IEEE80211_AUTH_SHARED; else authmode = IEEE80211_AUTH_OPEN; return set80211param(priv, IEEE80211_IOC_AUTHMODE, authmode); } static void handle_read(void *ctx, const u8 *src_addr, const u8 *buf, size_t len) { struct bsd_driver_data *drv = ctx; drv_event_eapol_rx(drv->ctx, src_addr, buf, len); } static int wpa_driver_bsd_associate(void *priv, struct wpa_driver_associate_params *params) { struct bsd_driver_data *drv = priv; struct ieee80211req_mlme mlme; u32 mode; int privacy; int ret = 0; wpa_printf(MSG_DEBUG, "%s: ssid '%.*s' wpa ie len %u pairwise %u group %u key mgmt %u" , __func__ , (unsigned int) params->ssid_len, params->ssid , (unsigned int) params->wpa_ie_len , params->pairwise_suite , params->group_suite , params->key_mgmt_suite ); switch (params->mode) { case IEEE80211_MODE_INFRA: mode = 0 /* STA */; break; case IEEE80211_MODE_IBSS: mode = IFM_IEEE80211_IBSS; break; case IEEE80211_MODE_AP: mode = IFM_IEEE80211_HOSTAP; break; default: wpa_printf(MSG_ERROR, "%s: unknown operation mode", __func__); return -1; } if (bsd_set_mediaopt(drv, IFM_OMASK, mode) < 0) { wpa_printf(MSG_ERROR, "%s: failed to set operation mode", __func__); return -1; } if (params->mode == IEEE80211_MODE_AP) { drv->sock_xmit = l2_packet_init(drv->ifname, NULL, ETH_P_EAPOL, handle_read, drv, 0); if (drv->sock_xmit == NULL) return -1; drv->is_ap = 1; return 0; } if (wpa_driver_bsd_set_drop_unencrypted(drv, params->drop_unencrypted) < 0) ret = -1; if (wpa_driver_bsd_set_auth_alg(drv, params->auth_alg) < 0) ret = -1; /* XXX error handling is wrong but unclear what to do... */ if (wpa_driver_bsd_set_wpa_ie(drv, params->wpa_ie, params->wpa_ie_len) < 0) return -1; privacy = !(params->pairwise_suite == WPA_CIPHER_NONE && params->group_suite == WPA_CIPHER_NONE && params->key_mgmt_suite == WPA_KEY_MGMT_NONE && params->wpa_ie_len == 0); wpa_printf(MSG_DEBUG, "%s: set PRIVACY %u", __func__, privacy); if (set80211param(drv, IEEE80211_IOC_PRIVACY, privacy) < 0) return -1; if (params->wpa_ie_len && set80211param(drv, IEEE80211_IOC_WPA, params->wpa_ie[0] == WLAN_EID_RSN ? 2 : 1) < 0) return -1; os_memset(&mlme, 0, sizeof(mlme)); mlme.im_op = IEEE80211_MLME_ASSOC; if (params->ssid != NULL) os_memcpy(mlme.im_ssid, params->ssid, params->ssid_len); mlme.im_ssid_len = params->ssid_len; if (params->bssid != NULL) os_memcpy(mlme.im_macaddr, params->bssid, IEEE80211_ADDR_LEN); if (set80211var(drv, IEEE80211_IOC_MLME, &mlme, sizeof(mlme)) < 0) return -1; return ret; } static int wpa_driver_bsd_scan(void *priv, struct wpa_driver_scan_params *params) { struct bsd_driver_data *drv = priv; #ifdef IEEE80211_IOC_SCAN_MAX_SSID struct ieee80211_scan_req sr; int i; #endif /* IEEE80211_IOC_SCAN_MAX_SSID */ if (bsd_set_mediaopt(drv, IFM_OMASK, 0 /* STA */) < 0) { wpa_printf(MSG_ERROR, "%s: failed to set operation mode", __func__); return -1; } if (set80211param(drv, IEEE80211_IOC_ROAMING, IEEE80211_ROAMING_MANUAL) < 0) { wpa_printf(MSG_ERROR, "%s: failed to set " "wpa_supplicant-based roaming: %s", __func__, strerror(errno)); return -1; } if (wpa_driver_bsd_set_wpa(drv, 1) < 0) { wpa_printf(MSG_ERROR, "%s: failed to set wpa: %s", __func__, strerror(errno)); return -1; } /* NB: interface must be marked UP to do a scan */ if (bsd_ctrl_iface(drv, 1) < 0) return -1; #ifdef IEEE80211_IOC_SCAN_MAX_SSID os_memset(&sr, 0, sizeof(sr)); sr.sr_flags = IEEE80211_IOC_SCAN_ACTIVE | IEEE80211_IOC_SCAN_ONCE | IEEE80211_IOC_SCAN_NOJOIN; sr.sr_duration = IEEE80211_IOC_SCAN_FOREVER; if (params->num_ssids > 0) { sr.sr_nssid = params->num_ssids; #if 0 /* Boundary check is done by upper layer */ if (sr.sr_nssid > IEEE80211_IOC_SCAN_MAX_SSID) sr.sr_nssid = IEEE80211_IOC_SCAN_MAX_SSID; #endif /* NB: check scan cache first */ sr.sr_flags |= IEEE80211_IOC_SCAN_CHECK; } for (i = 0; i < sr.sr_nssid; i++) { sr.sr_ssid[i].len = params->ssids[i].ssid_len; os_memcpy(sr.sr_ssid[i].ssid, params->ssids[i].ssid, sr.sr_ssid[i].len); } /* NB: net80211 delivers a scan complete event so no need to poll */ return set80211var(drv, IEEE80211_IOC_SCAN_REQ, &sr, sizeof(sr)); #else /* IEEE80211_IOC_SCAN_MAX_SSID */ /* set desired ssid before scan */ if (bsd_set_ssid(drv, params->ssids[0].ssid, params->ssids[0].ssid_len) < 0) return -1; /* NB: net80211 delivers a scan complete event so no need to poll */ return set80211param(drv, IEEE80211_IOC_SCAN_REQ, 0); #endif /* IEEE80211_IOC_SCAN_MAX_SSID */ } static void wpa_driver_bsd_event_receive(int sock, void *ctx, void *sock_ctx) { struct bsd_driver_data *drv = sock_ctx; struct if_announcemsghdr *ifan; struct if_msghdr *ifm; struct rt_msghdr *rtm; union wpa_event_data event; struct ieee80211_michael_event *mic; struct ieee80211_leave_event *leave; struct ieee80211_join_event *join; int n; n = read(sock, drv->event_buf, drv->event_buf_len); if (n < 0) { if (errno != EINTR && errno != EAGAIN) wpa_printf(MSG_ERROR, "%s read() failed: %s\n", __func__, strerror(errno)); return; } rtm = (struct rt_msghdr *) drv->event_buf; if (rtm->rtm_version != RTM_VERSION) { wpa_printf(MSG_DEBUG, "Invalid routing message version=%d", rtm->rtm_version); return; } os_memset(&event, 0, sizeof(event)); switch (rtm->rtm_type) { case RTM_IFANNOUNCE: ifan = (struct if_announcemsghdr *) rtm; if (ifan->ifan_index != drv->ifindex) break; os_strlcpy(event.interface_status.ifname, drv->ifname, sizeof(event.interface_status.ifname)); switch (ifan->ifan_what) { case IFAN_DEPARTURE: event.interface_status.ievent = EVENT_INTERFACE_REMOVED; default: return; } wpa_printf(MSG_DEBUG, "RTM_IFANNOUNCE: Interface '%s' %s", event.interface_status.ifname, ifan->ifan_what == IFAN_DEPARTURE ? "removed" : "added"); wpa_supplicant_event(ctx, EVENT_INTERFACE_STATUS, &event); break; case RTM_IEEE80211: ifan = (struct if_announcemsghdr *) rtm; if (ifan->ifan_index != drv->ifindex) break; switch (ifan->ifan_what) { case RTM_IEEE80211_ASSOC: case RTM_IEEE80211_REASSOC: if (drv->is_ap) break; wpa_supplicant_event(ctx, EVENT_ASSOC, NULL); break; case RTM_IEEE80211_DISASSOC: if (drv->is_ap) break; wpa_supplicant_event(ctx, EVENT_DISASSOC, NULL); break; case RTM_IEEE80211_SCAN: if (drv->is_ap) break; wpa_supplicant_event(ctx, EVENT_SCAN_RESULTS, NULL); break; case RTM_IEEE80211_LEAVE: leave = (struct ieee80211_leave_event *) &ifan[1]; drv_event_disassoc(ctx, leave->iev_addr); break; case RTM_IEEE80211_JOIN: #ifdef RTM_IEEE80211_REJOIN case RTM_IEEE80211_REJOIN: #endif join = (struct ieee80211_join_event *) &ifan[1]; bsd_new_sta(drv, ctx, join->iev_addr); break; case RTM_IEEE80211_REPLAY: /* ignore */ break; case RTM_IEEE80211_MICHAEL: mic = (struct ieee80211_michael_event *) &ifan[1]; wpa_printf(MSG_DEBUG, "Michael MIC failure wireless event: " "keyix=%u src_addr=" MACSTR, mic->iev_keyix, MAC2STR(mic->iev_src)); os_memset(&event, 0, sizeof(event)); event.michael_mic_failure.unicast = !IEEE80211_IS_MULTICAST(mic->iev_dst); wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &event); break; } break; case RTM_IFINFO: ifm = (struct if_msghdr *) rtm; if (ifm->ifm_index != drv->ifindex) break; if ((rtm->rtm_flags & RTF_UP) == 0) { os_strlcpy(event.interface_status.ifname, drv->ifname, sizeof(event.interface_status.ifname)); event.interface_status.ievent = EVENT_INTERFACE_REMOVED; wpa_printf(MSG_DEBUG, "RTM_IFINFO: Interface '%s' DOWN", event.interface_status.ifname); wpa_supplicant_event(ctx, EVENT_INTERFACE_STATUS, &event); } break; } } static void wpa_driver_bsd_add_scan_entry(struct wpa_scan_results *res, struct ieee80211req_scan_result *sr) { struct wpa_scan_res *result, **tmp; size_t extra_len; u8 *pos; extra_len = 2 + sr->isr_ssid_len; extra_len += 2 + sr->isr_nrates; extra_len += 3; /* ERP IE */ extra_len += sr->isr_ie_len; result = os_zalloc(sizeof(*result) + extra_len); if (result == NULL) return; os_memcpy(result->bssid, sr->isr_bssid, ETH_ALEN); result->freq = sr->isr_freq; result->beacon_int = sr->isr_intval; result->caps = sr->isr_capinfo; result->qual = sr->isr_rssi; result->noise = sr->isr_noise; /* * the rssi value reported by the kernel is in 0.5dB steps relative to * the reported noise floor. see ieee80211_node.h for details. */ result->level = sr->isr_rssi / 2 + sr->isr_noise; pos = (u8 *)(result + 1); *pos++ = WLAN_EID_SSID; *pos++ = sr->isr_ssid_len; os_memcpy(pos, sr + 1, sr->isr_ssid_len); pos += sr->isr_ssid_len; /* * Deal all rates as supported rate. * Because net80211 doesn't report extended supported rate or not. */ *pos++ = WLAN_EID_SUPP_RATES; *pos++ = sr->isr_nrates; os_memcpy(pos, sr->isr_rates, sr->isr_nrates); pos += sr->isr_nrates; *pos++ = WLAN_EID_ERP_INFO; *pos++ = 1; *pos++ = sr->isr_erp; os_memcpy(pos, (u8 *)(sr + 1) + sr->isr_ssid_len, sr->isr_ie_len); pos += sr->isr_ie_len; result->ie_len = pos - (u8 *)(result + 1); tmp = os_realloc_array(res->res, res->num + 1, sizeof(struct wpa_scan_res *)); if (tmp == NULL) { os_free(result); return; } tmp[res->num++] = result; res->res = tmp; } struct wpa_scan_results * wpa_driver_bsd_get_scan_results2(void *priv) { struct ieee80211req_scan_result *sr; struct wpa_scan_results *res; int len, rest; uint8_t buf[24*1024], *pos; len = get80211var(priv, IEEE80211_IOC_SCAN_RESULTS, buf, 24*1024); if (len < 0) return NULL; res = os_zalloc(sizeof(*res)); if (res == NULL) return NULL; pos = buf; rest = len; while (rest >= sizeof(struct ieee80211req_scan_result)) { sr = (struct ieee80211req_scan_result *)pos; wpa_driver_bsd_add_scan_entry(res, sr); pos += sr->isr_len; rest -= sr->isr_len; } wpa_printf(MSG_DEBUG, "Received %d bytes of scan results (%lu BSSes)", len, (unsigned long)res->num); return res; } static int wpa_driver_bsd_capa(struct bsd_driver_data *drv) { #ifdef IEEE80211_IOC_DEVCAPS /* kernel definitions copied from net80211/ieee80211_var.h */ #define IEEE80211_CIPHER_WEP 0 #define IEEE80211_CIPHER_TKIP 1 #define IEEE80211_CIPHER_AES_CCM 3 #define IEEE80211_CRYPTO_WEP (1<capa.key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA | WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK; if (devcaps.dc_drivercaps & IEEE80211_C_WPA2) drv->capa.key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA2 | WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK; if (devcaps.dc_cryptocaps & IEEE80211_CRYPTO_WEP) drv->capa.enc |= WPA_DRIVER_CAPA_ENC_WEP40 | WPA_DRIVER_CAPA_ENC_WEP104; if (devcaps.dc_cryptocaps & IEEE80211_CRYPTO_TKIP) drv->capa.enc |= WPA_DRIVER_CAPA_ENC_TKIP; if (devcaps.dc_cryptocaps & IEEE80211_CRYPTO_AES_CCM) drv->capa.enc |= WPA_DRIVER_CAPA_ENC_CCMP; if (devcaps.dc_drivercaps & IEEE80211_C_HOSTAP) drv->capa.flags |= WPA_DRIVER_FLAGS_AP; #undef IEEE80211_CIPHER_WEP #undef IEEE80211_CIPHER_TKIP #undef IEEE80211_CIPHER_AES_CCM #undef IEEE80211_CRYPTO_WEP #undef IEEE80211_CRYPTO_TKIP #undef IEEE80211_CRYPTO_AES_CCM #undef IEEE80211_C_HOSTAP #undef IEEE80211_C_WPA1 #undef IEEE80211_C_WPA2 #else /* IEEE80211_IOC_DEVCAPS */ /* For now, assume TKIP, CCMP, WPA, WPA2 are supported */ drv->capa.key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA | WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK | WPA_DRIVER_CAPA_KEY_MGMT_WPA2 | WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK; drv->capa.enc = WPA_DRIVER_CAPA_ENC_WEP40 | WPA_DRIVER_CAPA_ENC_WEP104 | WPA_DRIVER_CAPA_ENC_TKIP | WPA_DRIVER_CAPA_ENC_CCMP; drv->capa.flags |= WPA_DRIVER_FLAGS_AP; #endif /* IEEE80211_IOC_DEVCAPS */ #ifdef IEEE80211_IOC_SCAN_MAX_SSID drv->capa.max_scan_ssids = IEEE80211_IOC_SCAN_MAX_SSID; #else /* IEEE80211_IOC_SCAN_MAX_SSID */ drv->capa.max_scan_ssids = 1; #endif /* IEEE80211_IOC_SCAN_MAX_SSID */ drv->capa.auth = WPA_DRIVER_AUTH_OPEN | WPA_DRIVER_AUTH_SHARED | WPA_DRIVER_AUTH_LEAP; return 0; } static enum ieee80211_opmode get80211opmode(struct bsd_driver_data *drv) { struct ifmediareq ifmr; (void) memset(&ifmr, 0, sizeof(ifmr)); (void) os_strlcpy(ifmr.ifm_name, drv->ifname, sizeof(ifmr.ifm_name)); if (ioctl(drv->sock, SIOCGIFMEDIA, (caddr_t)&ifmr) >= 0) { if (ifmr.ifm_current & IFM_IEEE80211_ADHOC) { if (ifmr.ifm_current & IFM_FLAG0) return IEEE80211_M_AHDEMO; else return IEEE80211_M_IBSS; } if (ifmr.ifm_current & IFM_IEEE80211_HOSTAP) return IEEE80211_M_HOSTAP; if (ifmr.ifm_current & IFM_IEEE80211_MONITOR) return IEEE80211_M_MONITOR; #ifdef IEEE80211_M_MBSS if (ifmr.ifm_current & IFM_IEEE80211_MBSS) return IEEE80211_M_MBSS; #endif /* IEEE80211_M_MBSS */ } return IEEE80211_M_STA; } static void * wpa_driver_bsd_init(void *ctx, const char *ifname) { #define GETPARAM(drv, param, v) \ (((v) = get80211param(drv, param)) != -1) struct bsd_driver_data *drv; drv = os_zalloc(sizeof(*drv)); if (drv == NULL) return NULL; drv->event_buf_len = rtbuf_len(); drv->event_buf = os_malloc(drv->event_buf_len); if (drv->event_buf == NULL) { wpa_printf(MSG_ERROR, "%s: os_malloc() failed", __func__); goto fail1; } /* * NB: We require the interface name be mappable to an index. * This implies we do not support having wpa_supplicant * wait for an interface to appear. This seems ok; that * doesn't belong here; it's really the job of devd. */ drv->ifindex = if_nametoindex(ifname); if (drv->ifindex == 0) { wpa_printf(MSG_DEBUG, "%s: interface %s does not exist", __func__, ifname); goto fail1; } drv->sock = socket(PF_INET, SOCK_DGRAM, 0); if (drv->sock < 0) goto fail1; os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); /* Down interface during setup. */ if (bsd_ctrl_iface(drv, 0) < 0) goto fail; drv->route = socket(PF_ROUTE, SOCK_RAW, 0); if (drv->route < 0) goto fail; eloop_register_read_sock(drv->route, wpa_driver_bsd_event_receive, ctx, drv); drv->ctx = ctx; if (!GETPARAM(drv, IEEE80211_IOC_ROAMING, drv->prev_roaming)) { wpa_printf(MSG_DEBUG, "%s: failed to get roaming state: %s", __func__, strerror(errno)); goto fail; } if (!GETPARAM(drv, IEEE80211_IOC_PRIVACY, drv->prev_privacy)) { wpa_printf(MSG_DEBUG, "%s: failed to get privacy state: %s", __func__, strerror(errno)); goto fail; } if (!GETPARAM(drv, IEEE80211_IOC_WPA, drv->prev_wpa)) { wpa_printf(MSG_DEBUG, "%s: failed to get wpa state: %s", __func__, strerror(errno)); goto fail; } if (wpa_driver_bsd_capa(drv)) goto fail; drv->opmode = get80211opmode(drv); return drv; fail: close(drv->sock); fail1: os_free(drv->event_buf); os_free(drv); return NULL; #undef GETPARAM } static void wpa_driver_bsd_deinit(void *priv) { struct bsd_driver_data *drv = priv; wpa_driver_bsd_set_wpa(drv, 0); eloop_unregister_read_sock(drv->route); /* NB: mark interface down */ bsd_ctrl_iface(drv, 0); wpa_driver_bsd_set_wpa_internal(drv, drv->prev_wpa, drv->prev_privacy); if (set80211param(drv, IEEE80211_IOC_ROAMING, drv->prev_roaming) < 0) wpa_printf(MSG_DEBUG, "%s: failed to restore roaming state", __func__); if (drv->sock_xmit != NULL) l2_packet_deinit(drv->sock_xmit); (void) close(drv->route); /* ioctl socket */ (void) close(drv->sock); /* event socket */ os_free(drv->event_buf); os_free(drv); } static int wpa_driver_bsd_get_capa(void *priv, struct wpa_driver_capa *capa) { struct bsd_driver_data *drv = priv; os_memcpy(capa, &drv->capa, sizeof(*capa)); return 0; } #endif /* HOSTAPD */ const struct wpa_driver_ops wpa_driver_bsd_ops = { .name = "bsd", .desc = "BSD 802.11 support", #ifdef HOSTAPD .hapd_init = bsd_init, .hapd_deinit = bsd_deinit, .set_privacy = bsd_set_privacy, .get_seqnum = bsd_get_seqnum, .flush = bsd_flush, .read_sta_data = bsd_read_sta_driver_data, .sta_disassoc = bsd_sta_disassoc, .sta_deauth = bsd_sta_deauth, .sta_set_flags = bsd_set_sta_authorized, .commit = bsd_commit, #else /* HOSTAPD */ .init = wpa_driver_bsd_init, .deinit = wpa_driver_bsd_deinit, .get_bssid = wpa_driver_bsd_get_bssid, .get_ssid = wpa_driver_bsd_get_ssid, .set_countermeasures = wpa_driver_bsd_set_countermeasures, .scan2 = wpa_driver_bsd_scan, .get_scan_results2 = wpa_driver_bsd_get_scan_results2, .deauthenticate = wpa_driver_bsd_deauthenticate, .associate = wpa_driver_bsd_associate, .get_capa = wpa_driver_bsd_get_capa, #endif /* HOSTAPD */ .set_freq = bsd_set_freq, .set_key = bsd_set_key, .set_ieee8021x = bsd_set_ieee8021x, .hapd_set_ssid = bsd_set_ssid, .hapd_get_ssid = bsd_get_ssid, .hapd_send_eapol = bsd_send_eapol, .set_generic_elem = bsd_set_opt_ie, }; wpa-2.1/src/drivers/driver_common.c000066400000000000000000000032361227414666700174420ustar00rootroot00000000000000/* * Common driver-related functions * Copyright (c) 2003-2011, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "includes.h" #include "utils/common.h" #include "driver.h" void wpa_scan_results_free(struct wpa_scan_results *res) { size_t i; if (res == NULL) return; for (i = 0; i < res->num; i++) os_free(res->res[i]); os_free(res->res); os_free(res); } const char * event_to_string(enum wpa_event_type event) { #define E2S(n) case EVENT_ ## n: return #n switch (event) { E2S(ASSOC); E2S(DISASSOC); E2S(MICHAEL_MIC_FAILURE); E2S(SCAN_RESULTS); E2S(ASSOCINFO); E2S(INTERFACE_STATUS); E2S(PMKID_CANDIDATE); E2S(STKSTART); E2S(TDLS); E2S(FT_RESPONSE); E2S(IBSS_RSN_START); E2S(AUTH); E2S(DEAUTH); E2S(ASSOC_REJECT); E2S(AUTH_TIMED_OUT); E2S(ASSOC_TIMED_OUT); E2S(FT_RRB_RX); E2S(WPS_BUTTON_PUSHED); E2S(TX_STATUS); E2S(RX_FROM_UNKNOWN); E2S(RX_MGMT); E2S(REMAIN_ON_CHANNEL); E2S(CANCEL_REMAIN_ON_CHANNEL); E2S(MLME_RX); E2S(RX_PROBE_REQ); E2S(NEW_STA); E2S(EAPOL_RX); E2S(SIGNAL_CHANGE); E2S(INTERFACE_ENABLED); E2S(INTERFACE_DISABLED); E2S(CHANNEL_LIST_CHANGED); E2S(INTERFACE_UNAVAILABLE); E2S(BEST_CHANNEL); E2S(UNPROT_DEAUTH); E2S(UNPROT_DISASSOC); E2S(STATION_LOW_ACK); E2S(IBSS_PEER_LOST); E2S(DRIVER_GTK_REKEY); E2S(SCHED_SCAN_STOPPED); E2S(DRIVER_CLIENT_POLL_OK); E2S(EAPOL_TX_STATUS); E2S(CH_SWITCH); E2S(WNM); E2S(CONNECT_FAILED_REASON); E2S(DFS_RADAR_DETECTED); E2S(DFS_CAC_FINISHED); E2S(DFS_CAC_ABORTED); E2S(DFS_NOP_FINISHED); E2S(SURVEY); E2S(SCAN_STARTED); E2S(AVOID_FREQUENCIES); } return "UNKNOWN"; #undef E2S } wpa-2.1/src/drivers/driver_hostap.c000066400000000000000000000711621227414666700174530ustar00rootroot00000000000000/* * Driver interaction with Linux Host AP driver * Copyright (c) 2003-2005, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "includes.h" #include #include "linux_wext.h" #include "common.h" #include "driver.h" #include "driver_wext.h" #include "eloop.h" #include "driver_hostap.h" #include #include #include "priv_netlink.h" #include "netlink.h" #include "linux_ioctl.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" /* MTU to be set for the wlan#ap device; this is mainly needed for IEEE 802.1X * frames that might be longer than normal default MTU and they are not * fragmented */ #define HOSTAPD_MTU 2290 static const u8 rfc1042_header[6] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 }; struct hostap_driver_data { struct hostapd_data *hapd; char iface[IFNAMSIZ + 1]; int sock; /* raw packet socket for driver access */ int ioctl_sock; /* socket for ioctl() use */ struct netlink_data *netlink; int we_version; u8 *generic_ie; size_t generic_ie_len; u8 *wps_ie; size_t wps_ie_len; }; static int hostapd_ioctl(void *priv, struct prism2_hostapd_param *param, int len); static int hostap_set_iface_flags(void *priv, int dev_up); static void handle_data(struct hostap_driver_data *drv, u8 *buf, size_t len, u16 stype) { struct ieee80211_hdr *hdr; u16 fc, ethertype; u8 *pos, *sa; size_t left; union wpa_event_data event; if (len < sizeof(struct ieee80211_hdr)) return; hdr = (struct ieee80211_hdr *) buf; fc = le_to_host16(hdr->frame_control); if ((fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) != WLAN_FC_TODS) { printf("Not ToDS data frame (fc=0x%04x)\n", fc); return; } sa = hdr->addr2; os_memset(&event, 0, sizeof(event)); event.rx_from_unknown.bssid = get_hdr_bssid(hdr, len); event.rx_from_unknown.addr = sa; wpa_supplicant_event(drv->hapd, EVENT_RX_FROM_UNKNOWN, &event); pos = (u8 *) (hdr + 1); left = len - sizeof(*hdr); if (left < sizeof(rfc1042_header)) { printf("Too short data frame\n"); return; } if (memcmp(pos, rfc1042_header, sizeof(rfc1042_header)) != 0) { printf("Data frame with no RFC1042 header\n"); return; } pos += sizeof(rfc1042_header); left -= sizeof(rfc1042_header); if (left < 2) { printf("No ethertype in data frame\n"); return; } ethertype = WPA_GET_BE16(pos); pos += 2; left -= 2; switch (ethertype) { case ETH_P_PAE: drv_event_eapol_rx(drv->hapd, sa, pos, left); break; default: printf("Unknown ethertype 0x%04x in data frame\n", ethertype); break; } } static void handle_tx_callback(struct hostap_driver_data *drv, u8 *buf, size_t len, int ok) { struct ieee80211_hdr *hdr; u16 fc; union wpa_event_data event; hdr = (struct ieee80211_hdr *) buf; fc = le_to_host16(hdr->frame_control); os_memset(&event, 0, sizeof(event)); event.tx_status.type = WLAN_FC_GET_TYPE(fc); event.tx_status.stype = WLAN_FC_GET_STYPE(fc); event.tx_status.dst = hdr->addr1; event.tx_status.data = buf; event.tx_status.data_len = len; event.tx_status.ack = ok; wpa_supplicant_event(drv->hapd, EVENT_TX_STATUS, &event); } static void handle_frame(struct hostap_driver_data *drv, u8 *buf, size_t len) { struct ieee80211_hdr *hdr; u16 fc, extra_len, type, stype; size_t data_len = len; int ver; union wpa_event_data event; /* PSPOLL is only 16 bytes, but driver does not (at least yet) pass * these to user space */ if (len < 24) { wpa_printf(MSG_MSGDUMP, "handle_frame: too short (%lu)", (unsigned long) len); return; } hdr = (struct ieee80211_hdr *) buf; fc = le_to_host16(hdr->frame_control); type = WLAN_FC_GET_TYPE(fc); stype = WLAN_FC_GET_STYPE(fc); if (type != WLAN_FC_TYPE_MGMT || stype != WLAN_FC_STYPE_BEACON) { wpa_hexdump(MSG_MSGDUMP, "Received management frame", buf, len); } ver = fc & WLAN_FC_PVER; /* protocol version 3 is reserved for indicating extra data after the * payload, version 2 for indicating ACKed frame (TX callbacks), and * version 1 for indicating failed frame (no ACK, TX callbacks) */ if (ver == 3) { u8 *pos = buf + len - 2; extra_len = WPA_GET_LE16(pos); printf("extra data in frame (elen=%d)\n", extra_len); if ((size_t) extra_len + 2 > len) { printf(" extra data overflow\n"); return; } len -= extra_len + 2; } else if (ver == 1 || ver == 2) { handle_tx_callback(drv, buf, data_len, ver == 2 ? 1 : 0); return; } else if (ver != 0) { printf("unknown protocol version %d\n", ver); return; } switch (type) { case WLAN_FC_TYPE_MGMT: os_memset(&event, 0, sizeof(event)); event.rx_mgmt.frame = buf; event.rx_mgmt.frame_len = data_len; wpa_supplicant_event(drv->hapd, EVENT_RX_MGMT, &event); break; case WLAN_FC_TYPE_CTRL: wpa_printf(MSG_DEBUG, "CTRL"); break; case WLAN_FC_TYPE_DATA: wpa_printf(MSG_DEBUG, "DATA"); handle_data(drv, buf, data_len, stype); break; default: wpa_printf(MSG_DEBUG, "unknown frame type %d", type); break; } } static void handle_read(int sock, void *eloop_ctx, void *sock_ctx) { struct hostap_driver_data *drv = eloop_ctx; int len; unsigned char buf[3000]; len = recv(sock, buf, sizeof(buf), 0); if (len < 0) { perror("recv"); return; } handle_frame(drv, buf, len); } static int hostap_init_sockets(struct hostap_driver_data *drv, u8 *own_addr) { struct ifreq ifr; struct sockaddr_ll addr; drv->sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); if (drv->sock < 0) { perror("socket[PF_PACKET,SOCK_RAW]"); return -1; } if (eloop_register_read_sock(drv->sock, handle_read, drv, NULL)) { printf("Could not register read socket\n"); return -1; } memset(&ifr, 0, sizeof(ifr)); snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%sap", drv->iface); if (ioctl(drv->sock, SIOCGIFINDEX, &ifr) != 0) { perror("ioctl(SIOCGIFINDEX)"); return -1; } if (hostap_set_iface_flags(drv, 1)) { return -1; } memset(&addr, 0, sizeof(addr)); addr.sll_family = AF_PACKET; addr.sll_ifindex = ifr.ifr_ifindex; wpa_printf(MSG_DEBUG, "Opening raw packet socket for ifindex %d", addr.sll_ifindex); if (bind(drv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { perror("bind"); return -1; } return linux_get_ifhwaddr(drv->sock, drv->iface, own_addr); } static int hostap_send_mlme(void *priv, const u8 *msg, size_t len, int noack) { struct hostap_driver_data *drv = priv; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) msg; int res; /* Request TX callback */ hdr->frame_control |= host_to_le16(BIT(1)); res = send(drv->sock, msg, len, 0); hdr->frame_control &= ~host_to_le16(BIT(1)); return res; } static int hostap_send_eapol(void *priv, const u8 *addr, const u8 *data, size_t data_len, int encrypt, const u8 *own_addr, u32 flags) { struct hostap_driver_data *drv = priv; struct ieee80211_hdr *hdr; size_t len; u8 *pos; int res; len = sizeof(*hdr) + sizeof(rfc1042_header) + 2 + data_len; hdr = os_zalloc(len); if (hdr == NULL) { printf("malloc() failed for hostapd_send_data(len=%lu)\n", (unsigned long) len); return -1; } hdr->frame_control = IEEE80211_FC(WLAN_FC_TYPE_DATA, WLAN_FC_STYPE_DATA); hdr->frame_control |= host_to_le16(WLAN_FC_FROMDS); if (encrypt) hdr->frame_control |= host_to_le16(WLAN_FC_ISWEP); memcpy(hdr->IEEE80211_DA_FROMDS, addr, ETH_ALEN); memcpy(hdr->IEEE80211_BSSID_FROMDS, own_addr, ETH_ALEN); memcpy(hdr->IEEE80211_SA_FROMDS, own_addr, ETH_ALEN); pos = (u8 *) (hdr + 1); memcpy(pos, rfc1042_header, sizeof(rfc1042_header)); pos += sizeof(rfc1042_header); *((u16 *) pos) = htons(ETH_P_PAE); pos += 2; memcpy(pos, data, data_len); res = hostap_send_mlme(drv, (u8 *) hdr, len, 0); if (res < 0) { wpa_printf(MSG_ERROR, "hostap_send_eapol - packet len: %lu - " "failed: %d (%s)", (unsigned long) len, errno, strerror(errno)); } free(hdr); return res; } static int hostap_sta_set_flags(void *priv, const u8 *addr, int total_flags, int flags_or, int flags_and) { struct hostap_driver_data *drv = priv; struct prism2_hostapd_param param; if (flags_or & WPA_STA_AUTHORIZED) flags_or = BIT(5); /* WLAN_STA_AUTHORIZED */ if (!(flags_and & WPA_STA_AUTHORIZED)) flags_and = ~BIT(5); else flags_and = ~0; memset(¶m, 0, sizeof(param)); param.cmd = PRISM2_HOSTAPD_SET_FLAGS_STA; memcpy(param.sta_addr, addr, ETH_ALEN); param.u.set_flags_sta.flags_or = flags_or; param.u.set_flags_sta.flags_and = flags_and; return hostapd_ioctl(drv, ¶m, sizeof(param)); } static int hostap_set_iface_flags(void *priv, int dev_up) { struct hostap_driver_data *drv = priv; struct ifreq ifr; char ifname[IFNAMSIZ]; os_snprintf(ifname, IFNAMSIZ, "%sap", drv->iface); if (linux_set_iface_flags(drv->ioctl_sock, ifname, dev_up) < 0) return -1; if (dev_up) { memset(&ifr, 0, sizeof(ifr)); os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); ifr.ifr_mtu = HOSTAPD_MTU; if (ioctl(drv->ioctl_sock, SIOCSIFMTU, &ifr) != 0) { perror("ioctl[SIOCSIFMTU]"); printf("Setting MTU failed - trying to survive with " "current value\n"); } } return 0; } static int hostapd_ioctl(void *priv, struct prism2_hostapd_param *param, int len) { struct hostap_driver_data *drv = priv; struct iwreq iwr; memset(&iwr, 0, sizeof(iwr)); os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); iwr.u.data.pointer = (caddr_t) param; iwr.u.data.length = len; if (ioctl(drv->ioctl_sock, PRISM2_IOCTL_HOSTAPD, &iwr) < 0) { perror("ioctl[PRISM2_IOCTL_HOSTAPD]"); return -1; } return 0; } static int wpa_driver_hostap_set_key(const char *ifname, void *priv, enum wpa_alg alg, const u8 *addr, int key_idx, int set_tx, const u8 *seq, size_t seq_len, const u8 *key, size_t key_len) { struct hostap_driver_data *drv = priv; struct prism2_hostapd_param *param; u8 *buf; size_t blen; int ret = 0; blen = sizeof(*param) + key_len; buf = os_zalloc(blen); if (buf == NULL) return -1; param = (struct prism2_hostapd_param *) buf; param->cmd = PRISM2_SET_ENCRYPTION; if (addr == NULL) memset(param->sta_addr, 0xff, ETH_ALEN); else memcpy(param->sta_addr, addr, ETH_ALEN); switch (alg) { case WPA_ALG_NONE: os_strlcpy((char *) param->u.crypt.alg, "NONE", HOSTAP_CRYPT_ALG_NAME_LEN); break; case WPA_ALG_WEP: os_strlcpy((char *) param->u.crypt.alg, "WEP", HOSTAP_CRYPT_ALG_NAME_LEN); break; case WPA_ALG_TKIP: os_strlcpy((char *) param->u.crypt.alg, "TKIP", HOSTAP_CRYPT_ALG_NAME_LEN); break; case WPA_ALG_CCMP: os_strlcpy((char *) param->u.crypt.alg, "CCMP", HOSTAP_CRYPT_ALG_NAME_LEN); break; default: os_free(buf); return -1; } param->u.crypt.flags = set_tx ? HOSTAP_CRYPT_FLAG_SET_TX_KEY : 0; param->u.crypt.idx = key_idx; param->u.crypt.key_len = key_len; memcpy((u8 *) (param + 1), key, key_len); if (hostapd_ioctl(drv, param, blen)) { printf("Failed to set encryption.\n"); ret = -1; } free(buf); return ret; } static int hostap_get_seqnum(const char *ifname, void *priv, const u8 *addr, int idx, u8 *seq) { struct hostap_driver_data *drv = priv; struct prism2_hostapd_param *param; u8 *buf; size_t blen; int ret = 0; blen = sizeof(*param) + 32; buf = os_zalloc(blen); if (buf == NULL) return -1; param = (struct prism2_hostapd_param *) buf; param->cmd = PRISM2_GET_ENCRYPTION; if (addr == NULL) memset(param->sta_addr, 0xff, ETH_ALEN); else memcpy(param->sta_addr, addr, ETH_ALEN); param->u.crypt.idx = idx; if (hostapd_ioctl(drv, param, blen)) { printf("Failed to get encryption.\n"); ret = -1; } else { memcpy(seq, param->u.crypt.seq, 8); } free(buf); return ret; } static int hostap_ioctl_prism2param(void *priv, int param, int value) { struct hostap_driver_data *drv = priv; struct iwreq iwr; int *i; memset(&iwr, 0, sizeof(iwr)); os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); i = (int *) iwr.u.name; *i++ = param; *i++ = value; if (ioctl(drv->ioctl_sock, PRISM2_IOCTL_PRISM2_PARAM, &iwr) < 0) { perror("ioctl[PRISM2_IOCTL_PRISM2_PARAM]"); return -1; } return 0; } static int hostap_set_ieee8021x(void *priv, struct wpa_bss_params *params) { struct hostap_driver_data *drv = priv; int enabled = params->enabled; /* enable kernel driver support for IEEE 802.1X */ if (hostap_ioctl_prism2param(drv, PRISM2_PARAM_IEEE_802_1X, enabled)) { printf("Could not setup IEEE 802.1X support in kernel driver." "\n"); return -1; } if (!enabled) return 0; /* use host driver implementation of encryption to allow * individual keys and passing plaintext EAPOL frames */ if (hostap_ioctl_prism2param(drv, PRISM2_PARAM_HOST_DECRYPT, 1) || hostap_ioctl_prism2param(drv, PRISM2_PARAM_HOST_ENCRYPT, 1)) { printf("Could not setup host-based encryption in kernel " "driver.\n"); return -1; } return 0; } static int hostap_set_privacy(void *priv, int enabled) { struct hostap_drvier_data *drv = priv; return hostap_ioctl_prism2param(drv, PRISM2_PARAM_PRIVACY_INVOKED, enabled); } static int hostap_set_ssid(void *priv, const u8 *buf, int len) { struct hostap_driver_data *drv = priv; struct iwreq iwr; memset(&iwr, 0, sizeof(iwr)); os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); iwr.u.essid.flags = 1; /* SSID active */ iwr.u.essid.pointer = (caddr_t) buf; iwr.u.essid.length = len + 1; if (ioctl(drv->ioctl_sock, SIOCSIWESSID, &iwr) < 0) { perror("ioctl[SIOCSIWESSID]"); printf("len=%d\n", len); return -1; } return 0; } static int hostap_flush(void *priv) { struct hostap_driver_data *drv = priv; struct prism2_hostapd_param param; memset(¶m, 0, sizeof(param)); param.cmd = PRISM2_HOSTAPD_FLUSH; return hostapd_ioctl(drv, ¶m, sizeof(param)); } static int hostap_read_sta_data(void *priv, struct hostap_sta_driver_data *data, const u8 *addr) { struct hostap_driver_data *drv = priv; char buf[1024], line[128], *pos; FILE *f; unsigned long val; memset(data, 0, sizeof(*data)); snprintf(buf, sizeof(buf), "/proc/net/hostap/%s/" MACSTR, drv->iface, MAC2STR(addr)); f = fopen(buf, "r"); if (!f) return -1; /* Need to read proc file with in one piece, so use large enough * buffer. */ setbuffer(f, buf, sizeof(buf)); while (fgets(line, sizeof(line), f)) { pos = strchr(line, '='); if (!pos) continue; *pos++ = '\0'; val = strtoul(pos, NULL, 10); if (strcmp(line, "rx_packets") == 0) data->rx_packets = val; else if (strcmp(line, "tx_packets") == 0) data->tx_packets = val; else if (strcmp(line, "rx_bytes") == 0) data->rx_bytes = val; else if (strcmp(line, "tx_bytes") == 0) data->tx_bytes = val; } fclose(f); return 0; } static int hostap_sta_add(void *priv, struct hostapd_sta_add_params *params) { struct hostap_driver_data *drv = priv; struct prism2_hostapd_param param; int tx_supp_rates = 0; size_t i; #define WLAN_RATE_1M BIT(0) #define WLAN_RATE_2M BIT(1) #define WLAN_RATE_5M5 BIT(2) #define WLAN_RATE_11M BIT(3) for (i = 0; i < params->supp_rates_len; i++) { if ((params->supp_rates[i] & 0x7f) == 2) tx_supp_rates |= WLAN_RATE_1M; if ((params->supp_rates[i] & 0x7f) == 4) tx_supp_rates |= WLAN_RATE_2M; if ((params->supp_rates[i] & 0x7f) == 11) tx_supp_rates |= WLAN_RATE_5M5; if ((params->supp_rates[i] & 0x7f) == 22) tx_supp_rates |= WLAN_RATE_11M; } memset(¶m, 0, sizeof(param)); param.cmd = PRISM2_HOSTAPD_ADD_STA; memcpy(param.sta_addr, params->addr, ETH_ALEN); param.u.add_sta.aid = params->aid; param.u.add_sta.capability = params->capability; param.u.add_sta.tx_supp_rates = tx_supp_rates; return hostapd_ioctl(drv, ¶m, sizeof(param)); } static int hostap_sta_remove(void *priv, const u8 *addr) { struct hostap_driver_data *drv = priv; struct prism2_hostapd_param param; hostap_sta_set_flags(drv, addr, 0, 0, ~WPA_STA_AUTHORIZED); memset(¶m, 0, sizeof(param)); param.cmd = PRISM2_HOSTAPD_REMOVE_STA; memcpy(param.sta_addr, addr, ETH_ALEN); if (hostapd_ioctl(drv, ¶m, sizeof(param))) { printf("Could not remove station from kernel driver.\n"); return -1; } return 0; } static int hostap_get_inact_sec(void *priv, const u8 *addr) { struct hostap_driver_data *drv = priv; struct prism2_hostapd_param param; memset(¶m, 0, sizeof(param)); param.cmd = PRISM2_HOSTAPD_GET_INFO_STA; memcpy(param.sta_addr, addr, ETH_ALEN); if (hostapd_ioctl(drv, ¶m, sizeof(param))) { return -1; } return param.u.get_info_sta.inactive_sec; } static int hostap_sta_clear_stats(void *priv, const u8 *addr) { struct hostap_driver_data *drv = priv; struct prism2_hostapd_param param; memset(¶m, 0, sizeof(param)); param.cmd = PRISM2_HOSTAPD_STA_CLEAR_STATS; memcpy(param.sta_addr, addr, ETH_ALEN); if (hostapd_ioctl(drv, ¶m, sizeof(param))) { return -1; } return 0; } static int hostapd_ioctl_set_generic_elem(struct hostap_driver_data *drv) { struct prism2_hostapd_param *param; int res; size_t blen, elem_len; elem_len = drv->generic_ie_len + drv->wps_ie_len; blen = PRISM2_HOSTAPD_GENERIC_ELEMENT_HDR_LEN + elem_len; if (blen < sizeof(*param)) blen = sizeof(*param); param = os_zalloc(blen); if (param == NULL) return -1; param->cmd = PRISM2_HOSTAPD_SET_GENERIC_ELEMENT; param->u.generic_elem.len = elem_len; if (drv->generic_ie) { os_memcpy(param->u.generic_elem.data, drv->generic_ie, drv->generic_ie_len); } if (drv->wps_ie) { os_memcpy(¶m->u.generic_elem.data[drv->generic_ie_len], drv->wps_ie, drv->wps_ie_len); } wpa_hexdump(MSG_DEBUG, "hostap: Set generic IE", param->u.generic_elem.data, elem_len); res = hostapd_ioctl(drv, param, blen); os_free(param); return res; } static int hostap_set_generic_elem(void *priv, const u8 *elem, size_t elem_len) { struct hostap_driver_data *drv = priv; os_free(drv->generic_ie); drv->generic_ie = NULL; drv->generic_ie_len = 0; if (elem) { drv->generic_ie = os_malloc(elem_len); if (drv->generic_ie == NULL) return -1; os_memcpy(drv->generic_ie, elem, elem_len); drv->generic_ie_len = elem_len; } return hostapd_ioctl_set_generic_elem(drv); } static int hostap_set_ap_wps_ie(void *priv, const struct wpabuf *beacon, const struct wpabuf *proberesp, const struct wpabuf *assocresp) { struct hostap_driver_data *drv = priv; /* * Host AP driver supports only one set of extra IEs, so we need to * use the Probe Response IEs also for Beacon frames since they include * more information. */ os_free(drv->wps_ie); drv->wps_ie = NULL; drv->wps_ie_len = 0; if (proberesp) { drv->wps_ie = os_malloc(wpabuf_len(proberesp)); if (drv->wps_ie == NULL) return -1; os_memcpy(drv->wps_ie, wpabuf_head(proberesp), wpabuf_len(proberesp)); drv->wps_ie_len = wpabuf_len(proberesp); } return hostapd_ioctl_set_generic_elem(drv); } static void hostapd_wireless_event_wireless_custom(struct hostap_driver_data *drv, char *custom) { wpa_printf(MSG_DEBUG, "Custom wireless event: '%s'", custom); if (strncmp(custom, "MLME-MICHAELMICFAILURE.indication", 33) == 0) { char *pos; u8 addr[ETH_ALEN]; pos = strstr(custom, "addr="); if (pos == NULL) { wpa_printf(MSG_DEBUG, "MLME-MICHAELMICFAILURE.indication " "without sender address ignored"); return; } pos += 5; if (hwaddr_aton(pos, addr) == 0) { union wpa_event_data data; os_memset(&data, 0, sizeof(data)); data.michael_mic_failure.unicast = 1; data.michael_mic_failure.src = addr; wpa_supplicant_event(drv->hapd, EVENT_MICHAEL_MIC_FAILURE, &data); } else { wpa_printf(MSG_DEBUG, "MLME-MICHAELMICFAILURE.indication " "with invalid MAC address"); } } } static void hostapd_wireless_event_wireless(struct hostap_driver_data *drv, char *data, int len) { struct iw_event iwe_buf, *iwe = &iwe_buf; char *pos, *end, *custom, *buf; pos = data; end = data + len; while (pos + IW_EV_LCP_LEN <= end) { /* Event data may be unaligned, so make a local, aligned copy * before processing. */ memcpy(&iwe_buf, pos, IW_EV_LCP_LEN); wpa_printf(MSG_DEBUG, "Wireless event: cmd=0x%x len=%d", iwe->cmd, iwe->len); if (iwe->len <= IW_EV_LCP_LEN) return; custom = pos + IW_EV_POINT_LEN; if (drv->we_version > 18 && (iwe->cmd == IWEVMICHAELMICFAILURE || iwe->cmd == IWEVCUSTOM)) { /* WE-19 removed the pointer from struct iw_point */ char *dpos = (char *) &iwe_buf.u.data.length; int dlen = dpos - (char *) &iwe_buf; memcpy(dpos, pos + IW_EV_LCP_LEN, sizeof(struct iw_event) - dlen); } else { memcpy(&iwe_buf, pos, sizeof(struct iw_event)); custom += IW_EV_POINT_OFF; } switch (iwe->cmd) { case IWEVCUSTOM: if (custom + iwe->u.data.length > end) return; buf = malloc(iwe->u.data.length + 1); if (buf == NULL) return; memcpy(buf, custom, iwe->u.data.length); buf[iwe->u.data.length] = '\0'; hostapd_wireless_event_wireless_custom(drv, buf); free(buf); break; } pos += iwe->len; } } static void hostapd_wireless_event_rtm_newlink(void *ctx, struct ifinfomsg *ifi, u8 *buf, size_t len) { struct hostap_driver_data *drv = ctx; int attrlen, rta_len; struct rtattr *attr; /* TODO: use ifi->ifi_index to filter out wireless events from other * interfaces */ attrlen = len; attr = (struct rtattr *) buf; rta_len = RTA_ALIGN(sizeof(struct rtattr)); while (RTA_OK(attr, attrlen)) { if (attr->rta_type == IFLA_WIRELESS) { hostapd_wireless_event_wireless( drv, ((char *) attr) + rta_len, attr->rta_len - rta_len); } attr = RTA_NEXT(attr, attrlen); } } static int hostap_get_we_version(struct hostap_driver_data *drv) { struct iw_range *range; struct iwreq iwr; int minlen; size_t buflen; drv->we_version = 0; /* * Use larger buffer than struct iw_range in order to allow the * structure to grow in the future. */ buflen = sizeof(struct iw_range) + 500; range = os_zalloc(buflen); if (range == NULL) return -1; memset(&iwr, 0, sizeof(iwr)); os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); iwr.u.data.pointer = (caddr_t) range; iwr.u.data.length = buflen; minlen = ((char *) &range->enc_capa) - (char *) range + sizeof(range->enc_capa); if (ioctl(drv->ioctl_sock, SIOCGIWRANGE, &iwr) < 0) { perror("ioctl[SIOCGIWRANGE]"); free(range); return -1; } else if (iwr.u.data.length >= minlen && range->we_version_compiled >= 18) { wpa_printf(MSG_DEBUG, "SIOCGIWRANGE: WE(compiled)=%d " "WE(source)=%d enc_capa=0x%x", range->we_version_compiled, range->we_version_source, range->enc_capa); drv->we_version = range->we_version_compiled; } free(range); return 0; } static int hostap_wireless_event_init(struct hostap_driver_data *drv) { struct netlink_config *cfg; hostap_get_we_version(drv); cfg = os_zalloc(sizeof(*cfg)); if (cfg == NULL) return -1; cfg->ctx = drv; cfg->newlink_cb = hostapd_wireless_event_rtm_newlink; drv->netlink = netlink_init(cfg); if (drv->netlink == NULL) { os_free(cfg); return -1; } return 0; } static void * hostap_init(struct hostapd_data *hapd, struct wpa_init_params *params) { struct hostap_driver_data *drv; drv = os_zalloc(sizeof(struct hostap_driver_data)); if (drv == NULL) { printf("Could not allocate memory for hostapd driver data\n"); return NULL; } drv->hapd = hapd; drv->ioctl_sock = drv->sock = -1; memcpy(drv->iface, params->ifname, sizeof(drv->iface)); drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0); if (drv->ioctl_sock < 0) { perror("socket[PF_INET,SOCK_DGRAM]"); free(drv); return NULL; } if (hostap_ioctl_prism2param(drv, PRISM2_PARAM_HOSTAPD, 1)) { printf("Could not enable hostapd mode for interface %s\n", drv->iface); close(drv->ioctl_sock); free(drv); return NULL; } if (hostap_init_sockets(drv, params->own_addr) || hostap_wireless_event_init(drv)) { close(drv->ioctl_sock); free(drv); return NULL; } return drv; } static void hostap_driver_deinit(void *priv) { struct hostap_driver_data *drv = priv; netlink_deinit(drv->netlink); (void) hostap_set_iface_flags(drv, 0); (void) hostap_ioctl_prism2param(drv, PRISM2_PARAM_HOSTAPD, 0); (void) hostap_ioctl_prism2param(drv, PRISM2_PARAM_HOSTAPD_STA, 0); if (drv->ioctl_sock >= 0) close(drv->ioctl_sock); if (drv->sock >= 0) close(drv->sock); os_free(drv->generic_ie); os_free(drv->wps_ie); free(drv); } static int hostap_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, int reason) { struct hostap_driver_data *drv = priv; struct ieee80211_mgmt mgmt; if (is_broadcast_ether_addr(addr)) { /* * New Prism2.5/3 STA firmware versions seem to have issues * with this broadcast deauth frame. This gets the firmware in * odd state where nothing works correctly, so let's skip * sending this for the hostap driver. */ return 0; } memset(&mgmt, 0, sizeof(mgmt)); mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_DEAUTH); memcpy(mgmt.da, addr, ETH_ALEN); memcpy(mgmt.sa, own_addr, ETH_ALEN); memcpy(mgmt.bssid, own_addr, ETH_ALEN); mgmt.u.deauth.reason_code = host_to_le16(reason); return hostap_send_mlme(drv, (u8 *) &mgmt, IEEE80211_HDRLEN + sizeof(mgmt.u.deauth), 0); } static int hostap_set_freq(void *priv, struct hostapd_freq_params *freq) { struct hostap_driver_data *drv = priv; struct iwreq iwr; os_memset(&iwr, 0, sizeof(iwr)); os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); iwr.u.freq.m = freq->channel; iwr.u.freq.e = 0; if (ioctl(drv->ioctl_sock, SIOCSIWFREQ, &iwr) < 0) { perror("ioctl[SIOCSIWFREQ]"); return -1; } return 0; } static int hostap_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr, int reason) { struct hostap_driver_data *drv = priv; struct ieee80211_mgmt mgmt; memset(&mgmt, 0, sizeof(mgmt)); mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_DISASSOC); memcpy(mgmt.da, addr, ETH_ALEN); memcpy(mgmt.sa, own_addr, ETH_ALEN); memcpy(mgmt.bssid, own_addr, ETH_ALEN); mgmt.u.disassoc.reason_code = host_to_le16(reason); return hostap_send_mlme(drv, (u8 *) &mgmt, IEEE80211_HDRLEN + sizeof(mgmt.u.disassoc), 0); } static struct hostapd_hw_modes * hostap_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags) { struct hostapd_hw_modes *mode; int i, clen, rlen; const short chan2freq[14] = { 2412, 2417, 2422, 2427, 2432, 2437, 2442, 2447, 2452, 2457, 2462, 2467, 2472, 2484 }; mode = os_zalloc(sizeof(struct hostapd_hw_modes)); if (mode == NULL) return NULL; *num_modes = 1; *flags = 0; mode->mode = HOSTAPD_MODE_IEEE80211B; mode->num_channels = 14; mode->num_rates = 4; clen = mode->num_channels * sizeof(struct hostapd_channel_data); rlen = mode->num_rates * sizeof(int); mode->channels = os_zalloc(clen); mode->rates = os_zalloc(rlen); if (mode->channels == NULL || mode->rates == NULL) { os_free(mode->channels); os_free(mode->rates); os_free(mode); return NULL; } for (i = 0; i < 14; i++) { mode->channels[i].chan = i + 1; mode->channels[i].freq = chan2freq[i]; /* TODO: Get allowed channel list from the driver */ if (i >= 11) mode->channels[i].flag = HOSTAPD_CHAN_DISABLED; } mode->rates[0] = 10; mode->rates[1] = 20; mode->rates[2] = 55; mode->rates[3] = 110; return mode; } static void wpa_driver_hostap_poll_client(void *priv, const u8 *own_addr, const u8 *addr, int qos) { struct ieee80211_hdr hdr; os_memset(&hdr, 0, sizeof(hdr)); /* * WLAN_FC_STYPE_NULLFUNC would be more appropriate, * but it is apparently not retried so TX Exc events * are not received for it. * This is the reason the driver overrides the default * handling. */ hdr.frame_control = IEEE80211_FC(WLAN_FC_TYPE_DATA, WLAN_FC_STYPE_DATA); hdr.frame_control |= host_to_le16(WLAN_FC_FROMDS); os_memcpy(hdr.IEEE80211_DA_FROMDS, addr, ETH_ALEN); os_memcpy(hdr.IEEE80211_BSSID_FROMDS, own_addr, ETH_ALEN); os_memcpy(hdr.IEEE80211_SA_FROMDS, own_addr, ETH_ALEN); hostap_send_mlme(priv, (u8 *)&hdr, sizeof(hdr), 0); } const struct wpa_driver_ops wpa_driver_hostap_ops = { .name = "hostap", .desc = "Host AP driver (Intersil Prism2/2.5/3)", .set_key = wpa_driver_hostap_set_key, .hapd_init = hostap_init, .hapd_deinit = hostap_driver_deinit, .set_ieee8021x = hostap_set_ieee8021x, .set_privacy = hostap_set_privacy, .get_seqnum = hostap_get_seqnum, .flush = hostap_flush, .set_generic_elem = hostap_set_generic_elem, .read_sta_data = hostap_read_sta_data, .hapd_send_eapol = hostap_send_eapol, .sta_set_flags = hostap_sta_set_flags, .sta_deauth = hostap_sta_deauth, .sta_disassoc = hostap_sta_disassoc, .sta_remove = hostap_sta_remove, .hapd_set_ssid = hostap_set_ssid, .send_mlme = hostap_send_mlme, .sta_add = hostap_sta_add, .get_inact_sec = hostap_get_inact_sec, .sta_clear_stats = hostap_sta_clear_stats, .get_hw_feature_data = hostap_get_hw_feature_data, .set_ap_wps_ie = hostap_set_ap_wps_ie, .set_freq = hostap_set_freq, .poll_client = wpa_driver_hostap_poll_client, }; wpa-2.1/src/drivers/driver_hostap.h000066400000000000000000000140261227414666700174540ustar00rootroot00000000000000/* * Driver interaction with Linux Host AP driver * Copyright (c) 2002-2006, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef HOSTAP_DRIVER_H #define HOSTAP_DRIVER_H /* netdevice private ioctls (used, e.g., with iwpriv from user space) */ /* New wireless extensions API - SET/GET convention (even ioctl numbers are * root only) */ #define PRISM2_IOCTL_PRISM2_PARAM (SIOCIWFIRSTPRIV + 0) #define PRISM2_IOCTL_GET_PRISM2_PARAM (SIOCIWFIRSTPRIV + 1) #define PRISM2_IOCTL_WRITEMIF (SIOCIWFIRSTPRIV + 2) #define PRISM2_IOCTL_READMIF (SIOCIWFIRSTPRIV + 3) #define PRISM2_IOCTL_MONITOR (SIOCIWFIRSTPRIV + 4) #define PRISM2_IOCTL_RESET (SIOCIWFIRSTPRIV + 6) #define PRISM2_IOCTL_INQUIRE (SIOCIWFIRSTPRIV + 8) #define PRISM2_IOCTL_WDS_ADD (SIOCIWFIRSTPRIV + 10) #define PRISM2_IOCTL_WDS_DEL (SIOCIWFIRSTPRIV + 12) #define PRISM2_IOCTL_SET_RID_WORD (SIOCIWFIRSTPRIV + 14) #define PRISM2_IOCTL_MACCMD (SIOCIWFIRSTPRIV + 16) #define PRISM2_IOCTL_ADDMAC (SIOCIWFIRSTPRIV + 18) #define PRISM2_IOCTL_DELMAC (SIOCIWFIRSTPRIV + 20) #define PRISM2_IOCTL_KICKMAC (SIOCIWFIRSTPRIV + 22) /* following are not in SIOCGIWPRIV list; check permission in the driver code */ #define PRISM2_IOCTL_DOWNLOAD (SIOCDEVPRIVATE + 13) #define PRISM2_IOCTL_HOSTAPD (SIOCDEVPRIVATE + 14) /* PRISM2_IOCTL_PRISM2_PARAM ioctl() subtypes: */ enum { /* PRISM2_PARAM_PTYPE = 1, */ /* REMOVED 2003-10-22 */ PRISM2_PARAM_TXRATECTRL = 2, PRISM2_PARAM_BEACON_INT = 3, PRISM2_PARAM_PSEUDO_IBSS = 4, PRISM2_PARAM_ALC = 5, /* PRISM2_PARAM_TXPOWER = 6, */ /* REMOVED 2003-10-22 */ PRISM2_PARAM_DUMP = 7, PRISM2_PARAM_OTHER_AP_POLICY = 8, PRISM2_PARAM_AP_MAX_INACTIVITY = 9, PRISM2_PARAM_AP_BRIDGE_PACKETS = 10, PRISM2_PARAM_DTIM_PERIOD = 11, PRISM2_PARAM_AP_NULLFUNC_ACK = 12, PRISM2_PARAM_MAX_WDS = 13, PRISM2_PARAM_AP_AUTOM_AP_WDS = 14, PRISM2_PARAM_AP_AUTH_ALGS = 15, PRISM2_PARAM_MONITOR_ALLOW_FCSERR = 16, PRISM2_PARAM_HOST_ENCRYPT = 17, PRISM2_PARAM_HOST_DECRYPT = 18, PRISM2_PARAM_BUS_MASTER_THRESHOLD_RX = 19, PRISM2_PARAM_BUS_MASTER_THRESHOLD_TX = 20, PRISM2_PARAM_HOST_ROAMING = 21, PRISM2_PARAM_BCRX_STA_KEY = 22, PRISM2_PARAM_IEEE_802_1X = 23, PRISM2_PARAM_ANTSEL_TX = 24, PRISM2_PARAM_ANTSEL_RX = 25, PRISM2_PARAM_MONITOR_TYPE = 26, PRISM2_PARAM_WDS_TYPE = 27, PRISM2_PARAM_HOSTSCAN = 28, PRISM2_PARAM_AP_SCAN = 29, PRISM2_PARAM_ENH_SEC = 30, PRISM2_PARAM_IO_DEBUG = 31, PRISM2_PARAM_BASIC_RATES = 32, PRISM2_PARAM_OPER_RATES = 33, PRISM2_PARAM_HOSTAPD = 34, PRISM2_PARAM_HOSTAPD_STA = 35, PRISM2_PARAM_WPA = 36, PRISM2_PARAM_PRIVACY_INVOKED = 37, PRISM2_PARAM_TKIP_COUNTERMEASURES = 38, PRISM2_PARAM_DROP_UNENCRYPTED = 39, PRISM2_PARAM_SCAN_CHANNEL_MASK = 40, }; enum { HOSTAP_ANTSEL_DO_NOT_TOUCH = 0, HOSTAP_ANTSEL_DIVERSITY = 1, HOSTAP_ANTSEL_LOW = 2, HOSTAP_ANTSEL_HIGH = 3 }; /* PRISM2_IOCTL_MACCMD ioctl() subcommands: */ enum { AP_MAC_CMD_POLICY_OPEN = 0, AP_MAC_CMD_POLICY_ALLOW = 1, AP_MAC_CMD_POLICY_DENY = 2, AP_MAC_CMD_FLUSH = 3, AP_MAC_CMD_KICKALL = 4 }; /* PRISM2_IOCTL_DOWNLOAD ioctl() dl_cmd: */ enum { PRISM2_DOWNLOAD_VOLATILE = 1 /* RAM */, /* Note! Old versions of prism2_srec have a fatal error in CRC-16 * calculation, which will corrupt all non-volatile downloads. * PRISM2_DOWNLOAD_NON_VOLATILE used to be 2, but it is now 3 to * prevent use of old versions of prism2_srec for non-volatile * download. */ PRISM2_DOWNLOAD_NON_VOLATILE = 3 /* FLASH */, PRISM2_DOWNLOAD_VOLATILE_GENESIS = 4 /* RAM in Genesis mode */, /* Persistent versions of volatile download commands (keep firmware * data in memory and automatically re-download after hw_reset */ PRISM2_DOWNLOAD_VOLATILE_PERSISTENT = 5, PRISM2_DOWNLOAD_VOLATILE_GENESIS_PERSISTENT = 6, }; struct prism2_download_param { u32 dl_cmd; u32 start_addr; u32 num_areas; struct prism2_download_area { u32 addr; /* wlan card address */ u32 len; caddr_t ptr; /* pointer to data in user space */ } data[0]; }; #define PRISM2_MAX_DOWNLOAD_AREA_LEN 131072 #define PRISM2_MAX_DOWNLOAD_LEN 262144 /* PRISM2_IOCTL_HOSTAPD ioctl() cmd: */ enum { PRISM2_HOSTAPD_FLUSH = 1, PRISM2_HOSTAPD_ADD_STA = 2, PRISM2_HOSTAPD_REMOVE_STA = 3, PRISM2_HOSTAPD_GET_INFO_STA = 4, /* REMOVED: PRISM2_HOSTAPD_RESET_TXEXC_STA = 5, */ PRISM2_SET_ENCRYPTION = 6, PRISM2_GET_ENCRYPTION = 7, PRISM2_HOSTAPD_SET_FLAGS_STA = 8, PRISM2_HOSTAPD_GET_RID = 9, PRISM2_HOSTAPD_SET_RID = 10, PRISM2_HOSTAPD_SET_ASSOC_AP_ADDR = 11, PRISM2_HOSTAPD_SET_GENERIC_ELEMENT = 12, PRISM2_HOSTAPD_MLME = 13, PRISM2_HOSTAPD_SCAN_REQ = 14, PRISM2_HOSTAPD_STA_CLEAR_STATS = 15, }; #define PRISM2_HOSTAPD_MAX_BUF_SIZE 1024 #define PRISM2_HOSTAPD_RID_HDR_LEN \ ((size_t) (&((struct prism2_hostapd_param *) 0)->u.rid.data)) #define PRISM2_HOSTAPD_GENERIC_ELEMENT_HDR_LEN \ ((size_t) (&((struct prism2_hostapd_param *) 0)->u.generic_elem.data)) /* Maximum length for algorithm names (-1 for nul termination) used in ioctl() */ #define HOSTAP_CRYPT_ALG_NAME_LEN 16 struct prism2_hostapd_param { u32 cmd; u8 sta_addr[ETH_ALEN]; union { struct { u16 aid; u16 capability; u8 tx_supp_rates; } add_sta; struct { u32 inactive_sec; } get_info_sta; struct { u8 alg[HOSTAP_CRYPT_ALG_NAME_LEN]; u32 flags; u32 err; u8 idx; u8 seq[8]; /* sequence counter (set: RX, get: TX) */ u16 key_len; u8 key[0]; } crypt; struct { u32 flags_and; u32 flags_or; } set_flags_sta; struct { u16 rid; u16 len; u8 data[0]; } rid; struct { u8 len; u8 data[0]; } generic_elem; struct { #define MLME_STA_DEAUTH 0 #define MLME_STA_DISASSOC 1 u16 cmd; u16 reason_code; } mlme; struct { u8 ssid_len; u8 ssid[32]; } scan_req; } u; }; #define HOSTAP_CRYPT_FLAG_SET_TX_KEY BIT(0) #define HOSTAP_CRYPT_FLAG_PERMANENT BIT(1) #define HOSTAP_CRYPT_ERR_UNKNOWN_ALG 2 #define HOSTAP_CRYPT_ERR_UNKNOWN_ADDR 3 #define HOSTAP_CRYPT_ERR_CRYPT_INIT_FAILED 4 #define HOSTAP_CRYPT_ERR_KEY_SET_FAILED 5 #define HOSTAP_CRYPT_ERR_TX_KEY_SET_FAILED 6 #define HOSTAP_CRYPT_ERR_CARD_CONF_FAILED 7 #endif /* HOSTAP_DRIVER_H */ wpa-2.1/src/drivers/driver_madwifi.c000066400000000000000000001034121227414666700175670ustar00rootroot00000000000000/* * hostapd - driver interaction with MADWIFI 802.11 driver * Copyright (c) 2004, Sam Leffler * Copyright (c) 2004, Video54 Technologies * Copyright (c) 2004-2007, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. * * This driver wrapper is only for hostapd AP mode functionality. Station * (wpa_supplicant) operations with madwifi are supported by the driver_wext.c * wrapper. */ #include "includes.h" #include #include "common.h" #include "driver.h" #include "driver_wext.h" #include "eloop.h" #include "common/ieee802_11_defs.h" #include "linux_wext.h" /* * Avoid conflicts with wpa_supplicant definitions by undefining a definition. */ #undef WME_OUI_TYPE #include #include #ifdef WME_NUM_AC /* Assume this is built against BSD branch of madwifi driver. */ #define MADWIFI_BSD #include #endif /* WME_NUM_AC */ #include #include #ifdef CONFIG_WPS #ifdef IEEE80211_IOCTL_FILTERFRAME #include #ifndef ETH_P_80211_RAW #define ETH_P_80211_RAW 0x0019 #endif #endif /* IEEE80211_IOCTL_FILTERFRAME */ #endif /* CONFIG_WPS */ /* * Avoid conflicts with hostapd definitions by undefining couple of defines * from madwifi header files. */ #undef RSN_VERSION #undef WPA_VERSION #undef WPA_OUI_TYPE #undef WME_OUI_TYPE #ifdef IEEE80211_IOCTL_SETWMMPARAMS /* Assume this is built against madwifi-ng */ #define MADWIFI_NG #endif /* IEEE80211_IOCTL_SETWMMPARAMS */ #define WPA_KEY_RSC_LEN 8 #include "priv_netlink.h" #include "netlink.h" #include "linux_ioctl.h" #include "l2_packet/l2_packet.h" struct madwifi_driver_data { struct hostapd_data *hapd; /* back pointer */ char iface[IFNAMSIZ + 1]; int ifindex; struct l2_packet_data *sock_xmit; /* raw packet xmit socket */ struct l2_packet_data *sock_recv; /* raw packet recv socket */ int ioctl_sock; /* socket for ioctl() use */ struct netlink_data *netlink; int we_version; u8 acct_mac[ETH_ALEN]; struct hostap_sta_driver_data acct_data; struct l2_packet_data *sock_raw; /* raw 802.11 management frames */ }; static int madwifi_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, int reason_code); static int set80211priv(struct madwifi_driver_data *drv, int op, void *data, int len) { struct iwreq iwr; int do_inline = len < IFNAMSIZ; memset(&iwr, 0, sizeof(iwr)); os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); #ifdef IEEE80211_IOCTL_FILTERFRAME /* FILTERFRAME must be NOT inline, regardless of size. */ if (op == IEEE80211_IOCTL_FILTERFRAME) do_inline = 0; #endif /* IEEE80211_IOCTL_FILTERFRAME */ if (op == IEEE80211_IOCTL_SET_APPIEBUF) do_inline = 0; if (do_inline) { /* * Argument data fits inline; put it there. */ memcpy(iwr.u.name, data, len); } else { /* * Argument data too big for inline transfer; setup a * parameter block instead; the kernel will transfer * the data for the driver. */ iwr.u.data.pointer = data; iwr.u.data.length = len; } if (ioctl(drv->ioctl_sock, op, &iwr) < 0) { #ifdef MADWIFI_NG int first = IEEE80211_IOCTL_SETPARAM; static const char *opnames[] = { "ioctl[IEEE80211_IOCTL_SETPARAM]", "ioctl[IEEE80211_IOCTL_GETPARAM]", "ioctl[IEEE80211_IOCTL_SETMODE]", "ioctl[IEEE80211_IOCTL_GETMODE]", "ioctl[IEEE80211_IOCTL_SETWMMPARAMS]", "ioctl[IEEE80211_IOCTL_GETWMMPARAMS]", "ioctl[IEEE80211_IOCTL_SETCHANLIST]", "ioctl[IEEE80211_IOCTL_GETCHANLIST]", "ioctl[IEEE80211_IOCTL_CHANSWITCH]", "ioctl[IEEE80211_IOCTL_GET_APPIEBUF]", "ioctl[IEEE80211_IOCTL_SET_APPIEBUF]", "ioctl[IEEE80211_IOCTL_GETSCANRESULTS]", "ioctl[IEEE80211_IOCTL_FILTERFRAME]", "ioctl[IEEE80211_IOCTL_GETCHANINFO]", "ioctl[IEEE80211_IOCTL_SETOPTIE]", "ioctl[IEEE80211_IOCTL_GETOPTIE]", "ioctl[IEEE80211_IOCTL_SETMLME]", NULL, "ioctl[IEEE80211_IOCTL_SETKEY]", NULL, "ioctl[IEEE80211_IOCTL_DELKEY]", NULL, "ioctl[IEEE80211_IOCTL_ADDMAC]", NULL, "ioctl[IEEE80211_IOCTL_DELMAC]", NULL, "ioctl[IEEE80211_IOCTL_WDSMAC]", NULL, "ioctl[IEEE80211_IOCTL_WDSDELMAC]", NULL, "ioctl[IEEE80211_IOCTL_KICKMAC]", }; #else /* MADWIFI_NG */ int first = IEEE80211_IOCTL_SETPARAM; static const char *opnames[] = { "ioctl[IEEE80211_IOCTL_SETPARAM]", "ioctl[IEEE80211_IOCTL_GETPARAM]", "ioctl[IEEE80211_IOCTL_SETKEY]", "ioctl[SIOCIWFIRSTPRIV+3]", "ioctl[IEEE80211_IOCTL_DELKEY]", "ioctl[SIOCIWFIRSTPRIV+5]", "ioctl[IEEE80211_IOCTL_SETMLME]", "ioctl[SIOCIWFIRSTPRIV+7]", "ioctl[IEEE80211_IOCTL_SETOPTIE]", "ioctl[IEEE80211_IOCTL_GETOPTIE]", "ioctl[IEEE80211_IOCTL_ADDMAC]", "ioctl[SIOCIWFIRSTPRIV+11]", "ioctl[IEEE80211_IOCTL_DELMAC]", "ioctl[SIOCIWFIRSTPRIV+13]", "ioctl[IEEE80211_IOCTL_CHANLIST]", "ioctl[SIOCIWFIRSTPRIV+15]", "ioctl[IEEE80211_IOCTL_GETRSN]", "ioctl[SIOCIWFIRSTPRIV+17]", "ioctl[IEEE80211_IOCTL_GETKEY]", }; #endif /* MADWIFI_NG */ int idx = op - first; if (first <= op && idx < (int) ARRAY_SIZE(opnames) && opnames[idx]) perror(opnames[idx]); else perror("ioctl[unknown???]"); return -1; } return 0; } static int set80211param(struct madwifi_driver_data *drv, int op, int arg) { struct iwreq iwr; memset(&iwr, 0, sizeof(iwr)); os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); iwr.u.mode = op; memcpy(iwr.u.name+sizeof(__u32), &arg, sizeof(arg)); if (ioctl(drv->ioctl_sock, IEEE80211_IOCTL_SETPARAM, &iwr) < 0) { perror("ioctl[IEEE80211_IOCTL_SETPARAM]"); wpa_printf(MSG_DEBUG, "%s: Failed to set parameter (op %d " "arg %d)", __func__, op, arg); return -1; } return 0; } #ifndef CONFIG_NO_STDOUT_DEBUG static const char * ether_sprintf(const u8 *addr) { static char buf[sizeof(MACSTR)]; if (addr != NULL) snprintf(buf, sizeof(buf), MACSTR, MAC2STR(addr)); else snprintf(buf, sizeof(buf), MACSTR, 0,0,0,0,0,0); return buf; } #endif /* CONFIG_NO_STDOUT_DEBUG */ /* * Configure WPA parameters. */ static int madwifi_configure_wpa(struct madwifi_driver_data *drv, struct wpa_bss_params *params) { int v; switch (params->wpa_group) { case WPA_CIPHER_CCMP: v = IEEE80211_CIPHER_AES_CCM; break; case WPA_CIPHER_TKIP: v = IEEE80211_CIPHER_TKIP; break; case WPA_CIPHER_WEP104: v = IEEE80211_CIPHER_WEP; break; case WPA_CIPHER_WEP40: v = IEEE80211_CIPHER_WEP; break; case WPA_CIPHER_NONE: v = IEEE80211_CIPHER_NONE; break; default: wpa_printf(MSG_ERROR, "Unknown group key cipher %u", params->wpa_group); return -1; } wpa_printf(MSG_DEBUG, "%s: group key cipher=%d", __func__, v); if (set80211param(drv, IEEE80211_PARAM_MCASTCIPHER, v)) { printf("Unable to set group key cipher to %u\n", v); return -1; } if (v == IEEE80211_CIPHER_WEP) { /* key length is done only for specific ciphers */ v = (params->wpa_group == WPA_CIPHER_WEP104 ? 13 : 5); if (set80211param(drv, IEEE80211_PARAM_MCASTKEYLEN, v)) { printf("Unable to set group key length to %u\n", v); return -1; } } v = 0; if (params->wpa_pairwise & WPA_CIPHER_CCMP) v |= 1<wpa_pairwise & WPA_CIPHER_TKIP) v |= 1<wpa_pairwise & WPA_CIPHER_NONE) v |= 1<wpa_key_mgmt); if (set80211param(drv, IEEE80211_PARAM_KEYMGTALGS, params->wpa_key_mgmt)) { printf("Unable to set key management algorithms to 0x%x\n", params->wpa_key_mgmt); return -1; } v = 0; if (params->rsn_preauth) v |= BIT(0); wpa_printf(MSG_DEBUG, "%s: rsn capabilities=0x%x", __func__, params->rsn_preauth); if (set80211param(drv, IEEE80211_PARAM_RSNCAPS, v)) { printf("Unable to set RSN capabilities to 0x%x\n", v); return -1; } wpa_printf(MSG_DEBUG, "%s: enable WPA=0x%x", __func__, params->wpa); if (set80211param(drv, IEEE80211_PARAM_WPA, params->wpa)) { printf("Unable to set WPA to %u\n", params->wpa); return -1; } return 0; } static int madwifi_set_ieee8021x(void *priv, struct wpa_bss_params *params) { struct madwifi_driver_data *drv = priv; wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, params->enabled); if (!params->enabled) { /* XXX restore state */ return set80211param(priv, IEEE80211_PARAM_AUTHMODE, IEEE80211_AUTH_AUTO); } if (!params->wpa && !params->ieee802_1x) { wpa_printf(MSG_WARNING, "No 802.1X or WPA enabled!"); return -1; } if (params->wpa && madwifi_configure_wpa(drv, params) != 0) { wpa_printf(MSG_WARNING, "Error configuring WPA state!"); return -1; } if (set80211param(priv, IEEE80211_PARAM_AUTHMODE, (params->wpa ? IEEE80211_AUTH_WPA : IEEE80211_AUTH_8021X))) { wpa_printf(MSG_WARNING, "Error enabling WPA/802.1X!"); return -1; } return 0; } static int madwifi_set_privacy(void *priv, int enabled) { struct madwifi_driver_data *drv = priv; wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled); return set80211param(drv, IEEE80211_PARAM_PRIVACY, enabled); } static int madwifi_set_sta_authorized(void *priv, const u8 *addr, int authorized) { struct madwifi_driver_data *drv = priv; struct ieee80211req_mlme mlme; int ret; wpa_printf(MSG_DEBUG, "%s: addr=%s authorized=%d", __func__, ether_sprintf(addr), authorized); if (authorized) mlme.im_op = IEEE80211_MLME_AUTHORIZE; else mlme.im_op = IEEE80211_MLME_UNAUTHORIZE; mlme.im_reason = 0; memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme)); if (ret < 0) { wpa_printf(MSG_DEBUG, "%s: Failed to %sauthorize STA " MACSTR, __func__, authorized ? "" : "un", MAC2STR(addr)); } return ret; } static int madwifi_sta_set_flags(void *priv, const u8 *addr, int total_flags, int flags_or, int flags_and) { /* For now, only support setting Authorized flag */ if (flags_or & WPA_STA_AUTHORIZED) return madwifi_set_sta_authorized(priv, addr, 1); if (!(flags_and & WPA_STA_AUTHORIZED)) return madwifi_set_sta_authorized(priv, addr, 0); return 0; } static int madwifi_del_key(void *priv, const u8 *addr, int key_idx) { struct madwifi_driver_data *drv = priv; struct ieee80211req_del_key wk; int ret; wpa_printf(MSG_DEBUG, "%s: addr=%s key_idx=%d", __func__, ether_sprintf(addr), key_idx); memset(&wk, 0, sizeof(wk)); if (addr != NULL) { memcpy(wk.idk_macaddr, addr, IEEE80211_ADDR_LEN); wk.idk_keyix = (u8) IEEE80211_KEYIX_NONE; } else { wk.idk_keyix = key_idx; } ret = set80211priv(drv, IEEE80211_IOCTL_DELKEY, &wk, sizeof(wk)); if (ret < 0) { wpa_printf(MSG_DEBUG, "%s: Failed to delete key (addr %s" " key_idx %d)", __func__, ether_sprintf(addr), key_idx); } return ret; } static int wpa_driver_madwifi_set_key(const char *ifname, void *priv, enum wpa_alg alg, const u8 *addr, int key_idx, int set_tx, const u8 *seq, size_t seq_len, const u8 *key, size_t key_len) { struct madwifi_driver_data *drv = priv; struct ieee80211req_key wk; u_int8_t cipher; int ret; if (alg == WPA_ALG_NONE) return madwifi_del_key(drv, addr, key_idx); wpa_printf(MSG_DEBUG, "%s: alg=%d addr=%s key_idx=%d", __func__, alg, ether_sprintf(addr), key_idx); if (alg == WPA_ALG_WEP) cipher = IEEE80211_CIPHER_WEP; else if (alg == WPA_ALG_TKIP) cipher = IEEE80211_CIPHER_TKIP; else if (alg == WPA_ALG_CCMP) cipher = IEEE80211_CIPHER_AES_CCM; else { printf("%s: unknown/unsupported algorithm %d\n", __func__, alg); return -1; } if (key_len > sizeof(wk.ik_keydata)) { printf("%s: key length %lu too big\n", __func__, (unsigned long) key_len); return -3; } memset(&wk, 0, sizeof(wk)); wk.ik_type = cipher; wk.ik_flags = IEEE80211_KEY_RECV | IEEE80211_KEY_XMIT; if (addr == NULL || is_broadcast_ether_addr(addr)) { memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN); wk.ik_keyix = key_idx; wk.ik_flags |= IEEE80211_KEY_DEFAULT; } else { memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN); wk.ik_keyix = IEEE80211_KEYIX_NONE; } wk.ik_keylen = key_len; memcpy(wk.ik_keydata, key, key_len); ret = set80211priv(drv, IEEE80211_IOCTL_SETKEY, &wk, sizeof(wk)); if (ret < 0) { wpa_printf(MSG_DEBUG, "%s: Failed to set key (addr %s" " key_idx %d alg %d key_len %lu set_tx %d)", __func__, ether_sprintf(wk.ik_macaddr), key_idx, alg, (unsigned long) key_len, set_tx); } return ret; } static int madwifi_get_seqnum(const char *ifname, void *priv, const u8 *addr, int idx, u8 *seq) { struct madwifi_driver_data *drv = priv; struct ieee80211req_key wk; wpa_printf(MSG_DEBUG, "%s: addr=%s idx=%d", __func__, ether_sprintf(addr), idx); memset(&wk, 0, sizeof(wk)); if (addr == NULL) memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN); else memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN); wk.ik_keyix = idx; if (set80211priv(drv, IEEE80211_IOCTL_GETKEY, &wk, sizeof(wk))) { wpa_printf(MSG_DEBUG, "%s: Failed to get encryption data " "(addr " MACSTR " key_idx %d)", __func__, MAC2STR(wk.ik_macaddr), idx); return -1; } #ifdef WORDS_BIGENDIAN { /* * wk.ik_keytsc is in host byte order (big endian), need to * swap it to match with the byte order used in WPA. */ int i; u8 tmp[WPA_KEY_RSC_LEN]; memcpy(tmp, &wk.ik_keytsc, sizeof(wk.ik_keytsc)); for (i = 0; i < WPA_KEY_RSC_LEN; i++) { seq[i] = tmp[WPA_KEY_RSC_LEN - i - 1]; } } #else /* WORDS_BIGENDIAN */ memcpy(seq, &wk.ik_keytsc, sizeof(wk.ik_keytsc)); #endif /* WORDS_BIGENDIAN */ return 0; } static int madwifi_flush(void *priv) { #ifdef MADWIFI_BSD u8 allsta[IEEE80211_ADDR_LEN]; memset(allsta, 0xff, IEEE80211_ADDR_LEN); return madwifi_sta_deauth(priv, NULL, allsta, IEEE80211_REASON_AUTH_LEAVE); #else /* MADWIFI_BSD */ return 0; /* XXX */ #endif /* MADWIFI_BSD */ } static int madwifi_read_sta_driver_data(void *priv, struct hostap_sta_driver_data *data, const u8 *addr) { struct madwifi_driver_data *drv = priv; #ifdef MADWIFI_BSD struct ieee80211req_sta_stats stats; memset(data, 0, sizeof(*data)); /* * Fetch statistics for station from the system. */ memset(&stats, 0, sizeof(stats)); memcpy(stats.is_u.macaddr, addr, IEEE80211_ADDR_LEN); if (set80211priv(drv, #ifdef MADWIFI_NG IEEE80211_IOCTL_STA_STATS, #else /* MADWIFI_NG */ IEEE80211_IOCTL_GETSTASTATS, #endif /* MADWIFI_NG */ &stats, sizeof(stats))) { wpa_printf(MSG_DEBUG, "%s: Failed to fetch STA stats (addr " MACSTR ")", __func__, MAC2STR(addr)); if (memcmp(addr, drv->acct_mac, ETH_ALEN) == 0) { memcpy(data, &drv->acct_data, sizeof(*data)); return 0; } printf("Failed to get station stats information element.\n"); return -1; } data->rx_packets = stats.is_stats.ns_rx_data; data->rx_bytes = stats.is_stats.ns_rx_bytes; data->tx_packets = stats.is_stats.ns_tx_data; data->tx_bytes = stats.is_stats.ns_tx_bytes; return 0; #else /* MADWIFI_BSD */ char buf[1024], line[128], *pos; FILE *f; unsigned long val; memset(data, 0, sizeof(*data)); snprintf(buf, sizeof(buf), "/proc/net/madwifi/%s/" MACSTR, drv->iface, MAC2STR(addr)); f = fopen(buf, "r"); if (!f) { if (memcmp(addr, drv->acct_mac, ETH_ALEN) != 0) return -1; memcpy(data, &drv->acct_data, sizeof(*data)); return 0; } /* Need to read proc file with in one piece, so use large enough * buffer. */ setbuffer(f, buf, sizeof(buf)); while (fgets(line, sizeof(line), f)) { pos = strchr(line, '='); if (!pos) continue; *pos++ = '\0'; val = strtoul(pos, NULL, 10); if (strcmp(line, "rx_packets") == 0) data->rx_packets = val; else if (strcmp(line, "tx_packets") == 0) data->tx_packets = val; else if (strcmp(line, "rx_bytes") == 0) data->rx_bytes = val; else if (strcmp(line, "tx_bytes") == 0) data->tx_bytes = val; } fclose(f); return 0; #endif /* MADWIFI_BSD */ } static int madwifi_sta_clear_stats(void *priv, const u8 *addr) { #if defined(MADWIFI_BSD) && defined(IEEE80211_MLME_CLEAR_STATS) struct madwifi_driver_data *drv = priv; struct ieee80211req_mlme mlme; int ret; wpa_printf(MSG_DEBUG, "%s: addr=%s", __func__, ether_sprintf(addr)); mlme.im_op = IEEE80211_MLME_CLEAR_STATS; memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme)); if (ret < 0) { wpa_printf(MSG_DEBUG, "%s: Failed to clear STA stats (addr " MACSTR ")", __func__, MAC2STR(addr)); } return ret; #else /* MADWIFI_BSD && IEEE80211_MLME_CLEAR_STATS */ return 0; /* FIX */ #endif /* MADWIFI_BSD && IEEE80211_MLME_CLEAR_STATS */ } static int madwifi_set_opt_ie(void *priv, const u8 *ie, size_t ie_len) { /* * Do nothing; we setup parameters at startup that define the * contents of the beacon information element. */ return 0; } static int madwifi_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, int reason_code) { struct madwifi_driver_data *drv = priv; struct ieee80211req_mlme mlme; int ret; wpa_printf(MSG_DEBUG, "%s: addr=%s reason_code=%d", __func__, ether_sprintf(addr), reason_code); mlme.im_op = IEEE80211_MLME_DEAUTH; mlme.im_reason = reason_code; memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme)); if (ret < 0) { wpa_printf(MSG_DEBUG, "%s: Failed to deauth STA (addr " MACSTR " reason %d)", __func__, MAC2STR(addr), reason_code); } return ret; } static int madwifi_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr, int reason_code) { struct madwifi_driver_data *drv = priv; struct ieee80211req_mlme mlme; int ret; wpa_printf(MSG_DEBUG, "%s: addr=%s reason_code=%d", __func__, ether_sprintf(addr), reason_code); mlme.im_op = IEEE80211_MLME_DISASSOC; mlme.im_reason = reason_code; memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme)); if (ret < 0) { wpa_printf(MSG_DEBUG, "%s: Failed to disassoc STA (addr " MACSTR " reason %d)", __func__, MAC2STR(addr), reason_code); } return ret; } #ifdef CONFIG_WPS #ifdef IEEE80211_IOCTL_FILTERFRAME static void madwifi_raw_receive(void *ctx, const u8 *src_addr, const u8 *buf, size_t len) { struct madwifi_driver_data *drv = ctx; const struct ieee80211_mgmt *mgmt; u16 fc; union wpa_event_data event; /* Send Probe Request information to WPS processing */ if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req)) return; mgmt = (const struct ieee80211_mgmt *) buf; fc = le_to_host16(mgmt->frame_control); if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT || WLAN_FC_GET_STYPE(fc) != WLAN_FC_STYPE_PROBE_REQ) return; os_memset(&event, 0, sizeof(event)); event.rx_probe_req.sa = mgmt->sa; event.rx_probe_req.da = mgmt->da; event.rx_probe_req.bssid = mgmt->bssid; event.rx_probe_req.ie = mgmt->u.probe_req.variable; event.rx_probe_req.ie_len = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req)); wpa_supplicant_event(drv->hapd, EVENT_RX_PROBE_REQ, &event); } #endif /* IEEE80211_IOCTL_FILTERFRAME */ #endif /* CONFIG_WPS */ static int madwifi_receive_probe_req(struct madwifi_driver_data *drv) { int ret = 0; #ifdef CONFIG_WPS #ifdef IEEE80211_IOCTL_FILTERFRAME struct ieee80211req_set_filter filt; wpa_printf(MSG_DEBUG, "%s Enter", __func__); filt.app_filterype = IEEE80211_FILTER_TYPE_PROBE_REQ; ret = set80211priv(drv, IEEE80211_IOCTL_FILTERFRAME, &filt, sizeof(struct ieee80211req_set_filter)); if (ret) return ret; drv->sock_raw = l2_packet_init(drv->iface, NULL, ETH_P_80211_RAW, madwifi_raw_receive, drv, 1); if (drv->sock_raw == NULL) return -1; #endif /* IEEE80211_IOCTL_FILTERFRAME */ #endif /* CONFIG_WPS */ return ret; } #ifdef CONFIG_WPS static int madwifi_set_wps_ie(void *priv, const u8 *ie, size_t len, u32 frametype) { struct madwifi_driver_data *drv = priv; u8 buf[256]; struct ieee80211req_getset_appiebuf *beac_ie; wpa_printf(MSG_DEBUG, "%s buflen = %lu", __func__, (unsigned long) len); beac_ie = (struct ieee80211req_getset_appiebuf *) buf; beac_ie->app_frmtype = frametype; beac_ie->app_buflen = len; memcpy(&(beac_ie->app_buf[0]), ie, len); return set80211priv(drv, IEEE80211_IOCTL_SET_APPIEBUF, beac_ie, sizeof(struct ieee80211req_getset_appiebuf) + len); } static int madwifi_set_ap_wps_ie(void *priv, const struct wpabuf *beacon, const struct wpabuf *proberesp, const struct wpabuf *assocresp) { if (madwifi_set_wps_ie(priv, beacon ? wpabuf_head(beacon) : NULL, beacon ? wpabuf_len(beacon) : 0, IEEE80211_APPIE_FRAME_BEACON) < 0) return -1; return madwifi_set_wps_ie(priv, proberesp ? wpabuf_head(proberesp) : NULL, proberesp ? wpabuf_len(proberesp) : 0, IEEE80211_APPIE_FRAME_PROBE_RESP); } #else /* CONFIG_WPS */ #define madwifi_set_ap_wps_ie NULL #endif /* CONFIG_WPS */ static int madwifi_set_freq(void *priv, struct hostapd_freq_params *freq) { struct madwifi_driver_data *drv = priv; struct iwreq iwr; os_memset(&iwr, 0, sizeof(iwr)); os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); iwr.u.freq.m = freq->channel; iwr.u.freq.e = 0; if (ioctl(drv->ioctl_sock, SIOCSIWFREQ, &iwr) < 0) { perror("ioctl[SIOCSIWFREQ]"); return -1; } return 0; } static void madwifi_new_sta(struct madwifi_driver_data *drv, u8 addr[IEEE80211_ADDR_LEN]) { struct hostapd_data *hapd = drv->hapd; struct ieee80211req_wpaie ie; int ielen = 0; u8 *iebuf = NULL; /* * Fetch negotiated WPA/RSN parameters from the system. */ memset(&ie, 0, sizeof(ie)); memcpy(ie.wpa_macaddr, addr, IEEE80211_ADDR_LEN); if (set80211priv(drv, IEEE80211_IOCTL_GETWPAIE, &ie, sizeof(ie))) { wpa_printf(MSG_DEBUG, "%s: Failed to get WPA/RSN IE", __func__); goto no_ie; } wpa_hexdump(MSG_MSGDUMP, "madwifi req WPA IE", ie.wpa_ie, IEEE80211_MAX_OPT_IE); iebuf = ie.wpa_ie; /* madwifi seems to return some random data if WPA/RSN IE is not set. * Assume the IE was not included if the IE type is unknown. */ if (iebuf[0] != WLAN_EID_VENDOR_SPECIFIC) iebuf[1] = 0; #ifdef MADWIFI_NG wpa_hexdump(MSG_MSGDUMP, "madwifi req RSN IE", ie.rsn_ie, IEEE80211_MAX_OPT_IE); if (iebuf[1] == 0 && ie.rsn_ie[1] > 0) { /* madwifi-ng svn #1453 added rsn_ie. Use it, if wpa_ie was not * set. This is needed for WPA2. */ iebuf = ie.rsn_ie; if (iebuf[0] != WLAN_EID_RSN) iebuf[1] = 0; } #endif /* MADWIFI_NG */ ielen = iebuf[1]; if (ielen == 0) iebuf = NULL; else ielen += 2; no_ie: drv_event_assoc(hapd, addr, iebuf, ielen, 0); if (memcmp(addr, drv->acct_mac, ETH_ALEN) == 0) { /* Cached accounting data is not valid anymore. */ memset(drv->acct_mac, 0, ETH_ALEN); memset(&drv->acct_data, 0, sizeof(drv->acct_data)); } } static void madwifi_wireless_event_wireless_custom(struct madwifi_driver_data *drv, char *custom) { wpa_printf(MSG_DEBUG, "Custom wireless event: '%s'", custom); if (strncmp(custom, "MLME-MICHAELMICFAILURE.indication", 33) == 0) { char *pos; u8 addr[ETH_ALEN]; pos = strstr(custom, "addr="); if (pos == NULL) { wpa_printf(MSG_DEBUG, "MLME-MICHAELMICFAILURE.indication " "without sender address ignored"); return; } pos += 5; if (hwaddr_aton(pos, addr) == 0) { union wpa_event_data data; os_memset(&data, 0, sizeof(data)); data.michael_mic_failure.unicast = 1; data.michael_mic_failure.src = addr; wpa_supplicant_event(drv->hapd, EVENT_MICHAEL_MIC_FAILURE, &data); } else { wpa_printf(MSG_DEBUG, "MLME-MICHAELMICFAILURE.indication " "with invalid MAC address"); } } else if (strncmp(custom, "STA-TRAFFIC-STAT", 16) == 0) { char *key, *value; u32 val; key = custom; while ((key = strchr(key, '\n')) != NULL) { key++; value = strchr(key, '='); if (value == NULL) continue; *value++ = '\0'; val = strtoul(value, NULL, 10); if (strcmp(key, "mac") == 0) hwaddr_aton(value, drv->acct_mac); else if (strcmp(key, "rx_packets") == 0) drv->acct_data.rx_packets = val; else if (strcmp(key, "tx_packets") == 0) drv->acct_data.tx_packets = val; else if (strcmp(key, "rx_bytes") == 0) drv->acct_data.rx_bytes = val; else if (strcmp(key, "tx_bytes") == 0) drv->acct_data.tx_bytes = val; key = value; } } } static void madwifi_wireless_event_wireless(struct madwifi_driver_data *drv, char *data, int len) { struct iw_event iwe_buf, *iwe = &iwe_buf; char *pos, *end, *custom, *buf; pos = data; end = data + len; while (pos + IW_EV_LCP_LEN <= end) { /* Event data may be unaligned, so make a local, aligned copy * before processing. */ memcpy(&iwe_buf, pos, IW_EV_LCP_LEN); wpa_printf(MSG_MSGDUMP, "Wireless event: cmd=0x%x len=%d", iwe->cmd, iwe->len); if (iwe->len <= IW_EV_LCP_LEN) return; custom = pos + IW_EV_POINT_LEN; if (drv->we_version > 18 && (iwe->cmd == IWEVMICHAELMICFAILURE || iwe->cmd == IWEVCUSTOM)) { /* WE-19 removed the pointer from struct iw_point */ char *dpos = (char *) &iwe_buf.u.data.length; int dlen = dpos - (char *) &iwe_buf; memcpy(dpos, pos + IW_EV_LCP_LEN, sizeof(struct iw_event) - dlen); } else { memcpy(&iwe_buf, pos, sizeof(struct iw_event)); custom += IW_EV_POINT_OFF; } switch (iwe->cmd) { case IWEVEXPIRED: drv_event_disassoc(drv->hapd, (u8 *) iwe->u.addr.sa_data); break; case IWEVREGISTERED: madwifi_new_sta(drv, (u8 *) iwe->u.addr.sa_data); break; case IWEVCUSTOM: if (custom + iwe->u.data.length > end) return; buf = malloc(iwe->u.data.length + 1); if (buf == NULL) return; /* XXX */ memcpy(buf, custom, iwe->u.data.length); buf[iwe->u.data.length] = '\0'; madwifi_wireless_event_wireless_custom(drv, buf); free(buf); break; } pos += iwe->len; } } static void madwifi_wireless_event_rtm_newlink(void *ctx, struct ifinfomsg *ifi, u8 *buf, size_t len) { struct madwifi_driver_data *drv = ctx; int attrlen, rta_len; struct rtattr *attr; if (ifi->ifi_index != drv->ifindex) return; attrlen = len; attr = (struct rtattr *) buf; rta_len = RTA_ALIGN(sizeof(struct rtattr)); while (RTA_OK(attr, attrlen)) { if (attr->rta_type == IFLA_WIRELESS) { madwifi_wireless_event_wireless( drv, ((char *) attr) + rta_len, attr->rta_len - rta_len); } attr = RTA_NEXT(attr, attrlen); } } static int madwifi_get_we_version(struct madwifi_driver_data *drv) { struct iw_range *range; struct iwreq iwr; int minlen; size_t buflen; drv->we_version = 0; /* * Use larger buffer than struct iw_range in order to allow the * structure to grow in the future. */ buflen = sizeof(struct iw_range) + 500; range = os_zalloc(buflen); if (range == NULL) return -1; memset(&iwr, 0, sizeof(iwr)); os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); iwr.u.data.pointer = (caddr_t) range; iwr.u.data.length = buflen; minlen = ((char *) &range->enc_capa) - (char *) range + sizeof(range->enc_capa); if (ioctl(drv->ioctl_sock, SIOCGIWRANGE, &iwr) < 0) { perror("ioctl[SIOCGIWRANGE]"); free(range); return -1; } else if (iwr.u.data.length >= minlen && range->we_version_compiled >= 18) { wpa_printf(MSG_DEBUG, "SIOCGIWRANGE: WE(compiled)=%d " "WE(source)=%d enc_capa=0x%x", range->we_version_compiled, range->we_version_source, range->enc_capa); drv->we_version = range->we_version_compiled; } free(range); return 0; } static int madwifi_wireless_event_init(struct madwifi_driver_data *drv) { struct netlink_config *cfg; madwifi_get_we_version(drv); cfg = os_zalloc(sizeof(*cfg)); if (cfg == NULL) return -1; cfg->ctx = drv; cfg->newlink_cb = madwifi_wireless_event_rtm_newlink; drv->netlink = netlink_init(cfg); if (drv->netlink == NULL) { os_free(cfg); return -1; } return 0; } static int madwifi_send_eapol(void *priv, const u8 *addr, const u8 *data, size_t data_len, int encrypt, const u8 *own_addr, u32 flags) { struct madwifi_driver_data *drv = priv; unsigned char buf[3000]; unsigned char *bp = buf; struct l2_ethhdr *eth; size_t len; int status; /* * Prepend the Ethernet header. If the caller left us * space at the front we could just insert it but since * we don't know we copy to a local buffer. Given the frequency * and size of frames this probably doesn't matter. */ len = data_len + sizeof(struct l2_ethhdr); if (len > sizeof(buf)) { bp = malloc(len); if (bp == NULL) { printf("EAPOL frame discarded, cannot malloc temp " "buffer of size %lu!\n", (unsigned long) len); return -1; } } eth = (struct l2_ethhdr *) bp; memcpy(eth->h_dest, addr, ETH_ALEN); memcpy(eth->h_source, own_addr, ETH_ALEN); eth->h_proto = host_to_be16(ETH_P_EAPOL); memcpy(eth+1, data, data_len); wpa_hexdump(MSG_MSGDUMP, "TX EAPOL", bp, len); status = l2_packet_send(drv->sock_xmit, addr, ETH_P_EAPOL, bp, len); if (bp != buf) free(bp); return status; } static void handle_read(void *ctx, const u8 *src_addr, const u8 *buf, size_t len) { struct madwifi_driver_data *drv = ctx; drv_event_eapol_rx(drv->hapd, src_addr, buf + sizeof(struct l2_ethhdr), len - sizeof(struct l2_ethhdr)); } static void * madwifi_init(struct hostapd_data *hapd, struct wpa_init_params *params) { struct madwifi_driver_data *drv; struct ifreq ifr; struct iwreq iwr; char brname[IFNAMSIZ]; drv = os_zalloc(sizeof(struct madwifi_driver_data)); if (drv == NULL) { printf("Could not allocate memory for madwifi driver data\n"); return NULL; } drv->hapd = hapd; drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0); if (drv->ioctl_sock < 0) { perror("socket[PF_INET,SOCK_DGRAM]"); goto bad; } memcpy(drv->iface, params->ifname, sizeof(drv->iface)); memset(&ifr, 0, sizeof(ifr)); os_strlcpy(ifr.ifr_name, drv->iface, sizeof(ifr.ifr_name)); if (ioctl(drv->ioctl_sock, SIOCGIFINDEX, &ifr) != 0) { perror("ioctl(SIOCGIFINDEX)"); goto bad; } drv->ifindex = ifr.ifr_ifindex; drv->sock_xmit = l2_packet_init(drv->iface, NULL, ETH_P_EAPOL, handle_read, drv, 1); if (drv->sock_xmit == NULL) goto bad; if (l2_packet_get_own_addr(drv->sock_xmit, params->own_addr)) goto bad; if (params->bridge[0]) { wpa_printf(MSG_DEBUG, "Configure bridge %s for EAPOL traffic.", params->bridge[0]); drv->sock_recv = l2_packet_init(params->bridge[0], NULL, ETH_P_EAPOL, handle_read, drv, 1); if (drv->sock_recv == NULL) goto bad; } else if (linux_br_get(brname, drv->iface) == 0) { wpa_printf(MSG_DEBUG, "Interface in bridge %s; configure for " "EAPOL receive", brname); drv->sock_recv = l2_packet_init(brname, NULL, ETH_P_EAPOL, handle_read, drv, 1); if (drv->sock_recv == NULL) goto bad; } else drv->sock_recv = drv->sock_xmit; memset(&iwr, 0, sizeof(iwr)); os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); iwr.u.mode = IW_MODE_MASTER; if (ioctl(drv->ioctl_sock, SIOCSIWMODE, &iwr) < 0) { perror("ioctl[SIOCSIWMODE]"); printf("Could not set interface to master mode!\n"); goto bad; } /* mark down during setup */ linux_set_iface_flags(drv->ioctl_sock, drv->iface, 0); madwifi_set_privacy(drv, 0); /* default to no privacy */ madwifi_receive_probe_req(drv); if (madwifi_wireless_event_init(drv)) goto bad; return drv; bad: if (drv->sock_xmit != NULL) l2_packet_deinit(drv->sock_xmit); if (drv->ioctl_sock >= 0) close(drv->ioctl_sock); if (drv != NULL) free(drv); return NULL; } static void madwifi_deinit(void *priv) { struct madwifi_driver_data *drv = priv; netlink_deinit(drv->netlink); (void) linux_set_iface_flags(drv->ioctl_sock, drv->iface, 0); if (drv->ioctl_sock >= 0) close(drv->ioctl_sock); if (drv->sock_recv != NULL && drv->sock_recv != drv->sock_xmit) l2_packet_deinit(drv->sock_recv); if (drv->sock_xmit != NULL) l2_packet_deinit(drv->sock_xmit); if (drv->sock_raw) l2_packet_deinit(drv->sock_raw); free(drv); } static int madwifi_set_ssid(void *priv, const u8 *buf, int len) { struct madwifi_driver_data *drv = priv; struct iwreq iwr; memset(&iwr, 0, sizeof(iwr)); os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); iwr.u.essid.flags = 1; /* SSID active */ iwr.u.essid.pointer = (caddr_t) buf; iwr.u.essid.length = len + 1; if (ioctl(drv->ioctl_sock, SIOCSIWESSID, &iwr) < 0) { perror("ioctl[SIOCSIWESSID]"); printf("len=%d\n", len); return -1; } return 0; } static int madwifi_get_ssid(void *priv, u8 *buf, int len) { struct madwifi_driver_data *drv = priv; struct iwreq iwr; int ret = 0; memset(&iwr, 0, sizeof(iwr)); os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); iwr.u.essid.pointer = (caddr_t) buf; iwr.u.essid.length = len; if (ioctl(drv->ioctl_sock, SIOCGIWESSID, &iwr) < 0) { perror("ioctl[SIOCGIWESSID]"); ret = -1; } else ret = iwr.u.essid.length; return ret; } static int madwifi_set_countermeasures(void *priv, int enabled) { struct madwifi_driver_data *drv = priv; wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled); return set80211param(drv, IEEE80211_PARAM_COUNTERMEASURES, enabled); } static int madwifi_commit(void *priv) { struct madwifi_driver_data *drv = priv; return linux_set_iface_flags(drv->ioctl_sock, drv->iface, 1); } const struct wpa_driver_ops wpa_driver_madwifi_ops = { .name = "madwifi", .desc = "MADWIFI 802.11 support (Atheros, etc.)", .set_key = wpa_driver_madwifi_set_key, .hapd_init = madwifi_init, .hapd_deinit = madwifi_deinit, .set_ieee8021x = madwifi_set_ieee8021x, .set_privacy = madwifi_set_privacy, .get_seqnum = madwifi_get_seqnum, .flush = madwifi_flush, .set_generic_elem = madwifi_set_opt_ie, .sta_set_flags = madwifi_sta_set_flags, .read_sta_data = madwifi_read_sta_driver_data, .hapd_send_eapol = madwifi_send_eapol, .sta_disassoc = madwifi_sta_disassoc, .sta_deauth = madwifi_sta_deauth, .hapd_set_ssid = madwifi_set_ssid, .hapd_get_ssid = madwifi_get_ssid, .hapd_set_countermeasures = madwifi_set_countermeasures, .sta_clear_stats = madwifi_sta_clear_stats, .commit = madwifi_commit, .set_ap_wps_ie = madwifi_set_ap_wps_ie, .set_freq = madwifi_set_freq, }; wpa-2.1/src/drivers/driver_ndis.c000066400000000000000000002501051227414666700171060ustar00rootroot00000000000000/* * WPA Supplicant - Windows/NDIS driver interface * Copyright (c) 2004-2007, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifdef __CYGWIN__ /* Avoid some header file conflicts by not including standard headers for * cygwin builds when Packet32.h is included. */ #include "build_config.h" int close(int fd); #else /* __CYGWIN__ */ #include "includes.h" #endif /* __CYGWIN__ */ #ifdef CONFIG_USE_NDISUIO #include #else /* CONFIG_USE_NDISUIO */ #include #endif /* CONFIG_USE_NDISUIO */ #ifdef __MINGW32_VERSION #include #else /* __MINGW32_VERSION */ #include #endif /* __MINGW32_VERSION */ #ifdef _WIN32_WCE #include #include #include #endif /* _WIN32_WCE */ #include "common.h" #include "driver.h" #include "eloop.h" #include "common/ieee802_11_defs.h" #include "driver_ndis.h" int wpa_driver_register_event_cb(struct wpa_driver_ndis_data *drv); #ifdef CONFIG_NDIS_EVENTS_INTEGRATED void wpa_driver_ndis_event_pipe_cb(void *eloop_data, void *user_data); #endif /* CONFIG_NDIS_EVENTS_INTEGRATED */ static void wpa_driver_ndis_deinit(void *priv); static void wpa_driver_ndis_poll(void *drv); static void wpa_driver_ndis_poll_timeout(void *eloop_ctx, void *timeout_ctx); static int wpa_driver_ndis_adapter_init(struct wpa_driver_ndis_data *drv); static int wpa_driver_ndis_adapter_open(struct wpa_driver_ndis_data *drv); static void wpa_driver_ndis_adapter_close(struct wpa_driver_ndis_data *drv); static const u8 pae_group_addr[ETH_ALEN] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 }; /* FIX: to be removed once this can be compiled with the complete NDIS * header files */ #ifndef OID_802_11_BSSID #define OID_802_11_BSSID 0x0d010101 #define OID_802_11_SSID 0x0d010102 #define OID_802_11_INFRASTRUCTURE_MODE 0x0d010108 #define OID_802_11_ADD_WEP 0x0D010113 #define OID_802_11_REMOVE_WEP 0x0D010114 #define OID_802_11_DISASSOCIATE 0x0D010115 #define OID_802_11_BSSID_LIST 0x0d010217 #define OID_802_11_AUTHENTICATION_MODE 0x0d010118 #define OID_802_11_PRIVACY_FILTER 0x0d010119 #define OID_802_11_BSSID_LIST_SCAN 0x0d01011A #define OID_802_11_WEP_STATUS 0x0d01011B #define OID_802_11_ENCRYPTION_STATUS OID_802_11_WEP_STATUS #define OID_802_11_ADD_KEY 0x0d01011D #define OID_802_11_REMOVE_KEY 0x0d01011E #define OID_802_11_ASSOCIATION_INFORMATION 0x0d01011F #define OID_802_11_TEST 0x0d010120 #define OID_802_11_CAPABILITY 0x0d010122 #define OID_802_11_PMKID 0x0d010123 #define NDIS_802_11_LENGTH_SSID 32 #define NDIS_802_11_LENGTH_RATES 8 #define NDIS_802_11_LENGTH_RATES_EX 16 typedef UCHAR NDIS_802_11_MAC_ADDRESS[6]; typedef struct NDIS_802_11_SSID { ULONG SsidLength; UCHAR Ssid[NDIS_802_11_LENGTH_SSID]; } NDIS_802_11_SSID; typedef LONG NDIS_802_11_RSSI; typedef enum NDIS_802_11_NETWORK_TYPE { Ndis802_11FH, Ndis802_11DS, Ndis802_11OFDM5, Ndis802_11OFDM24, Ndis802_11NetworkTypeMax } NDIS_802_11_NETWORK_TYPE; typedef struct NDIS_802_11_CONFIGURATION_FH { ULONG Length; ULONG HopPattern; ULONG HopSet; ULONG DwellTime; } NDIS_802_11_CONFIGURATION_FH; typedef struct NDIS_802_11_CONFIGURATION { ULONG Length; ULONG BeaconPeriod; ULONG ATIMWindow; ULONG DSConfig; NDIS_802_11_CONFIGURATION_FH FHConfig; } NDIS_802_11_CONFIGURATION; typedef enum NDIS_802_11_NETWORK_INFRASTRUCTURE { Ndis802_11IBSS, Ndis802_11Infrastructure, Ndis802_11AutoUnknown, Ndis802_11InfrastructureMax } NDIS_802_11_NETWORK_INFRASTRUCTURE; typedef enum NDIS_802_11_AUTHENTICATION_MODE { Ndis802_11AuthModeOpen, Ndis802_11AuthModeShared, Ndis802_11AuthModeAutoSwitch, Ndis802_11AuthModeWPA, Ndis802_11AuthModeWPAPSK, Ndis802_11AuthModeWPANone, Ndis802_11AuthModeWPA2, Ndis802_11AuthModeWPA2PSK, Ndis802_11AuthModeMax } NDIS_802_11_AUTHENTICATION_MODE; typedef enum NDIS_802_11_WEP_STATUS { Ndis802_11WEPEnabled, Ndis802_11Encryption1Enabled = Ndis802_11WEPEnabled, Ndis802_11WEPDisabled, Ndis802_11EncryptionDisabled = Ndis802_11WEPDisabled, Ndis802_11WEPKeyAbsent, Ndis802_11Encryption1KeyAbsent = Ndis802_11WEPKeyAbsent, Ndis802_11WEPNotSupported, Ndis802_11EncryptionNotSupported = Ndis802_11WEPNotSupported, Ndis802_11Encryption2Enabled, Ndis802_11Encryption2KeyAbsent, Ndis802_11Encryption3Enabled, Ndis802_11Encryption3KeyAbsent } NDIS_802_11_WEP_STATUS, NDIS_802_11_ENCRYPTION_STATUS; typedef enum NDIS_802_11_PRIVACY_FILTER { Ndis802_11PrivFilterAcceptAll, Ndis802_11PrivFilter8021xWEP } NDIS_802_11_PRIVACY_FILTER; typedef UCHAR NDIS_802_11_RATES[NDIS_802_11_LENGTH_RATES]; typedef UCHAR NDIS_802_11_RATES_EX[NDIS_802_11_LENGTH_RATES_EX]; typedef struct NDIS_WLAN_BSSID_EX { ULONG Length; NDIS_802_11_MAC_ADDRESS MacAddress; /* BSSID */ UCHAR Reserved[2]; NDIS_802_11_SSID Ssid; ULONG Privacy; NDIS_802_11_RSSI Rssi; NDIS_802_11_NETWORK_TYPE NetworkTypeInUse; NDIS_802_11_CONFIGURATION Configuration; NDIS_802_11_NETWORK_INFRASTRUCTURE InfrastructureMode; NDIS_802_11_RATES_EX SupportedRates; ULONG IELength; UCHAR IEs[1]; } NDIS_WLAN_BSSID_EX; typedef struct NDIS_802_11_BSSID_LIST_EX { ULONG NumberOfItems; NDIS_WLAN_BSSID_EX Bssid[1]; } NDIS_802_11_BSSID_LIST_EX; typedef struct NDIS_802_11_FIXED_IEs { UCHAR Timestamp[8]; USHORT BeaconInterval; USHORT Capabilities; } NDIS_802_11_FIXED_IEs; typedef struct NDIS_802_11_WEP { ULONG Length; ULONG KeyIndex; ULONG KeyLength; UCHAR KeyMaterial[1]; } NDIS_802_11_WEP; typedef ULONG NDIS_802_11_KEY_INDEX; typedef ULONGLONG NDIS_802_11_KEY_RSC; typedef struct NDIS_802_11_KEY { ULONG Length; ULONG KeyIndex; ULONG KeyLength; NDIS_802_11_MAC_ADDRESS BSSID; NDIS_802_11_KEY_RSC KeyRSC; UCHAR KeyMaterial[1]; } NDIS_802_11_KEY; typedef struct NDIS_802_11_REMOVE_KEY { ULONG Length; ULONG KeyIndex; NDIS_802_11_MAC_ADDRESS BSSID; } NDIS_802_11_REMOVE_KEY; typedef struct NDIS_802_11_AI_REQFI { USHORT Capabilities; USHORT ListenInterval; NDIS_802_11_MAC_ADDRESS CurrentAPAddress; } NDIS_802_11_AI_REQFI; typedef struct NDIS_802_11_AI_RESFI { USHORT Capabilities; USHORT StatusCode; USHORT AssociationId; } NDIS_802_11_AI_RESFI; typedef struct NDIS_802_11_ASSOCIATION_INFORMATION { ULONG Length; USHORT AvailableRequestFixedIEs; NDIS_802_11_AI_REQFI RequestFixedIEs; ULONG RequestIELength; ULONG OffsetRequestIEs; USHORT AvailableResponseFixedIEs; NDIS_802_11_AI_RESFI ResponseFixedIEs; ULONG ResponseIELength; ULONG OffsetResponseIEs; } NDIS_802_11_ASSOCIATION_INFORMATION; typedef struct NDIS_802_11_AUTHENTICATION_ENCRYPTION { NDIS_802_11_AUTHENTICATION_MODE AuthModeSupported; NDIS_802_11_ENCRYPTION_STATUS EncryptStatusSupported; } NDIS_802_11_AUTHENTICATION_ENCRYPTION; typedef struct NDIS_802_11_CAPABILITY { ULONG Length; ULONG Version; ULONG NoOfPMKIDs; ULONG NoOfAuthEncryptPairsSupported; NDIS_802_11_AUTHENTICATION_ENCRYPTION AuthenticationEncryptionSupported[1]; } NDIS_802_11_CAPABILITY; typedef UCHAR NDIS_802_11_PMKID_VALUE[16]; typedef struct BSSID_INFO { NDIS_802_11_MAC_ADDRESS BSSID; NDIS_802_11_PMKID_VALUE PMKID; } BSSID_INFO; typedef struct NDIS_802_11_PMKID { ULONG Length; ULONG BSSIDInfoCount; BSSID_INFO BSSIDInfo[1]; } NDIS_802_11_PMKID; typedef enum NDIS_802_11_STATUS_TYPE { Ndis802_11StatusType_Authentication, Ndis802_11StatusType_PMKID_CandidateList = 2, Ndis802_11StatusTypeMax } NDIS_802_11_STATUS_TYPE; typedef struct NDIS_802_11_STATUS_INDICATION { NDIS_802_11_STATUS_TYPE StatusType; } NDIS_802_11_STATUS_INDICATION; typedef struct PMKID_CANDIDATE { NDIS_802_11_MAC_ADDRESS BSSID; ULONG Flags; } PMKID_CANDIDATE; #define NDIS_802_11_PMKID_CANDIDATE_PREAUTH_ENABLED 0x01 typedef struct NDIS_802_11_PMKID_CANDIDATE_LIST { ULONG Version; ULONG NumCandidates; PMKID_CANDIDATE CandidateList[1]; } NDIS_802_11_PMKID_CANDIDATE_LIST; typedef struct NDIS_802_11_AUTHENTICATION_REQUEST { ULONG Length; NDIS_802_11_MAC_ADDRESS Bssid; ULONG Flags; } NDIS_802_11_AUTHENTICATION_REQUEST; #define NDIS_802_11_AUTH_REQUEST_REAUTH 0x01 #define NDIS_802_11_AUTH_REQUEST_KEYUPDATE 0x02 #define NDIS_802_11_AUTH_REQUEST_PAIRWISE_ERROR 0x06 #define NDIS_802_11_AUTH_REQUEST_GROUP_ERROR 0x0E #endif /* OID_802_11_BSSID */ #ifndef OID_802_11_PMKID /* Platform SDK for XP did not include WPA2, so add needed definitions */ #define OID_802_11_CAPABILITY 0x0d010122 #define OID_802_11_PMKID 0x0d010123 #define Ndis802_11AuthModeWPA2 6 #define Ndis802_11AuthModeWPA2PSK 7 #define Ndis802_11StatusType_PMKID_CandidateList 2 typedef struct NDIS_802_11_AUTHENTICATION_ENCRYPTION { NDIS_802_11_AUTHENTICATION_MODE AuthModeSupported; NDIS_802_11_ENCRYPTION_STATUS EncryptStatusSupported; } NDIS_802_11_AUTHENTICATION_ENCRYPTION; typedef struct NDIS_802_11_CAPABILITY { ULONG Length; ULONG Version; ULONG NoOfPMKIDs; ULONG NoOfAuthEncryptPairsSupported; NDIS_802_11_AUTHENTICATION_ENCRYPTION AuthenticationEncryptionSupported[1]; } NDIS_802_11_CAPABILITY; typedef UCHAR NDIS_802_11_PMKID_VALUE[16]; typedef struct BSSID_INFO { NDIS_802_11_MAC_ADDRESS BSSID; NDIS_802_11_PMKID_VALUE PMKID; } BSSID_INFO; typedef struct NDIS_802_11_PMKID { ULONG Length; ULONG BSSIDInfoCount; BSSID_INFO BSSIDInfo[1]; } NDIS_802_11_PMKID; typedef struct PMKID_CANDIDATE { NDIS_802_11_MAC_ADDRESS BSSID; ULONG Flags; } PMKID_CANDIDATE; #define NDIS_802_11_PMKID_CANDIDATE_PREAUTH_ENABLED 0x01 typedef struct NDIS_802_11_PMKID_CANDIDATE_LIST { ULONG Version; ULONG NumCandidates; PMKID_CANDIDATE CandidateList[1]; } NDIS_802_11_PMKID_CANDIDATE_LIST; #endif /* OID_802_11_CAPABILITY */ #ifndef OID_DOT11_CURRENT_OPERATION_MODE /* Native 802.11 OIDs */ #define OID_DOT11_NDIS_START 0x0D010300 #define OID_DOT11_CURRENT_OPERATION_MODE (OID_DOT11_NDIS_START + 8) #define OID_DOT11_SCAN_REQUEST (OID_DOT11_NDIS_START + 11) typedef enum _DOT11_BSS_TYPE { dot11_BSS_type_infrastructure = 1, dot11_BSS_type_independent = 2, dot11_BSS_type_any = 3 } DOT11_BSS_TYPE, * PDOT11_BSS_TYPE; typedef UCHAR DOT11_MAC_ADDRESS[6]; typedef DOT11_MAC_ADDRESS * PDOT11_MAC_ADDRESS; typedef enum _DOT11_SCAN_TYPE { dot11_scan_type_active = 1, dot11_scan_type_passive = 2, dot11_scan_type_auto = 3, dot11_scan_type_forced = 0x80000000 } DOT11_SCAN_TYPE, * PDOT11_SCAN_TYPE; typedef struct _DOT11_SCAN_REQUEST_V2 { DOT11_BSS_TYPE dot11BSSType; DOT11_MAC_ADDRESS dot11BSSID; DOT11_SCAN_TYPE dot11ScanType; BOOLEAN bRestrictedScan; ULONG udot11SSIDsOffset; ULONG uNumOfdot11SSIDs; BOOLEAN bUseRequestIE; ULONG uRequestIDsOffset; ULONG uNumOfRequestIDs; ULONG uPhyTypeInfosOffset; ULONG uNumOfPhyTypeInfos; ULONG uIEsOffset; ULONG uIEsLength; UCHAR ucBuffer[1]; } DOT11_SCAN_REQUEST_V2, * PDOT11_SCAN_REQUEST_V2; #endif /* OID_DOT11_CURRENT_OPERATION_MODE */ #ifdef CONFIG_USE_NDISUIO #ifndef _WIN32_WCE #ifdef __MINGW32_VERSION typedef ULONG NDIS_OID; #endif /* __MINGW32_VERSION */ /* from nuiouser.h */ #define FSCTL_NDISUIO_BASE FILE_DEVICE_NETWORK #define _NDISUIO_CTL_CODE(_Function, _Method, _Access) \ CTL_CODE(FSCTL_NDISUIO_BASE, _Function, _Method, _Access) #define IOCTL_NDISUIO_OPEN_DEVICE \ _NDISUIO_CTL_CODE(0x200, METHOD_BUFFERED, \ FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_NDISUIO_QUERY_OID_VALUE \ _NDISUIO_CTL_CODE(0x201, METHOD_BUFFERED, \ FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_NDISUIO_SET_OID_VALUE \ _NDISUIO_CTL_CODE(0x205, METHOD_BUFFERED, \ FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_NDISUIO_SET_ETHER_TYPE \ _NDISUIO_CTL_CODE(0x202, METHOD_BUFFERED, \ FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_NDISUIO_QUERY_BINDING \ _NDISUIO_CTL_CODE(0x203, METHOD_BUFFERED, \ FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_NDISUIO_BIND_WAIT \ _NDISUIO_CTL_CODE(0x204, METHOD_BUFFERED, \ FILE_READ_ACCESS | FILE_WRITE_ACCESS) typedef struct _NDISUIO_QUERY_OID { NDIS_OID Oid; UCHAR Data[sizeof(ULONG)]; } NDISUIO_QUERY_OID, *PNDISUIO_QUERY_OID; typedef struct _NDISUIO_SET_OID { NDIS_OID Oid; UCHAR Data[sizeof(ULONG)]; } NDISUIO_SET_OID, *PNDISUIO_SET_OID; typedef struct _NDISUIO_QUERY_BINDING { ULONG BindingIndex; ULONG DeviceNameOffset; ULONG DeviceNameLength; ULONG DeviceDescrOffset; ULONG DeviceDescrLength; } NDISUIO_QUERY_BINDING, *PNDISUIO_QUERY_BINDING; #endif /* _WIN32_WCE */ #endif /* CONFIG_USE_NDISUIO */ static int ndis_get_oid(struct wpa_driver_ndis_data *drv, unsigned int oid, char *data, size_t len) { #ifdef CONFIG_USE_NDISUIO NDISUIO_QUERY_OID *o; size_t buflen = sizeof(*o) + len; DWORD written; int ret; size_t hdrlen; o = os_zalloc(buflen); if (o == NULL) return -1; o->Oid = oid; #ifdef _WIN32_WCE o->ptcDeviceName = drv->adapter_name; #endif /* _WIN32_WCE */ if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_QUERY_OID_VALUE, o, sizeof(NDISUIO_QUERY_OID), o, buflen, &written, NULL)) { wpa_printf(MSG_DEBUG, "NDIS: IOCTL_NDISUIO_QUERY_OID_VALUE " "failed (oid=%08x): %d", oid, (int) GetLastError()); os_free(o); return -1; } hdrlen = sizeof(NDISUIO_QUERY_OID) - sizeof(o->Data); if (written < hdrlen) { wpa_printf(MSG_DEBUG, "NDIS: query oid=%08x written (%d); " "too short", oid, (unsigned int) written); os_free(o); return -1; } written -= hdrlen; if (written > len) { wpa_printf(MSG_DEBUG, "NDIS: query oid=%08x written (%d) > " "len (%d)",oid, (unsigned int) written, len); os_free(o); return -1; } os_memcpy(data, o->Data, written); ret = written; os_free(o); return ret; #else /* CONFIG_USE_NDISUIO */ char *buf; PACKET_OID_DATA *o; int ret; buf = os_zalloc(sizeof(*o) + len); if (buf == NULL) return -1; o = (PACKET_OID_DATA *) buf; o->Oid = oid; o->Length = len; if (!PacketRequest(drv->adapter, FALSE, o)) { wpa_printf(MSG_DEBUG, "%s: oid=0x%x len (%d) failed", __func__, oid, len); os_free(buf); return -1; } if (o->Length > len) { wpa_printf(MSG_DEBUG, "%s: oid=0x%x Length (%d) > len (%d)", __func__, oid, (unsigned int) o->Length, len); os_free(buf); return -1; } os_memcpy(data, o->Data, o->Length); ret = o->Length; os_free(buf); return ret; #endif /* CONFIG_USE_NDISUIO */ } static int ndis_set_oid(struct wpa_driver_ndis_data *drv, unsigned int oid, const char *data, size_t len) { #ifdef CONFIG_USE_NDISUIO NDISUIO_SET_OID *o; size_t buflen, reallen; DWORD written; char txt[50]; os_snprintf(txt, sizeof(txt), "NDIS: Set OID %08x", oid); wpa_hexdump_key(MSG_MSGDUMP, txt, (const u8 *) data, len); buflen = sizeof(*o) + len; reallen = buflen - sizeof(o->Data); o = os_zalloc(buflen); if (o == NULL) return -1; o->Oid = oid; #ifdef _WIN32_WCE o->ptcDeviceName = drv->adapter_name; #endif /* _WIN32_WCE */ if (data) os_memcpy(o->Data, data, len); if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_SET_OID_VALUE, o, reallen, NULL, 0, &written, NULL)) { wpa_printf(MSG_DEBUG, "NDIS: IOCTL_NDISUIO_SET_OID_VALUE " "(oid=%08x) failed: %d", oid, (int) GetLastError()); os_free(o); return -1; } os_free(o); return 0; #else /* CONFIG_USE_NDISUIO */ char *buf; PACKET_OID_DATA *o; char txt[50]; os_snprintf(txt, sizeof(txt), "NDIS: Set OID %08x", oid); wpa_hexdump_key(MSG_MSGDUMP, txt, (const u8 *) data, len); buf = os_zalloc(sizeof(*o) + len); if (buf == NULL) return -1; o = (PACKET_OID_DATA *) buf; o->Oid = oid; o->Length = len; if (data) os_memcpy(o->Data, data, len); if (!PacketRequest(drv->adapter, TRUE, o)) { wpa_printf(MSG_DEBUG, "%s: oid=0x%x len (%d) failed", __func__, oid, len); os_free(buf); return -1; } os_free(buf); return 0; #endif /* CONFIG_USE_NDISUIO */ } static int ndis_set_auth_mode(struct wpa_driver_ndis_data *drv, int mode) { u32 auth_mode = mode; if (ndis_set_oid(drv, OID_802_11_AUTHENTICATION_MODE, (char *) &auth_mode, sizeof(auth_mode)) < 0) { wpa_printf(MSG_DEBUG, "NDIS: Failed to set " "OID_802_11_AUTHENTICATION_MODE (%d)", (int) auth_mode); return -1; } return 0; } static int ndis_get_auth_mode(struct wpa_driver_ndis_data *drv) { u32 auth_mode; int res; res = ndis_get_oid(drv, OID_802_11_AUTHENTICATION_MODE, (char *) &auth_mode, sizeof(auth_mode)); if (res != sizeof(auth_mode)) { wpa_printf(MSG_DEBUG, "NDIS: Failed to get " "OID_802_11_AUTHENTICATION_MODE"); return -1; } return auth_mode; } static int ndis_set_encr_status(struct wpa_driver_ndis_data *drv, int encr) { u32 encr_status = encr; if (ndis_set_oid(drv, OID_802_11_ENCRYPTION_STATUS, (char *) &encr_status, sizeof(encr_status)) < 0) { wpa_printf(MSG_DEBUG, "NDIS: Failed to set " "OID_802_11_ENCRYPTION_STATUS (%d)", encr); return -1; } return 0; } static int ndis_get_encr_status(struct wpa_driver_ndis_data *drv) { u32 encr; int res; res = ndis_get_oid(drv, OID_802_11_ENCRYPTION_STATUS, (char *) &encr, sizeof(encr)); if (res != sizeof(encr)) { wpa_printf(MSG_DEBUG, "NDIS: Failed to get " "OID_802_11_ENCRYPTION_STATUS"); return -1; } return encr; } static int wpa_driver_ndis_get_bssid(void *priv, u8 *bssid) { struct wpa_driver_ndis_data *drv = priv; if (drv->wired) { /* * Report PAE group address as the "BSSID" for wired * connection. */ os_memcpy(bssid, pae_group_addr, ETH_ALEN); return 0; } return ndis_get_oid(drv, OID_802_11_BSSID, (char *) bssid, ETH_ALEN) < 0 ? -1 : 0; } static int wpa_driver_ndis_get_ssid(void *priv, u8 *ssid) { struct wpa_driver_ndis_data *drv = priv; NDIS_802_11_SSID buf; int res; res = ndis_get_oid(drv, OID_802_11_SSID, (char *) &buf, sizeof(buf)); if (res < 4) { wpa_printf(MSG_DEBUG, "NDIS: Failed to get SSID"); if (drv->wired) { wpa_printf(MSG_DEBUG, "NDIS: Allow get_ssid failure " "with a wired interface"); return 0; } return -1; } os_memcpy(ssid, buf.Ssid, buf.SsidLength); return buf.SsidLength; } static int wpa_driver_ndis_set_ssid(struct wpa_driver_ndis_data *drv, const u8 *ssid, size_t ssid_len) { NDIS_802_11_SSID buf; os_memset(&buf, 0, sizeof(buf)); buf.SsidLength = ssid_len; os_memcpy(buf.Ssid, ssid, ssid_len); /* * Make sure radio is marked enabled here so that scan request will not * force SSID to be changed to a random one in order to enable radio at * that point. */ drv->radio_enabled = 1; return ndis_set_oid(drv, OID_802_11_SSID, (char *) &buf, sizeof(buf)); } /* Disconnect using OID_802_11_DISASSOCIATE. This will also turn the radio off. */ static int wpa_driver_ndis_radio_off(struct wpa_driver_ndis_data *drv) { drv->radio_enabled = 0; return ndis_set_oid(drv, OID_802_11_DISASSOCIATE, " ", 4); } /* Disconnect by setting SSID to random (i.e., likely not used). */ static int wpa_driver_ndis_disconnect(struct wpa_driver_ndis_data *drv) { char ssid[32]; int i; for (i = 0; i < 32; i++) ssid[i] = rand() & 0xff; return wpa_driver_ndis_set_ssid(drv, (u8 *) ssid, 32); } static int wpa_driver_ndis_deauthenticate(void *priv, const u8 *addr, int reason_code) { struct wpa_driver_ndis_data *drv = priv; return wpa_driver_ndis_disconnect(drv); } static void wpa_driver_ndis_scan_timeout(void *eloop_ctx, void *timeout_ctx) { wpa_printf(MSG_DEBUG, "Scan timeout - try to get results"); wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL); } static int wpa_driver_ndis_scan_native80211( struct wpa_driver_ndis_data *drv, struct wpa_driver_scan_params *params) { DOT11_SCAN_REQUEST_V2 req; int res; os_memset(&req, 0, sizeof(req)); req.dot11BSSType = dot11_BSS_type_any; os_memset(req.dot11BSSID, 0xff, ETH_ALEN); req.dot11ScanType = dot11_scan_type_auto; res = ndis_set_oid(drv, OID_DOT11_SCAN_REQUEST, (char *) &req, sizeof(req)); eloop_cancel_timeout(wpa_driver_ndis_scan_timeout, drv, drv->ctx); eloop_register_timeout(7, 0, wpa_driver_ndis_scan_timeout, drv, drv->ctx); return res; } static int wpa_driver_ndis_scan(void *priv, struct wpa_driver_scan_params *params) { struct wpa_driver_ndis_data *drv = priv; int res; if (drv->native80211) return wpa_driver_ndis_scan_native80211(drv, params); if (!drv->radio_enabled) { wpa_printf(MSG_DEBUG, "NDIS: turning radio on before the first" " scan"); if (wpa_driver_ndis_disconnect(drv) < 0) { wpa_printf(MSG_DEBUG, "NDIS: failed to enable radio"); } drv->radio_enabled = 1; } res = ndis_set_oid(drv, OID_802_11_BSSID_LIST_SCAN, " ", 4); eloop_cancel_timeout(wpa_driver_ndis_scan_timeout, drv, drv->ctx); eloop_register_timeout(7, 0, wpa_driver_ndis_scan_timeout, drv, drv->ctx); return res; } static const u8 * wpa_scan_get_ie(const struct wpa_scan_res *res, u8 ie) { const u8 *end, *pos; pos = (const u8 *) (res + 1); end = pos + res->ie_len; while (pos + 1 < end) { if (pos + 2 + pos[1] > end) break; if (pos[0] == ie) return pos; pos += 2 + pos[1]; } return NULL; } static struct wpa_scan_res * wpa_driver_ndis_add_scan_ssid( struct wpa_scan_res *r, NDIS_802_11_SSID *ssid) { struct wpa_scan_res *nr; u8 *pos; if (wpa_scan_get_ie(r, WLAN_EID_SSID)) return r; /* SSID IE already present */ if (ssid->SsidLength == 0 || ssid->SsidLength > 32) return r; /* No valid SSID inside scan data */ nr = os_realloc(r, sizeof(*r) + r->ie_len + 2 + ssid->SsidLength); if (nr == NULL) return r; pos = ((u8 *) (nr + 1)) + nr->ie_len; *pos++ = WLAN_EID_SSID; *pos++ = ssid->SsidLength; os_memcpy(pos, ssid->Ssid, ssid->SsidLength); nr->ie_len += 2 + ssid->SsidLength; return nr; } static struct wpa_scan_results * wpa_driver_ndis_get_scan_results(void *priv) { struct wpa_driver_ndis_data *drv = priv; NDIS_802_11_BSSID_LIST_EX *b; size_t blen, count, i; int len; char *pos; struct wpa_scan_results *results; struct wpa_scan_res *r; blen = 65535; b = os_zalloc(blen); if (b == NULL) return NULL; len = ndis_get_oid(drv, OID_802_11_BSSID_LIST, (char *) b, blen); if (len < 0) { wpa_printf(MSG_DEBUG, "NDIS: failed to get scan results"); os_free(b); return NULL; } count = b->NumberOfItems; results = os_zalloc(sizeof(*results)); if (results == NULL) { os_free(b); return NULL; } results->res = os_calloc(count, sizeof(struct wpa_scan_res *)); if (results->res == NULL) { os_free(results); os_free(b); return NULL; } pos = (char *) &b->Bssid[0]; for (i = 0; i < count; i++) { NDIS_WLAN_BSSID_EX *bss = (NDIS_WLAN_BSSID_EX *) pos; NDIS_802_11_FIXED_IEs *fixed; if (bss->IELength < sizeof(NDIS_802_11_FIXED_IEs)) { wpa_printf(MSG_DEBUG, "NDIS: too small IELength=%d", (int) bss->IELength); break; } if (((char *) bss->IEs) + bss->IELength > (char *) b + blen) { /* * Some NDIS drivers have been reported to include an * entry with an invalid IELength in scan results and * this has crashed wpa_supplicant, so validate the * returned value before using it. */ wpa_printf(MSG_DEBUG, "NDIS: skipped invalid scan " "result IE (BSSID=" MACSTR ") IELength=%d", MAC2STR(bss->MacAddress), (int) bss->IELength); break; } r = os_zalloc(sizeof(*r) + bss->IELength - sizeof(NDIS_802_11_FIXED_IEs)); if (r == NULL) break; os_memcpy(r->bssid, bss->MacAddress, ETH_ALEN); r->level = (int) bss->Rssi; r->freq = bss->Configuration.DSConfig / 1000; fixed = (NDIS_802_11_FIXED_IEs *) bss->IEs; r->beacon_int = WPA_GET_LE16((u8 *) &fixed->BeaconInterval); r->caps = WPA_GET_LE16((u8 *) &fixed->Capabilities); r->tsf = WPA_GET_LE64(fixed->Timestamp); os_memcpy(r + 1, bss->IEs + sizeof(NDIS_802_11_FIXED_IEs), bss->IELength - sizeof(NDIS_802_11_FIXED_IEs)); r->ie_len = bss->IELength - sizeof(NDIS_802_11_FIXED_IEs); r = wpa_driver_ndis_add_scan_ssid(r, &bss->Ssid); results->res[results->num++] = r; pos += bss->Length; if (pos > (char *) b + blen) break; } os_free(b); return results; } static int wpa_driver_ndis_remove_key(struct wpa_driver_ndis_data *drv, int key_idx, const u8 *addr, const u8 *bssid, int pairwise) { NDIS_802_11_REMOVE_KEY rkey; NDIS_802_11_KEY_INDEX index; int res, res2; os_memset(&rkey, 0, sizeof(rkey)); rkey.Length = sizeof(rkey); rkey.KeyIndex = key_idx; if (pairwise) rkey.KeyIndex |= 1 << 30; os_memcpy(rkey.BSSID, bssid, ETH_ALEN); res = ndis_set_oid(drv, OID_802_11_REMOVE_KEY, (char *) &rkey, sizeof(rkey)); if (!pairwise) { index = key_idx; res2 = ndis_set_oid(drv, OID_802_11_REMOVE_WEP, (char *) &index, sizeof(index)); } else res2 = 0; if (res < 0 && res2 < 0) return -1; return 0; } static int wpa_driver_ndis_add_wep(struct wpa_driver_ndis_data *drv, int pairwise, int key_idx, int set_tx, const u8 *key, size_t key_len) { NDIS_802_11_WEP *wep; size_t len; int res; len = 12 + key_len; wep = os_zalloc(len); if (wep == NULL) return -1; wep->Length = len; wep->KeyIndex = key_idx; if (set_tx) wep->KeyIndex |= 1 << 31; #if 0 /* Setting bit30 does not seem to work with some NDIS drivers */ if (pairwise) wep->KeyIndex |= 1 << 30; #endif wep->KeyLength = key_len; os_memcpy(wep->KeyMaterial, key, key_len); wpa_hexdump_key(MSG_MSGDUMP, "NDIS: OID_802_11_ADD_WEP", (u8 *) wep, len); res = ndis_set_oid(drv, OID_802_11_ADD_WEP, (char *) wep, len); os_free(wep); return res; } static int wpa_driver_ndis_set_key(const char *ifname, void *priv, enum wpa_alg alg, const u8 *addr, int key_idx, int set_tx, const u8 *seq, size_t seq_len, const u8 *key, size_t key_len) { struct wpa_driver_ndis_data *drv = priv; size_t len, i; NDIS_802_11_KEY *nkey; int res, pairwise; u8 bssid[ETH_ALEN]; if (addr == NULL || is_broadcast_ether_addr(addr)) { /* Group Key */ pairwise = 0; if (wpa_driver_ndis_get_bssid(drv, bssid) < 0) os_memset(bssid, 0xff, ETH_ALEN); } else { /* Pairwise Key */ pairwise = 1; os_memcpy(bssid, addr, ETH_ALEN); } if (alg == WPA_ALG_NONE || key_len == 0) { return wpa_driver_ndis_remove_key(drv, key_idx, addr, bssid, pairwise); } if (alg == WPA_ALG_WEP) { return wpa_driver_ndis_add_wep(drv, pairwise, key_idx, set_tx, key, key_len); } len = 12 + 6 + 6 + 8 + key_len; nkey = os_zalloc(len); if (nkey == NULL) return -1; nkey->Length = len; nkey->KeyIndex = key_idx; if (set_tx) nkey->KeyIndex |= 1 << 31; if (pairwise) nkey->KeyIndex |= 1 << 30; if (seq && seq_len) nkey->KeyIndex |= 1 << 29; nkey->KeyLength = key_len; os_memcpy(nkey->BSSID, bssid, ETH_ALEN); if (seq && seq_len) { for (i = 0; i < seq_len; i++) nkey->KeyRSC |= (ULONGLONG) seq[i] << (i * 8); } if (alg == WPA_ALG_TKIP && key_len == 32) { os_memcpy(nkey->KeyMaterial, key, 16); os_memcpy(nkey->KeyMaterial + 16, key + 24, 8); os_memcpy(nkey->KeyMaterial + 24, key + 16, 8); } else { os_memcpy(nkey->KeyMaterial, key, key_len); } wpa_hexdump_key(MSG_MSGDUMP, "NDIS: OID_802_11_ADD_KEY", (u8 *) nkey, len); res = ndis_set_oid(drv, OID_802_11_ADD_KEY, (char *) nkey, len); os_free(nkey); return res; } static int wpa_driver_ndis_associate(void *priv, struct wpa_driver_associate_params *params) { struct wpa_driver_ndis_data *drv = priv; u32 auth_mode, encr, priv_mode, mode; u8 bcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; drv->mode = params->mode; /* Note: Setting OID_802_11_INFRASTRUCTURE_MODE clears current keys, * so static WEP keys needs to be set again after this. */ if (params->mode == IEEE80211_MODE_IBSS) { mode = Ndis802_11IBSS; /* Need to make sure that BSSID polling is enabled for * IBSS mode. */ eloop_cancel_timeout(wpa_driver_ndis_poll_timeout, drv, NULL); eloop_register_timeout(1, 0, wpa_driver_ndis_poll_timeout, drv, NULL); } else mode = Ndis802_11Infrastructure; if (ndis_set_oid(drv, OID_802_11_INFRASTRUCTURE_MODE, (char *) &mode, sizeof(mode)) < 0) { wpa_printf(MSG_DEBUG, "NDIS: Failed to set " "OID_802_11_INFRASTRUCTURE_MODE (%d)", (int) mode); /* Try to continue anyway */ } if (params->key_mgmt_suite == WPA_KEY_MGMT_NONE || params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_NO_WPA) { /* Re-set WEP keys if static WEP configuration is used. */ int i; for (i = 0; i < 4; i++) { if (!params->wep_key[i]) continue; wpa_printf(MSG_DEBUG, "NDIS: Re-setting static WEP " "key %d", i); wpa_driver_ndis_set_key(drv->ifname, drv, WPA_ALG_WEP, bcast, i, i == params->wep_tx_keyidx, NULL, 0, params->wep_key[i], params->wep_key_len[i]); } } if (params->wpa_ie == NULL || params->wpa_ie_len == 0) { if (params->auth_alg & WPA_AUTH_ALG_SHARED) { if (params->auth_alg & WPA_AUTH_ALG_OPEN) auth_mode = Ndis802_11AuthModeAutoSwitch; else auth_mode = Ndis802_11AuthModeShared; } else auth_mode = Ndis802_11AuthModeOpen; priv_mode = Ndis802_11PrivFilterAcceptAll; } else if (params->wpa_ie[0] == WLAN_EID_RSN) { priv_mode = Ndis802_11PrivFilter8021xWEP; if (params->key_mgmt_suite == WPA_KEY_MGMT_PSK) auth_mode = Ndis802_11AuthModeWPA2PSK; else auth_mode = Ndis802_11AuthModeWPA2; #ifdef CONFIG_WPS } else if (params->key_mgmt_suite == WPA_KEY_MGMT_WPS) { auth_mode = Ndis802_11AuthModeOpen; priv_mode = Ndis802_11PrivFilterAcceptAll; if (params->wps == WPS_MODE_PRIVACY) { u8 dummy_key[5] = { 0x11, 0x22, 0x33, 0x44, 0x55 }; /* * Some NDIS drivers refuse to associate in open mode * configuration due to Privacy field mismatch, so use * a workaround to make the configuration look like * matching one for WPS provisioning. */ wpa_printf(MSG_DEBUG, "NDIS: Set dummy WEP key as a " "workaround to allow driver to associate " "for WPS"); wpa_driver_ndis_set_key(drv->ifname, drv, WPA_ALG_WEP, bcast, 0, 1, NULL, 0, dummy_key, sizeof(dummy_key)); } #endif /* CONFIG_WPS */ } else { priv_mode = Ndis802_11PrivFilter8021xWEP; if (params->key_mgmt_suite == WPA_KEY_MGMT_WPA_NONE) auth_mode = Ndis802_11AuthModeWPANone; else if (params->key_mgmt_suite == WPA_KEY_MGMT_PSK) auth_mode = Ndis802_11AuthModeWPAPSK; else auth_mode = Ndis802_11AuthModeWPA; } switch (params->pairwise_suite) { case WPA_CIPHER_CCMP: encr = Ndis802_11Encryption3Enabled; break; case WPA_CIPHER_TKIP: encr = Ndis802_11Encryption2Enabled; break; case WPA_CIPHER_WEP40: case WPA_CIPHER_WEP104: encr = Ndis802_11Encryption1Enabled; break; case WPA_CIPHER_NONE: #ifdef CONFIG_WPS if (params->wps == WPS_MODE_PRIVACY) { encr = Ndis802_11Encryption1Enabled; break; } #endif /* CONFIG_WPS */ if (params->group_suite == WPA_CIPHER_CCMP) encr = Ndis802_11Encryption3Enabled; else if (params->group_suite == WPA_CIPHER_TKIP) encr = Ndis802_11Encryption2Enabled; else encr = Ndis802_11EncryptionDisabled; break; default: #ifdef CONFIG_WPS if (params->wps == WPS_MODE_PRIVACY) { encr = Ndis802_11Encryption1Enabled; break; } #endif /* CONFIG_WPS */ encr = Ndis802_11EncryptionDisabled; break; }; if (ndis_set_oid(drv, OID_802_11_PRIVACY_FILTER, (char *) &priv_mode, sizeof(priv_mode)) < 0) { wpa_printf(MSG_DEBUG, "NDIS: Failed to set " "OID_802_11_PRIVACY_FILTER (%d)", (int) priv_mode); /* Try to continue anyway */ } ndis_set_auth_mode(drv, auth_mode); ndis_set_encr_status(drv, encr); if (params->bssid) { ndis_set_oid(drv, OID_802_11_BSSID, (char *) params->bssid, ETH_ALEN); drv->oid_bssid_set = 1; } else if (drv->oid_bssid_set) { ndis_set_oid(drv, OID_802_11_BSSID, "\xff\xff\xff\xff\xff\xff", ETH_ALEN); drv->oid_bssid_set = 0; } return wpa_driver_ndis_set_ssid(drv, params->ssid, params->ssid_len); } static int wpa_driver_ndis_set_pmkid(struct wpa_driver_ndis_data *drv) { int len, count, i, ret; struct ndis_pmkid_entry *entry; NDIS_802_11_PMKID *p; count = 0; entry = drv->pmkid; while (entry) { count++; if (count >= drv->no_of_pmkid) break; entry = entry->next; } len = 8 + count * sizeof(BSSID_INFO); p = os_zalloc(len); if (p == NULL) return -1; p->Length = len; p->BSSIDInfoCount = count; entry = drv->pmkid; for (i = 0; i < count; i++) { os_memcpy(&p->BSSIDInfo[i].BSSID, entry->bssid, ETH_ALEN); os_memcpy(&p->BSSIDInfo[i].PMKID, entry->pmkid, 16); entry = entry->next; } wpa_hexdump(MSG_MSGDUMP, "NDIS: OID_802_11_PMKID", (u8 *) p, len); ret = ndis_set_oid(drv, OID_802_11_PMKID, (char *) p, len); os_free(p); return ret; } static int wpa_driver_ndis_add_pmkid(void *priv, const u8 *bssid, const u8 *pmkid) { struct wpa_driver_ndis_data *drv = priv; struct ndis_pmkid_entry *entry, *prev; if (drv->no_of_pmkid == 0) return 0; prev = NULL; entry = drv->pmkid; while (entry) { if (os_memcmp(entry->bssid, bssid, ETH_ALEN) == 0) break; prev = entry; entry = entry->next; } if (entry) { /* Replace existing entry for this BSSID and move it into the * beginning of the list. */ os_memcpy(entry->pmkid, pmkid, 16); if (prev) { prev->next = entry->next; entry->next = drv->pmkid; drv->pmkid = entry; } } else { entry = os_malloc(sizeof(*entry)); if (entry) { os_memcpy(entry->bssid, bssid, ETH_ALEN); os_memcpy(entry->pmkid, pmkid, 16); entry->next = drv->pmkid; drv->pmkid = entry; } } return wpa_driver_ndis_set_pmkid(drv); } static int wpa_driver_ndis_remove_pmkid(void *priv, const u8 *bssid, const u8 *pmkid) { struct wpa_driver_ndis_data *drv = priv; struct ndis_pmkid_entry *entry, *prev; if (drv->no_of_pmkid == 0) return 0; entry = drv->pmkid; prev = NULL; while (entry) { if (os_memcmp(entry->bssid, bssid, ETH_ALEN) == 0 && os_memcmp(entry->pmkid, pmkid, 16) == 0) { if (prev) prev->next = entry->next; else drv->pmkid = entry->next; os_free(entry); break; } prev = entry; entry = entry->next; } return wpa_driver_ndis_set_pmkid(drv); } static int wpa_driver_ndis_flush_pmkid(void *priv) { struct wpa_driver_ndis_data *drv = priv; NDIS_802_11_PMKID p; struct ndis_pmkid_entry *pmkid, *prev; int prev_authmode, ret; if (drv->no_of_pmkid == 0) return 0; pmkid = drv->pmkid; drv->pmkid = NULL; while (pmkid) { prev = pmkid; pmkid = pmkid->next; os_free(prev); } /* * Some drivers may refuse OID_802_11_PMKID if authMode is not set to * WPA2, so change authMode temporarily, if needed. */ prev_authmode = ndis_get_auth_mode(drv); if (prev_authmode != Ndis802_11AuthModeWPA2) ndis_set_auth_mode(drv, Ndis802_11AuthModeWPA2); os_memset(&p, 0, sizeof(p)); p.Length = 8; p.BSSIDInfoCount = 0; wpa_hexdump(MSG_MSGDUMP, "NDIS: OID_802_11_PMKID (flush)", (u8 *) &p, 8); ret = ndis_set_oid(drv, OID_802_11_PMKID, (char *) &p, 8); if (prev_authmode != Ndis802_11AuthModeWPA2) ndis_set_auth_mode(drv, prev_authmode); return ret; } static int wpa_driver_ndis_get_associnfo(struct wpa_driver_ndis_data *drv) { char buf[512], *pos; NDIS_802_11_ASSOCIATION_INFORMATION *ai; int len; union wpa_event_data data; NDIS_802_11_BSSID_LIST_EX *b; size_t blen, i; len = ndis_get_oid(drv, OID_802_11_ASSOCIATION_INFORMATION, buf, sizeof(buf)); if (len < 0) { wpa_printf(MSG_DEBUG, "NDIS: failed to get association " "information"); return -1; } if (len > sizeof(buf)) { /* Some drivers seem to be producing incorrect length for this * data. Limit the length to the current buffer size to avoid * crashing in hexdump. The data seems to be otherwise valid, * so better try to use it. */ wpa_printf(MSG_DEBUG, "NDIS: ignored bogus association " "information length %d", len); len = ndis_get_oid(drv, OID_802_11_ASSOCIATION_INFORMATION, buf, sizeof(buf)); if (len < -1) { wpa_printf(MSG_DEBUG, "NDIS: re-reading association " "information failed"); return -1; } if (len > sizeof(buf)) { wpa_printf(MSG_DEBUG, "NDIS: ignored bogus association" " information length %d (re-read)", len); len = sizeof(buf); } } wpa_hexdump(MSG_MSGDUMP, "NDIS: association information", (u8 *) buf, len); if (len < sizeof(*ai)) { wpa_printf(MSG_DEBUG, "NDIS: too short association " "information"); return -1; } ai = (NDIS_802_11_ASSOCIATION_INFORMATION *) buf; wpa_printf(MSG_DEBUG, "NDIS: ReqFixed=0x%x RespFixed=0x%x off_req=%d " "off_resp=%d len_req=%d len_resp=%d", ai->AvailableRequestFixedIEs, ai->AvailableResponseFixedIEs, (int) ai->OffsetRequestIEs, (int) ai->OffsetResponseIEs, (int) ai->RequestIELength, (int) ai->ResponseIELength); if (ai->OffsetRequestIEs + ai->RequestIELength > (unsigned) len || ai->OffsetResponseIEs + ai->ResponseIELength > (unsigned) len) { wpa_printf(MSG_DEBUG, "NDIS: association information - " "IE overflow"); return -1; } wpa_hexdump(MSG_MSGDUMP, "NDIS: Request IEs", (u8 *) buf + ai->OffsetRequestIEs, ai->RequestIELength); wpa_hexdump(MSG_MSGDUMP, "NDIS: Response IEs", (u8 *) buf + ai->OffsetResponseIEs, ai->ResponseIELength); os_memset(&data, 0, sizeof(data)); data.assoc_info.req_ies = (u8 *) buf + ai->OffsetRequestIEs; data.assoc_info.req_ies_len = ai->RequestIELength; data.assoc_info.resp_ies = (u8 *) buf + ai->OffsetResponseIEs; data.assoc_info.resp_ies_len = ai->ResponseIELength; blen = 65535; b = os_zalloc(blen); if (b == NULL) goto skip_scan_results; len = ndis_get_oid(drv, OID_802_11_BSSID_LIST, (char *) b, blen); if (len < 0) { wpa_printf(MSG_DEBUG, "NDIS: failed to get scan results"); os_free(b); b = NULL; goto skip_scan_results; } wpa_printf(MSG_DEBUG, "NDIS: %d BSSID items to process for AssocInfo", (unsigned int) b->NumberOfItems); pos = (char *) &b->Bssid[0]; for (i = 0; i < b->NumberOfItems; i++) { NDIS_WLAN_BSSID_EX *bss = (NDIS_WLAN_BSSID_EX *) pos; if (os_memcmp(drv->bssid, bss->MacAddress, ETH_ALEN) == 0 && bss->IELength > sizeof(NDIS_802_11_FIXED_IEs)) { data.assoc_info.beacon_ies = ((u8 *) bss->IEs) + sizeof(NDIS_802_11_FIXED_IEs); data.assoc_info.beacon_ies_len = bss->IELength - sizeof(NDIS_802_11_FIXED_IEs); wpa_hexdump(MSG_MSGDUMP, "NDIS: Beacon IEs", data.assoc_info.beacon_ies, data.assoc_info.beacon_ies_len); break; } pos += bss->Length; if (pos > (char *) b + blen) break; } skip_scan_results: wpa_supplicant_event(drv->ctx, EVENT_ASSOCINFO, &data); os_free(b); return 0; } static void wpa_driver_ndis_poll_timeout(void *eloop_ctx, void *timeout_ctx) { struct wpa_driver_ndis_data *drv = eloop_ctx; u8 bssid[ETH_ALEN]; int poll; if (drv->wired) return; if (wpa_driver_ndis_get_bssid(drv, bssid)) { /* Disconnected */ if (!is_zero_ether_addr(drv->bssid)) { os_memset(drv->bssid, 0, ETH_ALEN); wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL); } } else { /* Connected */ if (os_memcmp(drv->bssid, bssid, ETH_ALEN) != 0) { os_memcpy(drv->bssid, bssid, ETH_ALEN); wpa_driver_ndis_get_associnfo(drv); wpa_supplicant_event(drv->ctx, EVENT_ASSOC, NULL); } } /* When using integrated NDIS event receiver, we can skip BSSID * polling when using infrastructure network. However, when using * IBSS mode, many driver do not seem to generate connection event, * so we need to enable BSSID polling to figure out when IBSS network * has been formed. */ poll = drv->mode == IEEE80211_MODE_IBSS; #ifndef CONFIG_NDIS_EVENTS_INTEGRATED #ifndef _WIN32_WCE poll = 1; #endif /* _WIN32_WCE */ #endif /* CONFIG_NDIS_EVENTS_INTEGRATED */ if (poll) { eloop_register_timeout(1, 0, wpa_driver_ndis_poll_timeout, drv, NULL); } } static void wpa_driver_ndis_poll(void *priv) { struct wpa_driver_ndis_data *drv = priv; eloop_cancel_timeout(wpa_driver_ndis_poll_timeout, drv, NULL); wpa_driver_ndis_poll_timeout(drv, NULL); } /* Called when driver generates Media Connect Event by calling * NdisMIndicateStatus() with NDIS_STATUS_MEDIA_CONNECT */ void wpa_driver_ndis_event_connect(struct wpa_driver_ndis_data *drv) { wpa_printf(MSG_DEBUG, "NDIS: Media Connect Event"); if (wpa_driver_ndis_get_bssid(drv, drv->bssid) == 0) { wpa_driver_ndis_get_associnfo(drv); wpa_supplicant_event(drv->ctx, EVENT_ASSOC, NULL); } } /* Called when driver generates Media Disconnect Event by calling * NdisMIndicateStatus() with NDIS_STATUS_MEDIA_DISCONNECT */ void wpa_driver_ndis_event_disconnect(struct wpa_driver_ndis_data *drv) { wpa_printf(MSG_DEBUG, "NDIS: Media Disconnect Event"); os_memset(drv->bssid, 0, ETH_ALEN); wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL); } static void wpa_driver_ndis_event_auth(struct wpa_driver_ndis_data *drv, const u8 *data, size_t data_len) { NDIS_802_11_AUTHENTICATION_REQUEST *req; int pairwise = 0, group = 0; union wpa_event_data event; if (data_len < sizeof(*req)) { wpa_printf(MSG_DEBUG, "NDIS: Too short Authentication Request " "Event (len=%d)", data_len); return; } req = (NDIS_802_11_AUTHENTICATION_REQUEST *) data; wpa_printf(MSG_DEBUG, "NDIS: Authentication Request Event: " "Bssid " MACSTR " Flags 0x%x", MAC2STR(req->Bssid), (int) req->Flags); if ((req->Flags & NDIS_802_11_AUTH_REQUEST_PAIRWISE_ERROR) == NDIS_802_11_AUTH_REQUEST_PAIRWISE_ERROR) pairwise = 1; else if ((req->Flags & NDIS_802_11_AUTH_REQUEST_GROUP_ERROR) == NDIS_802_11_AUTH_REQUEST_GROUP_ERROR) group = 1; if (pairwise || group) { os_memset(&event, 0, sizeof(event)); event.michael_mic_failure.unicast = pairwise; wpa_supplicant_event(drv->ctx, EVENT_MICHAEL_MIC_FAILURE, &event); } } static void wpa_driver_ndis_event_pmkid(struct wpa_driver_ndis_data *drv, const u8 *data, size_t data_len) { NDIS_802_11_PMKID_CANDIDATE_LIST *pmkid; size_t i; union wpa_event_data event; if (data_len < 8) { wpa_printf(MSG_DEBUG, "NDIS: Too short PMKID Candidate List " "Event (len=%d)", data_len); return; } pmkid = (NDIS_802_11_PMKID_CANDIDATE_LIST *) data; wpa_printf(MSG_DEBUG, "NDIS: PMKID Candidate List Event - Version %d " "NumCandidates %d", (int) pmkid->Version, (int) pmkid->NumCandidates); if (pmkid->Version != 1) { wpa_printf(MSG_DEBUG, "NDIS: Unsupported PMKID Candidate List " "Version %d", (int) pmkid->Version); return; } if (data_len < 8 + pmkid->NumCandidates * sizeof(PMKID_CANDIDATE)) { wpa_printf(MSG_DEBUG, "NDIS: PMKID Candidate List underflow"); return; } os_memset(&event, 0, sizeof(event)); for (i = 0; i < pmkid->NumCandidates; i++) { PMKID_CANDIDATE *p = &pmkid->CandidateList[i]; wpa_printf(MSG_DEBUG, "NDIS: %d: " MACSTR " Flags 0x%x", i, MAC2STR(p->BSSID), (int) p->Flags); os_memcpy(event.pmkid_candidate.bssid, p->BSSID, ETH_ALEN); event.pmkid_candidate.index = i; event.pmkid_candidate.preauth = p->Flags & NDIS_802_11_PMKID_CANDIDATE_PREAUTH_ENABLED; wpa_supplicant_event(drv->ctx, EVENT_PMKID_CANDIDATE, &event); } } /* Called when driver calls NdisMIndicateStatus() with * NDIS_STATUS_MEDIA_SPECIFIC_INDICATION */ void wpa_driver_ndis_event_media_specific(struct wpa_driver_ndis_data *drv, const u8 *data, size_t data_len) { NDIS_802_11_STATUS_INDICATION *status; if (data == NULL || data_len < sizeof(*status)) return; wpa_hexdump(MSG_DEBUG, "NDIS: Media Specific Indication", data, data_len); status = (NDIS_802_11_STATUS_INDICATION *) data; data += sizeof(status); data_len -= sizeof(status); switch (status->StatusType) { case Ndis802_11StatusType_Authentication: wpa_driver_ndis_event_auth(drv, data, data_len); break; case Ndis802_11StatusType_PMKID_CandidateList: wpa_driver_ndis_event_pmkid(drv, data, data_len); break; default: wpa_printf(MSG_DEBUG, "NDIS: Unknown StatusType %d", (int) status->StatusType); break; } } /* Called when an adapter is added */ void wpa_driver_ndis_event_adapter_arrival(struct wpa_driver_ndis_data *drv) { union wpa_event_data event; int i; wpa_printf(MSG_DEBUG, "NDIS: Notify Adapter Arrival"); for (i = 0; i < 30; i++) { /* Re-open Packet32/NDISUIO connection */ wpa_driver_ndis_adapter_close(drv); if (wpa_driver_ndis_adapter_init(drv) < 0 || wpa_driver_ndis_adapter_open(drv) < 0) { wpa_printf(MSG_DEBUG, "NDIS: Driver re-initialization " "(%d) failed", i); os_sleep(1, 0); } else { wpa_printf(MSG_DEBUG, "NDIS: Driver re-initialized"); break; } } os_memset(&event, 0, sizeof(event)); os_strlcpy(event.interface_status.ifname, drv->ifname, sizeof(event.interface_status.ifname)); event.interface_status.ievent = EVENT_INTERFACE_ADDED; wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event); } /* Called when an adapter is removed */ void wpa_driver_ndis_event_adapter_removal(struct wpa_driver_ndis_data *drv) { union wpa_event_data event; wpa_printf(MSG_DEBUG, "NDIS: Notify Adapter Removal"); os_memset(&event, 0, sizeof(event)); os_strlcpy(event.interface_status.ifname, drv->ifname, sizeof(event.interface_status.ifname)); event.interface_status.ievent = EVENT_INTERFACE_REMOVED; wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event); } static void wpa_driver_ndis_get_wpa_capability(struct wpa_driver_ndis_data *drv) { wpa_printf(MSG_DEBUG, "NDIS: verifying driver WPA capability"); if (ndis_set_auth_mode(drv, Ndis802_11AuthModeWPA) == 0 && ndis_get_auth_mode(drv) == Ndis802_11AuthModeWPA) { wpa_printf(MSG_DEBUG, "NDIS: WPA key management supported"); drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA; } if (ndis_set_auth_mode(drv, Ndis802_11AuthModeWPAPSK) == 0 && ndis_get_auth_mode(drv) == Ndis802_11AuthModeWPAPSK) { wpa_printf(MSG_DEBUG, "NDIS: WPA-PSK key management " "supported"); drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK; } if (ndis_set_encr_status(drv, Ndis802_11Encryption3Enabled) == 0 && ndis_get_encr_status(drv) == Ndis802_11Encryption3KeyAbsent) { wpa_printf(MSG_DEBUG, "NDIS: CCMP encryption supported"); drv->capa.enc |= WPA_DRIVER_CAPA_ENC_CCMP; } if (ndis_set_encr_status(drv, Ndis802_11Encryption2Enabled) == 0 && ndis_get_encr_status(drv) == Ndis802_11Encryption2KeyAbsent) { wpa_printf(MSG_DEBUG, "NDIS: TKIP encryption supported"); drv->capa.enc |= WPA_DRIVER_CAPA_ENC_TKIP; } if (ndis_set_encr_status(drv, Ndis802_11Encryption1Enabled) == 0 && ndis_get_encr_status(drv) == Ndis802_11Encryption1KeyAbsent) { wpa_printf(MSG_DEBUG, "NDIS: WEP encryption supported"); drv->capa.enc |= WPA_DRIVER_CAPA_ENC_WEP40 | WPA_DRIVER_CAPA_ENC_WEP104; } if (ndis_set_auth_mode(drv, Ndis802_11AuthModeShared) == 0 && ndis_get_auth_mode(drv) == Ndis802_11AuthModeShared) { drv->capa.auth |= WPA_DRIVER_AUTH_SHARED; } if (ndis_set_auth_mode(drv, Ndis802_11AuthModeOpen) == 0 && ndis_get_auth_mode(drv) == Ndis802_11AuthModeOpen) { drv->capa.auth |= WPA_DRIVER_AUTH_OPEN; } ndis_set_encr_status(drv, Ndis802_11EncryptionDisabled); /* Could also verify OID_802_11_ADD_KEY error reporting and * support for OID_802_11_ASSOCIATION_INFORMATION. */ if (drv->capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA && drv->capa.enc & (WPA_DRIVER_CAPA_ENC_TKIP | WPA_DRIVER_CAPA_ENC_CCMP)) { wpa_printf(MSG_DEBUG, "NDIS: driver supports WPA"); drv->has_capability = 1; } else { wpa_printf(MSG_DEBUG, "NDIS: no WPA support found"); } wpa_printf(MSG_DEBUG, "NDIS: driver capabilities: key_mgmt 0x%x " "enc 0x%x auth 0x%x", drv->capa.key_mgmt, drv->capa.enc, drv->capa.auth); } static void wpa_driver_ndis_get_capability(struct wpa_driver_ndis_data *drv) { char buf[512]; int len; size_t i; NDIS_802_11_CAPABILITY *c; drv->capa.flags = WPA_DRIVER_FLAGS_DRIVER_IE; len = ndis_get_oid(drv, OID_802_11_CAPABILITY, buf, sizeof(buf)); if (len < 0) { wpa_driver_ndis_get_wpa_capability(drv); return; } wpa_hexdump(MSG_MSGDUMP, "OID_802_11_CAPABILITY", (u8 *) buf, len); c = (NDIS_802_11_CAPABILITY *) buf; if (len < sizeof(*c) || c->Version != 2) { wpa_printf(MSG_DEBUG, "NDIS: unsupported " "OID_802_11_CAPABILITY data"); return; } wpa_printf(MSG_DEBUG, "NDIS: Driver supports OID_802_11_CAPABILITY - " "NoOfPMKIDs %d NoOfAuthEncrPairs %d", (int) c->NoOfPMKIDs, (int) c->NoOfAuthEncryptPairsSupported); drv->has_capability = 1; drv->no_of_pmkid = c->NoOfPMKIDs; for (i = 0; i < c->NoOfAuthEncryptPairsSupported; i++) { NDIS_802_11_AUTHENTICATION_ENCRYPTION *ae; ae = &c->AuthenticationEncryptionSupported[i]; if ((char *) (ae + 1) > buf + len) { wpa_printf(MSG_DEBUG, "NDIS: auth/encr pair list " "overflow"); break; } wpa_printf(MSG_MSGDUMP, "NDIS: %d - auth %d encr %d", i, (int) ae->AuthModeSupported, (int) ae->EncryptStatusSupported); switch (ae->AuthModeSupported) { case Ndis802_11AuthModeOpen: drv->capa.auth |= WPA_DRIVER_AUTH_OPEN; break; case Ndis802_11AuthModeShared: drv->capa.auth |= WPA_DRIVER_AUTH_SHARED; break; case Ndis802_11AuthModeWPA: drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA; break; case Ndis802_11AuthModeWPAPSK: drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK; break; case Ndis802_11AuthModeWPA2: drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA2; break; case Ndis802_11AuthModeWPA2PSK: drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK; break; case Ndis802_11AuthModeWPANone: drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE; break; default: break; } switch (ae->EncryptStatusSupported) { case Ndis802_11Encryption1Enabled: drv->capa.enc |= WPA_DRIVER_CAPA_ENC_WEP40; drv->capa.enc |= WPA_DRIVER_CAPA_ENC_WEP104; break; case Ndis802_11Encryption2Enabled: drv->capa.enc |= WPA_DRIVER_CAPA_ENC_TKIP; break; case Ndis802_11Encryption3Enabled: drv->capa.enc |= WPA_DRIVER_CAPA_ENC_CCMP; break; default: break; } } wpa_printf(MSG_DEBUG, "NDIS: driver capabilities: key_mgmt 0x%x " "enc 0x%x auth 0x%x", drv->capa.key_mgmt, drv->capa.enc, drv->capa.auth); } static int wpa_driver_ndis_get_capa(void *priv, struct wpa_driver_capa *capa) { struct wpa_driver_ndis_data *drv = priv; if (!drv->has_capability) return -1; os_memcpy(capa, &drv->capa, sizeof(*capa)); return 0; } static const char * wpa_driver_ndis_get_ifname(void *priv) { struct wpa_driver_ndis_data *drv = priv; return drv->ifname; } static const u8 * wpa_driver_ndis_get_mac_addr(void *priv) { struct wpa_driver_ndis_data *drv = priv; return drv->own_addr; } #ifdef _WIN32_WCE #define NDISUIO_MSG_SIZE (sizeof(NDISUIO_DEVICE_NOTIFICATION) + 512) static void ndisuio_notification_receive(void *eloop_data, void *user_ctx) { struct wpa_driver_ndis_data *drv = eloop_data; NDISUIO_DEVICE_NOTIFICATION *hdr; u8 buf[NDISUIO_MSG_SIZE]; DWORD len, flags; if (!ReadMsgQueue(drv->event_queue, buf, NDISUIO_MSG_SIZE, &len, 0, &flags)) { wpa_printf(MSG_DEBUG, "ndisuio_notification_receive: " "ReadMsgQueue failed: %d", (int) GetLastError()); return; } if (len < sizeof(NDISUIO_DEVICE_NOTIFICATION)) { wpa_printf(MSG_DEBUG, "ndisuio_notification_receive: " "Too short message (len=%d)", (int) len); return; } hdr = (NDISUIO_DEVICE_NOTIFICATION *) buf; wpa_printf(MSG_DEBUG, "NDIS: Notification received: len=%d type=0x%x", (int) len, hdr->dwNotificationType); switch (hdr->dwNotificationType) { #ifdef NDISUIO_NOTIFICATION_ADAPTER_ARRIVAL case NDISUIO_NOTIFICATION_ADAPTER_ARRIVAL: wpa_printf(MSG_DEBUG, "NDIS: ADAPTER_ARRIVAL"); wpa_driver_ndis_event_adapter_arrival(drv); break; #endif #ifdef NDISUIO_NOTIFICATION_ADAPTER_REMOVAL case NDISUIO_NOTIFICATION_ADAPTER_REMOVAL: wpa_printf(MSG_DEBUG, "NDIS: ADAPTER_REMOVAL"); wpa_driver_ndis_event_adapter_removal(drv); break; #endif case NDISUIO_NOTIFICATION_MEDIA_CONNECT: wpa_printf(MSG_DEBUG, "NDIS: MEDIA_CONNECT"); SetEvent(drv->connected_event); wpa_driver_ndis_event_connect(drv); break; case NDISUIO_NOTIFICATION_MEDIA_DISCONNECT: ResetEvent(drv->connected_event); wpa_printf(MSG_DEBUG, "NDIS: MEDIA_DISCONNECT"); wpa_driver_ndis_event_disconnect(drv); break; case NDISUIO_NOTIFICATION_MEDIA_SPECIFIC_NOTIFICATION: wpa_printf(MSG_DEBUG, "NDIS: MEDIA_SPECIFIC_NOTIFICATION"); #if _WIN32_WCE == 420 || _WIN32_WCE == 0x420 wpa_driver_ndis_event_media_specific( drv, hdr->pvStatusBuffer, hdr->uiStatusBufferSize); #else wpa_driver_ndis_event_media_specific( drv, ((const u8 *) hdr) + hdr->uiOffsetToStatusBuffer, (size_t) hdr->uiStatusBufferSize); #endif break; default: wpa_printf(MSG_DEBUG, "NDIS: Unknown notification type 0x%x", hdr->dwNotificationType); break; } } static void ndisuio_notification_deinit(struct wpa_driver_ndis_data *drv) { NDISUIO_REQUEST_NOTIFICATION req; memset(&req, 0, sizeof(req)); req.hMsgQueue = drv->event_queue; req.dwNotificationTypes = 0; if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_REQUEST_NOTIFICATION, &req, sizeof(req), NULL, 0, NULL, NULL)) { wpa_printf(MSG_INFO, "ndisuio_notification_deinit: " "IOCTL_NDISUIO_REQUEST_NOTIFICATION failed: %d", (int) GetLastError()); } if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_CANCEL_NOTIFICATION, NULL, 0, NULL, 0, NULL, NULL)) { wpa_printf(MSG_INFO, "ndisuio_notification_deinit: " "IOCTL_NDISUIO_CANCEL_NOTIFICATION failed: %d", (int) GetLastError()); } if (drv->event_queue) { eloop_unregister_event(drv->event_queue, sizeof(drv->event_queue)); CloseHandle(drv->event_queue); drv->event_queue = NULL; } if (drv->connected_event) { CloseHandle(drv->connected_event); drv->connected_event = NULL; } } static int ndisuio_notification_init(struct wpa_driver_ndis_data *drv) { MSGQUEUEOPTIONS opt; NDISUIO_REQUEST_NOTIFICATION req; drv->connected_event = CreateEvent(NULL, TRUE, FALSE, TEXT("WpaSupplicantConnected")); if (drv->connected_event == NULL) { wpa_printf(MSG_INFO, "ndisuio_notification_init: " "CreateEvent failed: %d", (int) GetLastError()); return -1; } memset(&opt, 0, sizeof(opt)); opt.dwSize = sizeof(opt); opt.dwMaxMessages = 5; opt.cbMaxMessage = NDISUIO_MSG_SIZE; opt.bReadAccess = TRUE; drv->event_queue = CreateMsgQueue(NULL, &opt); if (drv->event_queue == NULL) { wpa_printf(MSG_INFO, "ndisuio_notification_init: " "CreateMsgQueue failed: %d", (int) GetLastError()); ndisuio_notification_deinit(drv); return -1; } memset(&req, 0, sizeof(req)); req.hMsgQueue = drv->event_queue; req.dwNotificationTypes = #ifdef NDISUIO_NOTIFICATION_ADAPTER_ARRIVAL NDISUIO_NOTIFICATION_ADAPTER_ARRIVAL | #endif #ifdef NDISUIO_NOTIFICATION_ADAPTER_REMOVAL NDISUIO_NOTIFICATION_ADAPTER_REMOVAL | #endif NDISUIO_NOTIFICATION_MEDIA_CONNECT | NDISUIO_NOTIFICATION_MEDIA_DISCONNECT | NDISUIO_NOTIFICATION_MEDIA_SPECIFIC_NOTIFICATION; if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_REQUEST_NOTIFICATION, &req, sizeof(req), NULL, 0, NULL, NULL)) { wpa_printf(MSG_INFO, "ndisuio_notification_init: " "IOCTL_NDISUIO_REQUEST_NOTIFICATION failed: %d", (int) GetLastError()); ndisuio_notification_deinit(drv); return -1; } eloop_register_event(drv->event_queue, sizeof(drv->event_queue), ndisuio_notification_receive, drv, NULL); return 0; } #endif /* _WIN32_WCE */ static int wpa_driver_ndis_get_names(struct wpa_driver_ndis_data *drv) { #ifdef CONFIG_USE_NDISUIO NDISUIO_QUERY_BINDING *b; size_t blen = sizeof(*b) + 1024; int i, error, found = 0; DWORD written; char name[256], desc[256], *dpos; WCHAR *pos; size_t j, len, dlen; b = os_malloc(blen); if (b == NULL) return -1; for (i = 0; ; i++) { os_memset(b, 0, blen); b->BindingIndex = i; if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_QUERY_BINDING, b, sizeof(NDISUIO_QUERY_BINDING), b, blen, &written, NULL)) { error = (int) GetLastError(); if (error == ERROR_NO_MORE_ITEMS) break; wpa_printf(MSG_DEBUG, "IOCTL_NDISUIO_QUERY_BINDING " "failed: %d", error); break; } pos = (WCHAR *) ((char *) b + b->DeviceNameOffset); len = b->DeviceNameLength; if (len >= sizeof(name)) len = sizeof(name) - 1; for (j = 0; j < len; j++) name[j] = (char) pos[j]; name[len] = '\0'; pos = (WCHAR *) ((char *) b + b->DeviceDescrOffset); len = b->DeviceDescrLength; if (len >= sizeof(desc)) len = sizeof(desc) - 1; for (j = 0; j < len; j++) desc[j] = (char) pos[j]; desc[len] = '\0'; wpa_printf(MSG_DEBUG, "NDIS: %d - %s - %s", i, name, desc); if (os_strstr(name, drv->ifname)) { wpa_printf(MSG_DEBUG, "NDIS: Interface name match"); found = 1; break; } if (os_strncmp(desc, drv->ifname, os_strlen(drv->ifname)) == 0) { wpa_printf(MSG_DEBUG, "NDIS: Interface description " "match"); found = 1; break; } } if (!found) { wpa_printf(MSG_DEBUG, "NDIS: Could not find interface '%s'", drv->ifname); os_free(b); return -1; } os_strlcpy(drv->ifname, os_strncmp(name, "\\DEVICE\\", 8) == 0 ? name + 8 : name, sizeof(drv->ifname)); #ifdef _WIN32_WCE drv->adapter_name = wpa_strdup_tchar(drv->ifname); if (drv->adapter_name == NULL) { wpa_printf(MSG_ERROR, "NDIS: Failed to allocate memory for " "adapter name"); os_free(b); return -1; } #endif /* _WIN32_WCE */ dpos = os_strstr(desc, " - "); if (dpos) dlen = dpos - desc; else dlen = os_strlen(desc); drv->adapter_desc = dup_binstr(desc, dlen); os_free(b); if (drv->adapter_desc == NULL) return -1; wpa_printf(MSG_DEBUG, "NDIS: Adapter description prefix '%s'", drv->adapter_desc); return 0; #else /* CONFIG_USE_NDISUIO */ PTSTR _names; char *names, *pos, *pos2; ULONG len; BOOLEAN res; #define MAX_ADAPTERS 32 char *name[MAX_ADAPTERS]; char *desc[MAX_ADAPTERS]; int num_name, num_desc, i, found_name, found_desc; size_t dlen; wpa_printf(MSG_DEBUG, "NDIS: Packet.dll version: %s", PacketGetVersion()); len = 8192; _names = os_zalloc(len); if (_names == NULL) return -1; res = PacketGetAdapterNames(_names, &len); if (!res && len > 8192) { os_free(_names); _names = os_zalloc(len); if (_names == NULL) return -1; res = PacketGetAdapterNames(_names, &len); } if (!res) { wpa_printf(MSG_ERROR, "NDIS: Failed to get adapter list " "(PacketGetAdapterNames)"); os_free(_names); return -1; } names = (char *) _names; if (names[0] && names[1] == '\0' && names[2] && names[3] == '\0') { wpa_printf(MSG_DEBUG, "NDIS: Looks like adapter names are in " "UNICODE"); /* Convert to ASCII */ pos2 = pos = names; while (pos2 < names + len) { if (pos2[0] == '\0' && pos2[1] == '\0' && pos2[2] == '\0' && pos2[3] == '\0') { pos2 += 4; break; } *pos++ = pos2[0]; pos2 += 2; } os_memcpy(pos + 2, names, pos - names); pos += 2; } else pos = names; num_name = 0; while (pos < names + len) { name[num_name] = pos; while (*pos && pos < names + len) pos++; if (pos + 1 >= names + len) { os_free(names); return -1; } pos++; num_name++; if (num_name >= MAX_ADAPTERS) { wpa_printf(MSG_DEBUG, "NDIS: Too many adapters"); os_free(names); return -1; } if (*pos == '\0') { wpa_printf(MSG_DEBUG, "NDIS: %d adapter names found", num_name); pos++; break; } } num_desc = 0; while (pos < names + len) { desc[num_desc] = pos; while (*pos && pos < names + len) pos++; if (pos + 1 >= names + len) { os_free(names); return -1; } pos++; num_desc++; if (num_desc >= MAX_ADAPTERS) { wpa_printf(MSG_DEBUG, "NDIS: Too many adapter " "descriptions"); os_free(names); return -1; } if (*pos == '\0') { wpa_printf(MSG_DEBUG, "NDIS: %d adapter descriptions " "found", num_name); pos++; break; } } /* * Windows 98 with Packet.dll 3.0 alpha3 does not include adapter * descriptions. Fill in dummy descriptors to work around this. */ while (num_desc < num_name) desc[num_desc++] = "dummy description"; if (num_name != num_desc) { wpa_printf(MSG_DEBUG, "NDIS: mismatch in adapter name and " "description counts (%d != %d)", num_name, num_desc); os_free(names); return -1; } found_name = found_desc = -1; for (i = 0; i < num_name; i++) { wpa_printf(MSG_DEBUG, "NDIS: %d - %s - %s", i, name[i], desc[i]); if (found_name == -1 && os_strstr(name[i], drv->ifname)) found_name = i; if (found_desc == -1 && os_strncmp(desc[i], drv->ifname, os_strlen(drv->ifname)) == 0) found_desc = i; } if (found_name < 0 && found_desc >= 0) { wpa_printf(MSG_DEBUG, "NDIS: Matched interface '%s' based on " "description '%s'", name[found_desc], desc[found_desc]); found_name = found_desc; os_strlcpy(drv->ifname, os_strncmp(name[found_desc], "\\Device\\NPF_", 12) == 0 ? name[found_desc] + 12 : name[found_desc], sizeof(drv->ifname)); } if (found_name < 0) { wpa_printf(MSG_DEBUG, "NDIS: Could not find interface '%s'", drv->ifname); os_free(names); return -1; } i = found_name; pos = os_strrchr(desc[i], '('); if (pos) { dlen = pos - desc[i]; pos--; if (pos > desc[i] && *pos == ' ') dlen--; } else { dlen = os_strlen(desc[i]); } drv->adapter_desc = dup_binstr(desc[i], dlen); os_free(names); if (drv->adapter_desc == NULL) return -1; wpa_printf(MSG_DEBUG, "NDIS: Adapter description prefix '%s'", drv->adapter_desc); return 0; #endif /* CONFIG_USE_NDISUIO */ } #if defined(CONFIG_NATIVE_WINDOWS) || defined(__CYGWIN__) #ifndef _WIN32_WCE /* * These structures are undocumented for WinXP; only WinCE version is * documented. These would be included wzcsapi.h if it were available. Some * changes here have been needed to make the structures match with WinXP SP2. * It is unclear whether these work with any other version. */ typedef struct { LPWSTR wszGuid; } INTF_KEY_ENTRY, *PINTF_KEY_ENTRY; typedef struct { DWORD dwNumIntfs; PINTF_KEY_ENTRY pIntfs; } INTFS_KEY_TABLE, *PINTFS_KEY_TABLE; typedef struct { DWORD dwDataLen; LPBYTE pData; } RAW_DATA, *PRAW_DATA; typedef struct { LPWSTR wszGuid; LPWSTR wszDescr; ULONG ulMediaState; ULONG ulMediaType; ULONG ulPhysicalMediaType; INT nInfraMode; INT nAuthMode; INT nWepStatus; #ifndef _WIN32_WCE u8 pad[2]; /* why is this needed? */ #endif /* _WIN32_WCE */ DWORD dwCtlFlags; DWORD dwCapabilities; /* something added for WinXP SP2(?) */ RAW_DATA rdSSID; RAW_DATA rdBSSID; RAW_DATA rdBSSIDList; RAW_DATA rdStSSIDList; RAW_DATA rdCtrlData; #ifdef UNDER_CE BOOL bInitialized; #endif DWORD nWPAMCastCipher; /* add some extra buffer for later additions since this interface is * far from stable */ u8 later_additions[100]; } INTF_ENTRY, *PINTF_ENTRY; #define INTF_ALL 0xffffffff #define INTF_ALL_FLAGS 0x0000ffff #define INTF_CTLFLAGS 0x00000010 #define INTFCTL_ENABLED 0x8000 #endif /* _WIN32_WCE */ #ifdef _WIN32_WCE static int wpa_driver_ndis_rebind_adapter(struct wpa_driver_ndis_data *drv) { HANDLE ndis; TCHAR multi[100]; int len; len = _tcslen(drv->adapter_name); if (len > 80) return -1; ndis = CreateFile(DD_NDIS_DEVICE_NAME, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); if (ndis == INVALID_HANDLE_VALUE) { wpa_printf(MSG_DEBUG, "NDIS: Failed to open file to NDIS " "device: %d", (int) GetLastError()); return -1; } len++; memcpy(multi, drv->adapter_name, len * sizeof(TCHAR)); memcpy(&multi[len], TEXT("NDISUIO\0"), 9 * sizeof(TCHAR)); len += 9; if (!DeviceIoControl(ndis, IOCTL_NDIS_REBIND_ADAPTER, multi, len * sizeof(TCHAR), NULL, 0, NULL, NULL)) { wpa_printf(MSG_DEBUG, "NDIS: IOCTL_NDIS_REBIND_ADAPTER " "failed: 0x%x", (int) GetLastError()); wpa_hexdump_ascii(MSG_DEBUG, "NDIS: rebind multi_sz", (u8 *) multi, len * sizeof(TCHAR)); CloseHandle(ndis); return -1; } CloseHandle(ndis); wpa_printf(MSG_DEBUG, "NDIS: Requested NDIS rebind of NDISUIO " "protocol"); return 0; } #endif /* _WIN32_WCE */ static int wpa_driver_ndis_set_wzc(struct wpa_driver_ndis_data *drv, int enable) { #ifdef _WIN32_WCE HKEY hk, hk2; LONG ret; DWORD i, hnd, len; TCHAR keyname[256], devname[256]; #define WZC_DRIVER TEXT("Drivers\\BuiltIn\\ZeroConfig") if (enable) { HANDLE h; h = ActivateDeviceEx(WZC_DRIVER, NULL, 0, NULL); if (h == INVALID_HANDLE_VALUE || h == 0) { wpa_printf(MSG_DEBUG, "NDIS: Failed to re-enable WZC " "- ActivateDeviceEx failed: %d", (int) GetLastError()); return -1; } wpa_printf(MSG_DEBUG, "NDIS: WZC re-enabled"); return wpa_driver_ndis_rebind_adapter(drv); } /* * Unfortunately, just disabling the WZC for an interface is not enough * to free NDISUIO for us, so need to disable and unload WZC completely * for now when using WinCE with NDISUIO. In addition, must request * NDISUIO protocol to be rebound to the adapter in order to free the * NDISUIO binding that WZC hold before us. */ /* Enumerate HKLM\Drivers\Active\* to find a handle to WZC. */ ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, DEVLOAD_ACTIVE_KEY, 0, 0, &hk); if (ret != ERROR_SUCCESS) { wpa_printf(MSG_DEBUG, "NDIS: RegOpenKeyEx(DEVLOAD_ACTIVE_KEY) " "failed: %d %d", (int) ret, (int) GetLastError()); return -1; } for (i = 0; ; i++) { len = sizeof(keyname); ret = RegEnumKeyEx(hk, i, keyname, &len, NULL, NULL, NULL, NULL); if (ret != ERROR_SUCCESS) { wpa_printf(MSG_DEBUG, "NDIS: Could not find active " "WZC - assuming it is not running."); RegCloseKey(hk); return -1; } ret = RegOpenKeyEx(hk, keyname, 0, 0, &hk2); if (ret != ERROR_SUCCESS) { wpa_printf(MSG_DEBUG, "NDIS: RegOpenKeyEx(active dev) " "failed: %d %d", (int) ret, (int) GetLastError()); continue; } len = sizeof(devname); ret = RegQueryValueEx(hk2, DEVLOAD_DEVKEY_VALNAME, NULL, NULL, (LPBYTE) devname, &len); if (ret != ERROR_SUCCESS) { wpa_printf(MSG_DEBUG, "NDIS: RegQueryValueEx(" "DEVKEY_VALNAME) failed: %d %d", (int) ret, (int) GetLastError()); RegCloseKey(hk2); continue; } if (_tcscmp(devname, WZC_DRIVER) == 0) break; RegCloseKey(hk2); } RegCloseKey(hk); /* Found WZC - get handle to it. */ len = sizeof(hnd); ret = RegQueryValueEx(hk2, DEVLOAD_HANDLE_VALNAME, NULL, NULL, (PUCHAR) &hnd, &len); if (ret != ERROR_SUCCESS) { wpa_printf(MSG_DEBUG, "NDIS: RegQueryValueEx(HANDLE_VALNAME) " "failed: %d %d", (int) ret, (int) GetLastError()); RegCloseKey(hk2); return -1; } RegCloseKey(hk2); /* Deactivate WZC */ if (!DeactivateDevice((HANDLE) hnd)) { wpa_printf(MSG_DEBUG, "NDIS: DeactivateDevice failed: %d", (int) GetLastError()); return -1; } wpa_printf(MSG_DEBUG, "NDIS: Disabled WZC temporarily"); drv->wzc_disabled = 1; return wpa_driver_ndis_rebind_adapter(drv); #else /* _WIN32_WCE */ HMODULE hm; DWORD (WINAPI *wzc_enum_interf)(LPWSTR pSrvAddr, PINTFS_KEY_TABLE pIntfs); DWORD (WINAPI *wzc_query_interf)(LPWSTR pSrvAddr, DWORD dwInFlags, PINTF_ENTRY pIntf, LPDWORD pdwOutFlags); DWORD (WINAPI *wzc_set_interf)(LPWSTR pSrvAddr, DWORD dwInFlags, PINTF_ENTRY pIntf, LPDWORD pdwOutFlags); int ret = -1, j; DWORD res; INTFS_KEY_TABLE guids; INTF_ENTRY intf; char guid[128]; WCHAR *pos; DWORD flags, i; hm = LoadLibrary(TEXT("wzcsapi.dll")); if (hm == NULL) { wpa_printf(MSG_DEBUG, "NDIS: Failed to load wzcsapi.dll (%u) " "- WZC probably not running", (unsigned int) GetLastError()); return -1; } #ifdef _WIN32_WCE wzc_enum_interf = (void *) GetProcAddressA(hm, "WZCEnumInterfaces"); wzc_query_interf = (void *) GetProcAddressA(hm, "WZCQueryInterface"); wzc_set_interf = (void *) GetProcAddressA(hm, "WZCSetInterface"); #else /* _WIN32_WCE */ wzc_enum_interf = (void *) GetProcAddress(hm, "WZCEnumInterfaces"); wzc_query_interf = (void *) GetProcAddress(hm, "WZCQueryInterface"); wzc_set_interf = (void *) GetProcAddress(hm, "WZCSetInterface"); #endif /* _WIN32_WCE */ if (wzc_enum_interf == NULL || wzc_query_interf == NULL || wzc_set_interf == NULL) { wpa_printf(MSG_DEBUG, "NDIS: WZCEnumInterfaces, " "WZCQueryInterface, or WZCSetInterface not found " "in wzcsapi.dll"); goto fail; } os_memset(&guids, 0, sizeof(guids)); res = wzc_enum_interf(NULL, &guids); if (res != 0) { wpa_printf(MSG_DEBUG, "NDIS: WZCEnumInterfaces failed: %d; " "WZC service is apparently not running", (int) res); goto fail; } wpa_printf(MSG_DEBUG, "NDIS: WZCEnumInterfaces: %d interfaces", (int) guids.dwNumIntfs); for (i = 0; i < guids.dwNumIntfs; i++) { pos = guids.pIntfs[i].wszGuid; for (j = 0; j < sizeof(guid); j++) { guid[j] = (char) *pos; if (*pos == 0) break; pos++; } guid[sizeof(guid) - 1] = '\0'; wpa_printf(MSG_DEBUG, "NDIS: intfs %d GUID '%s'", (int) i, guid); if (os_strstr(drv->ifname, guid) == NULL) continue; wpa_printf(MSG_DEBUG, "NDIS: Current interface found from " "WZC"); break; } if (i >= guids.dwNumIntfs) { wpa_printf(MSG_DEBUG, "NDIS: Current interface not found from " "WZC"); goto fail; } os_memset(&intf, 0, sizeof(intf)); intf.wszGuid = guids.pIntfs[i].wszGuid; /* Set flags to verify that the structure has not changed. */ intf.dwCtlFlags = -1; flags = 0; res = wzc_query_interf(NULL, INTFCTL_ENABLED, &intf, &flags); if (res != 0) { wpa_printf(MSG_DEBUG, "NDIS: Could not query flags for the " "WZC interface: %d (0x%x)", (int) res, (int) res); wpa_printf(MSG_DEBUG, "NDIS: GetLastError: %u", (unsigned int) GetLastError()); goto fail; } wpa_printf(MSG_DEBUG, "NDIS: WZC interface flags 0x%x dwCtlFlags 0x%x", (int) flags, (int) intf.dwCtlFlags); if (intf.dwCtlFlags == -1) { wpa_printf(MSG_DEBUG, "NDIS: Looks like wzcsapi has changed " "again - could not disable WZC"); wpa_hexdump(MSG_MSGDUMP, "NDIS: intf", (u8 *) &intf, sizeof(intf)); goto fail; } if (enable) { if (!(intf.dwCtlFlags & INTFCTL_ENABLED)) { wpa_printf(MSG_DEBUG, "NDIS: Enabling WZC for this " "interface"); intf.dwCtlFlags |= INTFCTL_ENABLED; res = wzc_set_interf(NULL, INTFCTL_ENABLED, &intf, &flags); if (res != 0) { wpa_printf(MSG_DEBUG, "NDIS: Failed to enable " "WZC: %d (0x%x)", (int) res, (int) res); wpa_printf(MSG_DEBUG, "NDIS: GetLastError: %u", (unsigned int) GetLastError()); goto fail; } wpa_printf(MSG_DEBUG, "NDIS: Re-enabled WZC for this " "interface"); drv->wzc_disabled = 0; } } else { if (intf.dwCtlFlags & INTFCTL_ENABLED) { wpa_printf(MSG_DEBUG, "NDIS: Disabling WZC for this " "interface"); intf.dwCtlFlags &= ~INTFCTL_ENABLED; res = wzc_set_interf(NULL, INTFCTL_ENABLED, &intf, &flags); if (res != 0) { wpa_printf(MSG_DEBUG, "NDIS: Failed to " "disable WZC: %d (0x%x)", (int) res, (int) res); wpa_printf(MSG_DEBUG, "NDIS: GetLastError: %u", (unsigned int) GetLastError()); goto fail; } wpa_printf(MSG_DEBUG, "NDIS: Disabled WZC temporarily " "for this interface"); drv->wzc_disabled = 1; } else { wpa_printf(MSG_DEBUG, "NDIS: WZC was not enabled for " "this interface"); } } ret = 0; fail: FreeLibrary(hm); return ret; #endif /* _WIN32_WCE */ } #else /* CONFIG_NATIVE_WINDOWS || __CYGWIN__ */ static int wpa_driver_ndis_set_wzc(struct wpa_driver_ndis_data *drv, int enable) { return 0; } #endif /* CONFIG_NATIVE_WINDOWS || __CYGWIN__ */ #ifdef CONFIG_USE_NDISUIO /* * l2_packet_ndis.c is sharing the same handle to NDISUIO, so we must be able * to export this handle. This is somewhat ugly, but there is no better * mechanism available to pass data from driver interface to l2_packet wrapper. */ static HANDLE driver_ndis_ndisuio_handle = INVALID_HANDLE_VALUE; HANDLE driver_ndis_get_ndisuio_handle(void) { return driver_ndis_ndisuio_handle; } #endif /* CONFIG_USE_NDISUIO */ static int wpa_driver_ndis_adapter_init(struct wpa_driver_ndis_data *drv) { #ifdef CONFIG_USE_NDISUIO #ifndef _WIN32_WCE #define NDISUIO_DEVICE_NAME TEXT("\\\\.\\\\Ndisuio") DWORD written; #endif /* _WIN32_WCE */ drv->ndisuio = CreateFile(NDISUIO_DEVICE_NAME, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, INVALID_HANDLE_VALUE); if (drv->ndisuio == INVALID_HANDLE_VALUE) { wpa_printf(MSG_ERROR, "NDIS: Failed to open connection to " "NDISUIO: %d", (int) GetLastError()); return -1; } driver_ndis_ndisuio_handle = drv->ndisuio; #ifndef _WIN32_WCE if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_BIND_WAIT, NULL, 0, NULL, 0, &written, NULL)) { wpa_printf(MSG_ERROR, "NDIS: IOCTL_NDISUIO_BIND_WAIT failed: " "%d", (int) GetLastError()); CloseHandle(drv->ndisuio); drv->ndisuio = INVALID_HANDLE_VALUE; return -1; } #endif /* _WIN32_WCE */ return 0; #else /* CONFIG_USE_NDISUIO */ return 0; #endif /* CONFIG_USE_NDISUIO */ } static int wpa_driver_ndis_adapter_open(struct wpa_driver_ndis_data *drv) { #ifdef CONFIG_USE_NDISUIO DWORD written; #define MAX_NDIS_DEVICE_NAME_LEN 256 WCHAR ifname[MAX_NDIS_DEVICE_NAME_LEN]; size_t len, i, pos; const char *prefix = "\\DEVICE\\"; #ifdef _WIN32_WCE pos = 0; #else /* _WIN32_WCE */ pos = 8; #endif /* _WIN32_WCE */ len = pos + os_strlen(drv->ifname); if (len >= MAX_NDIS_DEVICE_NAME_LEN) return -1; for (i = 0; i < pos; i++) ifname[i] = (WCHAR) prefix[i]; for (i = pos; i < len; i++) ifname[i] = (WCHAR) drv->ifname[i - pos]; ifname[i] = L'\0'; if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_OPEN_DEVICE, ifname, len * sizeof(WCHAR), NULL, 0, &written, NULL)) { wpa_printf(MSG_ERROR, "NDIS: IOCTL_NDISUIO_OPEN_DEVICE " "failed: %d", (int) GetLastError()); wpa_hexdump_ascii(MSG_DEBUG, "NDIS: ifname", (const u8 *) ifname, len * sizeof(WCHAR)); CloseHandle(drv->ndisuio); drv->ndisuio = INVALID_HANDLE_VALUE; return -1; } wpa_printf(MSG_DEBUG, "NDIS: Opened NDISUIO device successfully"); return 0; #else /* CONFIG_USE_NDISUIO */ char ifname[128]; os_snprintf(ifname, sizeof(ifname), "\\Device\\NPF_%s", drv->ifname); drv->adapter = PacketOpenAdapter(ifname); if (drv->adapter == NULL) { wpa_printf(MSG_DEBUG, "NDIS: PacketOpenAdapter failed for " "'%s'", ifname); return -1; } return 0; #endif /* CONFIG_USE_NDISUIO */ } static void wpa_driver_ndis_adapter_close(struct wpa_driver_ndis_data *drv) { #ifdef CONFIG_USE_NDISUIO driver_ndis_ndisuio_handle = INVALID_HANDLE_VALUE; if (drv->ndisuio != INVALID_HANDLE_VALUE) CloseHandle(drv->ndisuio); #else /* CONFIG_USE_NDISUIO */ if (drv->adapter) PacketCloseAdapter(drv->adapter); #endif /* CONFIG_USE_NDISUIO */ } static int ndis_add_multicast(struct wpa_driver_ndis_data *drv) { if (ndis_set_oid(drv, OID_802_3_MULTICAST_LIST, (const char *) pae_group_addr, ETH_ALEN) < 0) { wpa_printf(MSG_DEBUG, "NDIS: Failed to add PAE group address " "to the multicast list"); return -1; } return 0; } static void * wpa_driver_ndis_init(void *ctx, const char *ifname) { struct wpa_driver_ndis_data *drv; u32 mode; drv = os_zalloc(sizeof(*drv)); if (drv == NULL) return NULL; drv->ctx = ctx; /* * Compatibility code to strip possible prefix from the GUID. Previous * versions include \Device\NPF_ prefix for all names, but the internal * interface name is now only the GUI. Both Packet32 and NDISUIO * prefixes are supported. */ if (os_strncmp(ifname, "\\Device\\NPF_", 12) == 0) ifname += 12; else if (os_strncmp(ifname, "\\DEVICE\\", 8) == 0) ifname += 8; os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); if (wpa_driver_ndis_adapter_init(drv) < 0) { os_free(drv); return NULL; } if (wpa_driver_ndis_get_names(drv) < 0) { wpa_driver_ndis_adapter_close(drv); os_free(drv); return NULL; } wpa_driver_ndis_set_wzc(drv, 0); if (wpa_driver_ndis_adapter_open(drv) < 0) { wpa_driver_ndis_adapter_close(drv); os_free(drv); return NULL; } if (ndis_get_oid(drv, OID_802_3_CURRENT_ADDRESS, (char *) drv->own_addr, ETH_ALEN) < 0) { wpa_printf(MSG_DEBUG, "NDIS: Get OID_802_3_CURRENT_ADDRESS " "failed"); wpa_driver_ndis_adapter_close(drv); os_free(drv); return NULL; } wpa_driver_ndis_get_capability(drv); /* Make sure that the driver does not have any obsolete PMKID entries. */ wpa_driver_ndis_flush_pmkid(drv); /* * Disconnect to make sure that driver re-associates if it was * connected. */ wpa_driver_ndis_disconnect(drv); eloop_register_timeout(1, 0, wpa_driver_ndis_poll_timeout, drv, NULL); #ifdef CONFIG_NDIS_EVENTS_INTEGRATED drv->events = ndis_events_init(&drv->events_pipe, &drv->event_avail, drv->ifname, drv->adapter_desc); if (drv->events == NULL) { wpa_driver_ndis_deinit(drv); return NULL; } eloop_register_event(drv->event_avail, sizeof(drv->event_avail), wpa_driver_ndis_event_pipe_cb, drv, NULL); #endif /* CONFIG_NDIS_EVENTS_INTEGRATED */ #ifdef _WIN32_WCE if (ndisuio_notification_init(drv) < 0) { wpa_driver_ndis_deinit(drv); return NULL; } #endif /* _WIN32_WCE */ /* Set mode here in case card was configured for ad-hoc mode * previously. */ mode = Ndis802_11Infrastructure; if (ndis_set_oid(drv, OID_802_11_INFRASTRUCTURE_MODE, (char *) &mode, sizeof(mode)) < 0) { char buf[8]; int res; wpa_printf(MSG_DEBUG, "NDIS: Failed to set " "OID_802_11_INFRASTRUCTURE_MODE (%d)", (int) mode); /* Try to continue anyway */ res = ndis_get_oid(drv, OID_DOT11_CURRENT_OPERATION_MODE, buf, sizeof(buf)); if (res > 0) { wpa_printf(MSG_INFO, "NDIS: The driver seems to use " "Native 802.11 OIDs. These are not yet " "fully supported."); drv->native80211 = 1; } else if (!drv->has_capability || drv->capa.enc == 0) { /* * Note: This will also happen with NDIS 6 drivers with * Vista. */ wpa_printf(MSG_DEBUG, "NDIS: Driver did not provide " "any wireless capabilities - assume it is " "a wired interface"); drv->wired = 1; drv->capa.flags |= WPA_DRIVER_FLAGS_WIRED; drv->has_capability = 1; ndis_add_multicast(drv); } } return drv; } static void wpa_driver_ndis_deinit(void *priv) { struct wpa_driver_ndis_data *drv = priv; #ifdef CONFIG_NDIS_EVENTS_INTEGRATED if (drv->events) { eloop_unregister_event(drv->event_avail, sizeof(drv->event_avail)); ndis_events_deinit(drv->events); } #endif /* CONFIG_NDIS_EVENTS_INTEGRATED */ #ifdef _WIN32_WCE ndisuio_notification_deinit(drv); #endif /* _WIN32_WCE */ eloop_cancel_timeout(wpa_driver_ndis_scan_timeout, drv, drv->ctx); eloop_cancel_timeout(wpa_driver_ndis_poll_timeout, drv, NULL); wpa_driver_ndis_flush_pmkid(drv); wpa_driver_ndis_disconnect(drv); if (wpa_driver_ndis_radio_off(drv) < 0) { wpa_printf(MSG_DEBUG, "NDIS: failed to disassociate and turn " "radio off"); } wpa_driver_ndis_adapter_close(drv); if (drv->wzc_disabled) wpa_driver_ndis_set_wzc(drv, 1); #ifdef _WIN32_WCE os_free(drv->adapter_name); #endif /* _WIN32_WCE */ os_free(drv->adapter_desc); os_free(drv); } static struct wpa_interface_info * wpa_driver_ndis_get_interfaces(void *global_priv) { struct wpa_interface_info *iface = NULL, *niface; #ifdef CONFIG_USE_NDISUIO NDISUIO_QUERY_BINDING *b; size_t blen = sizeof(*b) + 1024; int i, error; DWORD written; char name[256], desc[256]; WCHAR *pos; size_t j, len; HANDLE ndisuio; ndisuio = CreateFile(NDISUIO_DEVICE_NAME, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, INVALID_HANDLE_VALUE); if (ndisuio == INVALID_HANDLE_VALUE) { wpa_printf(MSG_ERROR, "NDIS: Failed to open connection to " "NDISUIO: %d", (int) GetLastError()); return NULL; } #ifndef _WIN32_WCE if (!DeviceIoControl(ndisuio, IOCTL_NDISUIO_BIND_WAIT, NULL, 0, NULL, 0, &written, NULL)) { wpa_printf(MSG_ERROR, "NDIS: IOCTL_NDISUIO_BIND_WAIT failed: " "%d", (int) GetLastError()); CloseHandle(ndisuio); return NULL; } #endif /* _WIN32_WCE */ b = os_malloc(blen); if (b == NULL) { CloseHandle(ndisuio); return NULL; } for (i = 0; ; i++) { os_memset(b, 0, blen); b->BindingIndex = i; if (!DeviceIoControl(ndisuio, IOCTL_NDISUIO_QUERY_BINDING, b, sizeof(NDISUIO_QUERY_BINDING), b, blen, &written, NULL)) { error = (int) GetLastError(); if (error == ERROR_NO_MORE_ITEMS) break; wpa_printf(MSG_DEBUG, "IOCTL_NDISUIO_QUERY_BINDING " "failed: %d", error); break; } pos = (WCHAR *) ((char *) b + b->DeviceNameOffset); len = b->DeviceNameLength; if (len >= sizeof(name)) len = sizeof(name) - 1; for (j = 0; j < len; j++) name[j] = (char) pos[j]; name[len] = '\0'; pos = (WCHAR *) ((char *) b + b->DeviceDescrOffset); len = b->DeviceDescrLength; if (len >= sizeof(desc)) len = sizeof(desc) - 1; for (j = 0; j < len; j++) desc[j] = (char) pos[j]; desc[len] = '\0'; wpa_printf(MSG_DEBUG, "NDIS: %d - %s - %s", i, name, desc); niface = os_zalloc(sizeof(*niface)); if (niface == NULL) break; niface->drv_name = "ndis"; if (os_strncmp(name, "\\DEVICE\\", 8) == 0) niface->ifname = os_strdup(name + 8); else niface->ifname = os_strdup(name); if (niface->ifname == NULL) { os_free(niface); break; } niface->desc = os_strdup(desc); niface->next = iface; iface = niface; } os_free(b); CloseHandle(ndisuio); #else /* CONFIG_USE_NDISUIO */ PTSTR _names; char *names, *pos, *pos2; ULONG len; BOOLEAN res; char *name[MAX_ADAPTERS]; char *desc[MAX_ADAPTERS]; int num_name, num_desc, i; wpa_printf(MSG_DEBUG, "NDIS: Packet.dll version: %s", PacketGetVersion()); len = 8192; _names = os_zalloc(len); if (_names == NULL) return NULL; res = PacketGetAdapterNames(_names, &len); if (!res && len > 8192) { os_free(_names); _names = os_zalloc(len); if (_names == NULL) return NULL; res = PacketGetAdapterNames(_names, &len); } if (!res) { wpa_printf(MSG_ERROR, "NDIS: Failed to get adapter list " "(PacketGetAdapterNames)"); os_free(_names); return NULL; } names = (char *) _names; if (names[0] && names[1] == '\0' && names[2] && names[3] == '\0') { wpa_printf(MSG_DEBUG, "NDIS: Looks like adapter names are in " "UNICODE"); /* Convert to ASCII */ pos2 = pos = names; while (pos2 < names + len) { if (pos2[0] == '\0' && pos2[1] == '\0' && pos2[2] == '\0' && pos2[3] == '\0') { pos2 += 4; break; } *pos++ = pos2[0]; pos2 += 2; } os_memcpy(pos + 2, names, pos - names); pos += 2; } else pos = names; num_name = 0; while (pos < names + len) { name[num_name] = pos; while (*pos && pos < names + len) pos++; if (pos + 1 >= names + len) { os_free(names); return NULL; } pos++; num_name++; if (num_name >= MAX_ADAPTERS) { wpa_printf(MSG_DEBUG, "NDIS: Too many adapters"); os_free(names); return NULL; } if (*pos == '\0') { wpa_printf(MSG_DEBUG, "NDIS: %d adapter names found", num_name); pos++; break; } } num_desc = 0; while (pos < names + len) { desc[num_desc] = pos; while (*pos && pos < names + len) pos++; if (pos + 1 >= names + len) { os_free(names); return NULL; } pos++; num_desc++; if (num_desc >= MAX_ADAPTERS) { wpa_printf(MSG_DEBUG, "NDIS: Too many adapter " "descriptions"); os_free(names); return NULL; } if (*pos == '\0') { wpa_printf(MSG_DEBUG, "NDIS: %d adapter descriptions " "found", num_name); pos++; break; } } /* * Windows 98 with Packet.dll 3.0 alpha3 does not include adapter * descriptions. Fill in dummy descriptors to work around this. */ while (num_desc < num_name) desc[num_desc++] = "dummy description"; if (num_name != num_desc) { wpa_printf(MSG_DEBUG, "NDIS: mismatch in adapter name and " "description counts (%d != %d)", num_name, num_desc); os_free(names); return NULL; } for (i = 0; i < num_name; i++) { niface = os_zalloc(sizeof(*niface)); if (niface == NULL) break; niface->drv_name = "ndis"; if (os_strncmp(name[i], "\\Device\\NPF_", 12) == 0) niface->ifname = os_strdup(name[i] + 12); else niface->ifname = os_strdup(name[i]); if (niface->ifname == NULL) { os_free(niface); break; } niface->desc = os_strdup(desc[i]); niface->next = iface; iface = niface; } #endif /* CONFIG_USE_NDISUIO */ return iface; } static const char *ndis_drv_name = "ndis"; static const char *ndis_drv_desc = "Windows NDIS driver"; struct wpa_driver_ops wpa_driver_ndis_ops; void driver_ndis_init_ops(void) { os_memset(&wpa_driver_ndis_ops, 0, sizeof(wpa_driver_ndis_ops)); wpa_driver_ndis_ops.name = ndis_drv_name; wpa_driver_ndis_ops.desc = ndis_drv_desc; wpa_driver_ndis_ops.get_bssid = wpa_driver_ndis_get_bssid; wpa_driver_ndis_ops.get_ssid = wpa_driver_ndis_get_ssid; wpa_driver_ndis_ops.set_key = wpa_driver_ndis_set_key; wpa_driver_ndis_ops.init = wpa_driver_ndis_init; wpa_driver_ndis_ops.deinit = wpa_driver_ndis_deinit; wpa_driver_ndis_ops.deauthenticate = wpa_driver_ndis_deauthenticate; wpa_driver_ndis_ops.associate = wpa_driver_ndis_associate; wpa_driver_ndis_ops.add_pmkid = wpa_driver_ndis_add_pmkid; wpa_driver_ndis_ops.remove_pmkid = wpa_driver_ndis_remove_pmkid; wpa_driver_ndis_ops.flush_pmkid = wpa_driver_ndis_flush_pmkid; wpa_driver_ndis_ops.get_capa = wpa_driver_ndis_get_capa; wpa_driver_ndis_ops.poll = wpa_driver_ndis_poll; wpa_driver_ndis_ops.get_ifname = wpa_driver_ndis_get_ifname; wpa_driver_ndis_ops.get_mac_addr = wpa_driver_ndis_get_mac_addr; wpa_driver_ndis_ops.get_scan_results2 = wpa_driver_ndis_get_scan_results; wpa_driver_ndis_ops.get_interfaces = wpa_driver_ndis_get_interfaces; wpa_driver_ndis_ops.scan2 = wpa_driver_ndis_scan; } wpa-2.1/src/drivers/driver_ndis.h000066400000000000000000000027571227414666700171230ustar00rootroot00000000000000/* * WPA Supplicant - Windows/NDIS driver interface * Copyright (c) 2004-2006, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef DRIVER_NDIS_H #define DRIVER_NDIS_H #ifdef CONFIG_NDIS_EVENTS_INTEGRATED struct ndis_events_data; struct ndis_events_data * ndis_events_init(HANDLE *read_pipe, HANDLE *event, const char *ifname, const char *desc); void ndis_events_deinit(struct ndis_events_data *events); #endif /* CONFIG_NDIS_EVENTS_INTEGRATED */ struct ndis_pmkid_entry { struct ndis_pmkid_entry *next; u8 bssid[ETH_ALEN]; u8 pmkid[16]; }; struct wpa_driver_ndis_data { void *ctx; char ifname[100]; /* GUID: {7EE3EFE5-C165-472F-986D-F6FBEDFE8C8D} */ #ifdef _WIN32_WCE TCHAR *adapter_name; HANDLE event_queue; /* NDISUIO notifier MsgQueue */ HANDLE connected_event; /* WpaSupplicantConnected event */ #endif /* _WIN32_WCE */ u8 own_addr[ETH_ALEN]; #ifdef CONFIG_USE_NDISUIO HANDLE ndisuio; #else /* CONFIG_USE_NDISUIO */ LPADAPTER adapter; #endif /* CONFIG_USE_NDISUIO */ u8 bssid[ETH_ALEN]; int has_capability; int no_of_pmkid; int radio_enabled; struct wpa_driver_capa capa; struct ndis_pmkid_entry *pmkid; char *adapter_desc; int wired; int native80211; int mode; int wzc_disabled; int oid_bssid_set; #ifdef CONFIG_NDIS_EVENTS_INTEGRATED HANDLE events_pipe, event_avail; struct ndis_events_data *events; #endif /* CONFIG_NDIS_EVENTS_INTEGRATED */ }; #endif /* DRIVER_NDIS_H */ wpa-2.1/src/drivers/driver_ndis_.c000066400000000000000000000052421227414666700172450ustar00rootroot00000000000000/* * WPA Supplicant - Windows/NDIS driver interface - event processing * Copyright (c) 2004-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 "driver.h" #include "eloop.h" /* Keep this event processing in a separate file and without WinPcap headers to * avoid conflicts with some of the header files. */ struct _ADAPTER; typedef struct _ADAPTER * LPADAPTER; #include "driver_ndis.h" void wpa_driver_ndis_event_connect(struct wpa_driver_ndis_data *drv); void wpa_driver_ndis_event_disconnect(struct wpa_driver_ndis_data *drv); void wpa_driver_ndis_event_media_specific(struct wpa_driver_ndis_data *drv, const u8 *data, size_t data_len); void wpa_driver_ndis_event_adapter_arrival(struct wpa_driver_ndis_data *drv); void wpa_driver_ndis_event_adapter_removal(struct wpa_driver_ndis_data *drv); enum event_types { EVENT_CONNECT, EVENT_DISCONNECT, EVENT_MEDIA_SPECIFIC, EVENT_ADAPTER_ARRIVAL, EVENT_ADAPTER_REMOVAL }; /* Event data: * enum event_types (as int, i.e., 4 octets) * data length (2 octets (big endian), optional) * data (variable len, optional) */ static void wpa_driver_ndis_event_process(struct wpa_driver_ndis_data *drv, u8 *buf, size_t len) { u8 *pos, *data = NULL; enum event_types type; size_t data_len = 0; wpa_hexdump(MSG_MSGDUMP, "NDIS: received event data", buf, len); if (len < sizeof(int)) return; type = *((int *) buf); pos = buf + sizeof(int); wpa_printf(MSG_DEBUG, "NDIS: event - type %d", type); if (buf + len - pos > 2) { data_len = (int) *pos++ << 8; data_len += *pos++; if (data_len > (size_t) (buf + len - pos)) { wpa_printf(MSG_DEBUG, "NDIS: event data overflow"); return; } data = pos; wpa_hexdump(MSG_MSGDUMP, "NDIS: event data", data, data_len); } switch (type) { case EVENT_CONNECT: wpa_driver_ndis_event_connect(drv); break; case EVENT_DISCONNECT: wpa_driver_ndis_event_disconnect(drv); break; case EVENT_MEDIA_SPECIFIC: wpa_driver_ndis_event_media_specific(drv, data, data_len); break; case EVENT_ADAPTER_ARRIVAL: wpa_driver_ndis_event_adapter_arrival(drv); break; case EVENT_ADAPTER_REMOVAL: wpa_driver_ndis_event_adapter_removal(drv); break; } } void wpa_driver_ndis_event_pipe_cb(void *eloop_data, void *user_data) { struct wpa_driver_ndis_data *drv = eloop_data; u8 buf[512]; DWORD len; ResetEvent(drv->event_avail); if (ReadFile(drv->events_pipe, buf, sizeof(buf), &len, NULL)) wpa_driver_ndis_event_process(drv, buf, len); else { wpa_printf(MSG_DEBUG, "%s: ReadFile() failed: %d", __func__, (int) GetLastError()); } } wpa-2.1/src/drivers/driver_nl80211.c000066400000000000000000011473471227414666700171740ustar00rootroot00000000000000/* * Driver interaction with Linux nl80211/cfg80211 * Copyright (c) 2002-2014, Jouni Malinen * Copyright (c) 2003-2004, Instant802 Networks, Inc. * Copyright (c) 2005-2006, Devicescape Software, Inc. * Copyright (c) 2007, Johannes Berg * Copyright (c) 2009-2010, Atheros Communications * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "includes.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "nl80211_copy.h" #include "common.h" #include "eloop.h" #include "utils/list.h" #include "common/qca-vendor.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" #include "l2_packet/l2_packet.h" #include "netlink.h" #include "linux_ioctl.h" #include "radiotap.h" #include "radiotap_iter.h" #include "rfkill.h" #include "driver.h" #ifndef SO_WIFI_STATUS # if defined(__sparc__) # define SO_WIFI_STATUS 0x0025 # elif defined(__parisc__) # define SO_WIFI_STATUS 0x4022 # else # define SO_WIFI_STATUS 41 # endif # define SCM_WIFI_STATUS SO_WIFI_STATUS #endif #ifndef SO_EE_ORIGIN_TXSTATUS #define SO_EE_ORIGIN_TXSTATUS 4 #endif #ifndef PACKET_TX_TIMESTAMP #define PACKET_TX_TIMESTAMP 16 #endif #ifdef ANDROID #include "android_drv.h" #endif /* ANDROID */ #ifdef CONFIG_LIBNL20 /* libnl 2.0 compatibility code */ #define nl_handle nl_sock #define nl80211_handle_alloc nl_socket_alloc_cb #define nl80211_handle_destroy nl_socket_free #else /* * libnl 1.1 has a bug, it tries to allocate socket numbers densely * but when you free a socket again it will mess up its bitmap and * and use the wrong number the next time it needs a socket ID. * Therefore, we wrap the handle alloc/destroy and add our own pid * accounting. */ static uint32_t port_bitmap[32] = { 0 }; static struct nl_handle *nl80211_handle_alloc(void *cb) { struct nl_handle *handle; uint32_t pid = getpid() & 0x3FFFFF; int i; handle = nl_handle_alloc_cb(cb); for (i = 0; i < 1024; i++) { if (port_bitmap[i / 32] & (1 << (i % 32))) continue; port_bitmap[i / 32] |= 1 << (i % 32); pid += i << 22; break; } nl_socket_set_local_port(handle, pid); return handle; } static void nl80211_handle_destroy(struct nl_handle *handle) { uint32_t port = nl_socket_get_local_port(handle); port >>= 22; port_bitmap[port / 32] &= ~(1 << (port % 32)); nl_handle_destroy(handle); } #endif /* CONFIG_LIBNL20 */ #ifdef ANDROID /* system/core/libnl_2 does not include nl_socket_set_nonblocking() */ static int android_nl_socket_set_nonblocking(struct nl_handle *handle) { return fcntl(nl_socket_get_fd(handle), F_SETFL, O_NONBLOCK); } #undef nl_socket_set_nonblocking #define nl_socket_set_nonblocking(h) android_nl_socket_set_nonblocking(h) #endif /* ANDROID */ static struct nl_handle * nl_create_handle(struct nl_cb *cb, const char *dbg) { struct nl_handle *handle; handle = nl80211_handle_alloc(cb); if (handle == NULL) { wpa_printf(MSG_ERROR, "nl80211: Failed to allocate netlink " "callbacks (%s)", dbg); return NULL; } if (genl_connect(handle)) { wpa_printf(MSG_ERROR, "nl80211: Failed to connect to generic " "netlink (%s)", dbg); nl80211_handle_destroy(handle); return NULL; } return handle; } static void nl_destroy_handles(struct nl_handle **handle) { if (*handle == NULL) return; nl80211_handle_destroy(*handle); *handle = NULL; } #if __WORDSIZE == 64 #define ELOOP_SOCKET_INVALID (intptr_t) 0x8888888888888889ULL #else #define ELOOP_SOCKET_INVALID (intptr_t) 0x88888889ULL #endif static void nl80211_register_eloop_read(struct nl_handle **handle, eloop_sock_handler handler, void *eloop_data) { nl_socket_set_nonblocking(*handle); eloop_register_read_sock(nl_socket_get_fd(*handle), handler, eloop_data, *handle); *handle = (void *) (((intptr_t) *handle) ^ ELOOP_SOCKET_INVALID); } static void nl80211_destroy_eloop_handle(struct nl_handle **handle) { *handle = (void *) (((intptr_t) *handle) ^ ELOOP_SOCKET_INVALID); eloop_unregister_read_sock(nl_socket_get_fd(*handle)); nl_destroy_handles(handle); } #ifndef IFF_LOWER_UP #define IFF_LOWER_UP 0x10000 /* driver signals L1 up */ #endif #ifndef IFF_DORMANT #define IFF_DORMANT 0x20000 /* driver signals dormant */ #endif #ifndef IF_OPER_DORMANT #define IF_OPER_DORMANT 5 #endif #ifndef IF_OPER_UP #define IF_OPER_UP 6 #endif struct nl80211_global { struct dl_list interfaces; int if_add_ifindex; u64 if_add_wdevid; int if_add_wdevid_set; struct netlink_data *netlink; struct nl_cb *nl_cb; struct nl_handle *nl; int nl80211_id; int ioctl_sock; /* socket for ioctl() use */ struct nl_handle *nl_event; }; struct nl80211_wiphy_data { struct dl_list list; struct dl_list bsss; struct dl_list drvs; struct nl_handle *nl_beacons; struct nl_cb *nl_cb; int wiphy_idx; }; static void nl80211_global_deinit(void *priv); struct i802_bss { struct wpa_driver_nl80211_data *drv; struct i802_bss *next; int ifindex; u64 wdev_id; char ifname[IFNAMSIZ + 1]; char brname[IFNAMSIZ]; unsigned int beacon_set:1; unsigned int added_if_into_bridge:1; unsigned int added_bridge:1; unsigned int in_deinit:1; unsigned int wdev_id_set:1; unsigned int added_if:1; u8 addr[ETH_ALEN]; int freq; int if_dynamic; void *ctx; struct nl_handle *nl_preq, *nl_mgmt; struct nl_cb *nl_cb; struct nl80211_wiphy_data *wiphy_data; struct dl_list wiphy_list; }; struct wpa_driver_nl80211_data { struct nl80211_global *global; struct dl_list list; struct dl_list wiphy_list; char phyname[32]; void *ctx; int ifindex; int if_removed; int if_disabled; int ignore_if_down_event; struct rfkill_data *rfkill; struct wpa_driver_capa capa; u8 *extended_capa, *extended_capa_mask; unsigned int extended_capa_len; int has_capability; int operstate; int scan_complete_events; enum scan_states { NO_SCAN, SCAN_REQUESTED, SCAN_STARTED, SCAN_COMPLETED, SCAN_ABORTED, SCHED_SCAN_STARTED, SCHED_SCAN_STOPPED, SCHED_SCAN_RESULTS } scan_state; struct nl_cb *nl_cb; u8 auth_bssid[ETH_ALEN]; u8 auth_attempt_bssid[ETH_ALEN]; u8 bssid[ETH_ALEN]; u8 prev_bssid[ETH_ALEN]; int associated; u8 ssid[32]; size_t ssid_len; enum nl80211_iftype nlmode; enum nl80211_iftype ap_scan_as_station; unsigned int assoc_freq; int monitor_sock; int monitor_ifidx; int monitor_refcount; unsigned int disabled_11b_rates:1; unsigned int pending_remain_on_chan:1; unsigned int in_interface_list:1; unsigned int device_ap_sme:1; unsigned int poll_command_supported:1; unsigned int data_tx_status:1; unsigned int scan_for_auth:1; unsigned int retry_auth:1; unsigned int use_monitor:1; unsigned int ignore_next_local_disconnect:1; unsigned int allow_p2p_device:1; unsigned int hostapd:1; unsigned int start_mode_ap:1; unsigned int start_iface_up:1; u64 remain_on_chan_cookie; u64 send_action_cookie; unsigned int last_mgmt_freq; struct wpa_driver_scan_filter *filter_ssids; size_t num_filter_ssids; struct i802_bss *first_bss; int eapol_tx_sock; int eapol_sock; /* socket for EAPOL frames */ int default_if_indices[16]; int *if_indices; int num_if_indices; /* From failed authentication command */ int auth_freq; u8 auth_bssid_[ETH_ALEN]; u8 auth_ssid[32]; size_t auth_ssid_len; int auth_alg; u8 *auth_ie; size_t auth_ie_len; u8 auth_wep_key[4][16]; size_t auth_wep_key_len[4]; int auth_wep_tx_keyidx; int auth_local_state_change; int auth_p2p; }; static void wpa_driver_nl80211_deinit(struct i802_bss *bss); static void wpa_driver_nl80211_scan_timeout(void *eloop_ctx, void *timeout_ctx); static int wpa_driver_nl80211_set_mode(struct i802_bss *bss, enum nl80211_iftype nlmode); static int wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv, const u8 *set_addr, int first); static int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv, const u8 *addr, int cmd, u16 reason_code, int local_state_change); static void nl80211_remove_monitor_interface( struct wpa_driver_nl80211_data *drv); static int nl80211_send_frame_cmd(struct i802_bss *bss, unsigned int freq, unsigned int wait, const u8 *buf, size_t buf_len, u64 *cookie, int no_cck, int no_ack, int offchanok); static int nl80211_register_frame(struct i802_bss *bss, struct nl_handle *hl_handle, u16 type, const u8 *match, size_t match_len); static int wpa_driver_nl80211_probe_req_report(struct i802_bss *bss, int report); #ifdef ANDROID static int android_pno_start(struct i802_bss *bss, struct wpa_driver_scan_params *params); static int android_pno_stop(struct i802_bss *bss); extern int wpa_driver_nl80211_driver_cmd(void *priv, char *cmd, char *buf, size_t buf_len); #endif /* ANDROID */ #ifdef ANDROID_P2P int wpa_driver_set_p2p_noa(void *priv, u8 count, int start, int duration); int wpa_driver_get_p2p_noa(void *priv, u8 *buf, size_t len); int wpa_driver_set_p2p_ps(void *priv, int legacy_ps, int opp_ps, int ctwindow); int wpa_driver_set_ap_wps_p2p_ie(void *priv, const struct wpabuf *beacon, const struct wpabuf *proberesp, const struct wpabuf *assocresp); #endif /* ANDROID_P2P */ static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx); static void del_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx); static int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx); static int wpa_driver_nl80211_if_remove(struct i802_bss *bss, enum wpa_driver_if_type type, const char *ifname); static int wpa_driver_nl80211_set_freq(struct i802_bss *bss, struct hostapd_freq_params *freq); static int nl80211_disable_11b_rates(struct wpa_driver_nl80211_data *drv, int ifindex, int disabled); static int nl80211_leave_ibss(struct wpa_driver_nl80211_data *drv); static int wpa_driver_nl80211_authenticate_retry( struct wpa_driver_nl80211_data *drv); static int i802_set_iface_flags(struct i802_bss *bss, int up); static const char * nl80211_command_to_string(enum nl80211_commands cmd) { #define C2S(x) case x: return #x; switch (cmd) { C2S(NL80211_CMD_UNSPEC) C2S(NL80211_CMD_GET_WIPHY) C2S(NL80211_CMD_SET_WIPHY) C2S(NL80211_CMD_NEW_WIPHY) C2S(NL80211_CMD_DEL_WIPHY) C2S(NL80211_CMD_GET_INTERFACE) C2S(NL80211_CMD_SET_INTERFACE) C2S(NL80211_CMD_NEW_INTERFACE) C2S(NL80211_CMD_DEL_INTERFACE) C2S(NL80211_CMD_GET_KEY) C2S(NL80211_CMD_SET_KEY) C2S(NL80211_CMD_NEW_KEY) C2S(NL80211_CMD_DEL_KEY) C2S(NL80211_CMD_GET_BEACON) C2S(NL80211_CMD_SET_BEACON) C2S(NL80211_CMD_START_AP) C2S(NL80211_CMD_STOP_AP) C2S(NL80211_CMD_GET_STATION) C2S(NL80211_CMD_SET_STATION) C2S(NL80211_CMD_NEW_STATION) C2S(NL80211_CMD_DEL_STATION) C2S(NL80211_CMD_GET_MPATH) C2S(NL80211_CMD_SET_MPATH) C2S(NL80211_CMD_NEW_MPATH) C2S(NL80211_CMD_DEL_MPATH) C2S(NL80211_CMD_SET_BSS) C2S(NL80211_CMD_SET_REG) C2S(NL80211_CMD_REQ_SET_REG) C2S(NL80211_CMD_GET_MESH_CONFIG) C2S(NL80211_CMD_SET_MESH_CONFIG) C2S(NL80211_CMD_SET_MGMT_EXTRA_IE) C2S(NL80211_CMD_GET_REG) C2S(NL80211_CMD_GET_SCAN) C2S(NL80211_CMD_TRIGGER_SCAN) C2S(NL80211_CMD_NEW_SCAN_RESULTS) C2S(NL80211_CMD_SCAN_ABORTED) C2S(NL80211_CMD_REG_CHANGE) C2S(NL80211_CMD_AUTHENTICATE) C2S(NL80211_CMD_ASSOCIATE) C2S(NL80211_CMD_DEAUTHENTICATE) C2S(NL80211_CMD_DISASSOCIATE) C2S(NL80211_CMD_MICHAEL_MIC_FAILURE) C2S(NL80211_CMD_REG_BEACON_HINT) C2S(NL80211_CMD_JOIN_IBSS) C2S(NL80211_CMD_LEAVE_IBSS) C2S(NL80211_CMD_TESTMODE) C2S(NL80211_CMD_CONNECT) C2S(NL80211_CMD_ROAM) C2S(NL80211_CMD_DISCONNECT) C2S(NL80211_CMD_SET_WIPHY_NETNS) C2S(NL80211_CMD_GET_SURVEY) C2S(NL80211_CMD_NEW_SURVEY_RESULTS) C2S(NL80211_CMD_SET_PMKSA) C2S(NL80211_CMD_DEL_PMKSA) C2S(NL80211_CMD_FLUSH_PMKSA) C2S(NL80211_CMD_REMAIN_ON_CHANNEL) C2S(NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL) C2S(NL80211_CMD_SET_TX_BITRATE_MASK) C2S(NL80211_CMD_REGISTER_FRAME) C2S(NL80211_CMD_FRAME) C2S(NL80211_CMD_FRAME_TX_STATUS) C2S(NL80211_CMD_SET_POWER_SAVE) C2S(NL80211_CMD_GET_POWER_SAVE) C2S(NL80211_CMD_SET_CQM) C2S(NL80211_CMD_NOTIFY_CQM) C2S(NL80211_CMD_SET_CHANNEL) C2S(NL80211_CMD_SET_WDS_PEER) C2S(NL80211_CMD_FRAME_WAIT_CANCEL) C2S(NL80211_CMD_JOIN_MESH) C2S(NL80211_CMD_LEAVE_MESH) C2S(NL80211_CMD_UNPROT_DEAUTHENTICATE) C2S(NL80211_CMD_UNPROT_DISASSOCIATE) C2S(NL80211_CMD_NEW_PEER_CANDIDATE) C2S(NL80211_CMD_GET_WOWLAN) C2S(NL80211_CMD_SET_WOWLAN) C2S(NL80211_CMD_START_SCHED_SCAN) C2S(NL80211_CMD_STOP_SCHED_SCAN) C2S(NL80211_CMD_SCHED_SCAN_RESULTS) C2S(NL80211_CMD_SCHED_SCAN_STOPPED) C2S(NL80211_CMD_SET_REKEY_OFFLOAD) C2S(NL80211_CMD_PMKSA_CANDIDATE) C2S(NL80211_CMD_TDLS_OPER) C2S(NL80211_CMD_TDLS_MGMT) C2S(NL80211_CMD_UNEXPECTED_FRAME) C2S(NL80211_CMD_PROBE_CLIENT) C2S(NL80211_CMD_REGISTER_BEACONS) C2S(NL80211_CMD_UNEXPECTED_4ADDR_FRAME) C2S(NL80211_CMD_SET_NOACK_MAP) C2S(NL80211_CMD_CH_SWITCH_NOTIFY) C2S(NL80211_CMD_START_P2P_DEVICE) C2S(NL80211_CMD_STOP_P2P_DEVICE) C2S(NL80211_CMD_CONN_FAILED) C2S(NL80211_CMD_SET_MCAST_RATE) C2S(NL80211_CMD_SET_MAC_ACL) C2S(NL80211_CMD_RADAR_DETECT) C2S(NL80211_CMD_GET_PROTOCOL_FEATURES) C2S(NL80211_CMD_UPDATE_FT_IES) C2S(NL80211_CMD_FT_EVENT) C2S(NL80211_CMD_CRIT_PROTOCOL_START) C2S(NL80211_CMD_CRIT_PROTOCOL_STOP) C2S(NL80211_CMD_GET_COALESCE) C2S(NL80211_CMD_SET_COALESCE) C2S(NL80211_CMD_CHANNEL_SWITCH) C2S(NL80211_CMD_VENDOR) C2S(NL80211_CMD_SET_QOS_MAP) default: return "NL80211_CMD_UNKNOWN"; } #undef C2S } /* Converts nl80211_chan_width to a common format */ static enum chan_width convert2width(int width) { switch (width) { case NL80211_CHAN_WIDTH_20_NOHT: return CHAN_WIDTH_20_NOHT; case NL80211_CHAN_WIDTH_20: return CHAN_WIDTH_20; case NL80211_CHAN_WIDTH_40: return CHAN_WIDTH_40; case NL80211_CHAN_WIDTH_80: return CHAN_WIDTH_80; case NL80211_CHAN_WIDTH_80P80: return CHAN_WIDTH_80P80; case NL80211_CHAN_WIDTH_160: return CHAN_WIDTH_160; } return CHAN_WIDTH_UNKNOWN; } static int is_ap_interface(enum nl80211_iftype nlmode) { return (nlmode == NL80211_IFTYPE_AP || nlmode == NL80211_IFTYPE_P2P_GO); } static int is_sta_interface(enum nl80211_iftype nlmode) { return (nlmode == NL80211_IFTYPE_STATION || nlmode == NL80211_IFTYPE_P2P_CLIENT); } static int is_p2p_net_interface(enum nl80211_iftype nlmode) { return (nlmode == NL80211_IFTYPE_P2P_CLIENT || nlmode == NL80211_IFTYPE_P2P_GO); } static void nl80211_mark_disconnected(struct wpa_driver_nl80211_data *drv) { if (drv->associated) os_memcpy(drv->prev_bssid, drv->bssid, ETH_ALEN); drv->associated = 0; os_memset(drv->bssid, 0, ETH_ALEN); } struct nl80211_bss_info_arg { struct wpa_driver_nl80211_data *drv; struct wpa_scan_results *res; unsigned int assoc_freq; u8 assoc_bssid[ETH_ALEN]; }; static int bss_info_handler(struct nl_msg *msg, void *arg); /* nl80211 code */ static int ack_handler(struct nl_msg *msg, void *arg) { int *err = arg; *err = 0; return NL_STOP; } static int finish_handler(struct nl_msg *msg, void *arg) { int *ret = arg; *ret = 0; return NL_SKIP; } static int error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err, void *arg) { int *ret = arg; *ret = err->error; return NL_SKIP; } static int no_seq_check(struct nl_msg *msg, void *arg) { return NL_OK; } static int send_and_recv(struct nl80211_global *global, struct nl_handle *nl_handle, struct nl_msg *msg, int (*valid_handler)(struct nl_msg *, void *), void *valid_data) { struct nl_cb *cb; int err = -ENOMEM; cb = nl_cb_clone(global->nl_cb); if (!cb) goto out; err = nl_send_auto_complete(nl_handle, msg); if (err < 0) goto out; err = 1; nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &err); nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &err); nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &err); if (valid_handler) nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, valid_handler, valid_data); while (err > 0) { int res = nl_recvmsgs(nl_handle, cb); if (res) { wpa_printf(MSG_INFO, "nl80211: %s->nl_recvmsgs failed: %d", __func__, res); } } out: nl_cb_put(cb); nlmsg_free(msg); return err; } static int send_and_recv_msgs_global(struct nl80211_global *global, struct nl_msg *msg, int (*valid_handler)(struct nl_msg *, void *), void *valid_data) { return send_and_recv(global, global->nl, msg, valid_handler, valid_data); } static int send_and_recv_msgs(struct wpa_driver_nl80211_data *drv, struct nl_msg *msg, int (*valid_handler)(struct nl_msg *, void *), void *valid_data) { return send_and_recv(drv->global, drv->global->nl, msg, valid_handler, valid_data); } struct family_data { const char *group; int id; }; static int nl80211_set_iface_id(struct nl_msg *msg, struct i802_bss *bss) { if (bss->wdev_id_set) NLA_PUT_U64(msg, NL80211_ATTR_WDEV, bss->wdev_id); else NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex); return 0; nla_put_failure: return -1; } static int family_handler(struct nl_msg *msg, void *arg) { struct family_data *res = arg; struct nlattr *tb[CTRL_ATTR_MAX + 1]; struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); struct nlattr *mcgrp; int i; nla_parse(tb, CTRL_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL); if (!tb[CTRL_ATTR_MCAST_GROUPS]) return NL_SKIP; nla_for_each_nested(mcgrp, tb[CTRL_ATTR_MCAST_GROUPS], i) { struct nlattr *tb2[CTRL_ATTR_MCAST_GRP_MAX + 1]; nla_parse(tb2, CTRL_ATTR_MCAST_GRP_MAX, nla_data(mcgrp), nla_len(mcgrp), NULL); if (!tb2[CTRL_ATTR_MCAST_GRP_NAME] || !tb2[CTRL_ATTR_MCAST_GRP_ID] || os_strncmp(nla_data(tb2[CTRL_ATTR_MCAST_GRP_NAME]), res->group, nla_len(tb2[CTRL_ATTR_MCAST_GRP_NAME])) != 0) continue; res->id = nla_get_u32(tb2[CTRL_ATTR_MCAST_GRP_ID]); break; }; return NL_SKIP; } static int nl_get_multicast_id(struct nl80211_global *global, const char *family, const char *group) { struct nl_msg *msg; int ret = -1; struct family_data res = { group, -ENOENT }; msg = nlmsg_alloc(); if (!msg) return -ENOMEM; genlmsg_put(msg, 0, 0, genl_ctrl_resolve(global->nl, "nlctrl"), 0, 0, CTRL_CMD_GETFAMILY, 0); NLA_PUT_STRING(msg, CTRL_ATTR_FAMILY_NAME, family); ret = send_and_recv_msgs_global(global, msg, family_handler, &res); msg = NULL; if (ret == 0) ret = res.id; nla_put_failure: nlmsg_free(msg); return ret; } static void * nl80211_cmd(struct wpa_driver_nl80211_data *drv, struct nl_msg *msg, int flags, uint8_t cmd) { return genlmsg_put(msg, 0, 0, drv->global->nl80211_id, 0, flags, cmd, 0); } struct wiphy_idx_data { int wiphy_idx; enum nl80211_iftype nlmode; u8 *macaddr; }; static int netdev_info_handler(struct nl_msg *msg, void *arg) { struct nlattr *tb[NL80211_ATTR_MAX + 1]; struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); struct wiphy_idx_data *info = arg; nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL); if (tb[NL80211_ATTR_WIPHY]) info->wiphy_idx = nla_get_u32(tb[NL80211_ATTR_WIPHY]); if (tb[NL80211_ATTR_IFTYPE]) info->nlmode = nla_get_u32(tb[NL80211_ATTR_IFTYPE]); if (tb[NL80211_ATTR_MAC] && info->macaddr) os_memcpy(info->macaddr, nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN); return NL_SKIP; } static int nl80211_get_wiphy_index(struct i802_bss *bss) { struct nl_msg *msg; struct wiphy_idx_data data = { .wiphy_idx = -1, .macaddr = NULL, }; msg = nlmsg_alloc(); if (!msg) return NL80211_IFTYPE_UNSPECIFIED; nl80211_cmd(bss->drv, msg, 0, NL80211_CMD_GET_INTERFACE); if (nl80211_set_iface_id(msg, bss) < 0) goto nla_put_failure; if (send_and_recv_msgs(bss->drv, msg, netdev_info_handler, &data) == 0) return data.wiphy_idx; msg = NULL; nla_put_failure: nlmsg_free(msg); return -1; } static enum nl80211_iftype nl80211_get_ifmode(struct i802_bss *bss) { struct nl_msg *msg; struct wiphy_idx_data data = { .nlmode = NL80211_IFTYPE_UNSPECIFIED, .macaddr = NULL, }; msg = nlmsg_alloc(); if (!msg) return -1; nl80211_cmd(bss->drv, msg, 0, NL80211_CMD_GET_INTERFACE); if (nl80211_set_iface_id(msg, bss) < 0) goto nla_put_failure; if (send_and_recv_msgs(bss->drv, msg, netdev_info_handler, &data) == 0) return data.nlmode; msg = NULL; nla_put_failure: nlmsg_free(msg); return NL80211_IFTYPE_UNSPECIFIED; } static int nl80211_get_macaddr(struct i802_bss *bss) { struct nl_msg *msg; struct wiphy_idx_data data = { .macaddr = bss->addr, }; msg = nlmsg_alloc(); if (!msg) return NL80211_IFTYPE_UNSPECIFIED; nl80211_cmd(bss->drv, msg, 0, NL80211_CMD_GET_INTERFACE); if (nl80211_set_iface_id(msg, bss) < 0) goto nla_put_failure; return send_and_recv_msgs(bss->drv, msg, netdev_info_handler, &data); nla_put_failure: nlmsg_free(msg); return NL80211_IFTYPE_UNSPECIFIED; } static int nl80211_register_beacons(struct wpa_driver_nl80211_data *drv, struct nl80211_wiphy_data *w) { struct nl_msg *msg; int ret = -1; msg = nlmsg_alloc(); if (!msg) return -1; nl80211_cmd(drv, msg, 0, NL80211_CMD_REGISTER_BEACONS); NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, w->wiphy_idx); ret = send_and_recv(drv->global, w->nl_beacons, msg, NULL, NULL); msg = NULL; if (ret) { wpa_printf(MSG_DEBUG, "nl80211: Register beacons command " "failed: ret=%d (%s)", ret, strerror(-ret)); goto nla_put_failure; } ret = 0; nla_put_failure: nlmsg_free(msg); return ret; } static void nl80211_recv_beacons(int sock, void *eloop_ctx, void *handle) { struct nl80211_wiphy_data *w = eloop_ctx; int res; wpa_printf(MSG_EXCESSIVE, "nl80211: Beacon event message available"); res = nl_recvmsgs(handle, w->nl_cb); if (res) { wpa_printf(MSG_INFO, "nl80211: %s->nl_recvmsgs failed: %d", __func__, res); } } static int process_beacon_event(struct nl_msg *msg, void *arg) { struct nl80211_wiphy_data *w = arg; struct wpa_driver_nl80211_data *drv; struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); struct nlattr *tb[NL80211_ATTR_MAX + 1]; union wpa_event_data event; nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL); if (gnlh->cmd != NL80211_CMD_FRAME) { wpa_printf(MSG_DEBUG, "nl80211: Unexpected beacon event? (%d)", gnlh->cmd); return NL_SKIP; } if (!tb[NL80211_ATTR_FRAME]) return NL_SKIP; dl_list_for_each(drv, &w->drvs, struct wpa_driver_nl80211_data, wiphy_list) { os_memset(&event, 0, sizeof(event)); event.rx_mgmt.frame = nla_data(tb[NL80211_ATTR_FRAME]); event.rx_mgmt.frame_len = nla_len(tb[NL80211_ATTR_FRAME]); wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event); } return NL_SKIP; } static struct nl80211_wiphy_data * nl80211_get_wiphy_data_ap(struct i802_bss *bss) { static DEFINE_DL_LIST(nl80211_wiphys); struct nl80211_wiphy_data *w; int wiphy_idx, found = 0; struct i802_bss *tmp_bss; if (bss->wiphy_data != NULL) return bss->wiphy_data; wiphy_idx = nl80211_get_wiphy_index(bss); dl_list_for_each(w, &nl80211_wiphys, struct nl80211_wiphy_data, list) { if (w->wiphy_idx == wiphy_idx) goto add; } /* alloc new one */ w = os_zalloc(sizeof(*w)); if (w == NULL) return NULL; w->wiphy_idx = wiphy_idx; dl_list_init(&w->bsss); dl_list_init(&w->drvs); w->nl_cb = nl_cb_alloc(NL_CB_DEFAULT); if (!w->nl_cb) { os_free(w); return NULL; } nl_cb_set(w->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, no_seq_check, NULL); nl_cb_set(w->nl_cb, NL_CB_VALID, NL_CB_CUSTOM, process_beacon_event, w); w->nl_beacons = nl_create_handle(bss->drv->global->nl_cb, "wiphy beacons"); if (w->nl_beacons == NULL) { os_free(w); return NULL; } if (nl80211_register_beacons(bss->drv, w)) { nl_destroy_handles(&w->nl_beacons); os_free(w); return NULL; } nl80211_register_eloop_read(&w->nl_beacons, nl80211_recv_beacons, w); dl_list_add(&nl80211_wiphys, &w->list); add: /* drv entry for this bss already there? */ dl_list_for_each(tmp_bss, &w->bsss, struct i802_bss, wiphy_list) { if (tmp_bss->drv == bss->drv) { found = 1; break; } } /* if not add it */ if (!found) dl_list_add(&w->drvs, &bss->drv->wiphy_list); dl_list_add(&w->bsss, &bss->wiphy_list); bss->wiphy_data = w; return w; } static void nl80211_put_wiphy_data_ap(struct i802_bss *bss) { struct nl80211_wiphy_data *w = bss->wiphy_data; struct i802_bss *tmp_bss; int found = 0; if (w == NULL) return; bss->wiphy_data = NULL; dl_list_del(&bss->wiphy_list); /* still any for this drv present? */ dl_list_for_each(tmp_bss, &w->bsss, struct i802_bss, wiphy_list) { if (tmp_bss->drv == bss->drv) { found = 1; break; } } /* if not remove it */ if (!found) dl_list_del(&bss->drv->wiphy_list); if (!dl_list_empty(&w->bsss)) return; nl80211_destroy_eloop_handle(&w->nl_beacons); nl_cb_put(w->nl_cb); dl_list_del(&w->list); os_free(w); } static int wpa_driver_nl80211_get_bssid(void *priv, u8 *bssid) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; if (!drv->associated) return -1; os_memcpy(bssid, drv->bssid, ETH_ALEN); return 0; } static int wpa_driver_nl80211_get_ssid(void *priv, u8 *ssid) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; if (!drv->associated) return -1; os_memcpy(ssid, drv->ssid, drv->ssid_len); return drv->ssid_len; } static void wpa_driver_nl80211_event_newlink( struct wpa_driver_nl80211_data *drv, char *ifname) { union wpa_event_data event; if (os_strcmp(drv->first_bss->ifname, ifname) == 0) { if (if_nametoindex(drv->first_bss->ifname) == 0) { wpa_printf(MSG_DEBUG, "nl80211: Interface %s does not exist - ignore RTM_NEWLINK", drv->first_bss->ifname); return; } if (!drv->if_removed) return; wpa_printf(MSG_DEBUG, "nl80211: Mark if_removed=0 for %s based on RTM_NEWLINK event", drv->first_bss->ifname); drv->if_removed = 0; } os_memset(&event, 0, sizeof(event)); os_strlcpy(event.interface_status.ifname, ifname, sizeof(event.interface_status.ifname)); event.interface_status.ievent = EVENT_INTERFACE_ADDED; wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event); } static void wpa_driver_nl80211_event_dellink( struct wpa_driver_nl80211_data *drv, char *ifname) { union wpa_event_data event; if (os_strcmp(drv->first_bss->ifname, ifname) == 0) { if (drv->if_removed) { wpa_printf(MSG_DEBUG, "nl80211: if_removed already set - ignore RTM_DELLINK event for %s", ifname); return; } wpa_printf(MSG_DEBUG, "RTM_DELLINK: Interface '%s' removed - mark if_removed=1", ifname); drv->if_removed = 1; } else { wpa_printf(MSG_DEBUG, "RTM_DELLINK: Interface '%s' removed", ifname); } os_memset(&event, 0, sizeof(event)); os_strlcpy(event.interface_status.ifname, ifname, sizeof(event.interface_status.ifname)); event.interface_status.ievent = EVENT_INTERFACE_REMOVED; wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event); } static int wpa_driver_nl80211_own_ifname(struct wpa_driver_nl80211_data *drv, u8 *buf, size_t len) { int attrlen, rta_len; struct rtattr *attr; attrlen = len; attr = (struct rtattr *) buf; rta_len = RTA_ALIGN(sizeof(struct rtattr)); while (RTA_OK(attr, attrlen)) { if (attr->rta_type == IFLA_IFNAME) { if (os_strcmp(((char *) attr) + rta_len, drv->first_bss->ifname) == 0) return 1; else break; } attr = RTA_NEXT(attr, attrlen); } return 0; } static int wpa_driver_nl80211_own_ifindex(struct wpa_driver_nl80211_data *drv, int ifindex, u8 *buf, size_t len) { if (drv->ifindex == ifindex) return 1; if (drv->if_removed && wpa_driver_nl80211_own_ifname(drv, buf, len)) { wpa_printf(MSG_DEBUG, "nl80211: Update ifindex for a removed " "interface"); wpa_driver_nl80211_finish_drv_init(drv, NULL, 0); return 1; } return 0; } static struct wpa_driver_nl80211_data * nl80211_find_drv(struct nl80211_global *global, int idx, u8 *buf, size_t len) { struct wpa_driver_nl80211_data *drv; dl_list_for_each(drv, &global->interfaces, struct wpa_driver_nl80211_data, list) { if (wpa_driver_nl80211_own_ifindex(drv, idx, buf, len) || have_ifidx(drv, idx)) return drv; } return NULL; } static void wpa_driver_nl80211_event_rtm_newlink(void *ctx, struct ifinfomsg *ifi, u8 *buf, size_t len) { struct nl80211_global *global = ctx; struct wpa_driver_nl80211_data *drv; int attrlen; struct rtattr *attr; u32 brid = 0; char namebuf[IFNAMSIZ]; char ifname[IFNAMSIZ + 1]; char extra[100], *pos, *end; drv = nl80211_find_drv(global, ifi->ifi_index, buf, len); if (!drv) { wpa_printf(MSG_DEBUG, "nl80211: Ignore RTM_NEWLINK event for foreign ifindex %d", ifi->ifi_index); return; } extra[0] = '\0'; pos = extra; end = pos + sizeof(extra); ifname[0] = '\0'; attrlen = len; attr = (struct rtattr *) buf; while (RTA_OK(attr, attrlen)) { switch (attr->rta_type) { case IFLA_IFNAME: if (RTA_PAYLOAD(attr) >= IFNAMSIZ) break; os_memcpy(ifname, RTA_DATA(attr), RTA_PAYLOAD(attr)); ifname[RTA_PAYLOAD(attr)] = '\0'; break; case IFLA_MASTER: brid = nla_get_u32((struct nlattr *) attr); pos += os_snprintf(pos, end - pos, " master=%u", brid); break; case IFLA_WIRELESS: pos += os_snprintf(pos, end - pos, " wext"); break; case IFLA_OPERSTATE: pos += os_snprintf(pos, end - pos, " operstate=%u", nla_get_u32((struct nlattr *) attr)); break; case IFLA_LINKMODE: pos += os_snprintf(pos, end - pos, " linkmode=%u", nla_get_u32((struct nlattr *) attr)); break; } attr = RTA_NEXT(attr, attrlen); } extra[sizeof(extra) - 1] = '\0'; wpa_printf(MSG_DEBUG, "RTM_NEWLINK: ifi_index=%d ifname=%s%s ifi_flags=0x%x (%s%s%s%s)", ifi->ifi_index, ifname, extra, ifi->ifi_flags, (ifi->ifi_flags & IFF_UP) ? "[UP]" : "", (ifi->ifi_flags & IFF_RUNNING) ? "[RUNNING]" : "", (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "", (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : ""); if (!drv->if_disabled && !(ifi->ifi_flags & IFF_UP)) { if (if_indextoname(ifi->ifi_index, namebuf) && linux_iface_up(drv->global->ioctl_sock, drv->first_bss->ifname) > 0) { wpa_printf(MSG_DEBUG, "nl80211: Ignore interface down " "event since interface %s is up", namebuf); return; } wpa_printf(MSG_DEBUG, "nl80211: Interface down"); if (drv->ignore_if_down_event) { wpa_printf(MSG_DEBUG, "nl80211: Ignore interface down " "event generated by mode change"); drv->ignore_if_down_event = 0; } else { drv->if_disabled = 1; wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_DISABLED, NULL); } } if (drv->if_disabled && (ifi->ifi_flags & IFF_UP)) { if (if_indextoname(ifi->ifi_index, namebuf) && linux_iface_up(drv->global->ioctl_sock, drv->first_bss->ifname) == 0) { wpa_printf(MSG_DEBUG, "nl80211: Ignore interface up " "event since interface %s is down", namebuf); } else if (if_nametoindex(drv->first_bss->ifname) == 0) { wpa_printf(MSG_DEBUG, "nl80211: Ignore interface up " "event since interface %s does not exist", drv->first_bss->ifname); } else if (drv->if_removed) { wpa_printf(MSG_DEBUG, "nl80211: Ignore interface up " "event since interface %s is marked " "removed", drv->first_bss->ifname); } else { wpa_printf(MSG_DEBUG, "nl80211: Interface up"); drv->if_disabled = 0; wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_ENABLED, NULL); } } /* * Some drivers send the association event before the operup event--in * this case, lifting operstate in wpa_driver_nl80211_set_operstate() * fails. This will hit us when wpa_supplicant does not need to do * IEEE 802.1X authentication */ if (drv->operstate == 1 && (ifi->ifi_flags & (IFF_LOWER_UP | IFF_DORMANT)) == IFF_LOWER_UP && !(ifi->ifi_flags & IFF_RUNNING)) { wpa_printf(MSG_DEBUG, "nl80211: Set IF_OPER_UP again based on ifi_flags and expected operstate"); netlink_send_oper_ifla(drv->global->netlink, drv->ifindex, -1, IF_OPER_UP); } if (ifname[0]) wpa_driver_nl80211_event_newlink(drv, ifname); if (ifi->ifi_family == AF_BRIDGE && brid) { /* device has been added to bridge */ if_indextoname(brid, namebuf); wpa_printf(MSG_DEBUG, "nl80211: Add ifindex %u for bridge %s", brid, namebuf); add_ifidx(drv, brid); } } static void wpa_driver_nl80211_event_rtm_dellink(void *ctx, struct ifinfomsg *ifi, u8 *buf, size_t len) { struct nl80211_global *global = ctx; struct wpa_driver_nl80211_data *drv; int attrlen; struct rtattr *attr; u32 brid = 0; char ifname[IFNAMSIZ + 1]; drv = nl80211_find_drv(global, ifi->ifi_index, buf, len); if (!drv) { wpa_printf(MSG_DEBUG, "nl80211: Ignore RTM_DELLINK event for foreign ifindex %d", ifi->ifi_index); return; } ifname[0] = '\0'; attrlen = len; attr = (struct rtattr *) buf; while (RTA_OK(attr, attrlen)) { switch (attr->rta_type) { case IFLA_IFNAME: if (RTA_PAYLOAD(attr) >= IFNAMSIZ) break; os_memcpy(ifname, RTA_DATA(attr), RTA_PAYLOAD(attr)); ifname[RTA_PAYLOAD(attr)] = '\0'; break; case IFLA_MASTER: brid = nla_get_u32((struct nlattr *) attr); break; } attr = RTA_NEXT(attr, attrlen); } if (ifname[0]) wpa_driver_nl80211_event_dellink(drv, ifname); if (ifi->ifi_family == AF_BRIDGE && brid) { /* device has been removed from bridge */ char namebuf[IFNAMSIZ]; if_indextoname(brid, namebuf); wpa_printf(MSG_DEBUG, "nl80211: Remove ifindex %u for bridge " "%s", brid, namebuf); del_ifidx(drv, brid); } } static void mlme_event_auth(struct wpa_driver_nl80211_data *drv, const u8 *frame, size_t len) { const struct ieee80211_mgmt *mgmt; union wpa_event_data event; wpa_printf(MSG_DEBUG, "nl80211: Authenticate event"); mgmt = (const struct ieee80211_mgmt *) frame; if (len < 24 + sizeof(mgmt->u.auth)) { wpa_printf(MSG_DEBUG, "nl80211: Too short association event " "frame"); return; } os_memcpy(drv->auth_bssid, mgmt->sa, ETH_ALEN); os_memset(drv->auth_attempt_bssid, 0, ETH_ALEN); os_memset(&event, 0, sizeof(event)); os_memcpy(event.auth.peer, mgmt->sa, ETH_ALEN); event.auth.auth_type = le_to_host16(mgmt->u.auth.auth_alg); event.auth.auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction); event.auth.status_code = le_to_host16(mgmt->u.auth.status_code); if (len > 24 + sizeof(mgmt->u.auth)) { event.auth.ies = mgmt->u.auth.variable; event.auth.ies_len = len - 24 - sizeof(mgmt->u.auth); } wpa_supplicant_event(drv->ctx, EVENT_AUTH, &event); } static unsigned int nl80211_get_assoc_freq(struct wpa_driver_nl80211_data *drv) { struct nl_msg *msg; int ret; struct nl80211_bss_info_arg arg; os_memset(&arg, 0, sizeof(arg)); msg = nlmsg_alloc(); if (!msg) goto nla_put_failure; nl80211_cmd(drv, msg, NLM_F_DUMP, NL80211_CMD_GET_SCAN); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); arg.drv = drv; ret = send_and_recv_msgs(drv, msg, bss_info_handler, &arg); msg = NULL; if (ret == 0) { wpa_printf(MSG_DEBUG, "nl80211: Operating frequency for the " "associated BSS from scan results: %u MHz", arg.assoc_freq); if (arg.assoc_freq) drv->assoc_freq = arg.assoc_freq; return drv->assoc_freq; } wpa_printf(MSG_DEBUG, "nl80211: Scan result fetch failed: ret=%d " "(%s)", ret, strerror(-ret)); nla_put_failure: nlmsg_free(msg); return drv->assoc_freq; } static void mlme_event_assoc(struct wpa_driver_nl80211_data *drv, const u8 *frame, size_t len) { const struct ieee80211_mgmt *mgmt; union wpa_event_data event; u16 status; wpa_printf(MSG_DEBUG, "nl80211: Associate event"); mgmt = (const struct ieee80211_mgmt *) frame; if (len < 24 + sizeof(mgmt->u.assoc_resp)) { wpa_printf(MSG_DEBUG, "nl80211: Too short association event " "frame"); return; } status = le_to_host16(mgmt->u.assoc_resp.status_code); if (status != WLAN_STATUS_SUCCESS) { os_memset(&event, 0, sizeof(event)); event.assoc_reject.bssid = mgmt->bssid; if (len > 24 + sizeof(mgmt->u.assoc_resp)) { event.assoc_reject.resp_ies = (u8 *) mgmt->u.assoc_resp.variable; event.assoc_reject.resp_ies_len = len - 24 - sizeof(mgmt->u.assoc_resp); } event.assoc_reject.status_code = status; wpa_supplicant_event(drv->ctx, EVENT_ASSOC_REJECT, &event); return; } drv->associated = 1; os_memcpy(drv->bssid, mgmt->sa, ETH_ALEN); os_memcpy(drv->prev_bssid, mgmt->sa, ETH_ALEN); os_memset(&event, 0, sizeof(event)); if (len > 24 + sizeof(mgmt->u.assoc_resp)) { event.assoc_info.resp_ies = (u8 *) mgmt->u.assoc_resp.variable; event.assoc_info.resp_ies_len = len - 24 - sizeof(mgmt->u.assoc_resp); } event.assoc_info.freq = drv->assoc_freq; wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event); } static void mlme_event_connect(struct wpa_driver_nl80211_data *drv, enum nl80211_commands cmd, struct nlattr *status, struct nlattr *addr, struct nlattr *req_ie, struct nlattr *resp_ie) { union wpa_event_data event; if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) { /* * Avoid reporting two association events that would confuse * the core code. */ wpa_printf(MSG_DEBUG, "nl80211: Ignore connect event (cmd=%d) " "when using userspace SME", cmd); return; } if (cmd == NL80211_CMD_CONNECT) wpa_printf(MSG_DEBUG, "nl80211: Connect event"); else if (cmd == NL80211_CMD_ROAM) wpa_printf(MSG_DEBUG, "nl80211: Roam event"); os_memset(&event, 0, sizeof(event)); if (cmd == NL80211_CMD_CONNECT && nla_get_u16(status) != WLAN_STATUS_SUCCESS) { if (addr) event.assoc_reject.bssid = nla_data(addr); if (resp_ie) { event.assoc_reject.resp_ies = nla_data(resp_ie); event.assoc_reject.resp_ies_len = nla_len(resp_ie); } event.assoc_reject.status_code = nla_get_u16(status); wpa_supplicant_event(drv->ctx, EVENT_ASSOC_REJECT, &event); return; } drv->associated = 1; if (addr) { os_memcpy(drv->bssid, nla_data(addr), ETH_ALEN); os_memcpy(drv->prev_bssid, drv->bssid, ETH_ALEN); } if (req_ie) { event.assoc_info.req_ies = nla_data(req_ie); event.assoc_info.req_ies_len = nla_len(req_ie); } if (resp_ie) { event.assoc_info.resp_ies = nla_data(resp_ie); event.assoc_info.resp_ies_len = nla_len(resp_ie); } event.assoc_info.freq = nl80211_get_assoc_freq(drv); wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event); } static void mlme_event_disconnect(struct wpa_driver_nl80211_data *drv, struct nlattr *reason, struct nlattr *addr, struct nlattr *by_ap) { union wpa_event_data data; unsigned int locally_generated = by_ap == NULL; if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) { /* * Avoid reporting two disassociation events that could * confuse the core code. */ wpa_printf(MSG_DEBUG, "nl80211: Ignore disconnect " "event when using userspace SME"); return; } if (drv->ignore_next_local_disconnect) { drv->ignore_next_local_disconnect = 0; if (locally_generated) { wpa_printf(MSG_DEBUG, "nl80211: Ignore disconnect " "event triggered during reassociation"); return; } wpa_printf(MSG_WARNING, "nl80211: Was expecting local " "disconnect but got another disconnect " "event first"); } wpa_printf(MSG_DEBUG, "nl80211: Disconnect event"); nl80211_mark_disconnected(drv); os_memset(&data, 0, sizeof(data)); if (reason) data.deauth_info.reason_code = nla_get_u16(reason); data.deauth_info.locally_generated = by_ap == NULL; wpa_supplicant_event(drv->ctx, EVENT_DEAUTH, &data); } static int calculate_chan_offset(int width, int freq, int cf1, int cf2) { int freq1 = 0; switch (convert2width(width)) { case CHAN_WIDTH_20_NOHT: case CHAN_WIDTH_20: return 0; case CHAN_WIDTH_40: freq1 = cf1 - 10; break; case CHAN_WIDTH_80: freq1 = cf1 - 30; break; case CHAN_WIDTH_160: freq1 = cf1 - 70; break; case CHAN_WIDTH_UNKNOWN: case CHAN_WIDTH_80P80: /* FIXME: implement this */ return 0; } return (abs(freq - freq1) / 20) % 2 == 0 ? 1 : -1; } static void mlme_event_ch_switch(struct wpa_driver_nl80211_data *drv, struct nlattr *ifindex, struct nlattr *freq, struct nlattr *type, struct nlattr *bw, struct nlattr *cf1, struct nlattr *cf2) { struct i802_bss *bss; union wpa_event_data data; int ht_enabled = 1; int chan_offset = 0; int ifidx; wpa_printf(MSG_DEBUG, "nl80211: Channel switch event"); if (!freq) return; ifidx = nla_get_u32(ifindex); for (bss = drv->first_bss; bss; bss = bss->next) if (bss->ifindex == ifidx) break; if (bss == NULL) { wpa_printf(MSG_WARNING, "nl80211: Unknown ifindex (%d) for channel switch, ignoring", ifidx); return; } if (type) { switch (nla_get_u32(type)) { case NL80211_CHAN_NO_HT: ht_enabled = 0; break; case NL80211_CHAN_HT20: break; case NL80211_CHAN_HT40PLUS: chan_offset = 1; break; case NL80211_CHAN_HT40MINUS: chan_offset = -1; break; } } else if (bw && cf1) { /* This can happen for example with VHT80 ch switch */ chan_offset = calculate_chan_offset(nla_get_u32(bw), nla_get_u32(freq), nla_get_u32(cf1), cf2 ? nla_get_u32(cf2) : 0); } else { wpa_printf(MSG_WARNING, "nl80211: Unknown secondary channel information - following channel definition calculations may fail"); } os_memset(&data, 0, sizeof(data)); data.ch_switch.freq = nla_get_u32(freq); data.ch_switch.ht_enabled = ht_enabled; data.ch_switch.ch_offset = chan_offset; if (bw) data.ch_switch.ch_width = convert2width(nla_get_u32(bw)); if (cf1) data.ch_switch.cf1 = nla_get_u32(cf1); if (cf2) data.ch_switch.cf2 = nla_get_u32(cf2); bss->freq = data.ch_switch.freq; wpa_supplicant_event(drv->ctx, EVENT_CH_SWITCH, &data); } static void mlme_timeout_event(struct wpa_driver_nl80211_data *drv, enum nl80211_commands cmd, struct nlattr *addr) { union wpa_event_data event; enum wpa_event_type ev; if (nla_len(addr) != ETH_ALEN) return; wpa_printf(MSG_DEBUG, "nl80211: MLME event %d; timeout with " MACSTR, cmd, MAC2STR((u8 *) nla_data(addr))); if (cmd == NL80211_CMD_AUTHENTICATE) ev = EVENT_AUTH_TIMED_OUT; else if (cmd == NL80211_CMD_ASSOCIATE) ev = EVENT_ASSOC_TIMED_OUT; else return; os_memset(&event, 0, sizeof(event)); os_memcpy(event.timeout_event.addr, nla_data(addr), ETH_ALEN); wpa_supplicant_event(drv->ctx, ev, &event); } static void mlme_event_mgmt(struct wpa_driver_nl80211_data *drv, struct nlattr *freq, struct nlattr *sig, const u8 *frame, size_t len) { const struct ieee80211_mgmt *mgmt; union wpa_event_data event; u16 fc, stype; int ssi_signal = 0; int rx_freq = 0; wpa_printf(MSG_MSGDUMP, "nl80211: Frame event"); mgmt = (const struct ieee80211_mgmt *) frame; if (len < 24) { wpa_printf(MSG_DEBUG, "nl80211: Too short management frame"); return; } fc = le_to_host16(mgmt->frame_control); stype = WLAN_FC_GET_STYPE(fc); if (sig) ssi_signal = (s32) nla_get_u32(sig); os_memset(&event, 0, sizeof(event)); if (freq) { event.rx_mgmt.freq = nla_get_u32(freq); rx_freq = drv->last_mgmt_freq = event.rx_mgmt.freq; } wpa_printf(MSG_DEBUG, "nl80211: RX frame freq=%d ssi_signal=%d stype=%u len=%u", rx_freq, ssi_signal, stype, (unsigned int) len); event.rx_mgmt.frame = frame; event.rx_mgmt.frame_len = len; event.rx_mgmt.ssi_signal = ssi_signal; wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event); } static void mlme_event_mgmt_tx_status(struct wpa_driver_nl80211_data *drv, struct nlattr *cookie, const u8 *frame, size_t len, struct nlattr *ack) { union wpa_event_data event; const struct ieee80211_hdr *hdr; u16 fc; wpa_printf(MSG_DEBUG, "nl80211: Frame TX status event"); if (!is_ap_interface(drv->nlmode)) { u64 cookie_val; if (!cookie) return; cookie_val = nla_get_u64(cookie); wpa_printf(MSG_DEBUG, "nl80211: Action TX status:" " cookie=0%llx%s (ack=%d)", (long long unsigned int) cookie_val, cookie_val == drv->send_action_cookie ? " (match)" : " (unknown)", ack != NULL); if (cookie_val != drv->send_action_cookie) return; } hdr = (const struct ieee80211_hdr *) frame; fc = le_to_host16(hdr->frame_control); os_memset(&event, 0, sizeof(event)); event.tx_status.type = WLAN_FC_GET_TYPE(fc); event.tx_status.stype = WLAN_FC_GET_STYPE(fc); event.tx_status.dst = hdr->addr1; event.tx_status.data = frame; event.tx_status.data_len = len; event.tx_status.ack = ack != NULL; wpa_supplicant_event(drv->ctx, EVENT_TX_STATUS, &event); } static void mlme_event_deauth_disassoc(struct wpa_driver_nl80211_data *drv, enum wpa_event_type type, const u8 *frame, size_t len) { const struct ieee80211_mgmt *mgmt; union wpa_event_data event; const u8 *bssid = NULL; u16 reason_code = 0; if (type == EVENT_DEAUTH) wpa_printf(MSG_DEBUG, "nl80211: Deauthenticate event"); else wpa_printf(MSG_DEBUG, "nl80211: Disassociate event"); mgmt = (const struct ieee80211_mgmt *) frame; if (len >= 24) { bssid = mgmt->bssid; if ((drv->capa.flags & WPA_DRIVER_FLAGS_SME) && !drv->associated && os_memcmp(bssid, drv->auth_bssid, ETH_ALEN) != 0 && os_memcmp(bssid, drv->auth_attempt_bssid, ETH_ALEN) != 0 && os_memcmp(bssid, drv->prev_bssid, ETH_ALEN) == 0) { /* * Avoid issues with some roaming cases where * disconnection event for the old AP may show up after * we have started connection with the new AP. */ wpa_printf(MSG_DEBUG, "nl80211: Ignore deauth/disassoc event from old AP " MACSTR " when already authenticating with " MACSTR, MAC2STR(bssid), MAC2STR(drv->auth_attempt_bssid)); return; } if (drv->associated != 0 && os_memcmp(bssid, drv->bssid, ETH_ALEN) != 0 && os_memcmp(bssid, drv->auth_bssid, ETH_ALEN) != 0) { /* * We have presumably received this deauth as a * response to a clear_state_mismatch() outgoing * deauth. Don't let it take us offline! */ wpa_printf(MSG_DEBUG, "nl80211: Deauth received " "from Unknown BSSID " MACSTR " -- ignoring", MAC2STR(bssid)); return; } } nl80211_mark_disconnected(drv); os_memset(&event, 0, sizeof(event)); /* Note: Same offset for Reason Code in both frame subtypes */ if (len >= 24 + sizeof(mgmt->u.deauth)) reason_code = le_to_host16(mgmt->u.deauth.reason_code); if (type == EVENT_DISASSOC) { event.disassoc_info.locally_generated = !os_memcmp(mgmt->sa, drv->first_bss->addr, ETH_ALEN); event.disassoc_info.addr = bssid; event.disassoc_info.reason_code = reason_code; if (frame + len > mgmt->u.disassoc.variable) { event.disassoc_info.ie = mgmt->u.disassoc.variable; event.disassoc_info.ie_len = frame + len - mgmt->u.disassoc.variable; } } else { event.deauth_info.locally_generated = !os_memcmp(mgmt->sa, drv->first_bss->addr, ETH_ALEN); event.deauth_info.addr = bssid; event.deauth_info.reason_code = reason_code; if (frame + len > mgmt->u.deauth.variable) { event.deauth_info.ie = mgmt->u.deauth.variable; event.deauth_info.ie_len = frame + len - mgmt->u.deauth.variable; } } wpa_supplicant_event(drv->ctx, type, &event); } static void mlme_event_unprot_disconnect(struct wpa_driver_nl80211_data *drv, enum wpa_event_type type, const u8 *frame, size_t len) { const struct ieee80211_mgmt *mgmt; union wpa_event_data event; u16 reason_code = 0; if (type == EVENT_UNPROT_DEAUTH) wpa_printf(MSG_DEBUG, "nl80211: Unprot Deauthenticate event"); else wpa_printf(MSG_DEBUG, "nl80211: Unprot Disassociate event"); if (len < 24) return; mgmt = (const struct ieee80211_mgmt *) frame; os_memset(&event, 0, sizeof(event)); /* Note: Same offset for Reason Code in both frame subtypes */ if (len >= 24 + sizeof(mgmt->u.deauth)) reason_code = le_to_host16(mgmt->u.deauth.reason_code); if (type == EVENT_UNPROT_DISASSOC) { event.unprot_disassoc.sa = mgmt->sa; event.unprot_disassoc.da = mgmt->da; event.unprot_disassoc.reason_code = reason_code; } else { event.unprot_deauth.sa = mgmt->sa; event.unprot_deauth.da = mgmt->da; event.unprot_deauth.reason_code = reason_code; } wpa_supplicant_event(drv->ctx, type, &event); } static void mlme_event(struct i802_bss *bss, enum nl80211_commands cmd, struct nlattr *frame, struct nlattr *addr, struct nlattr *timed_out, struct nlattr *freq, struct nlattr *ack, struct nlattr *cookie, struct nlattr *sig) { struct wpa_driver_nl80211_data *drv = bss->drv; const u8 *data; size_t len; if (timed_out && addr) { mlme_timeout_event(drv, cmd, addr); return; } if (frame == NULL) { wpa_printf(MSG_DEBUG, "nl80211: MLME event %d (%s) without frame data", cmd, nl80211_command_to_string(cmd)); return; } data = nla_data(frame); len = nla_len(frame); if (len < 4 + 2 * ETH_ALEN) { wpa_printf(MSG_MSGDUMP, "nl80211: MLME event %d (%s) on %s(" MACSTR ") - too short", cmd, nl80211_command_to_string(cmd), bss->ifname, MAC2STR(bss->addr)); return; } wpa_printf(MSG_MSGDUMP, "nl80211: MLME event %d (%s) on %s(" MACSTR ") A1=" MACSTR " A2=" MACSTR, cmd, nl80211_command_to_string(cmd), bss->ifname, MAC2STR(bss->addr), MAC2STR(data + 4), MAC2STR(data + 4 + ETH_ALEN)); if (cmd != NL80211_CMD_FRAME_TX_STATUS && !(data[4] & 0x01) && os_memcmp(bss->addr, data + 4, ETH_ALEN) != 0 && os_memcmp(bss->addr, data + 4 + ETH_ALEN, ETH_ALEN) != 0) { wpa_printf(MSG_MSGDUMP, "nl80211: %s: Ignore MLME frame event " "for foreign address", bss->ifname); return; } wpa_hexdump(MSG_MSGDUMP, "nl80211: MLME event frame", nla_data(frame), nla_len(frame)); switch (cmd) { case NL80211_CMD_AUTHENTICATE: mlme_event_auth(drv, nla_data(frame), nla_len(frame)); break; case NL80211_CMD_ASSOCIATE: mlme_event_assoc(drv, nla_data(frame), nla_len(frame)); break; case NL80211_CMD_DEAUTHENTICATE: mlme_event_deauth_disassoc(drv, EVENT_DEAUTH, nla_data(frame), nla_len(frame)); break; case NL80211_CMD_DISASSOCIATE: mlme_event_deauth_disassoc(drv, EVENT_DISASSOC, nla_data(frame), nla_len(frame)); break; case NL80211_CMD_FRAME: mlme_event_mgmt(drv, freq, sig, nla_data(frame), nla_len(frame)); break; case NL80211_CMD_FRAME_TX_STATUS: mlme_event_mgmt_tx_status(drv, cookie, nla_data(frame), nla_len(frame), ack); break; case NL80211_CMD_UNPROT_DEAUTHENTICATE: mlme_event_unprot_disconnect(drv, EVENT_UNPROT_DEAUTH, nla_data(frame), nla_len(frame)); break; case NL80211_CMD_UNPROT_DISASSOCIATE: mlme_event_unprot_disconnect(drv, EVENT_UNPROT_DISASSOC, nla_data(frame), nla_len(frame)); break; default: break; } } static void mlme_event_michael_mic_failure(struct i802_bss *bss, struct nlattr *tb[]) { union wpa_event_data data; wpa_printf(MSG_DEBUG, "nl80211: MLME event Michael MIC failure"); os_memset(&data, 0, sizeof(data)); if (tb[NL80211_ATTR_MAC]) { wpa_hexdump(MSG_DEBUG, "nl80211: Source MAC address", nla_data(tb[NL80211_ATTR_MAC]), nla_len(tb[NL80211_ATTR_MAC])); data.michael_mic_failure.src = nla_data(tb[NL80211_ATTR_MAC]); } if (tb[NL80211_ATTR_KEY_SEQ]) { wpa_hexdump(MSG_DEBUG, "nl80211: TSC", nla_data(tb[NL80211_ATTR_KEY_SEQ]), nla_len(tb[NL80211_ATTR_KEY_SEQ])); } if (tb[NL80211_ATTR_KEY_TYPE]) { enum nl80211_key_type key_type = nla_get_u32(tb[NL80211_ATTR_KEY_TYPE]); wpa_printf(MSG_DEBUG, "nl80211: Key Type %d", key_type); if (key_type == NL80211_KEYTYPE_PAIRWISE) data.michael_mic_failure.unicast = 1; } else data.michael_mic_failure.unicast = 1; if (tb[NL80211_ATTR_KEY_IDX]) { u8 key_id = nla_get_u8(tb[NL80211_ATTR_KEY_IDX]); wpa_printf(MSG_DEBUG, "nl80211: Key Id %d", key_id); } wpa_supplicant_event(bss->ctx, EVENT_MICHAEL_MIC_FAILURE, &data); } static void mlme_event_join_ibss(struct wpa_driver_nl80211_data *drv, struct nlattr *tb[]) { if (tb[NL80211_ATTR_MAC] == NULL) { wpa_printf(MSG_DEBUG, "nl80211: No address in IBSS joined " "event"); return; } os_memcpy(drv->bssid, nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN); drv->associated = 1; wpa_printf(MSG_DEBUG, "nl80211: IBSS " MACSTR " joined", MAC2STR(drv->bssid)); wpa_supplicant_event(drv->ctx, EVENT_ASSOC, NULL); } static void mlme_event_remain_on_channel(struct wpa_driver_nl80211_data *drv, int cancel_event, struct nlattr *tb[]) { unsigned int freq, chan_type, duration; union wpa_event_data data; u64 cookie; if (tb[NL80211_ATTR_WIPHY_FREQ]) freq = nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]); else freq = 0; if (tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) chan_type = nla_get_u32(tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE]); else chan_type = 0; if (tb[NL80211_ATTR_DURATION]) duration = nla_get_u32(tb[NL80211_ATTR_DURATION]); else duration = 0; if (tb[NL80211_ATTR_COOKIE]) cookie = nla_get_u64(tb[NL80211_ATTR_COOKIE]); else cookie = 0; wpa_printf(MSG_DEBUG, "nl80211: Remain-on-channel event (cancel=%d " "freq=%u channel_type=%u duration=%u cookie=0x%llx (%s))", cancel_event, freq, chan_type, duration, (long long unsigned int) cookie, cookie == drv->remain_on_chan_cookie ? "match" : "unknown"); if (cookie != drv->remain_on_chan_cookie) return; /* not for us */ if (cancel_event) drv->pending_remain_on_chan = 0; os_memset(&data, 0, sizeof(data)); data.remain_on_channel.freq = freq; data.remain_on_channel.duration = duration; wpa_supplicant_event(drv->ctx, cancel_event ? EVENT_CANCEL_REMAIN_ON_CHANNEL : EVENT_REMAIN_ON_CHANNEL, &data); } static void mlme_event_ft_event(struct wpa_driver_nl80211_data *drv, struct nlattr *tb[]) { union wpa_event_data data; os_memset(&data, 0, sizeof(data)); if (tb[NL80211_ATTR_IE]) { data.ft_ies.ies = nla_data(tb[NL80211_ATTR_IE]); data.ft_ies.ies_len = nla_len(tb[NL80211_ATTR_IE]); } if (tb[NL80211_ATTR_IE_RIC]) { data.ft_ies.ric_ies = nla_data(tb[NL80211_ATTR_IE_RIC]); data.ft_ies.ric_ies_len = nla_len(tb[NL80211_ATTR_IE_RIC]); } if (tb[NL80211_ATTR_MAC]) os_memcpy(data.ft_ies.target_ap, nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN); wpa_printf(MSG_DEBUG, "nl80211: FT event target_ap " MACSTR, MAC2STR(data.ft_ies.target_ap)); wpa_supplicant_event(drv->ctx, EVENT_FT_RESPONSE, &data); } static void send_scan_event(struct wpa_driver_nl80211_data *drv, int aborted, struct nlattr *tb[]) { union wpa_event_data event; struct nlattr *nl; int rem; struct scan_info *info; #define MAX_REPORT_FREQS 50 int freqs[MAX_REPORT_FREQS]; int num_freqs = 0; if (drv->scan_for_auth) { drv->scan_for_auth = 0; wpa_printf(MSG_DEBUG, "nl80211: Scan results for missing " "cfg80211 BSS entry"); wpa_driver_nl80211_authenticate_retry(drv); return; } os_memset(&event, 0, sizeof(event)); info = &event.scan_info; info->aborted = aborted; if (tb[NL80211_ATTR_SCAN_SSIDS]) { nla_for_each_nested(nl, tb[NL80211_ATTR_SCAN_SSIDS], rem) { struct wpa_driver_scan_ssid *s = &info->ssids[info->num_ssids]; s->ssid = nla_data(nl); s->ssid_len = nla_len(nl); wpa_printf(MSG_DEBUG, "nl80211: Scan probed for SSID '%s'", wpa_ssid_txt(s->ssid, s->ssid_len)); info->num_ssids++; if (info->num_ssids == WPAS_MAX_SCAN_SSIDS) break; } } if (tb[NL80211_ATTR_SCAN_FREQUENCIES]) { char msg[200], *pos, *end; int res; pos = msg; end = pos + sizeof(msg); *pos = '\0'; nla_for_each_nested(nl, tb[NL80211_ATTR_SCAN_FREQUENCIES], rem) { freqs[num_freqs] = nla_get_u32(nl); res = os_snprintf(pos, end - pos, " %d", freqs[num_freqs]); if (res > 0 && end - pos > res) pos += res; num_freqs++; if (num_freqs == MAX_REPORT_FREQS - 1) break; } info->freqs = freqs; info->num_freqs = num_freqs; wpa_printf(MSG_DEBUG, "nl80211: Scan included frequencies:%s", msg); } wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, &event); } static int get_link_signal(struct nl_msg *msg, void *arg) { struct nlattr *tb[NL80211_ATTR_MAX + 1]; struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); struct nlattr *sinfo[NL80211_STA_INFO_MAX + 1]; static struct nla_policy policy[NL80211_STA_INFO_MAX + 1] = { [NL80211_STA_INFO_SIGNAL] = { .type = NLA_U8 }, [NL80211_STA_INFO_SIGNAL_AVG] = { .type = NLA_U8 }, }; struct nlattr *rinfo[NL80211_RATE_INFO_MAX + 1]; static struct nla_policy rate_policy[NL80211_RATE_INFO_MAX + 1] = { [NL80211_RATE_INFO_BITRATE] = { .type = NLA_U16 }, [NL80211_RATE_INFO_MCS] = { .type = NLA_U8 }, [NL80211_RATE_INFO_40_MHZ_WIDTH] = { .type = NLA_FLAG }, [NL80211_RATE_INFO_SHORT_GI] = { .type = NLA_FLAG }, }; struct wpa_signal_info *sig_change = arg; nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL); if (!tb[NL80211_ATTR_STA_INFO] || nla_parse_nested(sinfo, NL80211_STA_INFO_MAX, tb[NL80211_ATTR_STA_INFO], policy)) return NL_SKIP; if (!sinfo[NL80211_STA_INFO_SIGNAL]) return NL_SKIP; sig_change->current_signal = (s8) nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL]); if (sinfo[NL80211_STA_INFO_SIGNAL_AVG]) sig_change->avg_signal = (s8) nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL_AVG]); else sig_change->avg_signal = 0; if (sinfo[NL80211_STA_INFO_TX_BITRATE]) { if (nla_parse_nested(rinfo, NL80211_RATE_INFO_MAX, sinfo[NL80211_STA_INFO_TX_BITRATE], rate_policy)) { sig_change->current_txrate = 0; } else { if (rinfo[NL80211_RATE_INFO_BITRATE]) { sig_change->current_txrate = nla_get_u16(rinfo[ NL80211_RATE_INFO_BITRATE]) * 100; } } } return NL_SKIP; } static int nl80211_get_link_signal(struct wpa_driver_nl80211_data *drv, struct wpa_signal_info *sig) { struct nl_msg *msg; sig->current_signal = -9999; sig->current_txrate = 0; msg = nlmsg_alloc(); if (!msg) return -ENOMEM; nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_STATION); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, drv->bssid); return send_and_recv_msgs(drv, msg, get_link_signal, sig); nla_put_failure: nlmsg_free(msg); return -ENOBUFS; } static int get_link_noise(struct nl_msg *msg, void *arg) { struct nlattr *tb[NL80211_ATTR_MAX + 1]; struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); struct nlattr *sinfo[NL80211_SURVEY_INFO_MAX + 1]; static struct nla_policy survey_policy[NL80211_SURVEY_INFO_MAX + 1] = { [NL80211_SURVEY_INFO_FREQUENCY] = { .type = NLA_U32 }, [NL80211_SURVEY_INFO_NOISE] = { .type = NLA_U8 }, }; struct wpa_signal_info *sig_change = arg; nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL); if (!tb[NL80211_ATTR_SURVEY_INFO]) { wpa_printf(MSG_DEBUG, "nl80211: survey data missing!"); return NL_SKIP; } if (nla_parse_nested(sinfo, NL80211_SURVEY_INFO_MAX, tb[NL80211_ATTR_SURVEY_INFO], survey_policy)) { wpa_printf(MSG_DEBUG, "nl80211: failed to parse nested " "attributes!"); return NL_SKIP; } if (!sinfo[NL80211_SURVEY_INFO_FREQUENCY]) return NL_SKIP; if (nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]) != sig_change->frequency) return NL_SKIP; if (!sinfo[NL80211_SURVEY_INFO_NOISE]) return NL_SKIP; sig_change->current_noise = (s8) nla_get_u8(sinfo[NL80211_SURVEY_INFO_NOISE]); return NL_SKIP; } static int nl80211_get_link_noise(struct wpa_driver_nl80211_data *drv, struct wpa_signal_info *sig_change) { struct nl_msg *msg; sig_change->current_noise = 9999; sig_change->frequency = drv->assoc_freq; msg = nlmsg_alloc(); if (!msg) return -ENOMEM; nl80211_cmd(drv, msg, NLM_F_DUMP, NL80211_CMD_GET_SURVEY); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); return send_and_recv_msgs(drv, msg, get_link_noise, sig_change); nla_put_failure: nlmsg_free(msg); return -ENOBUFS; } static int get_noise_for_scan_results(struct nl_msg *msg, void *arg) { struct nlattr *tb[NL80211_ATTR_MAX + 1]; struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); struct nlattr *sinfo[NL80211_SURVEY_INFO_MAX + 1]; static struct nla_policy survey_policy[NL80211_SURVEY_INFO_MAX + 1] = { [NL80211_SURVEY_INFO_FREQUENCY] = { .type = NLA_U32 }, [NL80211_SURVEY_INFO_NOISE] = { .type = NLA_U8 }, }; struct wpa_scan_results *scan_results = arg; struct wpa_scan_res *scan_res; size_t i; nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL); if (!tb[NL80211_ATTR_SURVEY_INFO]) { wpa_printf(MSG_DEBUG, "nl80211: Survey data missing"); return NL_SKIP; } if (nla_parse_nested(sinfo, NL80211_SURVEY_INFO_MAX, tb[NL80211_ATTR_SURVEY_INFO], survey_policy)) { wpa_printf(MSG_DEBUG, "nl80211: Failed to parse nested " "attributes"); return NL_SKIP; } if (!sinfo[NL80211_SURVEY_INFO_NOISE]) return NL_SKIP; if (!sinfo[NL80211_SURVEY_INFO_FREQUENCY]) return NL_SKIP; for (i = 0; i < scan_results->num; ++i) { scan_res = scan_results->res[i]; if (!scan_res) continue; if ((int) nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]) != scan_res->freq) continue; if (!(scan_res->flags & WPA_SCAN_NOISE_INVALID)) continue; scan_res->noise = (s8) nla_get_u8(sinfo[NL80211_SURVEY_INFO_NOISE]); scan_res->flags &= ~WPA_SCAN_NOISE_INVALID; } return NL_SKIP; } static int nl80211_get_noise_for_scan_results( struct wpa_driver_nl80211_data *drv, struct wpa_scan_results *scan_res) { struct nl_msg *msg; msg = nlmsg_alloc(); if (!msg) return -ENOMEM; nl80211_cmd(drv, msg, NLM_F_DUMP, NL80211_CMD_GET_SURVEY); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); return send_and_recv_msgs(drv, msg, get_noise_for_scan_results, scan_res); nla_put_failure: nlmsg_free(msg); return -ENOBUFS; } static void nl80211_cqm_event(struct wpa_driver_nl80211_data *drv, struct nlattr *tb[]) { static struct nla_policy cqm_policy[NL80211_ATTR_CQM_MAX + 1] = { [NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_U32 }, [NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U8 }, [NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .type = NLA_U32 }, [NL80211_ATTR_CQM_PKT_LOSS_EVENT] = { .type = NLA_U32 }, }; struct nlattr *cqm[NL80211_ATTR_CQM_MAX + 1]; enum nl80211_cqm_rssi_threshold_event event; union wpa_event_data ed; struct wpa_signal_info sig; int res; if (tb[NL80211_ATTR_CQM] == NULL || nla_parse_nested(cqm, NL80211_ATTR_CQM_MAX, tb[NL80211_ATTR_CQM], cqm_policy)) { wpa_printf(MSG_DEBUG, "nl80211: Ignore invalid CQM event"); return; } os_memset(&ed, 0, sizeof(ed)); if (cqm[NL80211_ATTR_CQM_PKT_LOSS_EVENT]) { if (!tb[NL80211_ATTR_MAC]) return; os_memcpy(ed.low_ack.addr, nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN); wpa_supplicant_event(drv->ctx, EVENT_STATION_LOW_ACK, &ed); return; } if (cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] == NULL) return; event = nla_get_u32(cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT]); if (event == NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH) { wpa_printf(MSG_DEBUG, "nl80211: Connection quality monitor " "event: RSSI high"); ed.signal_change.above_threshold = 1; } else if (event == NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW) { wpa_printf(MSG_DEBUG, "nl80211: Connection quality monitor " "event: RSSI low"); ed.signal_change.above_threshold = 0; } else return; res = nl80211_get_link_signal(drv, &sig); if (res == 0) { ed.signal_change.current_signal = sig.current_signal; ed.signal_change.current_txrate = sig.current_txrate; wpa_printf(MSG_DEBUG, "nl80211: Signal: %d dBm txrate: %d", sig.current_signal, sig.current_txrate); } res = nl80211_get_link_noise(drv, &sig); if (res == 0) { ed.signal_change.current_noise = sig.current_noise; wpa_printf(MSG_DEBUG, "nl80211: Noise: %d dBm", sig.current_noise); } wpa_supplicant_event(drv->ctx, EVENT_SIGNAL_CHANGE, &ed); } static void nl80211_new_station_event(struct wpa_driver_nl80211_data *drv, struct nlattr **tb) { u8 *addr; union wpa_event_data data; if (tb[NL80211_ATTR_MAC] == NULL) return; addr = nla_data(tb[NL80211_ATTR_MAC]); wpa_printf(MSG_DEBUG, "nl80211: New station " MACSTR, MAC2STR(addr)); if (is_ap_interface(drv->nlmode) && drv->device_ap_sme) { u8 *ies = NULL; size_t ies_len = 0; if (tb[NL80211_ATTR_IE]) { ies = nla_data(tb[NL80211_ATTR_IE]); ies_len = nla_len(tb[NL80211_ATTR_IE]); } wpa_hexdump(MSG_DEBUG, "nl80211: Assoc Req IEs", ies, ies_len); drv_event_assoc(drv->ctx, addr, ies, ies_len, 0); return; } if (drv->nlmode != NL80211_IFTYPE_ADHOC) return; os_memset(&data, 0, sizeof(data)); os_memcpy(data.ibss_rsn_start.peer, addr, ETH_ALEN); wpa_supplicant_event(drv->ctx, EVENT_IBSS_RSN_START, &data); } static void nl80211_del_station_event(struct wpa_driver_nl80211_data *drv, struct nlattr **tb) { u8 *addr; union wpa_event_data data; if (tb[NL80211_ATTR_MAC] == NULL) return; addr = nla_data(tb[NL80211_ATTR_MAC]); wpa_printf(MSG_DEBUG, "nl80211: Delete station " MACSTR, MAC2STR(addr)); if (is_ap_interface(drv->nlmode) && drv->device_ap_sme) { drv_event_disassoc(drv->ctx, addr); return; } if (drv->nlmode != NL80211_IFTYPE_ADHOC) return; os_memset(&data, 0, sizeof(data)); os_memcpy(data.ibss_peer_lost.peer, addr, ETH_ALEN); wpa_supplicant_event(drv->ctx, EVENT_IBSS_PEER_LOST, &data); } static void nl80211_rekey_offload_event(struct wpa_driver_nl80211_data *drv, struct nlattr **tb) { struct nlattr *rekey_info[NUM_NL80211_REKEY_DATA]; static struct nla_policy rekey_policy[NUM_NL80211_REKEY_DATA] = { [NL80211_REKEY_DATA_KEK] = { .minlen = NL80211_KEK_LEN, .maxlen = NL80211_KEK_LEN, }, [NL80211_REKEY_DATA_KCK] = { .minlen = NL80211_KCK_LEN, .maxlen = NL80211_KCK_LEN, }, [NL80211_REKEY_DATA_REPLAY_CTR] = { .minlen = NL80211_REPLAY_CTR_LEN, .maxlen = NL80211_REPLAY_CTR_LEN, }, }; union wpa_event_data data; if (!tb[NL80211_ATTR_MAC]) return; if (!tb[NL80211_ATTR_REKEY_DATA]) return; if (nla_parse_nested(rekey_info, MAX_NL80211_REKEY_DATA, tb[NL80211_ATTR_REKEY_DATA], rekey_policy)) return; if (!rekey_info[NL80211_REKEY_DATA_REPLAY_CTR]) return; os_memset(&data, 0, sizeof(data)); data.driver_gtk_rekey.bssid = nla_data(tb[NL80211_ATTR_MAC]); wpa_printf(MSG_DEBUG, "nl80211: Rekey offload event for BSSID " MACSTR, MAC2STR(data.driver_gtk_rekey.bssid)); data.driver_gtk_rekey.replay_ctr = nla_data(rekey_info[NL80211_REKEY_DATA_REPLAY_CTR]); wpa_hexdump(MSG_DEBUG, "nl80211: Rekey offload - Replay Counter", data.driver_gtk_rekey.replay_ctr, NL80211_REPLAY_CTR_LEN); wpa_supplicant_event(drv->ctx, EVENT_DRIVER_GTK_REKEY, &data); } static void nl80211_pmksa_candidate_event(struct wpa_driver_nl80211_data *drv, struct nlattr **tb) { struct nlattr *cand[NUM_NL80211_PMKSA_CANDIDATE]; static struct nla_policy cand_policy[NUM_NL80211_PMKSA_CANDIDATE] = { [NL80211_PMKSA_CANDIDATE_INDEX] = { .type = NLA_U32 }, [NL80211_PMKSA_CANDIDATE_BSSID] = { .minlen = ETH_ALEN, .maxlen = ETH_ALEN, }, [NL80211_PMKSA_CANDIDATE_PREAUTH] = { .type = NLA_FLAG }, }; union wpa_event_data data; wpa_printf(MSG_DEBUG, "nl80211: PMKSA candidate event"); if (!tb[NL80211_ATTR_PMKSA_CANDIDATE]) return; if (nla_parse_nested(cand, MAX_NL80211_PMKSA_CANDIDATE, tb[NL80211_ATTR_PMKSA_CANDIDATE], cand_policy)) return; if (!cand[NL80211_PMKSA_CANDIDATE_INDEX] || !cand[NL80211_PMKSA_CANDIDATE_BSSID]) return; os_memset(&data, 0, sizeof(data)); os_memcpy(data.pmkid_candidate.bssid, nla_data(cand[NL80211_PMKSA_CANDIDATE_BSSID]), ETH_ALEN); data.pmkid_candidate.index = nla_get_u32(cand[NL80211_PMKSA_CANDIDATE_INDEX]); data.pmkid_candidate.preauth = cand[NL80211_PMKSA_CANDIDATE_PREAUTH] != NULL; wpa_supplicant_event(drv->ctx, EVENT_PMKID_CANDIDATE, &data); } static void nl80211_client_probe_event(struct wpa_driver_nl80211_data *drv, struct nlattr **tb) { union wpa_event_data data; wpa_printf(MSG_DEBUG, "nl80211: Probe client event"); if (!tb[NL80211_ATTR_MAC] || !tb[NL80211_ATTR_ACK]) return; os_memset(&data, 0, sizeof(data)); os_memcpy(data.client_poll.addr, nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN); wpa_supplicant_event(drv->ctx, EVENT_DRIVER_CLIENT_POLL_OK, &data); } static void nl80211_tdls_oper_event(struct wpa_driver_nl80211_data *drv, struct nlattr **tb) { union wpa_event_data data; wpa_printf(MSG_DEBUG, "nl80211: TDLS operation event"); if (!tb[NL80211_ATTR_MAC] || !tb[NL80211_ATTR_TDLS_OPERATION]) return; os_memset(&data, 0, sizeof(data)); os_memcpy(data.tdls.peer, nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN); switch (nla_get_u8(tb[NL80211_ATTR_TDLS_OPERATION])) { case NL80211_TDLS_SETUP: wpa_printf(MSG_DEBUG, "nl80211: TDLS setup request for peer " MACSTR, MAC2STR(data.tdls.peer)); data.tdls.oper = TDLS_REQUEST_SETUP; break; case NL80211_TDLS_TEARDOWN: wpa_printf(MSG_DEBUG, "nl80211: TDLS teardown request for peer " MACSTR, MAC2STR(data.tdls.peer)); data.tdls.oper = TDLS_REQUEST_TEARDOWN; break; default: wpa_printf(MSG_DEBUG, "nl80211: Unsupported TDLS operatione " "event"); return; } if (tb[NL80211_ATTR_REASON_CODE]) { data.tdls.reason_code = nla_get_u16(tb[NL80211_ATTR_REASON_CODE]); } wpa_supplicant_event(drv->ctx, EVENT_TDLS, &data); } static void nl80211_stop_ap(struct wpa_driver_nl80211_data *drv, struct nlattr **tb) { wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_UNAVAILABLE, NULL); } static void nl80211_connect_failed_event(struct wpa_driver_nl80211_data *drv, struct nlattr **tb) { union wpa_event_data data; u32 reason; wpa_printf(MSG_DEBUG, "nl80211: Connect failed event"); if (!tb[NL80211_ATTR_MAC] || !tb[NL80211_ATTR_CONN_FAILED_REASON]) return; os_memset(&data, 0, sizeof(data)); os_memcpy(data.connect_failed_reason.addr, nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN); reason = nla_get_u32(tb[NL80211_ATTR_CONN_FAILED_REASON]); switch (reason) { case NL80211_CONN_FAIL_MAX_CLIENTS: wpa_printf(MSG_DEBUG, "nl80211: Max client reached"); data.connect_failed_reason.code = MAX_CLIENT_REACHED; break; case NL80211_CONN_FAIL_BLOCKED_CLIENT: wpa_printf(MSG_DEBUG, "nl80211: Blocked client " MACSTR " tried to connect", MAC2STR(data.connect_failed_reason.addr)); data.connect_failed_reason.code = BLOCKED_CLIENT; break; default: wpa_printf(MSG_DEBUG, "nl8021l: Unknown connect failed reason " "%u", reason); return; } wpa_supplicant_event(drv->ctx, EVENT_CONNECT_FAILED_REASON, &data); } static void nl80211_radar_event(struct wpa_driver_nl80211_data *drv, struct nlattr **tb) { union wpa_event_data data; enum nl80211_radar_event event_type; if (!tb[NL80211_ATTR_WIPHY_FREQ] || !tb[NL80211_ATTR_RADAR_EVENT]) return; os_memset(&data, 0, sizeof(data)); data.dfs_event.freq = nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]); event_type = nla_get_u32(tb[NL80211_ATTR_RADAR_EVENT]); /* Check HT params */ if (tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) { data.dfs_event.ht_enabled = 1; data.dfs_event.chan_offset = 0; switch (nla_get_u32(tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE])) { case NL80211_CHAN_NO_HT: data.dfs_event.ht_enabled = 0; break; case NL80211_CHAN_HT20: break; case NL80211_CHAN_HT40PLUS: data.dfs_event.chan_offset = 1; break; case NL80211_CHAN_HT40MINUS: data.dfs_event.chan_offset = -1; break; } } /* Get VHT params */ if (tb[NL80211_ATTR_CHANNEL_WIDTH]) data.dfs_event.chan_width = convert2width(nla_get_u32( tb[NL80211_ATTR_CHANNEL_WIDTH])); if (tb[NL80211_ATTR_CENTER_FREQ1]) data.dfs_event.cf1 = nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ1]); if (tb[NL80211_ATTR_CENTER_FREQ2]) data.dfs_event.cf2 = nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ2]); wpa_printf(MSG_DEBUG, "nl80211: DFS event on freq %d MHz, ht: %d, offset: %d, width: %d, cf1: %dMHz, cf2: %dMHz", data.dfs_event.freq, data.dfs_event.ht_enabled, data.dfs_event.chan_offset, data.dfs_event.chan_width, data.dfs_event.cf1, data.dfs_event.cf2); switch (event_type) { case NL80211_RADAR_DETECTED: wpa_supplicant_event(drv->ctx, EVENT_DFS_RADAR_DETECTED, &data); break; case NL80211_RADAR_CAC_FINISHED: wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_FINISHED, &data); break; case NL80211_RADAR_CAC_ABORTED: wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_ABORTED, &data); break; case NL80211_RADAR_NOP_FINISHED: wpa_supplicant_event(drv->ctx, EVENT_DFS_NOP_FINISHED, &data); break; default: wpa_printf(MSG_DEBUG, "nl80211: Unknown radar event %d " "received", event_type); break; } } static void nl80211_spurious_frame(struct i802_bss *bss, struct nlattr **tb, int wds) { struct wpa_driver_nl80211_data *drv = bss->drv; union wpa_event_data event; if (!tb[NL80211_ATTR_MAC]) return; os_memset(&event, 0, sizeof(event)); event.rx_from_unknown.bssid = bss->addr; event.rx_from_unknown.addr = nla_data(tb[NL80211_ATTR_MAC]); event.rx_from_unknown.wds = wds; wpa_supplicant_event(drv->ctx, EVENT_RX_FROM_UNKNOWN, &event); } static void qca_nl80211_avoid_freq(struct wpa_driver_nl80211_data *drv, const u8 *data, size_t len) { u32 i, count; union wpa_event_data event; struct wpa_freq_range *range = NULL; const struct qca_avoid_freq_list *freq_range; freq_range = (const struct qca_avoid_freq_list *) data; if (len < sizeof(freq_range->count)) return; count = freq_range->count; if (len < sizeof(freq_range->count) + count * sizeof(struct qca_avoid_freq_range)) { wpa_printf(MSG_DEBUG, "nl80211: Ignored too short avoid frequency list (len=%u)", (unsigned int) len); return; } if (count > 0) { range = os_calloc(count, sizeof(struct wpa_freq_range)); if (range == NULL) return; } os_memset(&event, 0, sizeof(event)); for (i = 0; i < count; i++) { unsigned int idx = event.freq_range.num; range[idx].min = freq_range->range[i].start_freq; range[idx].max = freq_range->range[i].end_freq; wpa_printf(MSG_DEBUG, "nl80211: Avoid frequency range: %u-%u", range[idx].min, range[idx].max); if (range[idx].min > range[idx].max) { wpa_printf(MSG_DEBUG, "nl80211: Ignore invalid frequency range"); continue; } event.freq_range.num++; } event.freq_range.range = range; wpa_supplicant_event(drv->ctx, EVENT_AVOID_FREQUENCIES, &event); os_free(range); } static void nl80211_vendor_event_qca(struct wpa_driver_nl80211_data *drv, u32 subcmd, u8 *data, size_t len) { switch (subcmd) { case QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY: qca_nl80211_avoid_freq(drv, data, len); break; default: wpa_printf(MSG_DEBUG, "nl80211: Ignore unsupported QCA vendor event %u", subcmd); break; } } static void nl80211_vendor_event(struct wpa_driver_nl80211_data *drv, struct nlattr **tb) { u32 vendor_id, subcmd, wiphy = 0; int wiphy_idx; u8 *data = NULL; size_t len = 0; if (!tb[NL80211_ATTR_VENDOR_ID] || !tb[NL80211_ATTR_VENDOR_SUBCMD]) return; vendor_id = nla_get_u32(tb[NL80211_ATTR_VENDOR_ID]); subcmd = nla_get_u32(tb[NL80211_ATTR_VENDOR_SUBCMD]); if (tb[NL80211_ATTR_WIPHY]) wiphy = nla_get_u32(tb[NL80211_ATTR_WIPHY]); wpa_printf(MSG_DEBUG, "nl80211: Vendor event: wiphy=%u vendor_id=0x%x subcmd=%u", wiphy, vendor_id, subcmd); if (tb[NL80211_ATTR_VENDOR_DATA]) { data = nla_data(tb[NL80211_ATTR_VENDOR_DATA]); len = nla_len(tb[NL80211_ATTR_VENDOR_DATA]); wpa_hexdump(MSG_MSGDUMP, "nl80211: Vendor data", data, len); } wiphy_idx = nl80211_get_wiphy_index(drv->first_bss); if (wiphy_idx >= 0 && wiphy_idx != (int) wiphy) { wpa_printf(MSG_DEBUG, "nl80211: Ignore vendor event for foreign wiphy %u (own: %d)", wiphy, wiphy_idx); return; } switch (vendor_id) { case OUI_QCA: nl80211_vendor_event_qca(drv, subcmd, data, len); break; default: wpa_printf(MSG_DEBUG, "nl80211: Ignore unsupported vendor event"); break; } } static void do_process_drv_event(struct i802_bss *bss, int cmd, struct nlattr **tb) { struct wpa_driver_nl80211_data *drv = bss->drv; union wpa_event_data data; wpa_printf(MSG_DEBUG, "nl80211: Drv Event %d (%s) received for %s", cmd, nl80211_command_to_string(cmd), bss->ifname); if (drv->ap_scan_as_station != NL80211_IFTYPE_UNSPECIFIED && (cmd == NL80211_CMD_NEW_SCAN_RESULTS || cmd == NL80211_CMD_SCAN_ABORTED)) { wpa_driver_nl80211_set_mode(drv->first_bss, drv->ap_scan_as_station); drv->ap_scan_as_station = NL80211_IFTYPE_UNSPECIFIED; } switch (cmd) { case NL80211_CMD_TRIGGER_SCAN: wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Scan trigger"); drv->scan_state = SCAN_STARTED; wpa_supplicant_event(drv->ctx, EVENT_SCAN_STARTED, NULL); break; case NL80211_CMD_START_SCHED_SCAN: wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Sched scan started"); drv->scan_state = SCHED_SCAN_STARTED; break; case NL80211_CMD_SCHED_SCAN_STOPPED: wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Sched scan stopped"); drv->scan_state = SCHED_SCAN_STOPPED; wpa_supplicant_event(drv->ctx, EVENT_SCHED_SCAN_STOPPED, NULL); break; case NL80211_CMD_NEW_SCAN_RESULTS: wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: New scan results available"); drv->scan_state = SCAN_COMPLETED; drv->scan_complete_events = 1; eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx); send_scan_event(drv, 0, tb); break; case NL80211_CMD_SCHED_SCAN_RESULTS: wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: New sched scan results available"); drv->scan_state = SCHED_SCAN_RESULTS; send_scan_event(drv, 0, tb); break; case NL80211_CMD_SCAN_ABORTED: wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Scan aborted"); drv->scan_state = SCAN_ABORTED; /* * Need to indicate that scan results are available in order * not to make wpa_supplicant stop its scanning. */ eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx); send_scan_event(drv, 1, tb); break; case NL80211_CMD_AUTHENTICATE: case NL80211_CMD_ASSOCIATE: case NL80211_CMD_DEAUTHENTICATE: case NL80211_CMD_DISASSOCIATE: case NL80211_CMD_FRAME_TX_STATUS: case NL80211_CMD_UNPROT_DEAUTHENTICATE: case NL80211_CMD_UNPROT_DISASSOCIATE: mlme_event(bss, cmd, tb[NL80211_ATTR_FRAME], tb[NL80211_ATTR_MAC], tb[NL80211_ATTR_TIMED_OUT], tb[NL80211_ATTR_WIPHY_FREQ], tb[NL80211_ATTR_ACK], tb[NL80211_ATTR_COOKIE], tb[NL80211_ATTR_RX_SIGNAL_DBM]); break; case NL80211_CMD_CONNECT: case NL80211_CMD_ROAM: mlme_event_connect(drv, cmd, tb[NL80211_ATTR_STATUS_CODE], tb[NL80211_ATTR_MAC], tb[NL80211_ATTR_REQ_IE], tb[NL80211_ATTR_RESP_IE]); break; case NL80211_CMD_CH_SWITCH_NOTIFY: mlme_event_ch_switch(drv, tb[NL80211_ATTR_IFINDEX], tb[NL80211_ATTR_WIPHY_FREQ], tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE], tb[NL80211_ATTR_CHANNEL_WIDTH], tb[NL80211_ATTR_CENTER_FREQ1], tb[NL80211_ATTR_CENTER_FREQ2]); break; case NL80211_CMD_DISCONNECT: mlme_event_disconnect(drv, tb[NL80211_ATTR_REASON_CODE], tb[NL80211_ATTR_MAC], tb[NL80211_ATTR_DISCONNECTED_BY_AP]); break; case NL80211_CMD_MICHAEL_MIC_FAILURE: mlme_event_michael_mic_failure(bss, tb); break; case NL80211_CMD_JOIN_IBSS: mlme_event_join_ibss(drv, tb); break; case NL80211_CMD_REMAIN_ON_CHANNEL: mlme_event_remain_on_channel(drv, 0, tb); break; case NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL: mlme_event_remain_on_channel(drv, 1, tb); break; case NL80211_CMD_NOTIFY_CQM: nl80211_cqm_event(drv, tb); break; case NL80211_CMD_REG_CHANGE: wpa_printf(MSG_DEBUG, "nl80211: Regulatory domain change"); if (tb[NL80211_ATTR_REG_INITIATOR] == NULL) break; os_memset(&data, 0, sizeof(data)); switch (nla_get_u8(tb[NL80211_ATTR_REG_INITIATOR])) { case NL80211_REGDOM_SET_BY_CORE: data.channel_list_changed.initiator = REGDOM_SET_BY_CORE; break; case NL80211_REGDOM_SET_BY_USER: data.channel_list_changed.initiator = REGDOM_SET_BY_USER; break; case NL80211_REGDOM_SET_BY_DRIVER: data.channel_list_changed.initiator = REGDOM_SET_BY_DRIVER; break; case NL80211_REGDOM_SET_BY_COUNTRY_IE: data.channel_list_changed.initiator = REGDOM_SET_BY_COUNTRY_IE; break; default: wpa_printf(MSG_DEBUG, "nl80211: Unknown reg change initiator %d received", nla_get_u8(tb[NL80211_ATTR_REG_INITIATOR])); break; } wpa_supplicant_event(drv->ctx, EVENT_CHANNEL_LIST_CHANGED, &data); break; case NL80211_CMD_REG_BEACON_HINT: wpa_printf(MSG_DEBUG, "nl80211: Regulatory beacon hint"); os_memset(&data, 0, sizeof(data)); data.channel_list_changed.initiator = REGDOM_BEACON_HINT; wpa_supplicant_event(drv->ctx, EVENT_CHANNEL_LIST_CHANGED, &data); break; case NL80211_CMD_NEW_STATION: nl80211_new_station_event(drv, tb); break; case NL80211_CMD_DEL_STATION: nl80211_del_station_event(drv, tb); break; case NL80211_CMD_SET_REKEY_OFFLOAD: nl80211_rekey_offload_event(drv, tb); break; case NL80211_CMD_PMKSA_CANDIDATE: nl80211_pmksa_candidate_event(drv, tb); break; case NL80211_CMD_PROBE_CLIENT: nl80211_client_probe_event(drv, tb); break; case NL80211_CMD_TDLS_OPER: nl80211_tdls_oper_event(drv, tb); break; case NL80211_CMD_CONN_FAILED: nl80211_connect_failed_event(drv, tb); break; case NL80211_CMD_FT_EVENT: mlme_event_ft_event(drv, tb); break; case NL80211_CMD_RADAR_DETECT: nl80211_radar_event(drv, tb); break; case NL80211_CMD_STOP_AP: nl80211_stop_ap(drv, tb); break; case NL80211_CMD_VENDOR: nl80211_vendor_event(drv, tb); break; default: wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Ignored unknown event " "(cmd=%d)", cmd); break; } } static int process_drv_event(struct nl_msg *msg, void *arg) { struct wpa_driver_nl80211_data *drv = arg; struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); struct nlattr *tb[NL80211_ATTR_MAX + 1]; struct i802_bss *bss; int ifidx = -1; nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL); if (tb[NL80211_ATTR_IFINDEX]) { ifidx = nla_get_u32(tb[NL80211_ATTR_IFINDEX]); for (bss = drv->first_bss; bss; bss = bss->next) if (ifidx == -1 || ifidx == bss->ifindex) { do_process_drv_event(bss, gnlh->cmd, tb); return NL_SKIP; } wpa_printf(MSG_DEBUG, "nl80211: Ignored event (cmd=%d) for foreign interface (ifindex %d)", gnlh->cmd, ifidx); } else if (tb[NL80211_ATTR_WDEV]) { u64 wdev_id = nla_get_u64(tb[NL80211_ATTR_WDEV]); wpa_printf(MSG_DEBUG, "nl80211: Process event on P2P device"); for (bss = drv->first_bss; bss; bss = bss->next) { if (bss->wdev_id_set && wdev_id == bss->wdev_id) { do_process_drv_event(bss, gnlh->cmd, tb); return NL_SKIP; } } wpa_printf(MSG_DEBUG, "nl80211: Ignored event (cmd=%d) for foreign interface (wdev 0x%llx)", gnlh->cmd, (long long unsigned int) wdev_id); } return NL_SKIP; } static int process_global_event(struct nl_msg *msg, void *arg) { struct nl80211_global *global = arg; struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); struct nlattr *tb[NL80211_ATTR_MAX + 1]; struct wpa_driver_nl80211_data *drv, *tmp; int ifidx = -1; struct i802_bss *bss; u64 wdev_id = 0; int wdev_id_set = 0; nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL); if (tb[NL80211_ATTR_IFINDEX]) ifidx = nla_get_u32(tb[NL80211_ATTR_IFINDEX]); else if (tb[NL80211_ATTR_WDEV]) { wdev_id = nla_get_u64(tb[NL80211_ATTR_WDEV]); wdev_id_set = 1; } dl_list_for_each_safe(drv, tmp, &global->interfaces, struct wpa_driver_nl80211_data, list) { for (bss = drv->first_bss; bss; bss = bss->next) { if ((ifidx == -1 && !wdev_id_set) || ifidx == bss->ifindex || (wdev_id_set && bss->wdev_id_set && wdev_id == bss->wdev_id)) { do_process_drv_event(bss, gnlh->cmd, tb); return NL_SKIP; } } } return NL_SKIP; } static int process_bss_event(struct nl_msg *msg, void *arg) { struct i802_bss *bss = arg; struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); struct nlattr *tb[NL80211_ATTR_MAX + 1]; nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL); wpa_printf(MSG_DEBUG, "nl80211: BSS Event %d (%s) received for %s", gnlh->cmd, nl80211_command_to_string(gnlh->cmd), bss->ifname); switch (gnlh->cmd) { case NL80211_CMD_FRAME: case NL80211_CMD_FRAME_TX_STATUS: mlme_event(bss, gnlh->cmd, tb[NL80211_ATTR_FRAME], tb[NL80211_ATTR_MAC], tb[NL80211_ATTR_TIMED_OUT], tb[NL80211_ATTR_WIPHY_FREQ], tb[NL80211_ATTR_ACK], tb[NL80211_ATTR_COOKIE], tb[NL80211_ATTR_RX_SIGNAL_DBM]); break; case NL80211_CMD_UNEXPECTED_FRAME: nl80211_spurious_frame(bss, tb, 0); break; case NL80211_CMD_UNEXPECTED_4ADDR_FRAME: nl80211_spurious_frame(bss, tb, 1); break; default: wpa_printf(MSG_DEBUG, "nl80211: Ignored unknown event " "(cmd=%d)", gnlh->cmd); break; } return NL_SKIP; } static void wpa_driver_nl80211_event_receive(int sock, void *eloop_ctx, void *handle) { struct nl_cb *cb = eloop_ctx; int res; wpa_printf(MSG_MSGDUMP, "nl80211: Event message available"); res = nl_recvmsgs(handle, cb); if (res) { wpa_printf(MSG_INFO, "nl80211: %s->nl_recvmsgs failed: %d", __func__, res); } } /** * wpa_driver_nl80211_set_country - ask nl80211 to set the regulatory domain * @priv: driver_nl80211 private data * @alpha2_arg: country to which to switch to * Returns: 0 on success, -1 on failure * * This asks nl80211 to set the regulatory domain for given * country ISO / IEC alpha2. */ static int wpa_driver_nl80211_set_country(void *priv, const char *alpha2_arg) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; char alpha2[3]; struct nl_msg *msg; msg = nlmsg_alloc(); if (!msg) return -ENOMEM; alpha2[0] = alpha2_arg[0]; alpha2[1] = alpha2_arg[1]; alpha2[2] = '\0'; nl80211_cmd(drv, msg, 0, NL80211_CMD_REQ_SET_REG); NLA_PUT_STRING(msg, NL80211_ATTR_REG_ALPHA2, alpha2); if (send_and_recv_msgs(drv, msg, NULL, NULL)) return -EINVAL; return 0; nla_put_failure: nlmsg_free(msg); return -EINVAL; } static int nl80211_get_country(struct nl_msg *msg, void *arg) { char *alpha2 = arg; struct nlattr *tb_msg[NL80211_ATTR_MAX + 1]; struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL); if (!tb_msg[NL80211_ATTR_REG_ALPHA2]) { wpa_printf(MSG_DEBUG, "nl80211: No country information available"); return NL_SKIP; } os_strlcpy(alpha2, nla_data(tb_msg[NL80211_ATTR_REG_ALPHA2]), 3); return NL_SKIP; } static int wpa_driver_nl80211_get_country(void *priv, char *alpha2) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; int ret; msg = nlmsg_alloc(); if (!msg) return -ENOMEM; nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_REG); alpha2[0] = '\0'; ret = send_and_recv_msgs(drv, msg, nl80211_get_country, alpha2); if (!alpha2[0]) ret = -1; return ret; } static int protocol_feature_handler(struct nl_msg *msg, void *arg) { u32 *feat = arg; struct nlattr *tb_msg[NL80211_ATTR_MAX + 1]; struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL); if (tb_msg[NL80211_ATTR_PROTOCOL_FEATURES]) *feat = nla_get_u32(tb_msg[NL80211_ATTR_PROTOCOL_FEATURES]); return NL_SKIP; } static u32 get_nl80211_protocol_features(struct wpa_driver_nl80211_data *drv) { u32 feat = 0; struct nl_msg *msg; msg = nlmsg_alloc(); if (!msg) goto nla_put_failure; nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_PROTOCOL_FEATURES); if (send_and_recv_msgs(drv, msg, protocol_feature_handler, &feat) == 0) return feat; msg = NULL; nla_put_failure: nlmsg_free(msg); return 0; } struct wiphy_info_data { struct wpa_driver_nl80211_data *drv; struct wpa_driver_capa *capa; unsigned int num_multichan_concurrent; unsigned int error:1; unsigned int device_ap_sme:1; unsigned int poll_command_supported:1; unsigned int data_tx_status:1; unsigned int monitor_supported:1; unsigned int auth_supported:1; unsigned int connect_supported:1; unsigned int p2p_go_supported:1; unsigned int p2p_client_supported:1; unsigned int p2p_concurrent:1; unsigned int channel_switch_supported:1; unsigned int set_qos_map_supported:1; }; static unsigned int probe_resp_offload_support(int supp_protocols) { unsigned int prot = 0; if (supp_protocols & NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS) prot |= WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS; if (supp_protocols & NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2) prot |= WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS2; if (supp_protocols & NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P) prot |= WPA_DRIVER_PROBE_RESP_OFFLOAD_P2P; if (supp_protocols & NL80211_PROBE_RESP_OFFLOAD_SUPPORT_80211U) prot |= WPA_DRIVER_PROBE_RESP_OFFLOAD_INTERWORKING; return prot; } static void wiphy_info_supported_iftypes(struct wiphy_info_data *info, struct nlattr *tb) { struct nlattr *nl_mode; int i; if (tb == NULL) return; nla_for_each_nested(nl_mode, tb, i) { switch (nla_type(nl_mode)) { case NL80211_IFTYPE_AP: info->capa->flags |= WPA_DRIVER_FLAGS_AP; break; case NL80211_IFTYPE_ADHOC: info->capa->flags |= WPA_DRIVER_FLAGS_IBSS; break; case NL80211_IFTYPE_P2P_DEVICE: info->capa->flags |= WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE; break; case NL80211_IFTYPE_P2P_GO: info->p2p_go_supported = 1; break; case NL80211_IFTYPE_P2P_CLIENT: info->p2p_client_supported = 1; break; case NL80211_IFTYPE_MONITOR: info->monitor_supported = 1; break; } } } static int wiphy_info_iface_comb_process(struct wiphy_info_data *info, struct nlattr *nl_combi) { struct nlattr *tb_comb[NUM_NL80211_IFACE_COMB]; struct nlattr *tb_limit[NUM_NL80211_IFACE_LIMIT]; struct nlattr *nl_limit, *nl_mode; int err, rem_limit, rem_mode; int combination_has_p2p = 0, combination_has_mgd = 0; static struct nla_policy iface_combination_policy[NUM_NL80211_IFACE_COMB] = { [NL80211_IFACE_COMB_LIMITS] = { .type = NLA_NESTED }, [NL80211_IFACE_COMB_MAXNUM] = { .type = NLA_U32 }, [NL80211_IFACE_COMB_STA_AP_BI_MATCH] = { .type = NLA_FLAG }, [NL80211_IFACE_COMB_NUM_CHANNELS] = { .type = NLA_U32 }, [NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS] = { .type = NLA_U32 }, }, iface_limit_policy[NUM_NL80211_IFACE_LIMIT] = { [NL80211_IFACE_LIMIT_TYPES] = { .type = NLA_NESTED }, [NL80211_IFACE_LIMIT_MAX] = { .type = NLA_U32 }, }; err = nla_parse_nested(tb_comb, MAX_NL80211_IFACE_COMB, nl_combi, iface_combination_policy); if (err || !tb_comb[NL80211_IFACE_COMB_LIMITS] || !tb_comb[NL80211_IFACE_COMB_MAXNUM] || !tb_comb[NL80211_IFACE_COMB_NUM_CHANNELS]) return 0; /* broken combination */ if (tb_comb[NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS]) info->capa->flags |= WPA_DRIVER_FLAGS_RADAR; nla_for_each_nested(nl_limit, tb_comb[NL80211_IFACE_COMB_LIMITS], rem_limit) { err = nla_parse_nested(tb_limit, MAX_NL80211_IFACE_LIMIT, nl_limit, iface_limit_policy); if (err || !tb_limit[NL80211_IFACE_LIMIT_TYPES]) return 0; /* broken combination */ nla_for_each_nested(nl_mode, tb_limit[NL80211_IFACE_LIMIT_TYPES], rem_mode) { int ift = nla_type(nl_mode); if (ift == NL80211_IFTYPE_P2P_GO || ift == NL80211_IFTYPE_P2P_CLIENT) combination_has_p2p = 1; if (ift == NL80211_IFTYPE_STATION) combination_has_mgd = 1; } if (combination_has_p2p && combination_has_mgd) break; } if (combination_has_p2p && combination_has_mgd) { info->p2p_concurrent = 1; info->num_multichan_concurrent = nla_get_u32(tb_comb[NL80211_IFACE_COMB_NUM_CHANNELS]); return 1; } return 0; } static void wiphy_info_iface_comb(struct wiphy_info_data *info, struct nlattr *tb) { struct nlattr *nl_combi; int rem_combi; if (tb == NULL) return; nla_for_each_nested(nl_combi, tb, rem_combi) { if (wiphy_info_iface_comb_process(info, nl_combi) > 0) break; } } static void wiphy_info_supp_cmds(struct wiphy_info_data *info, struct nlattr *tb) { struct nlattr *nl_cmd; int i; if (tb == NULL) return; nla_for_each_nested(nl_cmd, tb, i) { switch (nla_get_u32(nl_cmd)) { case NL80211_CMD_AUTHENTICATE: info->auth_supported = 1; break; case NL80211_CMD_CONNECT: info->connect_supported = 1; break; case NL80211_CMD_START_SCHED_SCAN: info->capa->sched_scan_supported = 1; break; case NL80211_CMD_PROBE_CLIENT: info->poll_command_supported = 1; break; case NL80211_CMD_CHANNEL_SWITCH: info->channel_switch_supported = 1; break; case NL80211_CMD_SET_QOS_MAP: info->set_qos_map_supported = 1; break; } } } static void wiphy_info_cipher_suites(struct wiphy_info_data *info, struct nlattr *tb) { int i, num; u32 *ciphers; if (tb == NULL) return; num = nla_len(tb) / sizeof(u32); ciphers = nla_data(tb); for (i = 0; i < num; i++) { u32 c = ciphers[i]; wpa_printf(MSG_DEBUG, "nl80211: Supported cipher %02x-%02x-%02x:%d", c >> 24, (c >> 16) & 0xff, (c >> 8) & 0xff, c & 0xff); switch (c) { case WLAN_CIPHER_SUITE_CCMP_256: info->capa->enc |= WPA_DRIVER_CAPA_ENC_CCMP_256; break; case WLAN_CIPHER_SUITE_GCMP_256: info->capa->enc |= WPA_DRIVER_CAPA_ENC_GCMP_256; break; case WLAN_CIPHER_SUITE_CCMP: info->capa->enc |= WPA_DRIVER_CAPA_ENC_CCMP; break; case WLAN_CIPHER_SUITE_GCMP: info->capa->enc |= WPA_DRIVER_CAPA_ENC_GCMP; break; case WLAN_CIPHER_SUITE_TKIP: info->capa->enc |= WPA_DRIVER_CAPA_ENC_TKIP; break; case WLAN_CIPHER_SUITE_WEP104: info->capa->enc |= WPA_DRIVER_CAPA_ENC_WEP104; break; case WLAN_CIPHER_SUITE_WEP40: info->capa->enc |= WPA_DRIVER_CAPA_ENC_WEP40; break; case WLAN_CIPHER_SUITE_AES_CMAC: info->capa->enc |= WPA_DRIVER_CAPA_ENC_BIP; break; case WLAN_CIPHER_SUITE_BIP_GMAC_128: info->capa->enc |= WPA_DRIVER_CAPA_ENC_BIP_GMAC_128; break; case WLAN_CIPHER_SUITE_BIP_GMAC_256: info->capa->enc |= WPA_DRIVER_CAPA_ENC_BIP_GMAC_256; break; case WLAN_CIPHER_SUITE_BIP_CMAC_256: info->capa->enc |= WPA_DRIVER_CAPA_ENC_BIP_CMAC_256; break; } } } static void wiphy_info_max_roc(struct wpa_driver_capa *capa, struct nlattr *tb) { if (tb) capa->max_remain_on_chan = nla_get_u32(tb); } static void wiphy_info_tdls(struct wpa_driver_capa *capa, struct nlattr *tdls, struct nlattr *ext_setup) { if (tdls == NULL) return; wpa_printf(MSG_DEBUG, "nl80211: TDLS supported"); capa->flags |= WPA_DRIVER_FLAGS_TDLS_SUPPORT; if (ext_setup) { wpa_printf(MSG_DEBUG, "nl80211: TDLS external setup"); capa->flags |= WPA_DRIVER_FLAGS_TDLS_EXTERNAL_SETUP; } } static void wiphy_info_feature_flags(struct wiphy_info_data *info, struct nlattr *tb) { u32 flags; struct wpa_driver_capa *capa = info->capa; if (tb == NULL) return; flags = nla_get_u32(tb); if (flags & NL80211_FEATURE_SK_TX_STATUS) info->data_tx_status = 1; if (flags & NL80211_FEATURE_INACTIVITY_TIMER) capa->flags |= WPA_DRIVER_FLAGS_INACTIVITY_TIMER; if (flags & NL80211_FEATURE_SAE) capa->flags |= WPA_DRIVER_FLAGS_SAE; if (flags & NL80211_FEATURE_NEED_OBSS_SCAN) capa->flags |= WPA_DRIVER_FLAGS_OBSS_SCAN; } static void wiphy_info_probe_resp_offload(struct wpa_driver_capa *capa, struct nlattr *tb) { u32 protocols; if (tb == NULL) return; protocols = nla_get_u32(tb); wpa_printf(MSG_DEBUG, "nl80211: Supports Probe Response offload in AP " "mode"); capa->flags |= WPA_DRIVER_FLAGS_PROBE_RESP_OFFLOAD; capa->probe_resp_offloads = probe_resp_offload_support(protocols); } static int wiphy_info_handler(struct nl_msg *msg, void *arg) { struct nlattr *tb[NL80211_ATTR_MAX + 1]; struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); struct wiphy_info_data *info = arg; struct wpa_driver_capa *capa = info->capa; struct wpa_driver_nl80211_data *drv = info->drv; nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL); if (tb[NL80211_ATTR_WIPHY_NAME]) os_strlcpy(drv->phyname, nla_get_string(tb[NL80211_ATTR_WIPHY_NAME]), sizeof(drv->phyname)); if (tb[NL80211_ATTR_MAX_NUM_SCAN_SSIDS]) capa->max_scan_ssids = nla_get_u8(tb[NL80211_ATTR_MAX_NUM_SCAN_SSIDS]); if (tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS]) capa->max_sched_scan_ssids = nla_get_u8(tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS]); if (tb[NL80211_ATTR_MAX_MATCH_SETS]) capa->max_match_sets = nla_get_u8(tb[NL80211_ATTR_MAX_MATCH_SETS]); if (tb[NL80211_ATTR_MAC_ACL_MAX]) capa->max_acl_mac_addrs = nla_get_u8(tb[NL80211_ATTR_MAC_ACL_MAX]); wiphy_info_supported_iftypes(info, tb[NL80211_ATTR_SUPPORTED_IFTYPES]); wiphy_info_iface_comb(info, tb[NL80211_ATTR_INTERFACE_COMBINATIONS]); wiphy_info_supp_cmds(info, tb[NL80211_ATTR_SUPPORTED_COMMANDS]); wiphy_info_cipher_suites(info, tb[NL80211_ATTR_CIPHER_SUITES]); if (tb[NL80211_ATTR_OFFCHANNEL_TX_OK]) { wpa_printf(MSG_DEBUG, "nl80211: Using driver-based " "off-channel TX"); capa->flags |= WPA_DRIVER_FLAGS_OFFCHANNEL_TX; } if (tb[NL80211_ATTR_ROAM_SUPPORT]) { wpa_printf(MSG_DEBUG, "nl80211: Using driver-based roaming"); capa->flags |= WPA_DRIVER_FLAGS_BSS_SELECTION; } wiphy_info_max_roc(capa, tb[NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION]); if (tb[NL80211_ATTR_SUPPORT_AP_UAPSD]) capa->flags |= WPA_DRIVER_FLAGS_AP_UAPSD; wiphy_info_tdls(capa, tb[NL80211_ATTR_TDLS_SUPPORT], tb[NL80211_ATTR_TDLS_EXTERNAL_SETUP]); if (tb[NL80211_ATTR_DEVICE_AP_SME]) info->device_ap_sme = 1; wiphy_info_feature_flags(info, tb[NL80211_ATTR_FEATURE_FLAGS]); wiphy_info_probe_resp_offload(capa, tb[NL80211_ATTR_PROBE_RESP_OFFLOAD]); if (tb[NL80211_ATTR_EXT_CAPA] && tb[NL80211_ATTR_EXT_CAPA_MASK] && drv->extended_capa == NULL) { drv->extended_capa = os_malloc(nla_len(tb[NL80211_ATTR_EXT_CAPA])); if (drv->extended_capa) { os_memcpy(drv->extended_capa, nla_data(tb[NL80211_ATTR_EXT_CAPA]), nla_len(tb[NL80211_ATTR_EXT_CAPA])); drv->extended_capa_len = nla_len(tb[NL80211_ATTR_EXT_CAPA]); } drv->extended_capa_mask = os_malloc(nla_len(tb[NL80211_ATTR_EXT_CAPA])); if (drv->extended_capa_mask) { os_memcpy(drv->extended_capa_mask, nla_data(tb[NL80211_ATTR_EXT_CAPA]), nla_len(tb[NL80211_ATTR_EXT_CAPA])); } else { os_free(drv->extended_capa); drv->extended_capa = NULL; drv->extended_capa_len = 0; } } if (tb[NL80211_ATTR_VENDOR_DATA]) { struct nlattr *nl; int rem; nla_for_each_nested(nl, tb[NL80211_ATTR_VENDOR_DATA], rem) { struct nl80211_vendor_cmd_info *vinfo; if (nla_len(nl) != sizeof(*vinfo)) { wpa_printf(MSG_DEBUG, "nl80211: Unexpected vendor data info"); continue; } vinfo = nla_data(nl); wpa_printf(MSG_DEBUG, "nl80211: Supported vendor command: vendor_id=0x%x subcmd=%u", vinfo->vendor_id, vinfo->subcmd); } } if (tb[NL80211_ATTR_VENDOR_EVENTS]) { struct nlattr *nl; int rem; nla_for_each_nested(nl, tb[NL80211_ATTR_VENDOR_EVENTS], rem) { struct nl80211_vendor_cmd_info *vinfo; if (nla_len(nl) != sizeof(*vinfo)) { wpa_printf(MSG_DEBUG, "nl80211: Unexpected vendor data info"); continue; } vinfo = nla_data(nl); wpa_printf(MSG_DEBUG, "nl80211: Supported vendor event: vendor_id=0x%x subcmd=%u", vinfo->vendor_id, vinfo->subcmd); } } return NL_SKIP; } static int wpa_driver_nl80211_get_info(struct wpa_driver_nl80211_data *drv, struct wiphy_info_data *info) { u32 feat; struct nl_msg *msg; os_memset(info, 0, sizeof(*info)); info->capa = &drv->capa; info->drv = drv; msg = nlmsg_alloc(); if (!msg) return -1; feat = get_nl80211_protocol_features(drv); if (feat & NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP) nl80211_cmd(drv, msg, NLM_F_DUMP, NL80211_CMD_GET_WIPHY); else nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_WIPHY); NLA_PUT_FLAG(msg, NL80211_ATTR_SPLIT_WIPHY_DUMP); if (nl80211_set_iface_id(msg, drv->first_bss) < 0) goto nla_put_failure; if (send_and_recv_msgs(drv, msg, wiphy_info_handler, info)) return -1; if (info->auth_supported) drv->capa.flags |= WPA_DRIVER_FLAGS_SME; else if (!info->connect_supported) { wpa_printf(MSG_INFO, "nl80211: Driver does not support " "authentication/association or connect commands"); info->error = 1; } if (info->p2p_go_supported && info->p2p_client_supported) drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_CAPABLE; if (info->p2p_concurrent) { wpa_printf(MSG_DEBUG, "nl80211: Use separate P2P group " "interface (driver advertised support)"); drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_CONCURRENT; drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_MGMT_AND_NON_P2P; } if (info->num_multichan_concurrent > 1) { wpa_printf(MSG_DEBUG, "nl80211: Enable multi-channel " "concurrent (driver advertised support)"); drv->capa.num_multichan_concurrent = info->num_multichan_concurrent; } /* default to 5000 since early versions of mac80211 don't set it */ if (!drv->capa.max_remain_on_chan) drv->capa.max_remain_on_chan = 5000; if (info->channel_switch_supported) drv->capa.flags |= WPA_DRIVER_FLAGS_AP_CSA; return 0; nla_put_failure: nlmsg_free(msg); return -1; } static int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv) { struct wiphy_info_data info; if (wpa_driver_nl80211_get_info(drv, &info)) return -1; if (info.error) return -1; drv->has_capability = 1; drv->capa.key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA | WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK | WPA_DRIVER_CAPA_KEY_MGMT_WPA2 | WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK; drv->capa.auth = WPA_DRIVER_AUTH_OPEN | WPA_DRIVER_AUTH_SHARED | WPA_DRIVER_AUTH_LEAP; drv->capa.flags |= WPA_DRIVER_FLAGS_SANE_ERROR_CODES; drv->capa.flags |= WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE; drv->capa.flags |= WPA_DRIVER_FLAGS_EAPOL_TX_STATUS; if (!info.device_ap_sme) { drv->capa.flags |= WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS; /* * No AP SME is currently assumed to also indicate no AP MLME * in the driver/firmware. */ drv->capa.flags |= WPA_DRIVER_FLAGS_AP_MLME; } drv->device_ap_sme = info.device_ap_sme; drv->poll_command_supported = info.poll_command_supported; drv->data_tx_status = info.data_tx_status; if (info.set_qos_map_supported) drv->capa.flags |= WPA_DRIVER_FLAGS_QOS_MAPPING; /* * If poll command and tx status are supported, mac80211 is new enough * to have everything we need to not need monitor interfaces. */ drv->use_monitor = !info.poll_command_supported || !info.data_tx_status; if (drv->device_ap_sme && drv->use_monitor) { /* * Non-mac80211 drivers may not support monitor interface. * Make sure we do not get stuck with incorrect capability here * by explicitly testing this. */ if (!info.monitor_supported) { wpa_printf(MSG_DEBUG, "nl80211: Disable use_monitor " "with device_ap_sme since no monitor mode " "support detected"); drv->use_monitor = 0; } } /* * If we aren't going to use monitor interfaces, but the * driver doesn't support data TX status, we won't get TX * status for EAPOL frames. */ if (!drv->use_monitor && !info.data_tx_status) drv->capa.flags &= ~WPA_DRIVER_FLAGS_EAPOL_TX_STATUS; return 0; } #ifdef ANDROID static int android_genl_ctrl_resolve(struct nl_handle *handle, const char *name) { /* * Android ICS has very minimal genl_ctrl_resolve() implementation, so * need to work around that. */ struct nl_cache *cache = NULL; struct genl_family *nl80211 = NULL; int id = -1; if (genl_ctrl_alloc_cache(handle, &cache) < 0) { wpa_printf(MSG_ERROR, "nl80211: Failed to allocate generic " "netlink cache"); goto fail; } nl80211 = genl_ctrl_search_by_name(cache, name); if (nl80211 == NULL) goto fail; id = genl_family_get_id(nl80211); fail: if (nl80211) genl_family_put(nl80211); if (cache) nl_cache_free(cache); return id; } #define genl_ctrl_resolve android_genl_ctrl_resolve #endif /* ANDROID */ static int wpa_driver_nl80211_init_nl_global(struct nl80211_global *global) { int ret; global->nl_cb = nl_cb_alloc(NL_CB_DEFAULT); if (global->nl_cb == NULL) { wpa_printf(MSG_ERROR, "nl80211: Failed to allocate netlink " "callbacks"); return -1; } global->nl = nl_create_handle(global->nl_cb, "nl"); if (global->nl == NULL) goto err; global->nl80211_id = genl_ctrl_resolve(global->nl, "nl80211"); if (global->nl80211_id < 0) { wpa_printf(MSG_ERROR, "nl80211: 'nl80211' generic netlink not " "found"); goto err; } global->nl_event = nl_create_handle(global->nl_cb, "event"); if (global->nl_event == NULL) goto err; ret = nl_get_multicast_id(global, "nl80211", "scan"); if (ret >= 0) ret = nl_socket_add_membership(global->nl_event, ret); if (ret < 0) { wpa_printf(MSG_ERROR, "nl80211: Could not add multicast " "membership for scan events: %d (%s)", ret, strerror(-ret)); goto err; } ret = nl_get_multicast_id(global, "nl80211", "mlme"); if (ret >= 0) ret = nl_socket_add_membership(global->nl_event, ret); if (ret < 0) { wpa_printf(MSG_ERROR, "nl80211: Could not add multicast " "membership for mlme events: %d (%s)", ret, strerror(-ret)); goto err; } ret = nl_get_multicast_id(global, "nl80211", "regulatory"); if (ret >= 0) ret = nl_socket_add_membership(global->nl_event, ret); if (ret < 0) { wpa_printf(MSG_DEBUG, "nl80211: Could not add multicast " "membership for regulatory events: %d (%s)", ret, strerror(-ret)); /* Continue without regulatory events */ } ret = nl_get_multicast_id(global, "nl80211", "vendor"); if (ret >= 0) ret = nl_socket_add_membership(global->nl_event, ret); if (ret < 0) { wpa_printf(MSG_DEBUG, "nl80211: Could not add multicast " "membership for vendor events: %d (%s)", ret, strerror(-ret)); /* Continue without vendor events */ } nl_cb_set(global->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, no_seq_check, NULL); nl_cb_set(global->nl_cb, NL_CB_VALID, NL_CB_CUSTOM, process_global_event, global); nl80211_register_eloop_read(&global->nl_event, wpa_driver_nl80211_event_receive, global->nl_cb); return 0; err: nl_destroy_handles(&global->nl_event); nl_destroy_handles(&global->nl); nl_cb_put(global->nl_cb); global->nl_cb = NULL; return -1; } static int wpa_driver_nl80211_init_nl(struct wpa_driver_nl80211_data *drv) { drv->nl_cb = nl_cb_alloc(NL_CB_DEFAULT); if (!drv->nl_cb) { wpa_printf(MSG_ERROR, "nl80211: Failed to alloc cb struct"); return -1; } nl_cb_set(drv->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, no_seq_check, NULL); nl_cb_set(drv->nl_cb, NL_CB_VALID, NL_CB_CUSTOM, process_drv_event, drv); return 0; } static void wpa_driver_nl80211_rfkill_blocked(void *ctx) { wpa_printf(MSG_DEBUG, "nl80211: RFKILL blocked"); /* * This may be for any interface; use ifdown event to disable * interface. */ } static void wpa_driver_nl80211_rfkill_unblocked(void *ctx) { struct wpa_driver_nl80211_data *drv = ctx; wpa_printf(MSG_DEBUG, "nl80211: RFKILL unblocked"); if (i802_set_iface_flags(drv->first_bss, 1)) { wpa_printf(MSG_DEBUG, "nl80211: Could not set interface UP " "after rfkill unblock"); return; } /* rtnetlink ifup handler will report interface as enabled */ } static void wpa_driver_nl80211_handle_eapol_tx_status(int sock, void *eloop_ctx, void *handle) { struct wpa_driver_nl80211_data *drv = eloop_ctx; u8 data[2048]; struct msghdr msg; struct iovec entry; u8 control[512]; struct cmsghdr *cmsg; int res, found_ee = 0, found_wifi = 0, acked = 0; union wpa_event_data event; memset(&msg, 0, sizeof(msg)); msg.msg_iov = &entry; msg.msg_iovlen = 1; entry.iov_base = data; entry.iov_len = sizeof(data); msg.msg_control = &control; msg.msg_controllen = sizeof(control); res = recvmsg(sock, &msg, MSG_ERRQUEUE); /* if error or not fitting 802.3 header, return */ if (res < 14) return; for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_WIFI_STATUS) { int *ack; found_wifi = 1; ack = (void *)CMSG_DATA(cmsg); acked = *ack; } if (cmsg->cmsg_level == SOL_PACKET && cmsg->cmsg_type == PACKET_TX_TIMESTAMP) { struct sock_extended_err *err = (struct sock_extended_err *)CMSG_DATA(cmsg); if (err->ee_origin == SO_EE_ORIGIN_TXSTATUS) found_ee = 1; } } if (!found_ee || !found_wifi) return; memset(&event, 0, sizeof(event)); event.eapol_tx_status.dst = data; event.eapol_tx_status.data = data + 14; event.eapol_tx_status.data_len = res - 14; event.eapol_tx_status.ack = acked; wpa_supplicant_event(drv->ctx, EVENT_EAPOL_TX_STATUS, &event); } static int nl80211_init_bss(struct i802_bss *bss) { bss->nl_cb = nl_cb_alloc(NL_CB_DEFAULT); if (!bss->nl_cb) return -1; nl_cb_set(bss->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, no_seq_check, NULL); nl_cb_set(bss->nl_cb, NL_CB_VALID, NL_CB_CUSTOM, process_bss_event, bss); return 0; } static void nl80211_destroy_bss(struct i802_bss *bss) { nl_cb_put(bss->nl_cb); bss->nl_cb = NULL; } static void * wpa_driver_nl80211_drv_init(void *ctx, const char *ifname, void *global_priv, int hostapd, const u8 *set_addr) { struct wpa_driver_nl80211_data *drv; struct rfkill_config *rcfg; struct i802_bss *bss; if (global_priv == NULL) return NULL; drv = os_zalloc(sizeof(*drv)); if (drv == NULL) return NULL; drv->global = global_priv; drv->ctx = ctx; drv->hostapd = !!hostapd; drv->eapol_sock = -1; drv->num_if_indices = sizeof(drv->default_if_indices) / sizeof(int); drv->if_indices = drv->default_if_indices; drv->first_bss = os_zalloc(sizeof(*drv->first_bss)); if (!drv->first_bss) { os_free(drv); return NULL; } bss = drv->first_bss; bss->drv = drv; bss->ctx = ctx; os_strlcpy(bss->ifname, ifname, sizeof(bss->ifname)); drv->monitor_ifidx = -1; drv->monitor_sock = -1; drv->eapol_tx_sock = -1; drv->ap_scan_as_station = NL80211_IFTYPE_UNSPECIFIED; if (wpa_driver_nl80211_init_nl(drv)) { os_free(drv); return NULL; } if (nl80211_init_bss(bss)) goto failed; rcfg = os_zalloc(sizeof(*rcfg)); if (rcfg == NULL) goto failed; rcfg->ctx = drv; os_strlcpy(rcfg->ifname, ifname, sizeof(rcfg->ifname)); rcfg->blocked_cb = wpa_driver_nl80211_rfkill_blocked; rcfg->unblocked_cb = wpa_driver_nl80211_rfkill_unblocked; drv->rfkill = rfkill_init(rcfg); if (drv->rfkill == NULL) { wpa_printf(MSG_DEBUG, "nl80211: RFKILL status not available"); os_free(rcfg); } if (linux_iface_up(drv->global->ioctl_sock, ifname) > 0) drv->start_iface_up = 1; if (wpa_driver_nl80211_finish_drv_init(drv, set_addr, 1)) goto failed; drv->eapol_tx_sock = socket(PF_PACKET, SOCK_DGRAM, 0); if (drv->eapol_tx_sock < 0) goto failed; if (drv->data_tx_status) { int enabled = 1; if (setsockopt(drv->eapol_tx_sock, SOL_SOCKET, SO_WIFI_STATUS, &enabled, sizeof(enabled)) < 0) { wpa_printf(MSG_DEBUG, "nl80211: wifi status sockopt failed\n"); drv->data_tx_status = 0; if (!drv->use_monitor) drv->capa.flags &= ~WPA_DRIVER_FLAGS_EAPOL_TX_STATUS; } else { eloop_register_read_sock(drv->eapol_tx_sock, wpa_driver_nl80211_handle_eapol_tx_status, drv, NULL); } } if (drv->global) { dl_list_add(&drv->global->interfaces, &drv->list); drv->in_interface_list = 1; } return bss; failed: wpa_driver_nl80211_deinit(bss); return NULL; } /** * wpa_driver_nl80211_init - Initialize nl80211 driver interface * @ctx: context to be used when calling wpa_supplicant functions, * e.g., wpa_supplicant_event() * @ifname: interface name, e.g., wlan0 * @global_priv: private driver global data from global_init() * Returns: Pointer to private data, %NULL on failure */ static void * wpa_driver_nl80211_init(void *ctx, const char *ifname, void *global_priv) { return wpa_driver_nl80211_drv_init(ctx, ifname, global_priv, 0, NULL); } static int nl80211_register_frame(struct i802_bss *bss, struct nl_handle *nl_handle, u16 type, const u8 *match, size_t match_len) { struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; int ret = -1; char buf[30]; msg = nlmsg_alloc(); if (!msg) return -1; buf[0] = '\0'; wpa_snprintf_hex(buf, sizeof(buf), match, match_len); wpa_printf(MSG_DEBUG, "nl80211: Register frame type=0x%x nl_handle=%p match=%s", type, nl_handle, buf); nl80211_cmd(drv, msg, 0, NL80211_CMD_REGISTER_ACTION); if (nl80211_set_iface_id(msg, bss) < 0) goto nla_put_failure; NLA_PUT_U16(msg, NL80211_ATTR_FRAME_TYPE, type); NLA_PUT(msg, NL80211_ATTR_FRAME_MATCH, match_len, match); ret = send_and_recv(drv->global, nl_handle, msg, NULL, NULL); msg = NULL; if (ret) { wpa_printf(MSG_DEBUG, "nl80211: Register frame command " "failed (type=%u): ret=%d (%s)", type, ret, strerror(-ret)); wpa_hexdump(MSG_DEBUG, "nl80211: Register frame match", match, match_len); goto nla_put_failure; } ret = 0; nla_put_failure: nlmsg_free(msg); return ret; } static int nl80211_alloc_mgmt_handle(struct i802_bss *bss) { struct wpa_driver_nl80211_data *drv = bss->drv; if (bss->nl_mgmt) { wpa_printf(MSG_DEBUG, "nl80211: Mgmt reporting " "already on! (nl_mgmt=%p)", bss->nl_mgmt); return -1; } bss->nl_mgmt = nl_create_handle(drv->nl_cb, "mgmt"); if (bss->nl_mgmt == NULL) return -1; return 0; } static void nl80211_mgmt_handle_register_eloop(struct i802_bss *bss) { nl80211_register_eloop_read(&bss->nl_mgmt, wpa_driver_nl80211_event_receive, bss->nl_cb); } static int nl80211_register_action_frame(struct i802_bss *bss, const u8 *match, size_t match_len) { u16 type = (WLAN_FC_TYPE_MGMT << 2) | (WLAN_FC_STYPE_ACTION << 4); return nl80211_register_frame(bss, bss->nl_mgmt, type, match, match_len); } static int nl80211_mgmt_subscribe_non_ap(struct i802_bss *bss) { struct wpa_driver_nl80211_data *drv = bss->drv; int ret = 0; if (nl80211_alloc_mgmt_handle(bss)) return -1; wpa_printf(MSG_DEBUG, "nl80211: Subscribe to mgmt frames with non-AP " "handle %p", bss->nl_mgmt); if (drv->nlmode == NL80211_IFTYPE_ADHOC) { u16 type = (WLAN_FC_TYPE_MGMT << 2) | (WLAN_FC_STYPE_AUTH << 4); /* register for any AUTH message */ nl80211_register_frame(bss, bss->nl_mgmt, type, NULL, 0); } #ifdef CONFIG_INTERWORKING /* QoS Map Configure */ if (nl80211_register_action_frame(bss, (u8 *) "\x01\x04", 2) < 0) ret = -1; #endif /* CONFIG_INTERWORKING */ #if defined(CONFIG_P2P) || defined(CONFIG_INTERWORKING) /* GAS Initial Request */ if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0a", 2) < 0) ret = -1; /* GAS Initial Response */ if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0b", 2) < 0) ret = -1; /* GAS Comeback Request */ if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0c", 2) < 0) ret = -1; /* GAS Comeback Response */ if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0d", 2) < 0) ret = -1; /* Protected GAS Initial Request */ if (nl80211_register_action_frame(bss, (u8 *) "\x09\x0a", 2) < 0) ret = -1; /* Protected GAS Initial Response */ if (nl80211_register_action_frame(bss, (u8 *) "\x09\x0b", 2) < 0) ret = -1; /* Protected GAS Comeback Request */ if (nl80211_register_action_frame(bss, (u8 *) "\x09\x0c", 2) < 0) ret = -1; /* Protected GAS Comeback Response */ if (nl80211_register_action_frame(bss, (u8 *) "\x09\x0d", 2) < 0) ret = -1; #endif /* CONFIG_P2P || CONFIG_INTERWORKING */ #ifdef CONFIG_P2P /* P2P Public Action */ if (nl80211_register_action_frame(bss, (u8 *) "\x04\x09\x50\x6f\x9a\x09", 6) < 0) ret = -1; /* P2P Action */ if (nl80211_register_action_frame(bss, (u8 *) "\x7f\x50\x6f\x9a\x09", 5) < 0) ret = -1; #endif /* CONFIG_P2P */ #ifdef CONFIG_IEEE80211W /* SA Query Response */ if (nl80211_register_action_frame(bss, (u8 *) "\x08\x01", 2) < 0) ret = -1; #endif /* CONFIG_IEEE80211W */ #ifdef CONFIG_TDLS if ((drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT)) { /* TDLS Discovery Response */ if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0e", 2) < 0) ret = -1; } #endif /* CONFIG_TDLS */ /* FT Action frames */ if (nl80211_register_action_frame(bss, (u8 *) "\x06", 1) < 0) ret = -1; else drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT | WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK; /* WNM - BSS Transition Management Request */ if (nl80211_register_action_frame(bss, (u8 *) "\x0a\x07", 2) < 0) ret = -1; /* WNM-Sleep Mode Response */ if (nl80211_register_action_frame(bss, (u8 *) "\x0a\x11", 2) < 0) ret = -1; nl80211_mgmt_handle_register_eloop(bss); return ret; } static int nl80211_register_spurious_class3(struct i802_bss *bss) { struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; int ret = -1; msg = nlmsg_alloc(); if (!msg) return -1; nl80211_cmd(drv, msg, 0, NL80211_CMD_UNEXPECTED_FRAME); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex); ret = send_and_recv(drv->global, bss->nl_mgmt, msg, NULL, NULL); msg = NULL; if (ret) { wpa_printf(MSG_DEBUG, "nl80211: Register spurious class3 " "failed: ret=%d (%s)", ret, strerror(-ret)); goto nla_put_failure; } ret = 0; nla_put_failure: nlmsg_free(msg); return ret; } static int nl80211_mgmt_subscribe_ap(struct i802_bss *bss) { static const int stypes[] = { WLAN_FC_STYPE_AUTH, WLAN_FC_STYPE_ASSOC_REQ, WLAN_FC_STYPE_REASSOC_REQ, WLAN_FC_STYPE_DISASSOC, WLAN_FC_STYPE_DEAUTH, WLAN_FC_STYPE_ACTION, WLAN_FC_STYPE_PROBE_REQ, /* Beacon doesn't work as mac80211 doesn't currently allow * it, but it wouldn't really be the right thing anyway as * it isn't per interface ... maybe just dump the scan * results periodically for OLBC? */ // WLAN_FC_STYPE_BEACON, }; unsigned int i; if (nl80211_alloc_mgmt_handle(bss)) return -1; wpa_printf(MSG_DEBUG, "nl80211: Subscribe to mgmt frames with AP " "handle %p", bss->nl_mgmt); for (i = 0; i < ARRAY_SIZE(stypes); i++) { if (nl80211_register_frame(bss, bss->nl_mgmt, (WLAN_FC_TYPE_MGMT << 2) | (stypes[i] << 4), NULL, 0) < 0) { goto out_err; } } if (nl80211_register_spurious_class3(bss)) goto out_err; if (nl80211_get_wiphy_data_ap(bss) == NULL) goto out_err; nl80211_mgmt_handle_register_eloop(bss); return 0; out_err: nl_destroy_handles(&bss->nl_mgmt); return -1; } static int nl80211_mgmt_subscribe_ap_dev_sme(struct i802_bss *bss) { if (nl80211_alloc_mgmt_handle(bss)) return -1; wpa_printf(MSG_DEBUG, "nl80211: Subscribe to mgmt frames with AP " "handle %p (device SME)", bss->nl_mgmt); if (nl80211_register_frame(bss, bss->nl_mgmt, (WLAN_FC_TYPE_MGMT << 2) | (WLAN_FC_STYPE_ACTION << 4), NULL, 0) < 0) goto out_err; nl80211_mgmt_handle_register_eloop(bss); return 0; out_err: nl_destroy_handles(&bss->nl_mgmt); return -1; } static void nl80211_mgmt_unsubscribe(struct i802_bss *bss, const char *reason) { if (bss->nl_mgmt == NULL) return; wpa_printf(MSG_DEBUG, "nl80211: Unsubscribe mgmt frames handle %p " "(%s)", bss->nl_mgmt, reason); nl80211_destroy_eloop_handle(&bss->nl_mgmt); nl80211_put_wiphy_data_ap(bss); } static void wpa_driver_nl80211_send_rfkill(void *eloop_ctx, void *timeout_ctx) { wpa_supplicant_event(timeout_ctx, EVENT_INTERFACE_DISABLED, NULL); } static void nl80211_del_p2pdev(struct i802_bss *bss) { struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; int ret; msg = nlmsg_alloc(); if (!msg) return; nl80211_cmd(drv, msg, 0, NL80211_CMD_DEL_INTERFACE); NLA_PUT_U64(msg, NL80211_ATTR_WDEV, bss->wdev_id); ret = send_and_recv_msgs(drv, msg, NULL, NULL); msg = NULL; wpa_printf(MSG_DEBUG, "nl80211: Delete P2P Device %s (0x%llx): %s", bss->ifname, (long long unsigned int) bss->wdev_id, strerror(-ret)); nla_put_failure: nlmsg_free(msg); } static int nl80211_set_p2pdev(struct i802_bss *bss, int start) { struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; int ret = -1; msg = nlmsg_alloc(); if (!msg) return -1; if (start) nl80211_cmd(drv, msg, 0, NL80211_CMD_START_P2P_DEVICE); else nl80211_cmd(drv, msg, 0, NL80211_CMD_STOP_P2P_DEVICE); NLA_PUT_U64(msg, NL80211_ATTR_WDEV, bss->wdev_id); ret = send_and_recv_msgs(drv, msg, NULL, NULL); msg = NULL; wpa_printf(MSG_DEBUG, "nl80211: %s P2P Device %s (0x%llx): %s", start ? "Start" : "Stop", bss->ifname, (long long unsigned int) bss->wdev_id, strerror(-ret)); nla_put_failure: nlmsg_free(msg); return ret; } static int i802_set_iface_flags(struct i802_bss *bss, int up) { enum nl80211_iftype nlmode; nlmode = nl80211_get_ifmode(bss); if (nlmode != NL80211_IFTYPE_P2P_DEVICE) { return linux_set_iface_flags(bss->drv->global->ioctl_sock, bss->ifname, up); } /* P2P Device has start/stop which is equivalent */ return nl80211_set_p2pdev(bss, up); } static int wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv, const u8 *set_addr, int first) { struct i802_bss *bss = drv->first_bss; int send_rfkill_event = 0; enum nl80211_iftype nlmode; drv->ifindex = if_nametoindex(bss->ifname); bss->ifindex = drv->ifindex; bss->wdev_id = drv->global->if_add_wdevid; bss->wdev_id_set = drv->global->if_add_wdevid_set; bss->if_dynamic = drv->ifindex == drv->global->if_add_ifindex; bss->if_dynamic = bss->if_dynamic || drv->global->if_add_wdevid_set; drv->global->if_add_wdevid_set = 0; if (wpa_driver_nl80211_capa(drv)) return -1; wpa_printf(MSG_DEBUG, "nl80211: interface %s in phy %s", bss->ifname, drv->phyname); if (set_addr && (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 0) || linux_set_ifhwaddr(drv->global->ioctl_sock, bss->ifname, set_addr))) return -1; if (first && nl80211_get_ifmode(bss) == NL80211_IFTYPE_AP) drv->start_mode_ap = 1; if (drv->hostapd) nlmode = NL80211_IFTYPE_AP; else if (bss->if_dynamic) nlmode = nl80211_get_ifmode(bss); else nlmode = NL80211_IFTYPE_STATION; if (wpa_driver_nl80211_set_mode(bss, nlmode) < 0) { wpa_printf(MSG_ERROR, "nl80211: Could not configure driver mode"); return -1; } if (nlmode == NL80211_IFTYPE_P2P_DEVICE) { int ret = nl80211_set_p2pdev(bss, 1); if (ret < 0) wpa_printf(MSG_ERROR, "nl80211: Could not start P2P device"); nl80211_get_macaddr(bss); return ret; } if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 1)) { if (rfkill_is_blocked(drv->rfkill)) { wpa_printf(MSG_DEBUG, "nl80211: Could not yet enable " "interface '%s' due to rfkill", bss->ifname); drv->if_disabled = 1; send_rfkill_event = 1; } else { wpa_printf(MSG_ERROR, "nl80211: Could not set " "interface '%s' UP", bss->ifname); return -1; } } if (!drv->hostapd) netlink_send_oper_ifla(drv->global->netlink, drv->ifindex, 1, IF_OPER_DORMANT); if (linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname, bss->addr)) return -1; if (send_rfkill_event) { eloop_register_timeout(0, 0, wpa_driver_nl80211_send_rfkill, drv, drv->ctx); } return 0; } static int wpa_driver_nl80211_del_beacon(struct wpa_driver_nl80211_data *drv) { struct nl_msg *msg; msg = nlmsg_alloc(); if (!msg) return -ENOMEM; wpa_printf(MSG_DEBUG, "nl80211: Remove beacon (ifindex=%d)", drv->ifindex); nl80211_cmd(drv, msg, 0, NL80211_CMD_DEL_BEACON); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); return send_and_recv_msgs(drv, msg, NULL, NULL); nla_put_failure: nlmsg_free(msg); return -ENOBUFS; } /** * wpa_driver_nl80211_deinit - Deinitialize nl80211 driver interface * @bss: Pointer to private nl80211 data from wpa_driver_nl80211_init() * * Shut down driver interface and processing of driver events. Free * private data buffer if one was allocated in wpa_driver_nl80211_init(). */ static void wpa_driver_nl80211_deinit(struct i802_bss *bss) { struct wpa_driver_nl80211_data *drv = bss->drv; bss->in_deinit = 1; if (drv->data_tx_status) eloop_unregister_read_sock(drv->eapol_tx_sock); if (drv->eapol_tx_sock >= 0) close(drv->eapol_tx_sock); if (bss->nl_preq) wpa_driver_nl80211_probe_req_report(bss, 0); if (bss->added_if_into_bridge) { if (linux_br_del_if(drv->global->ioctl_sock, bss->brname, bss->ifname) < 0) wpa_printf(MSG_INFO, "nl80211: Failed to remove " "interface %s from bridge %s: %s", bss->ifname, bss->brname, strerror(errno)); } if (bss->added_bridge) { if (linux_br_del(drv->global->ioctl_sock, bss->brname) < 0) wpa_printf(MSG_INFO, "nl80211: Failed to remove " "bridge %s: %s", bss->brname, strerror(errno)); } nl80211_remove_monitor_interface(drv); if (is_ap_interface(drv->nlmode)) wpa_driver_nl80211_del_beacon(drv); if (drv->eapol_sock >= 0) { eloop_unregister_read_sock(drv->eapol_sock); close(drv->eapol_sock); } if (drv->if_indices != drv->default_if_indices) os_free(drv->if_indices); if (drv->disabled_11b_rates) nl80211_disable_11b_rates(drv, drv->ifindex, 0); netlink_send_oper_ifla(drv->global->netlink, drv->ifindex, 0, IF_OPER_UP); rfkill_deinit(drv->rfkill); eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx); if (!drv->start_iface_up) (void) i802_set_iface_flags(bss, 0); if (drv->nlmode != NL80211_IFTYPE_P2P_DEVICE) { if (!drv->hostapd || !drv->start_mode_ap) wpa_driver_nl80211_set_mode(bss, NL80211_IFTYPE_STATION); nl80211_mgmt_unsubscribe(bss, "deinit"); } else { nl80211_mgmt_unsubscribe(bss, "deinit"); nl80211_del_p2pdev(bss); } nl_cb_put(drv->nl_cb); nl80211_destroy_bss(drv->first_bss); os_free(drv->filter_ssids); os_free(drv->auth_ie); if (drv->in_interface_list) dl_list_del(&drv->list); os_free(drv->extended_capa); os_free(drv->extended_capa_mask); os_free(drv->first_bss); os_free(drv); } /** * wpa_driver_nl80211_scan_timeout - Scan timeout to report scan completion * @eloop_ctx: Driver private data * @timeout_ctx: ctx argument given to wpa_driver_nl80211_init() * * This function can be used as registered timeout when starting a scan to * generate a scan completed event if the driver does not report this. */ static void wpa_driver_nl80211_scan_timeout(void *eloop_ctx, void *timeout_ctx) { struct wpa_driver_nl80211_data *drv = eloop_ctx; if (drv->ap_scan_as_station != NL80211_IFTYPE_UNSPECIFIED) { wpa_driver_nl80211_set_mode(drv->first_bss, drv->ap_scan_as_station); drv->ap_scan_as_station = NL80211_IFTYPE_UNSPECIFIED; } wpa_printf(MSG_DEBUG, "Scan timeout - try to get results"); wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL); } static struct nl_msg * nl80211_scan_common(struct wpa_driver_nl80211_data *drv, u8 cmd, struct wpa_driver_scan_params *params, u64 *wdev_id) { struct nl_msg *msg; size_t i; msg = nlmsg_alloc(); if (!msg) return NULL; nl80211_cmd(drv, msg, 0, cmd); if (!wdev_id) NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); else NLA_PUT_U64(msg, NL80211_ATTR_WDEV, *wdev_id); if (params->num_ssids) { struct nlattr *ssids; ssids = nla_nest_start(msg, NL80211_ATTR_SCAN_SSIDS); if (ssids == NULL) goto fail; for (i = 0; i < params->num_ssids; i++) { wpa_hexdump_ascii(MSG_MSGDUMP, "nl80211: Scan SSID", params->ssids[i].ssid, params->ssids[i].ssid_len); if (nla_put(msg, i + 1, params->ssids[i].ssid_len, params->ssids[i].ssid) < 0) goto fail; } nla_nest_end(msg, ssids); } if (params->extra_ies) { wpa_hexdump(MSG_MSGDUMP, "nl80211: Scan extra IEs", params->extra_ies, params->extra_ies_len); if (nla_put(msg, NL80211_ATTR_IE, params->extra_ies_len, params->extra_ies) < 0) goto fail; } if (params->freqs) { struct nlattr *freqs; freqs = nla_nest_start(msg, NL80211_ATTR_SCAN_FREQUENCIES); if (freqs == NULL) goto fail; for (i = 0; params->freqs[i]; i++) { wpa_printf(MSG_MSGDUMP, "nl80211: Scan frequency %u " "MHz", params->freqs[i]); if (nla_put_u32(msg, i + 1, params->freqs[i]) < 0) goto fail; } nla_nest_end(msg, freqs); } os_free(drv->filter_ssids); drv->filter_ssids = params->filter_ssids; params->filter_ssids = NULL; drv->num_filter_ssids = params->num_filter_ssids; if (params->only_new_results) { wpa_printf(MSG_DEBUG, "nl80211: Add NL80211_SCAN_FLAG_FLUSH"); NLA_PUT_U32(msg, NL80211_ATTR_SCAN_FLAGS, NL80211_SCAN_FLAG_FLUSH); } return msg; fail: nla_put_failure: nlmsg_free(msg); return NULL; } /** * wpa_driver_nl80211_scan - Request the driver to initiate scan * @bss: Pointer to private driver data from wpa_driver_nl80211_init() * @params: Scan parameters * Returns: 0 on success, -1 on failure */ static int wpa_driver_nl80211_scan(struct i802_bss *bss, struct wpa_driver_scan_params *params) { struct wpa_driver_nl80211_data *drv = bss->drv; int ret = -1, timeout; struct nl_msg *msg = NULL; wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: scan request"); drv->scan_for_auth = 0; msg = nl80211_scan_common(drv, NL80211_CMD_TRIGGER_SCAN, params, bss->wdev_id_set ? &bss->wdev_id : NULL); if (!msg) return -1; if (params->p2p_probe) { struct nlattr *rates; wpa_printf(MSG_DEBUG, "nl80211: P2P probe - mask SuppRates"); rates = nla_nest_start(msg, NL80211_ATTR_SCAN_SUPP_RATES); if (rates == NULL) goto nla_put_failure; /* * Remove 2.4 GHz rates 1, 2, 5.5, 11 Mbps from supported rates * by masking out everything else apart from the OFDM rates 6, * 9, 12, 18, 24, 36, 48, 54 Mbps from non-MCS rates. All 5 GHz * rates are left enabled. */ NLA_PUT(msg, NL80211_BAND_2GHZ, 8, "\x0c\x12\x18\x24\x30\x48\x60\x6c"); nla_nest_end(msg, rates); NLA_PUT_FLAG(msg, NL80211_ATTR_TX_NO_CCK_RATE); } ret = send_and_recv_msgs(drv, msg, NULL, NULL); msg = NULL; if (ret) { wpa_printf(MSG_DEBUG, "nl80211: Scan trigger failed: ret=%d " "(%s)", ret, strerror(-ret)); if (drv->hostapd && is_ap_interface(drv->nlmode)) { /* * mac80211 does not allow scan requests in AP mode, so * try to do this in station mode. */ if (wpa_driver_nl80211_set_mode( bss, NL80211_IFTYPE_STATION)) goto nla_put_failure; if (wpa_driver_nl80211_scan(bss, params)) { wpa_driver_nl80211_set_mode(bss, drv->nlmode); goto nla_put_failure; } /* Restore AP mode when processing scan results */ drv->ap_scan_as_station = drv->nlmode; ret = 0; } else goto nla_put_failure; } drv->scan_state = SCAN_REQUESTED; /* Not all drivers generate "scan completed" wireless event, so try to * read results after a timeout. */ timeout = 10; if (drv->scan_complete_events) { /* * The driver seems to deliver events to notify when scan is * complete, so use longer timeout to avoid race conditions * with scanning and following association request. */ timeout = 30; } wpa_printf(MSG_DEBUG, "Scan requested (ret=%d) - scan timeout %d " "seconds", ret, timeout); eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx); eloop_register_timeout(timeout, 0, wpa_driver_nl80211_scan_timeout, drv, drv->ctx); nla_put_failure: nlmsg_free(msg); return ret; } /** * wpa_driver_nl80211_sched_scan - Initiate a scheduled scan * @priv: Pointer to private driver data from wpa_driver_nl80211_init() * @params: Scan parameters * @interval: Interval between scan cycles in milliseconds * Returns: 0 on success, -1 on failure or if not supported */ static int wpa_driver_nl80211_sched_scan(void *priv, struct wpa_driver_scan_params *params, u32 interval) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; int ret = -1; struct nl_msg *msg; size_t i; wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: sched_scan request"); #ifdef ANDROID if (!drv->capa.sched_scan_supported) return android_pno_start(bss, params); #endif /* ANDROID */ msg = nl80211_scan_common(drv, NL80211_CMD_START_SCHED_SCAN, params, bss->wdev_id_set ? &bss->wdev_id : NULL); if (!msg) goto nla_put_failure; NLA_PUT_U32(msg, NL80211_ATTR_SCHED_SCAN_INTERVAL, interval); if ((drv->num_filter_ssids && (int) drv->num_filter_ssids <= drv->capa.max_match_sets) || params->filter_rssi) { struct nlattr *match_sets; match_sets = nla_nest_start(msg, NL80211_ATTR_SCHED_SCAN_MATCH); if (match_sets == NULL) goto nla_put_failure; for (i = 0; i < drv->num_filter_ssids; i++) { struct nlattr *match_set_ssid; wpa_hexdump_ascii(MSG_MSGDUMP, "nl80211: Sched scan filter SSID", drv->filter_ssids[i].ssid, drv->filter_ssids[i].ssid_len); match_set_ssid = nla_nest_start(msg, i + 1); if (match_set_ssid == NULL) goto nla_put_failure; NLA_PUT(msg, NL80211_ATTR_SCHED_SCAN_MATCH_SSID, drv->filter_ssids[i].ssid_len, drv->filter_ssids[i].ssid); if (params->filter_rssi) NLA_PUT_U32(msg, NL80211_SCHED_SCAN_MATCH_ATTR_RSSI, params->filter_rssi); nla_nest_end(msg, match_set_ssid); } /* * Due to backward compatibility code, newer kernels treat this * matchset (with only an RSSI filter) as the default for all * other matchsets, unless it's the only one, in which case the * matchset will actually allow all SSIDs above the RSSI. */ if (params->filter_rssi) { struct nlattr *match_set_rssi; match_set_rssi = nla_nest_start(msg, 0); if (match_set_rssi == NULL) goto nla_put_failure; NLA_PUT_U32(msg, NL80211_SCHED_SCAN_MATCH_ATTR_RSSI, params->filter_rssi); wpa_printf(MSG_MSGDUMP, "nl80211: Sched scan RSSI filter %d dBm", params->filter_rssi); nla_nest_end(msg, match_set_rssi); } nla_nest_end(msg, match_sets); } ret = send_and_recv_msgs(drv, msg, NULL, NULL); /* TODO: if we get an error here, we should fall back to normal scan */ msg = NULL; if (ret) { wpa_printf(MSG_DEBUG, "nl80211: Sched scan start failed: " "ret=%d (%s)", ret, strerror(-ret)); goto nla_put_failure; } wpa_printf(MSG_DEBUG, "nl80211: Sched scan requested (ret=%d) - " "scan interval %d msec", ret, interval); nla_put_failure: nlmsg_free(msg); return ret; } /** * wpa_driver_nl80211_stop_sched_scan - Stop a scheduled scan * @priv: Pointer to private driver data from wpa_driver_nl80211_init() * Returns: 0 on success, -1 on failure or if not supported */ static int wpa_driver_nl80211_stop_sched_scan(void *priv) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; int ret = 0; struct nl_msg *msg; #ifdef ANDROID if (!drv->capa.sched_scan_supported) return android_pno_stop(bss); #endif /* ANDROID */ msg = nlmsg_alloc(); if (!msg) return -1; nl80211_cmd(drv, msg, 0, NL80211_CMD_STOP_SCHED_SCAN); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); ret = send_and_recv_msgs(drv, msg, NULL, NULL); msg = NULL; if (ret) { wpa_printf(MSG_DEBUG, "nl80211: Sched scan stop failed: " "ret=%d (%s)", ret, strerror(-ret)); goto nla_put_failure; } wpa_printf(MSG_DEBUG, "nl80211: Sched scan stop sent (ret=%d)", ret); nla_put_failure: nlmsg_free(msg); return ret; } static const u8 * nl80211_get_ie(const u8 *ies, size_t ies_len, u8 ie) { const u8 *end, *pos; if (ies == NULL) return NULL; pos = ies; end = ies + ies_len; while (pos + 1 < end) { if (pos + 2 + pos[1] > end) break; if (pos[0] == ie) return pos; pos += 2 + pos[1]; } return NULL; } static int nl80211_scan_filtered(struct wpa_driver_nl80211_data *drv, const u8 *ie, size_t ie_len) { const u8 *ssid; size_t i; if (drv->filter_ssids == NULL) return 0; ssid = nl80211_get_ie(ie, ie_len, WLAN_EID_SSID); if (ssid == NULL) return 1; for (i = 0; i < drv->num_filter_ssids; i++) { if (ssid[1] == drv->filter_ssids[i].ssid_len && os_memcmp(ssid + 2, drv->filter_ssids[i].ssid, ssid[1]) == 0) return 0; } return 1; } static int bss_info_handler(struct nl_msg *msg, void *arg) { struct nlattr *tb[NL80211_ATTR_MAX + 1]; struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); struct nlattr *bss[NL80211_BSS_MAX + 1]; static struct nla_policy bss_policy[NL80211_BSS_MAX + 1] = { [NL80211_BSS_BSSID] = { .type = NLA_UNSPEC }, [NL80211_BSS_FREQUENCY] = { .type = NLA_U32 }, [NL80211_BSS_TSF] = { .type = NLA_U64 }, [NL80211_BSS_BEACON_INTERVAL] = { .type = NLA_U16 }, [NL80211_BSS_CAPABILITY] = { .type = NLA_U16 }, [NL80211_BSS_INFORMATION_ELEMENTS] = { .type = NLA_UNSPEC }, [NL80211_BSS_SIGNAL_MBM] = { .type = NLA_U32 }, [NL80211_BSS_SIGNAL_UNSPEC] = { .type = NLA_U8 }, [NL80211_BSS_STATUS] = { .type = NLA_U32 }, [NL80211_BSS_SEEN_MS_AGO] = { .type = NLA_U32 }, [NL80211_BSS_BEACON_IES] = { .type = NLA_UNSPEC }, }; struct nl80211_bss_info_arg *_arg = arg; struct wpa_scan_results *res = _arg->res; struct wpa_scan_res **tmp; struct wpa_scan_res *r; const u8 *ie, *beacon_ie; size_t ie_len, beacon_ie_len; u8 *pos; size_t i; nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL); if (!tb[NL80211_ATTR_BSS]) return NL_SKIP; if (nla_parse_nested(bss, NL80211_BSS_MAX, tb[NL80211_ATTR_BSS], bss_policy)) return NL_SKIP; if (bss[NL80211_BSS_STATUS]) { enum nl80211_bss_status status; status = nla_get_u32(bss[NL80211_BSS_STATUS]); if (status == NL80211_BSS_STATUS_ASSOCIATED && bss[NL80211_BSS_FREQUENCY]) { _arg->assoc_freq = nla_get_u32(bss[NL80211_BSS_FREQUENCY]); wpa_printf(MSG_DEBUG, "nl80211: Associated on %u MHz", _arg->assoc_freq); } if (status == NL80211_BSS_STATUS_ASSOCIATED && bss[NL80211_BSS_BSSID]) { os_memcpy(_arg->assoc_bssid, nla_data(bss[NL80211_BSS_BSSID]), ETH_ALEN); wpa_printf(MSG_DEBUG, "nl80211: Associated with " MACSTR, MAC2STR(_arg->assoc_bssid)); } } if (!res) return NL_SKIP; if (bss[NL80211_BSS_INFORMATION_ELEMENTS]) { ie = nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS]); ie_len = nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]); } else { ie = NULL; ie_len = 0; } if (bss[NL80211_BSS_BEACON_IES]) { beacon_ie = nla_data(bss[NL80211_BSS_BEACON_IES]); beacon_ie_len = nla_len(bss[NL80211_BSS_BEACON_IES]); } else { beacon_ie = NULL; beacon_ie_len = 0; } if (nl80211_scan_filtered(_arg->drv, ie ? ie : beacon_ie, ie ? ie_len : beacon_ie_len)) return NL_SKIP; r = os_zalloc(sizeof(*r) + ie_len + beacon_ie_len); if (r == NULL) return NL_SKIP; if (bss[NL80211_BSS_BSSID]) os_memcpy(r->bssid, nla_data(bss[NL80211_BSS_BSSID]), ETH_ALEN); if (bss[NL80211_BSS_FREQUENCY]) r->freq = nla_get_u32(bss[NL80211_BSS_FREQUENCY]); if (bss[NL80211_BSS_BEACON_INTERVAL]) r->beacon_int = nla_get_u16(bss[NL80211_BSS_BEACON_INTERVAL]); if (bss[NL80211_BSS_CAPABILITY]) r->caps = nla_get_u16(bss[NL80211_BSS_CAPABILITY]); r->flags |= WPA_SCAN_NOISE_INVALID; if (bss[NL80211_BSS_SIGNAL_MBM]) { r->level = nla_get_u32(bss[NL80211_BSS_SIGNAL_MBM]); r->level /= 100; /* mBm to dBm */ r->flags |= WPA_SCAN_LEVEL_DBM | WPA_SCAN_QUAL_INVALID; } else if (bss[NL80211_BSS_SIGNAL_UNSPEC]) { r->level = nla_get_u8(bss[NL80211_BSS_SIGNAL_UNSPEC]); r->flags |= WPA_SCAN_QUAL_INVALID; } else r->flags |= WPA_SCAN_LEVEL_INVALID | WPA_SCAN_QUAL_INVALID; if (bss[NL80211_BSS_TSF]) r->tsf = nla_get_u64(bss[NL80211_BSS_TSF]); if (bss[NL80211_BSS_SEEN_MS_AGO]) r->age = nla_get_u32(bss[NL80211_BSS_SEEN_MS_AGO]); r->ie_len = ie_len; pos = (u8 *) (r + 1); if (ie) { os_memcpy(pos, ie, ie_len); pos += ie_len; } r->beacon_ie_len = beacon_ie_len; if (beacon_ie) os_memcpy(pos, beacon_ie, beacon_ie_len); if (bss[NL80211_BSS_STATUS]) { enum nl80211_bss_status status; status = nla_get_u32(bss[NL80211_BSS_STATUS]); switch (status) { case NL80211_BSS_STATUS_AUTHENTICATED: r->flags |= WPA_SCAN_AUTHENTICATED; break; case NL80211_BSS_STATUS_ASSOCIATED: r->flags |= WPA_SCAN_ASSOCIATED; break; default: break; } } /* * cfg80211 maintains separate BSS table entries for APs if the same * BSSID,SSID pair is seen on multiple channels. wpa_supplicant does * not use frequency as a separate key in the BSS table, so filter out * duplicated entries. Prefer associated BSS entry in such a case in * order to get the correct frequency into the BSS table. Similarly, * prefer newer entries over older. */ for (i = 0; i < res->num; i++) { const u8 *s1, *s2; if (os_memcmp(res->res[i]->bssid, r->bssid, ETH_ALEN) != 0) continue; s1 = nl80211_get_ie((u8 *) (res->res[i] + 1), res->res[i]->ie_len, WLAN_EID_SSID); s2 = nl80211_get_ie((u8 *) (r + 1), r->ie_len, WLAN_EID_SSID); if (s1 == NULL || s2 == NULL || s1[1] != s2[1] || os_memcmp(s1, s2, 2 + s1[1]) != 0) continue; /* Same BSSID,SSID was already included in scan results */ wpa_printf(MSG_DEBUG, "nl80211: Remove duplicated scan result " "for " MACSTR, MAC2STR(r->bssid)); if (((r->flags & WPA_SCAN_ASSOCIATED) && !(res->res[i]->flags & WPA_SCAN_ASSOCIATED)) || r->age < res->res[i]->age) { os_free(res->res[i]); res->res[i] = r; } else os_free(r); return NL_SKIP; } tmp = os_realloc_array(res->res, res->num + 1, sizeof(struct wpa_scan_res *)); if (tmp == NULL) { os_free(r); return NL_SKIP; } tmp[res->num++] = r; res->res = tmp; return NL_SKIP; } static void clear_state_mismatch(struct wpa_driver_nl80211_data *drv, const u8 *addr) { if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) { wpa_printf(MSG_DEBUG, "nl80211: Clear possible state " "mismatch (" MACSTR ")", MAC2STR(addr)); wpa_driver_nl80211_mlme(drv, addr, NL80211_CMD_DEAUTHENTICATE, WLAN_REASON_PREV_AUTH_NOT_VALID, 1); } } static void wpa_driver_nl80211_check_bss_status( struct wpa_driver_nl80211_data *drv, struct wpa_scan_results *res) { size_t i; for (i = 0; i < res->num; i++) { struct wpa_scan_res *r = res->res[i]; if (r->flags & WPA_SCAN_AUTHENTICATED) { wpa_printf(MSG_DEBUG, "nl80211: Scan results " "indicates BSS status with " MACSTR " as authenticated", MAC2STR(r->bssid)); if (is_sta_interface(drv->nlmode) && os_memcmp(r->bssid, drv->bssid, ETH_ALEN) != 0 && os_memcmp(r->bssid, drv->auth_bssid, ETH_ALEN) != 0) { wpa_printf(MSG_DEBUG, "nl80211: Unknown BSSID" " in local state (auth=" MACSTR " assoc=" MACSTR ")", MAC2STR(drv->auth_bssid), MAC2STR(drv->bssid)); clear_state_mismatch(drv, r->bssid); } } if (r->flags & WPA_SCAN_ASSOCIATED) { wpa_printf(MSG_DEBUG, "nl80211: Scan results " "indicate BSS status with " MACSTR " as associated", MAC2STR(r->bssid)); if (is_sta_interface(drv->nlmode) && !drv->associated) { wpa_printf(MSG_DEBUG, "nl80211: Local state " "(not associated) does not match " "with BSS state"); clear_state_mismatch(drv, r->bssid); } else if (is_sta_interface(drv->nlmode) && os_memcmp(drv->bssid, r->bssid, ETH_ALEN) != 0) { wpa_printf(MSG_DEBUG, "nl80211: Local state " "(associated with " MACSTR ") does " "not match with BSS state", MAC2STR(drv->bssid)); clear_state_mismatch(drv, r->bssid); clear_state_mismatch(drv, drv->bssid); } } } } static struct wpa_scan_results * nl80211_get_scan_results(struct wpa_driver_nl80211_data *drv) { struct nl_msg *msg; struct wpa_scan_results *res; int ret; struct nl80211_bss_info_arg arg; res = os_zalloc(sizeof(*res)); if (res == NULL) return NULL; msg = nlmsg_alloc(); if (!msg) goto nla_put_failure; nl80211_cmd(drv, msg, NLM_F_DUMP, NL80211_CMD_GET_SCAN); if (nl80211_set_iface_id(msg, drv->first_bss) < 0) goto nla_put_failure; arg.drv = drv; arg.res = res; ret = send_and_recv_msgs(drv, msg, bss_info_handler, &arg); msg = NULL; if (ret == 0) { wpa_printf(MSG_DEBUG, "nl80211: Received scan results (%lu " "BSSes)", (unsigned long) res->num); nl80211_get_noise_for_scan_results(drv, res); return res; } wpa_printf(MSG_DEBUG, "nl80211: Scan result fetch failed: ret=%d " "(%s)", ret, strerror(-ret)); nla_put_failure: nlmsg_free(msg); wpa_scan_results_free(res); return NULL; } /** * wpa_driver_nl80211_get_scan_results - Fetch the latest scan results * @priv: Pointer to private wext data from wpa_driver_nl80211_init() * Returns: Scan results on success, -1 on failure */ static struct wpa_scan_results * wpa_driver_nl80211_get_scan_results(void *priv) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; struct wpa_scan_results *res; res = nl80211_get_scan_results(drv); if (res) wpa_driver_nl80211_check_bss_status(drv, res); return res; } static void nl80211_dump_scan(struct wpa_driver_nl80211_data *drv) { struct wpa_scan_results *res; size_t i; res = nl80211_get_scan_results(drv); if (res == NULL) { wpa_printf(MSG_DEBUG, "nl80211: Failed to get scan results"); return; } wpa_printf(MSG_DEBUG, "nl80211: Scan result dump"); for (i = 0; i < res->num; i++) { struct wpa_scan_res *r = res->res[i]; wpa_printf(MSG_DEBUG, "nl80211: %d/%d " MACSTR "%s%s", (int) i, (int) res->num, MAC2STR(r->bssid), r->flags & WPA_SCAN_AUTHENTICATED ? " [auth]" : "", r->flags & WPA_SCAN_ASSOCIATED ? " [assoc]" : ""); } wpa_scan_results_free(res); } static u32 wpa_alg_to_cipher_suite(enum wpa_alg alg, size_t key_len) { switch (alg) { case WPA_ALG_WEP: if (key_len == 5) return WLAN_CIPHER_SUITE_WEP40; return WLAN_CIPHER_SUITE_WEP104; case WPA_ALG_TKIP: return WLAN_CIPHER_SUITE_TKIP; case WPA_ALG_CCMP: return WLAN_CIPHER_SUITE_CCMP; case WPA_ALG_GCMP: return WLAN_CIPHER_SUITE_GCMP; case WPA_ALG_CCMP_256: return WLAN_CIPHER_SUITE_CCMP_256; case WPA_ALG_GCMP_256: return WLAN_CIPHER_SUITE_GCMP_256; case WPA_ALG_IGTK: return WLAN_CIPHER_SUITE_AES_CMAC; case WPA_ALG_BIP_GMAC_128: return WLAN_CIPHER_SUITE_BIP_GMAC_128; case WPA_ALG_BIP_GMAC_256: return WLAN_CIPHER_SUITE_BIP_GMAC_256; case WPA_ALG_BIP_CMAC_256: return WLAN_CIPHER_SUITE_BIP_CMAC_256; case WPA_ALG_SMS4: return WLAN_CIPHER_SUITE_SMS4; case WPA_ALG_KRK: return WLAN_CIPHER_SUITE_KRK; case WPA_ALG_NONE: case WPA_ALG_PMK: wpa_printf(MSG_ERROR, "nl80211: Unexpected encryption algorithm %d", alg); return 0; } wpa_printf(MSG_ERROR, "nl80211: Unsupported encryption algorithm %d", alg); return 0; } static u32 wpa_cipher_to_cipher_suite(unsigned int cipher) { switch (cipher) { case WPA_CIPHER_CCMP_256: return WLAN_CIPHER_SUITE_CCMP_256; case WPA_CIPHER_GCMP_256: return WLAN_CIPHER_SUITE_GCMP_256; case WPA_CIPHER_CCMP: return WLAN_CIPHER_SUITE_CCMP; case WPA_CIPHER_GCMP: return WLAN_CIPHER_SUITE_GCMP; case WPA_CIPHER_TKIP: return WLAN_CIPHER_SUITE_TKIP; case WPA_CIPHER_WEP104: return WLAN_CIPHER_SUITE_WEP104; case WPA_CIPHER_WEP40: return WLAN_CIPHER_SUITE_WEP40; } return 0; } static int wpa_cipher_to_cipher_suites(unsigned int ciphers, u32 suites[], int max_suites) { int num_suites = 0; if (num_suites < max_suites && ciphers & WPA_CIPHER_CCMP_256) suites[num_suites++] = WLAN_CIPHER_SUITE_CCMP_256; if (num_suites < max_suites && ciphers & WPA_CIPHER_GCMP_256) suites[num_suites++] = WLAN_CIPHER_SUITE_GCMP_256; if (num_suites < max_suites && ciphers & WPA_CIPHER_CCMP) suites[num_suites++] = WLAN_CIPHER_SUITE_CCMP; if (num_suites < max_suites && ciphers & WPA_CIPHER_GCMP) suites[num_suites++] = WLAN_CIPHER_SUITE_GCMP; if (num_suites < max_suites && ciphers & WPA_CIPHER_TKIP) suites[num_suites++] = WLAN_CIPHER_SUITE_TKIP; if (num_suites < max_suites && ciphers & WPA_CIPHER_WEP104) suites[num_suites++] = WLAN_CIPHER_SUITE_WEP104; if (num_suites < max_suites && ciphers & WPA_CIPHER_WEP40) suites[num_suites++] = WLAN_CIPHER_SUITE_WEP40; return num_suites; } static int wpa_driver_nl80211_set_key(const char *ifname, struct i802_bss *bss, enum wpa_alg alg, const u8 *addr, int key_idx, int set_tx, const u8 *seq, size_t seq_len, const u8 *key, size_t key_len) { struct wpa_driver_nl80211_data *drv = bss->drv; int ifindex; struct nl_msg *msg; int ret; int tdls = 0; /* Ignore for P2P Device */ if (drv->nlmode == NL80211_IFTYPE_P2P_DEVICE) return 0; ifindex = if_nametoindex(ifname); wpa_printf(MSG_DEBUG, "%s: ifindex=%d (%s) alg=%d addr=%p key_idx=%d " "set_tx=%d seq_len=%lu key_len=%lu", __func__, ifindex, ifname, alg, addr, key_idx, set_tx, (unsigned long) seq_len, (unsigned long) key_len); #ifdef CONFIG_TDLS if (key_idx == -1) { key_idx = 0; tdls = 1; } #endif /* CONFIG_TDLS */ msg = nlmsg_alloc(); if (!msg) return -ENOMEM; if (alg == WPA_ALG_NONE) { nl80211_cmd(drv, msg, 0, NL80211_CMD_DEL_KEY); } else { nl80211_cmd(drv, msg, 0, NL80211_CMD_NEW_KEY); NLA_PUT(msg, NL80211_ATTR_KEY_DATA, key_len, key); NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, wpa_alg_to_cipher_suite(alg, key_len)); } if (seq && seq_len) NLA_PUT(msg, NL80211_ATTR_KEY_SEQ, seq_len, seq); if (addr && !is_broadcast_ether_addr(addr)) { wpa_printf(MSG_DEBUG, " addr=" MACSTR, MAC2STR(addr)); NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); if (alg != WPA_ALG_WEP && key_idx && !set_tx) { wpa_printf(MSG_DEBUG, " RSN IBSS RX GTK"); NLA_PUT_U32(msg, NL80211_ATTR_KEY_TYPE, NL80211_KEYTYPE_GROUP); } } else if (addr && is_broadcast_ether_addr(addr)) { struct nlattr *types; wpa_printf(MSG_DEBUG, " broadcast key"); types = nla_nest_start(msg, NL80211_ATTR_KEY_DEFAULT_TYPES); if (!types) goto nla_put_failure; NLA_PUT_FLAG(msg, NL80211_KEY_DEFAULT_TYPE_MULTICAST); nla_nest_end(msg, types); } NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_idx); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex); ret = send_and_recv_msgs(drv, msg, NULL, NULL); if ((ret == -ENOENT || ret == -ENOLINK) && alg == WPA_ALG_NONE) ret = 0; if (ret) wpa_printf(MSG_DEBUG, "nl80211: set_key failed; err=%d %s)", ret, strerror(-ret)); /* * If we failed or don't need to set the default TX key (below), * we're done here. */ if (ret || !set_tx || alg == WPA_ALG_NONE || tdls) return ret; if (is_ap_interface(drv->nlmode) && addr && !is_broadcast_ether_addr(addr)) return ret; msg = nlmsg_alloc(); if (!msg) return -ENOMEM; nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_KEY); NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_idx); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex); if (alg == WPA_ALG_IGTK) NLA_PUT_FLAG(msg, NL80211_ATTR_KEY_DEFAULT_MGMT); else NLA_PUT_FLAG(msg, NL80211_ATTR_KEY_DEFAULT); if (addr && is_broadcast_ether_addr(addr)) { struct nlattr *types; types = nla_nest_start(msg, NL80211_ATTR_KEY_DEFAULT_TYPES); if (!types) goto nla_put_failure; NLA_PUT_FLAG(msg, NL80211_KEY_DEFAULT_TYPE_MULTICAST); nla_nest_end(msg, types); } else if (addr) { struct nlattr *types; types = nla_nest_start(msg, NL80211_ATTR_KEY_DEFAULT_TYPES); if (!types) goto nla_put_failure; NLA_PUT_FLAG(msg, NL80211_KEY_DEFAULT_TYPE_UNICAST); nla_nest_end(msg, types); } ret = send_and_recv_msgs(drv, msg, NULL, NULL); if (ret == -ENOENT) ret = 0; if (ret) wpa_printf(MSG_DEBUG, "nl80211: set_key default failed; " "err=%d %s)", ret, strerror(-ret)); return ret; nla_put_failure: nlmsg_free(msg); return -ENOBUFS; } static int nl_add_key(struct nl_msg *msg, enum wpa_alg alg, int key_idx, int defkey, const u8 *seq, size_t seq_len, const u8 *key, size_t key_len) { struct nlattr *key_attr = nla_nest_start(msg, NL80211_ATTR_KEY); if (!key_attr) return -1; if (defkey && alg == WPA_ALG_IGTK) NLA_PUT_FLAG(msg, NL80211_KEY_DEFAULT_MGMT); else if (defkey) NLA_PUT_FLAG(msg, NL80211_KEY_DEFAULT); NLA_PUT_U8(msg, NL80211_KEY_IDX, key_idx); NLA_PUT_U32(msg, NL80211_KEY_CIPHER, wpa_alg_to_cipher_suite(alg, key_len)); if (seq && seq_len) NLA_PUT(msg, NL80211_KEY_SEQ, seq_len, seq); NLA_PUT(msg, NL80211_KEY_DATA, key_len, key); nla_nest_end(msg, key_attr); return 0; nla_put_failure: return -1; } static int nl80211_set_conn_keys(struct wpa_driver_associate_params *params, struct nl_msg *msg) { int i, privacy = 0; struct nlattr *nl_keys, *nl_key; for (i = 0; i < 4; i++) { if (!params->wep_key[i]) continue; privacy = 1; break; } if (params->wps == WPS_MODE_PRIVACY) privacy = 1; if (params->pairwise_suite && params->pairwise_suite != WPA_CIPHER_NONE) privacy = 1; if (!privacy) return 0; NLA_PUT_FLAG(msg, NL80211_ATTR_PRIVACY); nl_keys = nla_nest_start(msg, NL80211_ATTR_KEYS); if (!nl_keys) goto nla_put_failure; for (i = 0; i < 4; i++) { if (!params->wep_key[i]) continue; nl_key = nla_nest_start(msg, i); if (!nl_key) goto nla_put_failure; NLA_PUT(msg, NL80211_KEY_DATA, params->wep_key_len[i], params->wep_key[i]); if (params->wep_key_len[i] == 5) NLA_PUT_U32(msg, NL80211_KEY_CIPHER, WLAN_CIPHER_SUITE_WEP40); else NLA_PUT_U32(msg, NL80211_KEY_CIPHER, WLAN_CIPHER_SUITE_WEP104); NLA_PUT_U8(msg, NL80211_KEY_IDX, i); if (i == params->wep_tx_keyidx) NLA_PUT_FLAG(msg, NL80211_KEY_DEFAULT); nla_nest_end(msg, nl_key); } nla_nest_end(msg, nl_keys); return 0; nla_put_failure: return -ENOBUFS; } static int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv, const u8 *addr, int cmd, u16 reason_code, int local_state_change) { int ret = -1; struct nl_msg *msg; msg = nlmsg_alloc(); if (!msg) return -1; nl80211_cmd(drv, msg, 0, cmd); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); NLA_PUT_U16(msg, NL80211_ATTR_REASON_CODE, reason_code); if (addr) NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); if (local_state_change) NLA_PUT_FLAG(msg, NL80211_ATTR_LOCAL_STATE_CHANGE); ret = send_and_recv_msgs(drv, msg, NULL, NULL); msg = NULL; if (ret) { wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: MLME command failed: reason=%u ret=%d (%s)", reason_code, ret, strerror(-ret)); goto nla_put_failure; } ret = 0; nla_put_failure: nlmsg_free(msg); return ret; } static int wpa_driver_nl80211_disconnect(struct wpa_driver_nl80211_data *drv, int reason_code) { int ret; wpa_printf(MSG_DEBUG, "%s(reason_code=%d)", __func__, reason_code); nl80211_mark_disconnected(drv); /* Disconnect command doesn't need BSSID - it uses cached value */ ret = wpa_driver_nl80211_mlme(drv, NULL, NL80211_CMD_DISCONNECT, reason_code, 0); /* * For locally generated disconnect, supplicant already generates a * DEAUTH event, so ignore the event from NL80211. */ drv->ignore_next_local_disconnect = ret == 0; return ret; } static int wpa_driver_nl80211_deauthenticate(struct i802_bss *bss, const u8 *addr, int reason_code) { struct wpa_driver_nl80211_data *drv = bss->drv; if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) return wpa_driver_nl80211_disconnect(drv, reason_code); wpa_printf(MSG_DEBUG, "%s(addr=" MACSTR " reason_code=%d)", __func__, MAC2STR(addr), reason_code); nl80211_mark_disconnected(drv); if (drv->nlmode == NL80211_IFTYPE_ADHOC) return nl80211_leave_ibss(drv); return wpa_driver_nl80211_mlme(drv, addr, NL80211_CMD_DEAUTHENTICATE, reason_code, 0); } static void nl80211_copy_auth_params(struct wpa_driver_nl80211_data *drv, struct wpa_driver_auth_params *params) { int i; drv->auth_freq = params->freq; drv->auth_alg = params->auth_alg; drv->auth_wep_tx_keyidx = params->wep_tx_keyidx; drv->auth_local_state_change = params->local_state_change; drv->auth_p2p = params->p2p; if (params->bssid) os_memcpy(drv->auth_bssid_, params->bssid, ETH_ALEN); else os_memset(drv->auth_bssid_, 0, ETH_ALEN); if (params->ssid) { os_memcpy(drv->auth_ssid, params->ssid, params->ssid_len); drv->auth_ssid_len = params->ssid_len; } else drv->auth_ssid_len = 0; os_free(drv->auth_ie); drv->auth_ie = NULL; drv->auth_ie_len = 0; if (params->ie) { drv->auth_ie = os_malloc(params->ie_len); if (drv->auth_ie) { os_memcpy(drv->auth_ie, params->ie, params->ie_len); drv->auth_ie_len = params->ie_len; } } for (i = 0; i < 4; i++) { if (params->wep_key[i] && params->wep_key_len[i] && params->wep_key_len[i] <= 16) { os_memcpy(drv->auth_wep_key[i], params->wep_key[i], params->wep_key_len[i]); drv->auth_wep_key_len[i] = params->wep_key_len[i]; } else drv->auth_wep_key_len[i] = 0; } } static int wpa_driver_nl80211_authenticate( struct i802_bss *bss, struct wpa_driver_auth_params *params) { struct wpa_driver_nl80211_data *drv = bss->drv; int ret = -1, i; struct nl_msg *msg; enum nl80211_auth_type type; enum nl80211_iftype nlmode; int count = 0; int is_retry; is_retry = drv->retry_auth; drv->retry_auth = 0; nl80211_mark_disconnected(drv); os_memset(drv->auth_bssid, 0, ETH_ALEN); if (params->bssid) os_memcpy(drv->auth_attempt_bssid, params->bssid, ETH_ALEN); else os_memset(drv->auth_attempt_bssid, 0, ETH_ALEN); /* FIX: IBSS mode */ nlmode = params->p2p ? NL80211_IFTYPE_P2P_CLIENT : NL80211_IFTYPE_STATION; if (drv->nlmode != nlmode && wpa_driver_nl80211_set_mode(bss, nlmode) < 0) return -1; retry: msg = nlmsg_alloc(); if (!msg) return -1; wpa_printf(MSG_DEBUG, "nl80211: Authenticate (ifindex=%d)", drv->ifindex); nl80211_cmd(drv, msg, 0, NL80211_CMD_AUTHENTICATE); for (i = 0; i < 4; i++) { if (!params->wep_key[i]) continue; wpa_driver_nl80211_set_key(bss->ifname, bss, WPA_ALG_WEP, NULL, i, i == params->wep_tx_keyidx, NULL, 0, params->wep_key[i], params->wep_key_len[i]); if (params->wep_tx_keyidx != i) continue; if (nl_add_key(msg, WPA_ALG_WEP, i, 1, NULL, 0, params->wep_key[i], params->wep_key_len[i])) { nlmsg_free(msg); return -1; } } NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); if (params->bssid) { wpa_printf(MSG_DEBUG, " * bssid=" MACSTR, MAC2STR(params->bssid)); NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid); } if (params->freq) { wpa_printf(MSG_DEBUG, " * freq=%d", params->freq); NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, params->freq); } if (params->ssid) { wpa_hexdump_ascii(MSG_DEBUG, " * SSID", params->ssid, params->ssid_len); NLA_PUT(msg, NL80211_ATTR_SSID, params->ssid_len, params->ssid); } wpa_hexdump(MSG_DEBUG, " * IEs", params->ie, params->ie_len); if (params->ie) NLA_PUT(msg, NL80211_ATTR_IE, params->ie_len, params->ie); if (params->sae_data) { wpa_hexdump(MSG_DEBUG, " * SAE data", params->sae_data, params->sae_data_len); NLA_PUT(msg, NL80211_ATTR_SAE_DATA, params->sae_data_len, params->sae_data); } if (params->auth_alg & WPA_AUTH_ALG_OPEN) type = NL80211_AUTHTYPE_OPEN_SYSTEM; else if (params->auth_alg & WPA_AUTH_ALG_SHARED) type = NL80211_AUTHTYPE_SHARED_KEY; else if (params->auth_alg & WPA_AUTH_ALG_LEAP) type = NL80211_AUTHTYPE_NETWORK_EAP; else if (params->auth_alg & WPA_AUTH_ALG_FT) type = NL80211_AUTHTYPE_FT; else if (params->auth_alg & WPA_AUTH_ALG_SAE) type = NL80211_AUTHTYPE_SAE; else goto nla_put_failure; wpa_printf(MSG_DEBUG, " * Auth Type %d", type); NLA_PUT_U32(msg, NL80211_ATTR_AUTH_TYPE, type); if (params->local_state_change) { wpa_printf(MSG_DEBUG, " * Local state change only"); NLA_PUT_FLAG(msg, NL80211_ATTR_LOCAL_STATE_CHANGE); } ret = send_and_recv_msgs(drv, msg, NULL, NULL); msg = NULL; if (ret) { wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: MLME command failed (auth): ret=%d (%s)", ret, strerror(-ret)); count++; if (ret == -EALREADY && count == 1 && params->bssid && !params->local_state_change) { /* * mac80211 does not currently accept new * authentication if we are already authenticated. As a * workaround, force deauthentication and try again. */ wpa_printf(MSG_DEBUG, "nl80211: Retry authentication " "after forced deauthentication"); wpa_driver_nl80211_deauthenticate( bss, params->bssid, WLAN_REASON_PREV_AUTH_NOT_VALID); nlmsg_free(msg); goto retry; } if (ret == -ENOENT && params->freq && !is_retry) { /* * cfg80211 has likely expired the BSS entry even * though it was previously available in our internal * BSS table. To recover quickly, start a single * channel scan on the specified channel. */ struct wpa_driver_scan_params scan; int freqs[2]; os_memset(&scan, 0, sizeof(scan)); scan.num_ssids = 1; if (params->ssid) { scan.ssids[0].ssid = params->ssid; scan.ssids[0].ssid_len = params->ssid_len; } freqs[0] = params->freq; freqs[1] = 0; scan.freqs = freqs; wpa_printf(MSG_DEBUG, "nl80211: Trigger single " "channel scan to refresh cfg80211 BSS " "entry"); ret = wpa_driver_nl80211_scan(bss, &scan); if (ret == 0) { nl80211_copy_auth_params(drv, params); drv->scan_for_auth = 1; } } else if (is_retry) { /* * Need to indicate this with an event since the return * value from the retry is not delivered to core code. */ union wpa_event_data event; wpa_printf(MSG_DEBUG, "nl80211: Authentication retry " "failed"); os_memset(&event, 0, sizeof(event)); os_memcpy(event.timeout_event.addr, drv->auth_bssid_, ETH_ALEN); wpa_supplicant_event(drv->ctx, EVENT_AUTH_TIMED_OUT, &event); } goto nla_put_failure; } ret = 0; wpa_printf(MSG_DEBUG, "nl80211: Authentication request send " "successfully"); nla_put_failure: nlmsg_free(msg); return ret; } static int wpa_driver_nl80211_authenticate_retry( struct wpa_driver_nl80211_data *drv) { struct wpa_driver_auth_params params; struct i802_bss *bss = drv->first_bss; int i; wpa_printf(MSG_DEBUG, "nl80211: Try to authenticate again"); os_memset(¶ms, 0, sizeof(params)); params.freq = drv->auth_freq; params.auth_alg = drv->auth_alg; params.wep_tx_keyidx = drv->auth_wep_tx_keyidx; params.local_state_change = drv->auth_local_state_change; params.p2p = drv->auth_p2p; if (!is_zero_ether_addr(drv->auth_bssid_)) params.bssid = drv->auth_bssid_; if (drv->auth_ssid_len) { params.ssid = drv->auth_ssid; params.ssid_len = drv->auth_ssid_len; } params.ie = drv->auth_ie; params.ie_len = drv->auth_ie_len; for (i = 0; i < 4; i++) { if (drv->auth_wep_key_len[i]) { params.wep_key[i] = drv->auth_wep_key[i]; params.wep_key_len[i] = drv->auth_wep_key_len[i]; } } drv->retry_auth = 1; return wpa_driver_nl80211_authenticate(bss, ¶ms); } struct phy_info_arg { u16 *num_modes; struct hostapd_hw_modes *modes; int last_mode, last_chan_idx; }; static void phy_info_ht_capa(struct hostapd_hw_modes *mode, struct nlattr *capa, struct nlattr *ampdu_factor, struct nlattr *ampdu_density, struct nlattr *mcs_set) { if (capa) mode->ht_capab = nla_get_u16(capa); if (ampdu_factor) mode->a_mpdu_params |= nla_get_u8(ampdu_factor) & 0x03; if (ampdu_density) mode->a_mpdu_params |= nla_get_u8(ampdu_density) << 2; if (mcs_set && nla_len(mcs_set) >= 16) { u8 *mcs; mcs = nla_data(mcs_set); os_memcpy(mode->mcs_set, mcs, 16); } } static void phy_info_vht_capa(struct hostapd_hw_modes *mode, struct nlattr *capa, struct nlattr *mcs_set) { if (capa) mode->vht_capab = nla_get_u32(capa); if (mcs_set && nla_len(mcs_set) >= 8) { u8 *mcs; mcs = nla_data(mcs_set); os_memcpy(mode->vht_mcs_set, mcs, 8); } } static void phy_info_freq(struct hostapd_hw_modes *mode, struct hostapd_channel_data *chan, struct nlattr *tb_freq[]) { u8 channel; chan->freq = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_FREQ]); chan->flag = 0; if (ieee80211_freq_to_chan(chan->freq, &channel) != NUM_HOSTAPD_MODES) chan->chan = channel; if (tb_freq[NL80211_FREQUENCY_ATTR_DISABLED]) chan->flag |= HOSTAPD_CHAN_DISABLED; if (tb_freq[NL80211_FREQUENCY_ATTR_NO_IR]) chan->flag |= HOSTAPD_CHAN_PASSIVE_SCAN | HOSTAPD_CHAN_NO_IBSS; if (tb_freq[NL80211_FREQUENCY_ATTR_RADAR]) chan->flag |= HOSTAPD_CHAN_RADAR; if (tb_freq[NL80211_FREQUENCY_ATTR_DFS_STATE]) { enum nl80211_dfs_state state = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_DFS_STATE]); switch (state) { case NL80211_DFS_USABLE: chan->flag |= HOSTAPD_CHAN_DFS_USABLE; break; case NL80211_DFS_AVAILABLE: chan->flag |= HOSTAPD_CHAN_DFS_AVAILABLE; break; case NL80211_DFS_UNAVAILABLE: chan->flag |= HOSTAPD_CHAN_DFS_UNAVAILABLE; break; } } } static int phy_info_freqs(struct phy_info_arg *phy_info, struct hostapd_hw_modes *mode, struct nlattr *tb) { static struct nla_policy freq_policy[NL80211_FREQUENCY_ATTR_MAX + 1] = { [NL80211_FREQUENCY_ATTR_FREQ] = { .type = NLA_U32 }, [NL80211_FREQUENCY_ATTR_DISABLED] = { .type = NLA_FLAG }, [NL80211_FREQUENCY_ATTR_NO_IR] = { .type = NLA_FLAG }, [NL80211_FREQUENCY_ATTR_RADAR] = { .type = NLA_FLAG }, [NL80211_FREQUENCY_ATTR_MAX_TX_POWER] = { .type = NLA_U32 }, [NL80211_FREQUENCY_ATTR_DFS_STATE] = { .type = NLA_U32 }, }; int new_channels = 0; struct hostapd_channel_data *channel; struct nlattr *tb_freq[NL80211_FREQUENCY_ATTR_MAX + 1]; struct nlattr *nl_freq; int rem_freq, idx; if (tb == NULL) return NL_OK; nla_for_each_nested(nl_freq, tb, rem_freq) { nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX, nla_data(nl_freq), nla_len(nl_freq), freq_policy); if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ]) continue; new_channels++; } channel = os_realloc_array(mode->channels, mode->num_channels + new_channels, sizeof(struct hostapd_channel_data)); if (!channel) return NL_SKIP; mode->channels = channel; mode->num_channels += new_channels; idx = phy_info->last_chan_idx; nla_for_each_nested(nl_freq, tb, rem_freq) { nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX, nla_data(nl_freq), nla_len(nl_freq), freq_policy); if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ]) continue; phy_info_freq(mode, &mode->channels[idx], tb_freq); idx++; } phy_info->last_chan_idx = idx; return NL_OK; } static int phy_info_rates(struct hostapd_hw_modes *mode, struct nlattr *tb) { static struct nla_policy rate_policy[NL80211_BITRATE_ATTR_MAX + 1] = { [NL80211_BITRATE_ATTR_RATE] = { .type = NLA_U32 }, [NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE] = { .type = NLA_FLAG }, }; struct nlattr *tb_rate[NL80211_BITRATE_ATTR_MAX + 1]; struct nlattr *nl_rate; int rem_rate, idx; if (tb == NULL) return NL_OK; nla_for_each_nested(nl_rate, tb, rem_rate) { nla_parse(tb_rate, NL80211_BITRATE_ATTR_MAX, nla_data(nl_rate), nla_len(nl_rate), rate_policy); if (!tb_rate[NL80211_BITRATE_ATTR_RATE]) continue; mode->num_rates++; } mode->rates = os_calloc(mode->num_rates, sizeof(int)); if (!mode->rates) return NL_SKIP; idx = 0; nla_for_each_nested(nl_rate, tb, rem_rate) { nla_parse(tb_rate, NL80211_BITRATE_ATTR_MAX, nla_data(nl_rate), nla_len(nl_rate), rate_policy); if (!tb_rate[NL80211_BITRATE_ATTR_RATE]) continue; mode->rates[idx] = nla_get_u32( tb_rate[NL80211_BITRATE_ATTR_RATE]); idx++; } return NL_OK; } static int phy_info_band(struct phy_info_arg *phy_info, struct nlattr *nl_band) { struct nlattr *tb_band[NL80211_BAND_ATTR_MAX + 1]; struct hostapd_hw_modes *mode; int ret; if (phy_info->last_mode != nl_band->nla_type) { mode = os_realloc_array(phy_info->modes, *phy_info->num_modes + 1, sizeof(*mode)); if (!mode) return NL_SKIP; phy_info->modes = mode; mode = &phy_info->modes[*(phy_info->num_modes)]; os_memset(mode, 0, sizeof(*mode)); mode->mode = NUM_HOSTAPD_MODES; mode->flags = HOSTAPD_MODE_FLAG_HT_INFO_KNOWN | HOSTAPD_MODE_FLAG_VHT_INFO_KNOWN; /* * Unsupported VHT MCS stream is defined as value 3, so the VHT * MCS RX/TX map must be initialized with 0xffff to mark all 8 * possible streams as unsupported. This will be overridden if * driver advertises VHT support. */ mode->vht_mcs_set[0] = 0xff; mode->vht_mcs_set[1] = 0xff; mode->vht_mcs_set[4] = 0xff; mode->vht_mcs_set[5] = 0xff; *(phy_info->num_modes) += 1; phy_info->last_mode = nl_band->nla_type; phy_info->last_chan_idx = 0; } else mode = &phy_info->modes[*(phy_info->num_modes) - 1]; nla_parse(tb_band, NL80211_BAND_ATTR_MAX, nla_data(nl_band), nla_len(nl_band), NULL); phy_info_ht_capa(mode, tb_band[NL80211_BAND_ATTR_HT_CAPA], tb_band[NL80211_BAND_ATTR_HT_AMPDU_FACTOR], tb_band[NL80211_BAND_ATTR_HT_AMPDU_DENSITY], tb_band[NL80211_BAND_ATTR_HT_MCS_SET]); phy_info_vht_capa(mode, tb_band[NL80211_BAND_ATTR_VHT_CAPA], tb_band[NL80211_BAND_ATTR_VHT_MCS_SET]); ret = phy_info_freqs(phy_info, mode, tb_band[NL80211_BAND_ATTR_FREQS]); if (ret != NL_OK) return ret; ret = phy_info_rates(mode, tb_band[NL80211_BAND_ATTR_RATES]); if (ret != NL_OK) return ret; return NL_OK; } static int phy_info_handler(struct nl_msg *msg, void *arg) { struct nlattr *tb_msg[NL80211_ATTR_MAX + 1]; struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); struct phy_info_arg *phy_info = arg; struct nlattr *nl_band; int rem_band; nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL); if (!tb_msg[NL80211_ATTR_WIPHY_BANDS]) return NL_SKIP; nla_for_each_nested(nl_band, tb_msg[NL80211_ATTR_WIPHY_BANDS], rem_band) { int res = phy_info_band(phy_info, nl_band); if (res != NL_OK) return res; } return NL_SKIP; } static struct hostapd_hw_modes * wpa_driver_nl80211_postprocess_modes(struct hostapd_hw_modes *modes, u16 *num_modes) { u16 m; struct hostapd_hw_modes *mode11g = NULL, *nmodes, *mode; int i, mode11g_idx = -1; /* heuristic to set up modes */ for (m = 0; m < *num_modes; m++) { if (!modes[m].num_channels) continue; if (modes[m].channels[0].freq < 4000) { modes[m].mode = HOSTAPD_MODE_IEEE80211B; for (i = 0; i < modes[m].num_rates; i++) { if (modes[m].rates[i] > 200) { modes[m].mode = HOSTAPD_MODE_IEEE80211G; break; } } } else if (modes[m].channels[0].freq > 50000) modes[m].mode = HOSTAPD_MODE_IEEE80211AD; else modes[m].mode = HOSTAPD_MODE_IEEE80211A; } /* If only 802.11g mode is included, use it to construct matching * 802.11b mode data. */ for (m = 0; m < *num_modes; m++) { if (modes[m].mode == HOSTAPD_MODE_IEEE80211B) return modes; /* 802.11b already included */ if (modes[m].mode == HOSTAPD_MODE_IEEE80211G) mode11g_idx = m; } if (mode11g_idx < 0) return modes; /* 2.4 GHz band not supported at all */ nmodes = os_realloc_array(modes, *num_modes + 1, sizeof(*nmodes)); if (nmodes == NULL) return modes; /* Could not add 802.11b mode */ mode = &nmodes[*num_modes]; os_memset(mode, 0, sizeof(*mode)); (*num_modes)++; modes = nmodes; mode->mode = HOSTAPD_MODE_IEEE80211B; mode11g = &modes[mode11g_idx]; mode->num_channels = mode11g->num_channels; mode->channels = os_malloc(mode11g->num_channels * sizeof(struct hostapd_channel_data)); if (mode->channels == NULL) { (*num_modes)--; return modes; /* Could not add 802.11b mode */ } os_memcpy(mode->channels, mode11g->channels, mode11g->num_channels * sizeof(struct hostapd_channel_data)); mode->num_rates = 0; mode->rates = os_malloc(4 * sizeof(int)); if (mode->rates == NULL) { os_free(mode->channels); (*num_modes)--; return modes; /* Could not add 802.11b mode */ } for (i = 0; i < mode11g->num_rates; i++) { if (mode11g->rates[i] != 10 && mode11g->rates[i] != 20 && mode11g->rates[i] != 55 && mode11g->rates[i] != 110) continue; mode->rates[mode->num_rates] = mode11g->rates[i]; mode->num_rates++; if (mode->num_rates == 4) break; } if (mode->num_rates == 0) { os_free(mode->channels); os_free(mode->rates); (*num_modes)--; return modes; /* No 802.11b rates */ } wpa_printf(MSG_DEBUG, "nl80211: Added 802.11b mode based on 802.11g " "information"); return modes; } static void nl80211_set_ht40_mode(struct hostapd_hw_modes *mode, int start, int end) { int c; for (c = 0; c < mode->num_channels; c++) { struct hostapd_channel_data *chan = &mode->channels[c]; if (chan->freq - 10 >= start && chan->freq + 10 <= end) chan->flag |= HOSTAPD_CHAN_HT40; } } static void nl80211_set_ht40_mode_sec(struct hostapd_hw_modes *mode, int start, int end) { int c; for (c = 0; c < mode->num_channels; c++) { struct hostapd_channel_data *chan = &mode->channels[c]; if (!(chan->flag & HOSTAPD_CHAN_HT40)) continue; if (chan->freq - 30 >= start && chan->freq - 10 <= end) chan->flag |= HOSTAPD_CHAN_HT40MINUS; if (chan->freq + 10 >= start && chan->freq + 30 <= end) chan->flag |= HOSTAPD_CHAN_HT40PLUS; } } static void nl80211_reg_rule_max_eirp(u32 start, u32 end, u32 max_eirp, struct phy_info_arg *results) { u16 m; for (m = 0; m < *results->num_modes; m++) { int c; struct hostapd_hw_modes *mode = &results->modes[m]; for (c = 0; c < mode->num_channels; c++) { struct hostapd_channel_data *chan = &mode->channels[c]; if ((u32) chan->freq - 10 >= start && (u32) chan->freq + 10 <= end) chan->max_tx_power = max_eirp; } } } static void nl80211_reg_rule_ht40(u32 start, u32 end, struct phy_info_arg *results) { u16 m; for (m = 0; m < *results->num_modes; m++) { if (!(results->modes[m].ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) continue; nl80211_set_ht40_mode(&results->modes[m], start, end); } } static void nl80211_reg_rule_sec(struct nlattr *tb[], struct phy_info_arg *results) { u32 start, end, max_bw; u16 m; if (tb[NL80211_ATTR_FREQ_RANGE_START] == NULL || tb[NL80211_ATTR_FREQ_RANGE_END] == NULL || tb[NL80211_ATTR_FREQ_RANGE_MAX_BW] == NULL) return; start = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_START]) / 1000; end = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_END]) / 1000; max_bw = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]) / 1000; if (max_bw < 20) return; for (m = 0; m < *results->num_modes; m++) { if (!(results->modes[m].ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) continue; nl80211_set_ht40_mode_sec(&results->modes[m], start, end); } } static void nl80211_set_vht_mode(struct hostapd_hw_modes *mode, int start, int end) { int c; for (c = 0; c < mode->num_channels; c++) { struct hostapd_channel_data *chan = &mode->channels[c]; if (chan->freq - 10 >= start && chan->freq + 70 <= end) chan->flag |= HOSTAPD_CHAN_VHT_10_70; if (chan->freq - 30 >= start && chan->freq + 50 <= end) chan->flag |= HOSTAPD_CHAN_VHT_30_50; if (chan->freq - 50 >= start && chan->freq + 30 <= end) chan->flag |= HOSTAPD_CHAN_VHT_50_30; if (chan->freq - 70 >= start && chan->freq + 10 <= end) chan->flag |= HOSTAPD_CHAN_VHT_70_10; } } static void nl80211_reg_rule_vht(struct nlattr *tb[], struct phy_info_arg *results) { u32 start, end, max_bw; u16 m; if (tb[NL80211_ATTR_FREQ_RANGE_START] == NULL || tb[NL80211_ATTR_FREQ_RANGE_END] == NULL || tb[NL80211_ATTR_FREQ_RANGE_MAX_BW] == NULL) return; start = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_START]) / 1000; end = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_END]) / 1000; max_bw = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]) / 1000; if (max_bw < 80) return; for (m = 0; m < *results->num_modes; m++) { if (!(results->modes[m].ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) continue; /* TODO: use a real VHT support indication */ if (!results->modes[m].vht_capab) continue; nl80211_set_vht_mode(&results->modes[m], start, end); } } static const char * dfs_domain_name(enum nl80211_dfs_regions region) { switch (region) { case NL80211_DFS_UNSET: return "DFS-UNSET"; case NL80211_DFS_FCC: return "DFS-FCC"; case NL80211_DFS_ETSI: return "DFS-ETSI"; case NL80211_DFS_JP: return "DFS-JP"; default: return "DFS-invalid"; } } static int nl80211_get_reg(struct nl_msg *msg, void *arg) { struct phy_info_arg *results = arg; struct nlattr *tb_msg[NL80211_ATTR_MAX + 1]; struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); struct nlattr *nl_rule; struct nlattr *tb_rule[NL80211_FREQUENCY_ATTR_MAX + 1]; int rem_rule; static struct nla_policy reg_policy[NL80211_FREQUENCY_ATTR_MAX + 1] = { [NL80211_ATTR_REG_RULE_FLAGS] = { .type = NLA_U32 }, [NL80211_ATTR_FREQ_RANGE_START] = { .type = NLA_U32 }, [NL80211_ATTR_FREQ_RANGE_END] = { .type = NLA_U32 }, [NL80211_ATTR_FREQ_RANGE_MAX_BW] = { .type = NLA_U32 }, [NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN] = { .type = NLA_U32 }, [NL80211_ATTR_POWER_RULE_MAX_EIRP] = { .type = NLA_U32 }, }; nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL); if (!tb_msg[NL80211_ATTR_REG_ALPHA2] || !tb_msg[NL80211_ATTR_REG_RULES]) { wpa_printf(MSG_DEBUG, "nl80211: No regulatory information " "available"); return NL_SKIP; } if (tb_msg[NL80211_ATTR_DFS_REGION]) { enum nl80211_dfs_regions dfs_domain; dfs_domain = nla_get_u8(tb_msg[NL80211_ATTR_DFS_REGION]); wpa_printf(MSG_DEBUG, "nl80211: Regulatory information - country=%s (%s)", (char *) nla_data(tb_msg[NL80211_ATTR_REG_ALPHA2]), dfs_domain_name(dfs_domain)); } else { wpa_printf(MSG_DEBUG, "nl80211: Regulatory information - country=%s", (char *) nla_data(tb_msg[NL80211_ATTR_REG_ALPHA2])); } nla_for_each_nested(nl_rule, tb_msg[NL80211_ATTR_REG_RULES], rem_rule) { u32 start, end, max_eirp = 0, max_bw = 0; nla_parse(tb_rule, NL80211_FREQUENCY_ATTR_MAX, nla_data(nl_rule), nla_len(nl_rule), reg_policy); if (tb_rule[NL80211_ATTR_FREQ_RANGE_START] == NULL || tb_rule[NL80211_ATTR_FREQ_RANGE_END] == NULL) continue; start = nla_get_u32(tb_rule[NL80211_ATTR_FREQ_RANGE_START]) / 1000; end = nla_get_u32(tb_rule[NL80211_ATTR_FREQ_RANGE_END]) / 1000; if (tb_rule[NL80211_ATTR_POWER_RULE_MAX_EIRP]) max_eirp = nla_get_u32(tb_rule[NL80211_ATTR_POWER_RULE_MAX_EIRP]) / 100; if (tb_rule[NL80211_ATTR_FREQ_RANGE_MAX_BW]) max_bw = nla_get_u32(tb_rule[NL80211_ATTR_FREQ_RANGE_MAX_BW]) / 1000; wpa_printf(MSG_DEBUG, "nl80211: %u-%u @ %u MHz %u mBm", start, end, max_bw, max_eirp); if (max_bw >= 40) nl80211_reg_rule_ht40(start, end, results); if (tb_rule[NL80211_ATTR_POWER_RULE_MAX_EIRP]) nl80211_reg_rule_max_eirp(start, end, max_eirp, results); } nla_for_each_nested(nl_rule, tb_msg[NL80211_ATTR_REG_RULES], rem_rule) { nla_parse(tb_rule, NL80211_FREQUENCY_ATTR_MAX, nla_data(nl_rule), nla_len(nl_rule), reg_policy); nl80211_reg_rule_sec(tb_rule, results); } nla_for_each_nested(nl_rule, tb_msg[NL80211_ATTR_REG_RULES], rem_rule) { nla_parse(tb_rule, NL80211_FREQUENCY_ATTR_MAX, nla_data(nl_rule), nla_len(nl_rule), reg_policy); nl80211_reg_rule_vht(tb_rule, results); } return NL_SKIP; } static int nl80211_set_regulatory_flags(struct wpa_driver_nl80211_data *drv, struct phy_info_arg *results) { struct nl_msg *msg; msg = nlmsg_alloc(); if (!msg) return -ENOMEM; nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_REG); return send_and_recv_msgs(drv, msg, nl80211_get_reg, results); } static struct hostapd_hw_modes * wpa_driver_nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags) { u32 feat; struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; struct phy_info_arg result = { .num_modes = num_modes, .modes = NULL, .last_mode = -1, }; *num_modes = 0; *flags = 0; msg = nlmsg_alloc(); if (!msg) return NULL; feat = get_nl80211_protocol_features(drv); if (feat & NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP) nl80211_cmd(drv, msg, NLM_F_DUMP, NL80211_CMD_GET_WIPHY); else nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_WIPHY); NLA_PUT_FLAG(msg, NL80211_ATTR_SPLIT_WIPHY_DUMP); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); if (send_and_recv_msgs(drv, msg, phy_info_handler, &result) == 0) { nl80211_set_regulatory_flags(drv, &result); return wpa_driver_nl80211_postprocess_modes(result.modes, num_modes); } msg = NULL; nla_put_failure: nlmsg_free(msg); return NULL; } static int wpa_driver_nl80211_send_mntr(struct wpa_driver_nl80211_data *drv, const void *data, size_t len, int encrypt, int noack) { __u8 rtap_hdr[] = { 0x00, 0x00, /* radiotap version */ 0x0e, 0x00, /* radiotap length */ 0x02, 0xc0, 0x00, 0x00, /* bmap: flags, tx and rx flags */ IEEE80211_RADIOTAP_F_FRAG, /* F_FRAG (fragment if required) */ 0x00, /* padding */ 0x00, 0x00, /* RX and TX flags to indicate that */ 0x00, 0x00, /* this is the injected frame directly */ }; struct iovec iov[2] = { { .iov_base = &rtap_hdr, .iov_len = sizeof(rtap_hdr), }, { .iov_base = (void *) data, .iov_len = len, } }; struct msghdr msg = { .msg_name = NULL, .msg_namelen = 0, .msg_iov = iov, .msg_iovlen = 2, .msg_control = NULL, .msg_controllen = 0, .msg_flags = 0, }; int res; u16 txflags = 0; if (encrypt) rtap_hdr[8] |= IEEE80211_RADIOTAP_F_WEP; if (drv->monitor_sock < 0) { wpa_printf(MSG_DEBUG, "nl80211: No monitor socket available " "for %s", __func__); return -1; } if (noack) txflags |= IEEE80211_RADIOTAP_F_TX_NOACK; WPA_PUT_LE16(&rtap_hdr[12], txflags); res = sendmsg(drv->monitor_sock, &msg, 0); if (res < 0) { wpa_printf(MSG_INFO, "nl80211: sendmsg: %s", strerror(errno)); return -1; } return 0; } static int wpa_driver_nl80211_send_frame(struct i802_bss *bss, const void *data, size_t len, int encrypt, int noack, unsigned int freq, int no_cck, int offchanok, unsigned int wait_time) { struct wpa_driver_nl80211_data *drv = bss->drv; u64 cookie; int res; if (freq == 0) { wpa_printf(MSG_DEBUG, "nl80211: send_frame - Use bss->freq=%u", bss->freq); freq = bss->freq; } if (drv->use_monitor) { wpa_printf(MSG_DEBUG, "nl80211: send_frame(freq=%u bss->freq=%u) -> send_mntr", freq, bss->freq); return wpa_driver_nl80211_send_mntr(drv, data, len, encrypt, noack); } wpa_printf(MSG_DEBUG, "nl80211: send_frame -> send_frame_cmd"); res = nl80211_send_frame_cmd(bss, freq, wait_time, data, len, &cookie, no_cck, noack, offchanok); if (res == 0 && !noack) { const struct ieee80211_mgmt *mgmt; u16 fc; mgmt = (const struct ieee80211_mgmt *) data; fc = le_to_host16(mgmt->frame_control); if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT && WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_ACTION) { wpa_printf(MSG_MSGDUMP, "nl80211: Update send_action_cookie from 0x%llx to 0x%llx", (long long unsigned int) drv->send_action_cookie, (long long unsigned int) cookie); drv->send_action_cookie = cookie; } } return res; } static int wpa_driver_nl80211_send_mlme(struct i802_bss *bss, const u8 *data, size_t data_len, int noack, unsigned int freq, int no_cck, int offchanok, unsigned int wait_time) { struct wpa_driver_nl80211_data *drv = bss->drv; struct ieee80211_mgmt *mgmt; int encrypt = 1; u16 fc; mgmt = (struct ieee80211_mgmt *) data; fc = le_to_host16(mgmt->frame_control); wpa_printf(MSG_DEBUG, "nl80211: send_mlme - noack=%d freq=%u no_cck=%d offchanok=%d wait_time=%u fc=0x%x nlmode=%d", noack, freq, no_cck, offchanok, wait_time, fc, drv->nlmode); if ((is_sta_interface(drv->nlmode) || drv->nlmode == NL80211_IFTYPE_P2P_DEVICE) && WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT && WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_PROBE_RESP) { /* * The use of last_mgmt_freq is a bit of a hack, * but it works due to the single-threaded nature * of wpa_supplicant. */ if (freq == 0) { wpa_printf(MSG_DEBUG, "nl80211: Use last_mgmt_freq=%d", drv->last_mgmt_freq); freq = drv->last_mgmt_freq; } return nl80211_send_frame_cmd(bss, freq, 0, data, data_len, NULL, 1, noack, 1); } if (drv->device_ap_sme && is_ap_interface(drv->nlmode)) { if (freq == 0) { wpa_printf(MSG_DEBUG, "nl80211: Use bss->freq=%d", bss->freq); freq = bss->freq; } return nl80211_send_frame_cmd(bss, freq, (int) freq == bss->freq ? 0 : wait_time, data, data_len, &drv->send_action_cookie, no_cck, noack, offchanok); } if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT && WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_AUTH) { /* * Only one of the authentication frame types is encrypted. * In order for static WEP encryption to work properly (i.e., * to not encrypt the frame), we need to tell mac80211 about * the frames that must not be encrypted. */ u16 auth_alg = le_to_host16(mgmt->u.auth.auth_alg); u16 auth_trans = le_to_host16(mgmt->u.auth.auth_transaction); if (auth_alg != WLAN_AUTH_SHARED_KEY || auth_trans != 3) encrypt = 0; } wpa_printf(MSG_DEBUG, "nl80211: send_mlme -> send_frame"); return wpa_driver_nl80211_send_frame(bss, data, data_len, encrypt, noack, freq, no_cck, offchanok, wait_time); } static int nl80211_set_bss(struct i802_bss *bss, int cts, int preamble, int slot, int ht_opmode, int ap_isolate, int *basic_rates) { struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; msg = nlmsg_alloc(); if (!msg) return -ENOMEM; nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_BSS); if (cts >= 0) NLA_PUT_U8(msg, NL80211_ATTR_BSS_CTS_PROT, cts); if (preamble >= 0) NLA_PUT_U8(msg, NL80211_ATTR_BSS_SHORT_PREAMBLE, preamble); if (slot >= 0) NLA_PUT_U8(msg, NL80211_ATTR_BSS_SHORT_SLOT_TIME, slot); if (ht_opmode >= 0) NLA_PUT_U16(msg, NL80211_ATTR_BSS_HT_OPMODE, ht_opmode); if (ap_isolate >= 0) NLA_PUT_U8(msg, NL80211_ATTR_AP_ISOLATE, ap_isolate); if (basic_rates) { u8 rates[NL80211_MAX_SUPP_RATES]; u8 rates_len = 0; int i; for (i = 0; i < NL80211_MAX_SUPP_RATES && basic_rates[i] >= 0; i++) rates[rates_len++] = basic_rates[i] / 5; NLA_PUT(msg, NL80211_ATTR_BSS_BASIC_RATES, rates_len, rates); } NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname)); return send_and_recv_msgs(drv, msg, NULL, NULL); nla_put_failure: nlmsg_free(msg); return -ENOBUFS; } static int wpa_driver_nl80211_set_acl(void *priv, struct hostapd_acl_params *params) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; struct nlattr *acl; unsigned int i; int ret = 0; if (!(drv->capa.max_acl_mac_addrs)) return -ENOTSUP; if (params->num_mac_acl > drv->capa.max_acl_mac_addrs) return -ENOTSUP; msg = nlmsg_alloc(); if (!msg) return -ENOMEM; wpa_printf(MSG_DEBUG, "nl80211: Set %s ACL (num_mac_acl=%u)", params->acl_policy ? "Accept" : "Deny", params->num_mac_acl); nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_MAC_ACL); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); NLA_PUT_U32(msg, NL80211_ATTR_ACL_POLICY, params->acl_policy ? NL80211_ACL_POLICY_DENY_UNLESS_LISTED : NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED); acl = nla_nest_start(msg, NL80211_ATTR_MAC_ADDRS); if (acl == NULL) goto nla_put_failure; for (i = 0; i < params->num_mac_acl; i++) NLA_PUT(msg, i + 1, ETH_ALEN, params->mac_acl[i].addr); nla_nest_end(msg, acl); ret = send_and_recv_msgs(drv, msg, NULL, NULL); msg = NULL; if (ret) { wpa_printf(MSG_DEBUG, "nl80211: Failed to set MAC ACL: %d (%s)", ret, strerror(-ret)); } nla_put_failure: nlmsg_free(msg); return ret; } static int wpa_driver_nl80211_set_ap(void *priv, struct wpa_driver_ap_params *params) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; u8 cmd = NL80211_CMD_NEW_BEACON; int ret; int beacon_set; int ifindex = if_nametoindex(bss->ifname); int num_suites; u32 suites[10], suite; u32 ver; beacon_set = bss->beacon_set; msg = nlmsg_alloc(); if (!msg) return -ENOMEM; wpa_printf(MSG_DEBUG, "nl80211: Set beacon (beacon_set=%d)", beacon_set); if (beacon_set) cmd = NL80211_CMD_SET_BEACON; nl80211_cmd(drv, msg, 0, cmd); wpa_hexdump(MSG_DEBUG, "nl80211: Beacon head", params->head, params->head_len); NLA_PUT(msg, NL80211_ATTR_BEACON_HEAD, params->head_len, params->head); wpa_hexdump(MSG_DEBUG, "nl80211: Beacon tail", params->tail, params->tail_len); NLA_PUT(msg, NL80211_ATTR_BEACON_TAIL, params->tail_len, params->tail); wpa_printf(MSG_DEBUG, "nl80211: ifindex=%d", ifindex); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex); wpa_printf(MSG_DEBUG, "nl80211: beacon_int=%d", params->beacon_int); NLA_PUT_U32(msg, NL80211_ATTR_BEACON_INTERVAL, params->beacon_int); wpa_printf(MSG_DEBUG, "nl80211: dtim_period=%d", params->dtim_period); NLA_PUT_U32(msg, NL80211_ATTR_DTIM_PERIOD, params->dtim_period); wpa_hexdump_ascii(MSG_DEBUG, "nl80211: ssid", params->ssid, params->ssid_len); NLA_PUT(msg, NL80211_ATTR_SSID, params->ssid_len, params->ssid); if (params->proberesp && params->proberesp_len) { wpa_hexdump(MSG_DEBUG, "nl80211: proberesp (offload)", params->proberesp, params->proberesp_len); NLA_PUT(msg, NL80211_ATTR_PROBE_RESP, params->proberesp_len, params->proberesp); } switch (params->hide_ssid) { case NO_SSID_HIDING: wpa_printf(MSG_DEBUG, "nl80211: hidden SSID not in use"); NLA_PUT_U32(msg, NL80211_ATTR_HIDDEN_SSID, NL80211_HIDDEN_SSID_NOT_IN_USE); break; case HIDDEN_SSID_ZERO_LEN: wpa_printf(MSG_DEBUG, "nl80211: hidden SSID zero len"); NLA_PUT_U32(msg, NL80211_ATTR_HIDDEN_SSID, NL80211_HIDDEN_SSID_ZERO_LEN); break; case HIDDEN_SSID_ZERO_CONTENTS: wpa_printf(MSG_DEBUG, "nl80211: hidden SSID zero contents"); NLA_PUT_U32(msg, NL80211_ATTR_HIDDEN_SSID, NL80211_HIDDEN_SSID_ZERO_CONTENTS); break; } wpa_printf(MSG_DEBUG, "nl80211: privacy=%d", params->privacy); if (params->privacy) NLA_PUT_FLAG(msg, NL80211_ATTR_PRIVACY); wpa_printf(MSG_DEBUG, "nl80211: auth_algs=0x%x", params->auth_algs); if ((params->auth_algs & (WPA_AUTH_ALG_OPEN | WPA_AUTH_ALG_SHARED)) == (WPA_AUTH_ALG_OPEN | WPA_AUTH_ALG_SHARED)) { /* Leave out the attribute */ } else if (params->auth_algs & WPA_AUTH_ALG_SHARED) NLA_PUT_U32(msg, NL80211_ATTR_AUTH_TYPE, NL80211_AUTHTYPE_SHARED_KEY); else NLA_PUT_U32(msg, NL80211_ATTR_AUTH_TYPE, NL80211_AUTHTYPE_OPEN_SYSTEM); wpa_printf(MSG_DEBUG, "nl80211: wpa_version=0x%x", params->wpa_version); ver = 0; if (params->wpa_version & WPA_PROTO_WPA) ver |= NL80211_WPA_VERSION_1; if (params->wpa_version & WPA_PROTO_RSN) ver |= NL80211_WPA_VERSION_2; if (ver) NLA_PUT_U32(msg, NL80211_ATTR_WPA_VERSIONS, ver); wpa_printf(MSG_DEBUG, "nl80211: key_mgmt_suites=0x%x", params->key_mgmt_suites); num_suites = 0; if (params->key_mgmt_suites & WPA_KEY_MGMT_IEEE8021X) suites[num_suites++] = WLAN_AKM_SUITE_8021X; if (params->key_mgmt_suites & WPA_KEY_MGMT_PSK) suites[num_suites++] = WLAN_AKM_SUITE_PSK; if (num_suites) { NLA_PUT(msg, NL80211_ATTR_AKM_SUITES, num_suites * sizeof(u32), suites); } if (params->key_mgmt_suites & WPA_KEY_MGMT_IEEE8021X && params->pairwise_ciphers & (WPA_CIPHER_WEP104 | WPA_CIPHER_WEP40)) NLA_PUT_FLAG(msg, NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT); wpa_printf(MSG_DEBUG, "nl80211: pairwise_ciphers=0x%x", params->pairwise_ciphers); num_suites = wpa_cipher_to_cipher_suites(params->pairwise_ciphers, suites, ARRAY_SIZE(suites)); if (num_suites) { NLA_PUT(msg, NL80211_ATTR_CIPHER_SUITES_PAIRWISE, num_suites * sizeof(u32), suites); } wpa_printf(MSG_DEBUG, "nl80211: group_cipher=0x%x", params->group_cipher); suite = wpa_cipher_to_cipher_suite(params->group_cipher); if (suite) NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, suite); if (params->beacon_ies) { wpa_hexdump_buf(MSG_DEBUG, "nl80211: beacon_ies", params->beacon_ies); NLA_PUT(msg, NL80211_ATTR_IE, wpabuf_len(params->beacon_ies), wpabuf_head(params->beacon_ies)); } if (params->proberesp_ies) { wpa_hexdump_buf(MSG_DEBUG, "nl80211: proberesp_ies", params->proberesp_ies); NLA_PUT(msg, NL80211_ATTR_IE_PROBE_RESP, wpabuf_len(params->proberesp_ies), wpabuf_head(params->proberesp_ies)); } if (params->assocresp_ies) { wpa_hexdump_buf(MSG_DEBUG, "nl80211: assocresp_ies", params->assocresp_ies); NLA_PUT(msg, NL80211_ATTR_IE_ASSOC_RESP, wpabuf_len(params->assocresp_ies), wpabuf_head(params->assocresp_ies)); } if (drv->capa.flags & WPA_DRIVER_FLAGS_INACTIVITY_TIMER) { wpa_printf(MSG_DEBUG, "nl80211: ap_max_inactivity=%d", params->ap_max_inactivity); NLA_PUT_U16(msg, NL80211_ATTR_INACTIVITY_TIMEOUT, params->ap_max_inactivity); } ret = send_and_recv_msgs(drv, msg, NULL, NULL); if (ret) { wpa_printf(MSG_DEBUG, "nl80211: Beacon set failed: %d (%s)", ret, strerror(-ret)); } else { bss->beacon_set = 1; nl80211_set_bss(bss, params->cts_protect, params->preamble, params->short_slot_time, params->ht_opmode, params->isolate, params->basic_rates); } return ret; nla_put_failure: nlmsg_free(msg); return -ENOBUFS; } static int nl80211_put_freq_params(struct nl_msg *msg, struct hostapd_freq_params *freq) { NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq->freq); if (freq->vht_enabled) { switch (freq->bandwidth) { case 20: NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH, NL80211_CHAN_WIDTH_20); break; case 40: NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH, NL80211_CHAN_WIDTH_40); break; case 80: if (freq->center_freq2) NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH, NL80211_CHAN_WIDTH_80P80); else NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH, NL80211_CHAN_WIDTH_80); break; case 160: NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH, NL80211_CHAN_WIDTH_160); break; default: return -EINVAL; } NLA_PUT_U32(msg, NL80211_ATTR_CENTER_FREQ1, freq->center_freq1); if (freq->center_freq2) NLA_PUT_U32(msg, NL80211_ATTR_CENTER_FREQ2, freq->center_freq2); } else if (freq->ht_enabled) { switch (freq->sec_channel_offset) { case -1: NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, NL80211_CHAN_HT40MINUS); break; case 1: NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, NL80211_CHAN_HT40PLUS); break; default: NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, NL80211_CHAN_HT20); break; } } return 0; nla_put_failure: return -ENOBUFS; } static int wpa_driver_nl80211_set_freq(struct i802_bss *bss, struct hostapd_freq_params *freq) { struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; int ret; wpa_printf(MSG_DEBUG, "nl80211: Set freq %d (ht_enabled=%d, vht_enabled=%d, bandwidth=%d MHz, cf1=%d MHz, cf2=%d MHz)", freq->freq, freq->ht_enabled, freq->vht_enabled, freq->bandwidth, freq->center_freq1, freq->center_freq2); msg = nlmsg_alloc(); if (!msg) return -1; nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_WIPHY); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); if (nl80211_put_freq_params(msg, freq) < 0) goto nla_put_failure; ret = send_and_recv_msgs(drv, msg, NULL, NULL); msg = NULL; if (ret == 0) { bss->freq = freq->freq; return 0; } wpa_printf(MSG_DEBUG, "nl80211: Failed to set channel (freq=%d): " "%d (%s)", freq->freq, ret, strerror(-ret)); nla_put_failure: nlmsg_free(msg); return -1; } static u32 sta_flags_nl80211(int flags) { u32 f = 0; if (flags & WPA_STA_AUTHORIZED) f |= BIT(NL80211_STA_FLAG_AUTHORIZED); if (flags & WPA_STA_WMM) f |= BIT(NL80211_STA_FLAG_WME); if (flags & WPA_STA_SHORT_PREAMBLE) f |= BIT(NL80211_STA_FLAG_SHORT_PREAMBLE); if (flags & WPA_STA_MFP) f |= BIT(NL80211_STA_FLAG_MFP); if (flags & WPA_STA_TDLS_PEER) f |= BIT(NL80211_STA_FLAG_TDLS_PEER); return f; } static int wpa_driver_nl80211_sta_add(void *priv, struct hostapd_sta_add_params *params) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; struct nl80211_sta_flag_update upd; int ret = -ENOBUFS; if ((params->flags & WPA_STA_TDLS_PEER) && !(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT)) return -EOPNOTSUPP; msg = nlmsg_alloc(); if (!msg) return -ENOMEM; wpa_printf(MSG_DEBUG, "nl80211: %s STA " MACSTR, params->set ? "Set" : "Add", MAC2STR(params->addr)); nl80211_cmd(drv, msg, 0, params->set ? NL80211_CMD_SET_STATION : NL80211_CMD_NEW_STATION); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname)); NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, params->addr); NLA_PUT(msg, NL80211_ATTR_STA_SUPPORTED_RATES, params->supp_rates_len, params->supp_rates); wpa_hexdump(MSG_DEBUG, " * supported rates", params->supp_rates, params->supp_rates_len); if (!params->set) { if (params->aid) { wpa_printf(MSG_DEBUG, " * aid=%u", params->aid); NLA_PUT_U16(msg, NL80211_ATTR_STA_AID, params->aid); } else { /* * cfg80211 validates that AID is non-zero, so we have * to make this a non-zero value for the TDLS case where * a dummy STA entry is used for now. */ wpa_printf(MSG_DEBUG, " * aid=1 (TDLS workaround)"); NLA_PUT_U16(msg, NL80211_ATTR_STA_AID, 1); } wpa_printf(MSG_DEBUG, " * listen_interval=%u", params->listen_interval); NLA_PUT_U16(msg, NL80211_ATTR_STA_LISTEN_INTERVAL, params->listen_interval); } else if (params->aid && (params->flags & WPA_STA_TDLS_PEER)) { wpa_printf(MSG_DEBUG, " * peer_aid=%u", params->aid); NLA_PUT_U16(msg, NL80211_ATTR_PEER_AID, params->aid); } if (params->ht_capabilities) { wpa_hexdump(MSG_DEBUG, " * ht_capabilities", (u8 *) params->ht_capabilities, sizeof(*params->ht_capabilities)); NLA_PUT(msg, NL80211_ATTR_HT_CAPABILITY, sizeof(*params->ht_capabilities), params->ht_capabilities); } if (params->vht_capabilities) { wpa_hexdump(MSG_DEBUG, " * vht_capabilities", (u8 *) params->vht_capabilities, sizeof(*params->vht_capabilities)); NLA_PUT(msg, NL80211_ATTR_VHT_CAPABILITY, sizeof(*params->vht_capabilities), params->vht_capabilities); } wpa_printf(MSG_DEBUG, " * capability=0x%x", params->capability); NLA_PUT_U16(msg, NL80211_ATTR_STA_CAPABILITY, params->capability); if (params->ext_capab) { wpa_hexdump(MSG_DEBUG, " * ext_capab", params->ext_capab, params->ext_capab_len); NLA_PUT(msg, NL80211_ATTR_STA_EXT_CAPABILITY, params->ext_capab_len, params->ext_capab); } if (params->supp_channels) { wpa_hexdump(MSG_DEBUG, " * supported channels", params->supp_channels, params->supp_channels_len); NLA_PUT(msg, NL80211_ATTR_STA_SUPPORTED_CHANNELS, params->supp_channels_len, params->supp_channels); } if (params->supp_oper_classes) { wpa_hexdump(MSG_DEBUG, " * supported operating classes", params->supp_oper_classes, params->supp_oper_classes_len); NLA_PUT(msg, NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES, params->supp_oper_classes_len, params->supp_oper_classes); } os_memset(&upd, 0, sizeof(upd)); upd.mask = sta_flags_nl80211(params->flags); upd.set = upd.mask; wpa_printf(MSG_DEBUG, " * flags set=0x%x mask=0x%x", upd.set, upd.mask); NLA_PUT(msg, NL80211_ATTR_STA_FLAGS2, sizeof(upd), &upd); if (params->flags & WPA_STA_WMM) { struct nlattr *wme = nla_nest_start(msg, NL80211_ATTR_STA_WME); if (!wme) goto nla_put_failure; wpa_printf(MSG_DEBUG, " * qosinfo=0x%x", params->qosinfo); NLA_PUT_U8(msg, NL80211_STA_WME_UAPSD_QUEUES, params->qosinfo & WMM_QOSINFO_STA_AC_MASK); NLA_PUT_U8(msg, NL80211_STA_WME_MAX_SP, (params->qosinfo >> WMM_QOSINFO_STA_SP_SHIFT) & WMM_QOSINFO_STA_SP_MASK); nla_nest_end(msg, wme); } ret = send_and_recv_msgs(drv, msg, NULL, NULL); msg = NULL; if (ret) wpa_printf(MSG_DEBUG, "nl80211: NL80211_CMD_%s_STATION " "result: %d (%s)", params->set ? "SET" : "NEW", ret, strerror(-ret)); if (ret == -EEXIST) ret = 0; nla_put_failure: nlmsg_free(msg); return ret; } static int wpa_driver_nl80211_sta_remove(struct i802_bss *bss, const u8 *addr) { struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; int ret; msg = nlmsg_alloc(); if (!msg) return -ENOMEM; nl80211_cmd(drv, msg, 0, NL80211_CMD_DEL_STATION); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname)); NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); ret = send_and_recv_msgs(drv, msg, NULL, NULL); wpa_printf(MSG_DEBUG, "nl80211: sta_remove -> DEL_STATION %s " MACSTR " --> %d (%s)", bss->ifname, MAC2STR(addr), ret, strerror(-ret)); if (ret == -ENOENT) return 0; return ret; nla_put_failure: nlmsg_free(msg); return -ENOBUFS; } static void nl80211_remove_iface(struct wpa_driver_nl80211_data *drv, int ifidx) { struct nl_msg *msg; wpa_printf(MSG_DEBUG, "nl80211: Remove interface ifindex=%d", ifidx); /* stop listening for EAPOL on this interface */ del_ifidx(drv, ifidx); msg = nlmsg_alloc(); if (!msg) goto nla_put_failure; nl80211_cmd(drv, msg, 0, NL80211_CMD_DEL_INTERFACE); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifidx); if (send_and_recv_msgs(drv, msg, NULL, NULL) == 0) return; msg = NULL; nla_put_failure: nlmsg_free(msg); wpa_printf(MSG_ERROR, "Failed to remove interface (ifidx=%d)", ifidx); } static const char * nl80211_iftype_str(enum nl80211_iftype mode) { switch (mode) { case NL80211_IFTYPE_ADHOC: return "ADHOC"; case NL80211_IFTYPE_STATION: return "STATION"; case NL80211_IFTYPE_AP: return "AP"; case NL80211_IFTYPE_AP_VLAN: return "AP_VLAN"; case NL80211_IFTYPE_WDS: return "WDS"; case NL80211_IFTYPE_MONITOR: return "MONITOR"; case NL80211_IFTYPE_MESH_POINT: return "MESH_POINT"; case NL80211_IFTYPE_P2P_CLIENT: return "P2P_CLIENT"; case NL80211_IFTYPE_P2P_GO: return "P2P_GO"; case NL80211_IFTYPE_P2P_DEVICE: return "P2P_DEVICE"; default: return "unknown"; } } static int nl80211_create_iface_once(struct wpa_driver_nl80211_data *drv, const char *ifname, enum nl80211_iftype iftype, const u8 *addr, int wds, int (*handler)(struct nl_msg *, void *), void *arg) { struct nl_msg *msg; int ifidx; int ret = -ENOBUFS; wpa_printf(MSG_DEBUG, "nl80211: Create interface iftype %d (%s)", iftype, nl80211_iftype_str(iftype)); msg = nlmsg_alloc(); if (!msg) return -1; nl80211_cmd(drv, msg, 0, NL80211_CMD_NEW_INTERFACE); if (nl80211_set_iface_id(msg, drv->first_bss) < 0) goto nla_put_failure; NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, ifname); NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, iftype); if (iftype == NL80211_IFTYPE_MONITOR) { struct nlattr *flags; flags = nla_nest_start(msg, NL80211_ATTR_MNTR_FLAGS); if (!flags) goto nla_put_failure; NLA_PUT_FLAG(msg, NL80211_MNTR_FLAG_COOK_FRAMES); nla_nest_end(msg, flags); } else if (wds) { NLA_PUT_U8(msg, NL80211_ATTR_4ADDR, wds); } ret = send_and_recv_msgs(drv, msg, handler, arg); msg = NULL; if (ret) { nla_put_failure: nlmsg_free(msg); wpa_printf(MSG_ERROR, "Failed to create interface %s: %d (%s)", ifname, ret, strerror(-ret)); return ret; } if (iftype == NL80211_IFTYPE_P2P_DEVICE) return 0; ifidx = if_nametoindex(ifname); wpa_printf(MSG_DEBUG, "nl80211: New interface %s created: ifindex=%d", ifname, ifidx); if (ifidx <= 0) return -1; /* start listening for EAPOL on this interface */ add_ifidx(drv, ifidx); if (addr && iftype != NL80211_IFTYPE_MONITOR && linux_set_ifhwaddr(drv->global->ioctl_sock, ifname, addr)) { nl80211_remove_iface(drv, ifidx); return -1; } return ifidx; } static int nl80211_create_iface(struct wpa_driver_nl80211_data *drv, const char *ifname, enum nl80211_iftype iftype, const u8 *addr, int wds, int (*handler)(struct nl_msg *, void *), void *arg, int use_existing) { int ret; ret = nl80211_create_iface_once(drv, ifname, iftype, addr, wds, handler, arg); /* if error occurred and interface exists already */ if (ret == -ENFILE && if_nametoindex(ifname)) { if (use_existing) { wpa_printf(MSG_DEBUG, "nl80211: Continue using existing interface %s", ifname); return -ENFILE; } wpa_printf(MSG_INFO, "Try to remove and re-create %s", ifname); /* Try to remove the interface that was already there. */ nl80211_remove_iface(drv, if_nametoindex(ifname)); /* Try to create the interface again */ ret = nl80211_create_iface_once(drv, ifname, iftype, addr, wds, handler, arg); } if (ret >= 0 && is_p2p_net_interface(iftype)) nl80211_disable_11b_rates(drv, ret, 1); return ret; } static void handle_tx_callback(void *ctx, u8 *buf, size_t len, int ok) { struct ieee80211_hdr *hdr; u16 fc; union wpa_event_data event; hdr = (struct ieee80211_hdr *) buf; fc = le_to_host16(hdr->frame_control); os_memset(&event, 0, sizeof(event)); event.tx_status.type = WLAN_FC_GET_TYPE(fc); event.tx_status.stype = WLAN_FC_GET_STYPE(fc); event.tx_status.dst = hdr->addr1; event.tx_status.data = buf; event.tx_status.data_len = len; event.tx_status.ack = ok; wpa_supplicant_event(ctx, EVENT_TX_STATUS, &event); } static void from_unknown_sta(struct wpa_driver_nl80211_data *drv, u8 *buf, size_t len) { struct ieee80211_hdr *hdr = (void *)buf; u16 fc; union wpa_event_data event; if (len < sizeof(*hdr)) return; fc = le_to_host16(hdr->frame_control); os_memset(&event, 0, sizeof(event)); event.rx_from_unknown.bssid = get_hdr_bssid(hdr, len); event.rx_from_unknown.addr = hdr->addr2; event.rx_from_unknown.wds = (fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) == (WLAN_FC_FROMDS | WLAN_FC_TODS); wpa_supplicant_event(drv->ctx, EVENT_RX_FROM_UNKNOWN, &event); } static void handle_frame(struct wpa_driver_nl80211_data *drv, u8 *buf, size_t len, int datarate, int ssi_signal) { struct ieee80211_hdr *hdr; u16 fc; union wpa_event_data event; hdr = (struct ieee80211_hdr *) buf; fc = le_to_host16(hdr->frame_control); switch (WLAN_FC_GET_TYPE(fc)) { case WLAN_FC_TYPE_MGMT: os_memset(&event, 0, sizeof(event)); event.rx_mgmt.frame = buf; event.rx_mgmt.frame_len = len; event.rx_mgmt.datarate = datarate; event.rx_mgmt.ssi_signal = ssi_signal; wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event); break; case WLAN_FC_TYPE_CTRL: /* can only get here with PS-Poll frames */ wpa_printf(MSG_DEBUG, "CTRL"); from_unknown_sta(drv, buf, len); break; case WLAN_FC_TYPE_DATA: from_unknown_sta(drv, buf, len); break; } } static void handle_monitor_read(int sock, void *eloop_ctx, void *sock_ctx) { struct wpa_driver_nl80211_data *drv = eloop_ctx; int len; unsigned char buf[3000]; struct ieee80211_radiotap_iterator iter; int ret; int datarate = 0, ssi_signal = 0; int injected = 0, failed = 0, rxflags = 0; len = recv(sock, buf, sizeof(buf), 0); if (len < 0) { wpa_printf(MSG_ERROR, "nl80211: Monitor socket recv failed: %s", strerror(errno)); return; } if (ieee80211_radiotap_iterator_init(&iter, (void*)buf, len)) { wpa_printf(MSG_INFO, "nl80211: received invalid radiotap frame"); return; } while (1) { ret = ieee80211_radiotap_iterator_next(&iter); if (ret == -ENOENT) break; if (ret) { wpa_printf(MSG_INFO, "nl80211: received invalid radiotap frame (%d)", ret); return; } switch (iter.this_arg_index) { case IEEE80211_RADIOTAP_FLAGS: if (*iter.this_arg & IEEE80211_RADIOTAP_F_FCS) len -= 4; break; case IEEE80211_RADIOTAP_RX_FLAGS: rxflags = 1; break; case IEEE80211_RADIOTAP_TX_FLAGS: injected = 1; failed = le_to_host16((*(uint16_t *) iter.this_arg)) & IEEE80211_RADIOTAP_F_TX_FAIL; break; case IEEE80211_RADIOTAP_DATA_RETRIES: break; case IEEE80211_RADIOTAP_CHANNEL: /* TODO: convert from freq/flags to channel number */ break; case IEEE80211_RADIOTAP_RATE: datarate = *iter.this_arg * 5; break; case IEEE80211_RADIOTAP_DBM_ANTSIGNAL: ssi_signal = (s8) *iter.this_arg; break; } } if (rxflags && injected) return; if (!injected) handle_frame(drv, buf + iter.max_length, len - iter.max_length, datarate, ssi_signal); else handle_tx_callback(drv->ctx, buf + iter.max_length, len - iter.max_length, !failed); } /* * we post-process the filter code later and rewrite * this to the offset to the last instruction */ #define PASS 0xFF #define FAIL 0xFE static struct sock_filter msock_filter_insns[] = { /* * do a little-endian load of the radiotap length field */ /* load lower byte into A */ BPF_STMT(BPF_LD | BPF_B | BPF_ABS, 2), /* put it into X (== index register) */ BPF_STMT(BPF_MISC| BPF_TAX, 0), /* load upper byte into A */ BPF_STMT(BPF_LD | BPF_B | BPF_ABS, 3), /* left-shift it by 8 */ BPF_STMT(BPF_ALU | BPF_LSH | BPF_K, 8), /* or with X */ BPF_STMT(BPF_ALU | BPF_OR | BPF_X, 0), /* put result into X */ BPF_STMT(BPF_MISC| BPF_TAX, 0), /* * Allow management frames through, this also gives us those * management frames that we sent ourselves with status */ /* load the lower byte of the IEEE 802.11 frame control field */ BPF_STMT(BPF_LD | BPF_B | BPF_IND, 0), /* mask off frame type and version */ BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0xF), /* accept frame if it's both 0, fall through otherwise */ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0, PASS, 0), /* * TODO: add a bit to radiotap RX flags that indicates * that the sending station is not associated, then * add a filter here that filters on our DA and that flag * to allow us to deauth frames to that bad station. * * For now allow all To DS data frames through. */ /* load the IEEE 802.11 frame control field */ BPF_STMT(BPF_LD | BPF_H | BPF_IND, 0), /* mask off frame type, version and DS status */ BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0x0F03), /* accept frame if version 0, type 2 and To DS, fall through otherwise */ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x0801, PASS, 0), #if 0 /* * drop non-data frames */ /* load the lower byte of the frame control field */ BPF_STMT(BPF_LD | BPF_B | BPF_IND, 0), /* mask off QoS bit */ BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0x0c), /* drop non-data frames */ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 8, 0, FAIL), #endif /* load the upper byte of the frame control field */ BPF_STMT(BPF_LD | BPF_B | BPF_IND, 1), /* mask off toDS/fromDS */ BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0x03), /* accept WDS frames */ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 3, PASS, 0), /* * add header length to index */ /* load the lower byte of the frame control field */ BPF_STMT(BPF_LD | BPF_B | BPF_IND, 0), /* mask off QoS bit */ BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0x80), /* right shift it by 6 to give 0 or 2 */ BPF_STMT(BPF_ALU | BPF_RSH | BPF_K, 6), /* add data frame header length */ BPF_STMT(BPF_ALU | BPF_ADD | BPF_K, 24), /* add index, was start of 802.11 header */ BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0), /* move to index, now start of LL header */ BPF_STMT(BPF_MISC | BPF_TAX, 0), /* * Accept empty data frames, we use those for * polling activity. */ BPF_STMT(BPF_LD | BPF_W | BPF_LEN, 0), BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_X, 0, PASS, 0), /* * Accept EAPOL frames */ BPF_STMT(BPF_LD | BPF_W | BPF_IND, 0), BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0xAAAA0300, 0, FAIL), BPF_STMT(BPF_LD | BPF_W | BPF_IND, 4), BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x0000888E, PASS, FAIL), /* keep these last two statements or change the code below */ /* return 0 == "DROP" */ BPF_STMT(BPF_RET | BPF_K, 0), /* return ~0 == "keep all" */ BPF_STMT(BPF_RET | BPF_K, ~0), }; static struct sock_fprog msock_filter = { .len = ARRAY_SIZE(msock_filter_insns), .filter = msock_filter_insns, }; static int add_monitor_filter(int s) { int idx; /* rewrite all PASS/FAIL jump offsets */ for (idx = 0; idx < msock_filter.len; idx++) { struct sock_filter *insn = &msock_filter_insns[idx]; if (BPF_CLASS(insn->code) == BPF_JMP) { if (insn->code == (BPF_JMP|BPF_JA)) { if (insn->k == PASS) insn->k = msock_filter.len - idx - 2; else if (insn->k == FAIL) insn->k = msock_filter.len - idx - 3; } if (insn->jt == PASS) insn->jt = msock_filter.len - idx - 2; else if (insn->jt == FAIL) insn->jt = msock_filter.len - idx - 3; if (insn->jf == PASS) insn->jf = msock_filter.len - idx - 2; else if (insn->jf == FAIL) insn->jf = msock_filter.len - idx - 3; } } if (setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, &msock_filter, sizeof(msock_filter))) { wpa_printf(MSG_ERROR, "nl80211: setsockopt(SO_ATTACH_FILTER) failed: %s", strerror(errno)); return -1; } return 0; } static void nl80211_remove_monitor_interface( struct wpa_driver_nl80211_data *drv) { if (drv->monitor_refcount > 0) drv->monitor_refcount--; wpa_printf(MSG_DEBUG, "nl80211: Remove monitor interface: refcount=%d", drv->monitor_refcount); if (drv->monitor_refcount > 0) return; if (drv->monitor_ifidx >= 0) { nl80211_remove_iface(drv, drv->monitor_ifidx); drv->monitor_ifidx = -1; } if (drv->monitor_sock >= 0) { eloop_unregister_read_sock(drv->monitor_sock); close(drv->monitor_sock); drv->monitor_sock = -1; } } static int nl80211_create_monitor_interface(struct wpa_driver_nl80211_data *drv) { char buf[IFNAMSIZ]; struct sockaddr_ll ll; int optval; socklen_t optlen; if (drv->monitor_ifidx >= 0) { drv->monitor_refcount++; wpa_printf(MSG_DEBUG, "nl80211: Re-use existing monitor interface: refcount=%d", drv->monitor_refcount); return 0; } if (os_strncmp(drv->first_bss->ifname, "p2p-", 4) == 0) { /* * P2P interface name is of the format p2p-%s-%d. For monitor * interface name corresponding to P2P GO, replace "p2p-" with * "mon-" to retain the same interface name length and to * indicate that it is a monitor interface. */ snprintf(buf, IFNAMSIZ, "mon-%s", drv->first_bss->ifname + 4); } else { /* Non-P2P interface with AP functionality. */ snprintf(buf, IFNAMSIZ, "mon.%s", drv->first_bss->ifname); } buf[IFNAMSIZ - 1] = '\0'; drv->monitor_ifidx = nl80211_create_iface(drv, buf, NL80211_IFTYPE_MONITOR, NULL, 0, NULL, NULL, 0); if (drv->monitor_ifidx == -EOPNOTSUPP) { /* * This is backward compatibility for a few versions of * the kernel only that didn't advertise the right * attributes for the only driver that then supported * AP mode w/o monitor -- ath6kl. */ wpa_printf(MSG_DEBUG, "nl80211: Driver does not support " "monitor interface type - try to run without it"); drv->device_ap_sme = 1; } if (drv->monitor_ifidx < 0) return -1; if (linux_set_iface_flags(drv->global->ioctl_sock, buf, 1)) goto error; memset(&ll, 0, sizeof(ll)); ll.sll_family = AF_PACKET; ll.sll_ifindex = drv->monitor_ifidx; drv->monitor_sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); if (drv->monitor_sock < 0) { wpa_printf(MSG_ERROR, "nl80211: socket[PF_PACKET,SOCK_RAW] failed: %s", strerror(errno)); goto error; } if (add_monitor_filter(drv->monitor_sock)) { wpa_printf(MSG_INFO, "Failed to set socket filter for monitor " "interface; do filtering in user space"); /* This works, but will cost in performance. */ } if (bind(drv->monitor_sock, (struct sockaddr *) &ll, sizeof(ll)) < 0) { wpa_printf(MSG_ERROR, "nl80211: monitor socket bind failed: %s", strerror(errno)); goto error; } optlen = sizeof(optval); optval = 20; if (setsockopt (drv->monitor_sock, SOL_SOCKET, SO_PRIORITY, &optval, optlen)) { wpa_printf(MSG_ERROR, "nl80211: Failed to set socket priority: %s", strerror(errno)); goto error; } if (eloop_register_read_sock(drv->monitor_sock, handle_monitor_read, drv, NULL)) { wpa_printf(MSG_INFO, "nl80211: Could not register monitor read socket"); goto error; } drv->monitor_refcount++; return 0; error: nl80211_remove_monitor_interface(drv); return -1; } static int nl80211_setup_ap(struct i802_bss *bss) { struct wpa_driver_nl80211_data *drv = bss->drv; wpa_printf(MSG_DEBUG, "nl80211: Setup AP(%s) - device_ap_sme=%d use_monitor=%d", bss->ifname, drv->device_ap_sme, drv->use_monitor); /* * Disable Probe Request reporting unless we need it in this way for * devices that include the AP SME, in the other case (unless using * monitor iface) we'll get it through the nl_mgmt socket instead. */ if (!drv->device_ap_sme) wpa_driver_nl80211_probe_req_report(bss, 0); if (!drv->device_ap_sme && !drv->use_monitor) if (nl80211_mgmt_subscribe_ap(bss)) return -1; if (drv->device_ap_sme && !drv->use_monitor) if (nl80211_mgmt_subscribe_ap_dev_sme(bss)) return -1; if (!drv->device_ap_sme && drv->use_monitor && nl80211_create_monitor_interface(drv) && !drv->device_ap_sme) return -1; if (drv->device_ap_sme && wpa_driver_nl80211_probe_req_report(bss, 1) < 0) { wpa_printf(MSG_DEBUG, "nl80211: Failed to enable " "Probe Request frame reporting in AP mode"); /* Try to survive without this */ } return 0; } static void nl80211_teardown_ap(struct i802_bss *bss) { struct wpa_driver_nl80211_data *drv = bss->drv; wpa_printf(MSG_DEBUG, "nl80211: Teardown AP(%s) - device_ap_sme=%d use_monitor=%d", bss->ifname, drv->device_ap_sme, drv->use_monitor); if (drv->device_ap_sme) { wpa_driver_nl80211_probe_req_report(bss, 0); if (!drv->use_monitor) nl80211_mgmt_unsubscribe(bss, "AP teardown (dev SME)"); } else if (drv->use_monitor) nl80211_remove_monitor_interface(drv); else nl80211_mgmt_unsubscribe(bss, "AP teardown"); bss->beacon_set = 0; } static int nl80211_send_eapol_data(struct i802_bss *bss, const u8 *addr, const u8 *data, size_t data_len) { struct sockaddr_ll ll; int ret; if (bss->drv->eapol_tx_sock < 0) { wpa_printf(MSG_DEBUG, "nl80211: No socket to send EAPOL"); return -1; } os_memset(&ll, 0, sizeof(ll)); ll.sll_family = AF_PACKET; ll.sll_ifindex = bss->ifindex; ll.sll_protocol = htons(ETH_P_PAE); ll.sll_halen = ETH_ALEN; os_memcpy(ll.sll_addr, addr, ETH_ALEN); ret = sendto(bss->drv->eapol_tx_sock, data, data_len, 0, (struct sockaddr *) &ll, sizeof(ll)); if (ret < 0) wpa_printf(MSG_ERROR, "nl80211: EAPOL TX: %s", strerror(errno)); return ret; } static const u8 rfc1042_header[6] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 }; static int wpa_driver_nl80211_hapd_send_eapol( void *priv, const u8 *addr, const u8 *data, size_t data_len, int encrypt, const u8 *own_addr, u32 flags) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; struct ieee80211_hdr *hdr; size_t len; u8 *pos; int res; int qos = flags & WPA_STA_WMM; if (drv->device_ap_sme || !drv->use_monitor) return nl80211_send_eapol_data(bss, addr, data, data_len); len = sizeof(*hdr) + (qos ? 2 : 0) + sizeof(rfc1042_header) + 2 + data_len; hdr = os_zalloc(len); if (hdr == NULL) { wpa_printf(MSG_INFO, "nl80211: Failed to allocate EAPOL buffer(len=%lu)", (unsigned long) len); return -1; } hdr->frame_control = IEEE80211_FC(WLAN_FC_TYPE_DATA, WLAN_FC_STYPE_DATA); hdr->frame_control |= host_to_le16(WLAN_FC_FROMDS); if (encrypt) hdr->frame_control |= host_to_le16(WLAN_FC_ISWEP); if (qos) { hdr->frame_control |= host_to_le16(WLAN_FC_STYPE_QOS_DATA << 4); } memcpy(hdr->IEEE80211_DA_FROMDS, addr, ETH_ALEN); memcpy(hdr->IEEE80211_BSSID_FROMDS, own_addr, ETH_ALEN); memcpy(hdr->IEEE80211_SA_FROMDS, own_addr, ETH_ALEN); pos = (u8 *) (hdr + 1); if (qos) { /* Set highest priority in QoS header */ pos[0] = 7; pos[1] = 0; pos += 2; } memcpy(pos, rfc1042_header, sizeof(rfc1042_header)); pos += sizeof(rfc1042_header); WPA_PUT_BE16(pos, ETH_P_PAE); pos += 2; memcpy(pos, data, data_len); res = wpa_driver_nl80211_send_frame(bss, (u8 *) hdr, len, encrypt, 0, 0, 0, 0, 0); if (res < 0) { wpa_printf(MSG_ERROR, "i802_send_eapol - packet len: %lu - " "failed: %d (%s)", (unsigned long) len, errno, strerror(errno)); } os_free(hdr); return res; } static int wpa_driver_nl80211_sta_set_flags(void *priv, const u8 *addr, int total_flags, int flags_or, int flags_and) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; struct nlattr *flags; struct nl80211_sta_flag_update upd; msg = nlmsg_alloc(); if (!msg) return -ENOMEM; nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_STATION); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname)); NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); /* * Backwards compatibility version using NL80211_ATTR_STA_FLAGS. This * can be removed eventually. */ flags = nla_nest_start(msg, NL80211_ATTR_STA_FLAGS); if (!flags) goto nla_put_failure; if (total_flags & WPA_STA_AUTHORIZED) NLA_PUT_FLAG(msg, NL80211_STA_FLAG_AUTHORIZED); if (total_flags & WPA_STA_WMM) NLA_PUT_FLAG(msg, NL80211_STA_FLAG_WME); if (total_flags & WPA_STA_SHORT_PREAMBLE) NLA_PUT_FLAG(msg, NL80211_STA_FLAG_SHORT_PREAMBLE); if (total_flags & WPA_STA_MFP) NLA_PUT_FLAG(msg, NL80211_STA_FLAG_MFP); if (total_flags & WPA_STA_TDLS_PEER) NLA_PUT_FLAG(msg, NL80211_STA_FLAG_TDLS_PEER); nla_nest_end(msg, flags); os_memset(&upd, 0, sizeof(upd)); upd.mask = sta_flags_nl80211(flags_or | ~flags_and); upd.set = sta_flags_nl80211(flags_or); NLA_PUT(msg, NL80211_ATTR_STA_FLAGS2, sizeof(upd), &upd); return send_and_recv_msgs(drv, msg, NULL, NULL); nla_put_failure: nlmsg_free(msg); return -ENOBUFS; } static int wpa_driver_nl80211_ap(struct wpa_driver_nl80211_data *drv, struct wpa_driver_associate_params *params) { enum nl80211_iftype nlmode, old_mode; struct hostapd_freq_params freq = { .freq = params->freq, }; if (params->p2p) { wpa_printf(MSG_DEBUG, "nl80211: Setup AP operations for P2P " "group (GO)"); nlmode = NL80211_IFTYPE_P2P_GO; } else nlmode = NL80211_IFTYPE_AP; old_mode = drv->nlmode; if (wpa_driver_nl80211_set_mode(drv->first_bss, nlmode)) { nl80211_remove_monitor_interface(drv); return -1; } if (wpa_driver_nl80211_set_freq(drv->first_bss, &freq)) { if (old_mode != nlmode) wpa_driver_nl80211_set_mode(drv->first_bss, old_mode); nl80211_remove_monitor_interface(drv); return -1; } return 0; } static int nl80211_leave_ibss(struct wpa_driver_nl80211_data *drv) { struct nl_msg *msg; int ret = -1; msg = nlmsg_alloc(); if (!msg) return -1; nl80211_cmd(drv, msg, 0, NL80211_CMD_LEAVE_IBSS); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); ret = send_and_recv_msgs(drv, msg, NULL, NULL); msg = NULL; if (ret) { wpa_printf(MSG_DEBUG, "nl80211: Leave IBSS failed: ret=%d " "(%s)", ret, strerror(-ret)); goto nla_put_failure; } ret = 0; wpa_printf(MSG_DEBUG, "nl80211: Leave IBSS request sent successfully"); nla_put_failure: if (wpa_driver_nl80211_set_mode(drv->first_bss, NL80211_IFTYPE_STATION)) { wpa_printf(MSG_INFO, "nl80211: Failed to set interface into " "station mode"); } nlmsg_free(msg); return ret; } static int wpa_driver_nl80211_ibss(struct wpa_driver_nl80211_data *drv, struct wpa_driver_associate_params *params) { struct nl_msg *msg; int ret = -1; int count = 0; wpa_printf(MSG_DEBUG, "nl80211: Join IBSS (ifindex=%d)", drv->ifindex); if (wpa_driver_nl80211_set_mode(drv->first_bss, NL80211_IFTYPE_ADHOC)) { wpa_printf(MSG_INFO, "nl80211: Failed to set interface into " "IBSS mode"); return -1; } retry: msg = nlmsg_alloc(); if (!msg) return -1; nl80211_cmd(drv, msg, 0, NL80211_CMD_JOIN_IBSS); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); if (params->ssid == NULL || params->ssid_len > sizeof(drv->ssid)) goto nla_put_failure; wpa_hexdump_ascii(MSG_DEBUG, " * SSID", params->ssid, params->ssid_len); NLA_PUT(msg, NL80211_ATTR_SSID, params->ssid_len, params->ssid); os_memcpy(drv->ssid, params->ssid, params->ssid_len); drv->ssid_len = params->ssid_len; wpa_printf(MSG_DEBUG, " * freq=%d", params->freq); NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, params->freq); ret = nl80211_set_conn_keys(params, msg); if (ret) goto nla_put_failure; if (params->bssid && params->fixed_bssid) { wpa_printf(MSG_DEBUG, " * BSSID=" MACSTR, MAC2STR(params->bssid)); NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid); } if (params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X || params->key_mgmt_suite == WPA_KEY_MGMT_PSK || params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SHA256 || params->key_mgmt_suite == WPA_KEY_MGMT_PSK_SHA256) { wpa_printf(MSG_DEBUG, " * control port"); NLA_PUT_FLAG(msg, NL80211_ATTR_CONTROL_PORT); } if (params->wpa_ie) { wpa_hexdump(MSG_DEBUG, " * Extra IEs for Beacon/Probe Response frames", params->wpa_ie, params->wpa_ie_len); NLA_PUT(msg, NL80211_ATTR_IE, params->wpa_ie_len, params->wpa_ie); } ret = send_and_recv_msgs(drv, msg, NULL, NULL); msg = NULL; if (ret) { wpa_printf(MSG_DEBUG, "nl80211: Join IBSS failed: ret=%d (%s)", ret, strerror(-ret)); count++; if (ret == -EALREADY && count == 1) { wpa_printf(MSG_DEBUG, "nl80211: Retry IBSS join after " "forced leave"); nl80211_leave_ibss(drv); nlmsg_free(msg); goto retry; } goto nla_put_failure; } ret = 0; wpa_printf(MSG_DEBUG, "nl80211: Join IBSS request sent successfully"); nla_put_failure: nlmsg_free(msg); return ret; } static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv, struct wpa_driver_associate_params *params, struct nl_msg *msg) { NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); if (params->bssid) { wpa_printf(MSG_DEBUG, " * bssid=" MACSTR, MAC2STR(params->bssid)); NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid); } if (params->freq) { wpa_printf(MSG_DEBUG, " * freq=%d", params->freq); NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, params->freq); drv->assoc_freq = params->freq; } else drv->assoc_freq = 0; if (params->bg_scan_period >= 0) { wpa_printf(MSG_DEBUG, " * bg scan period=%d", params->bg_scan_period); NLA_PUT_U16(msg, NL80211_ATTR_BG_SCAN_PERIOD, params->bg_scan_period); } if (params->ssid) { wpa_hexdump_ascii(MSG_DEBUG, " * SSID", params->ssid, params->ssid_len); NLA_PUT(msg, NL80211_ATTR_SSID, params->ssid_len, params->ssid); if (params->ssid_len > sizeof(drv->ssid)) goto nla_put_failure; os_memcpy(drv->ssid, params->ssid, params->ssid_len); drv->ssid_len = params->ssid_len; } wpa_hexdump(MSG_DEBUG, " * IEs", params->wpa_ie, params->wpa_ie_len); if (params->wpa_ie) NLA_PUT(msg, NL80211_ATTR_IE, params->wpa_ie_len, params->wpa_ie); if (params->wpa_proto) { enum nl80211_wpa_versions ver = 0; if (params->wpa_proto & WPA_PROTO_WPA) ver |= NL80211_WPA_VERSION_1; if (params->wpa_proto & WPA_PROTO_RSN) ver |= NL80211_WPA_VERSION_2; wpa_printf(MSG_DEBUG, " * WPA Versions 0x%x", ver); NLA_PUT_U32(msg, NL80211_ATTR_WPA_VERSIONS, ver); } if (params->pairwise_suite != WPA_CIPHER_NONE) { u32 cipher = wpa_cipher_to_cipher_suite(params->pairwise_suite); wpa_printf(MSG_DEBUG, " * pairwise=0x%x", cipher); NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITES_PAIRWISE, cipher); } if (params->group_suite != WPA_CIPHER_NONE) { u32 cipher = wpa_cipher_to_cipher_suite(params->group_suite); wpa_printf(MSG_DEBUG, " * group=0x%x", cipher); NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, cipher); } if (params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X || params->key_mgmt_suite == WPA_KEY_MGMT_PSK || params->key_mgmt_suite == WPA_KEY_MGMT_FT_IEEE8021X || params->key_mgmt_suite == WPA_KEY_MGMT_FT_PSK || params->key_mgmt_suite == WPA_KEY_MGMT_CCKM) { int mgmt = WLAN_AKM_SUITE_PSK; switch (params->key_mgmt_suite) { case WPA_KEY_MGMT_CCKM: mgmt = WLAN_AKM_SUITE_CCKM; break; case WPA_KEY_MGMT_IEEE8021X: mgmt = WLAN_AKM_SUITE_8021X; break; case WPA_KEY_MGMT_FT_IEEE8021X: mgmt = WLAN_AKM_SUITE_FT_8021X; break; case WPA_KEY_MGMT_FT_PSK: mgmt = WLAN_AKM_SUITE_FT_PSK; break; case WPA_KEY_MGMT_PSK: default: mgmt = WLAN_AKM_SUITE_PSK; break; } NLA_PUT_U32(msg, NL80211_ATTR_AKM_SUITES, mgmt); } NLA_PUT_FLAG(msg, NL80211_ATTR_CONTROL_PORT); if (params->mgmt_frame_protection == MGMT_FRAME_PROTECTION_REQUIRED) NLA_PUT_U32(msg, NL80211_ATTR_USE_MFP, NL80211_MFP_REQUIRED); if (params->disable_ht) NLA_PUT_FLAG(msg, NL80211_ATTR_DISABLE_HT); if (params->htcaps && params->htcaps_mask) { int sz = sizeof(struct ieee80211_ht_capabilities); NLA_PUT(msg, NL80211_ATTR_HT_CAPABILITY, sz, params->htcaps); NLA_PUT(msg, NL80211_ATTR_HT_CAPABILITY_MASK, sz, params->htcaps_mask); } #ifdef CONFIG_VHT_OVERRIDES if (params->disable_vht) { wpa_printf(MSG_DEBUG, " * VHT disabled"); NLA_PUT_FLAG(msg, NL80211_ATTR_DISABLE_VHT); } if (params->vhtcaps && params->vhtcaps_mask) { int sz = sizeof(struct ieee80211_vht_capabilities); NLA_PUT(msg, NL80211_ATTR_VHT_CAPABILITY, sz, params->vhtcaps); NLA_PUT(msg, NL80211_ATTR_VHT_CAPABILITY_MASK, sz, params->vhtcaps_mask); } #endif /* CONFIG_VHT_OVERRIDES */ if (params->p2p) wpa_printf(MSG_DEBUG, " * P2P group"); return 0; nla_put_failure: return -1; } static int wpa_driver_nl80211_try_connect( struct wpa_driver_nl80211_data *drv, struct wpa_driver_associate_params *params) { struct nl_msg *msg; enum nl80211_auth_type type; int ret; int algs; msg = nlmsg_alloc(); if (!msg) return -1; wpa_printf(MSG_DEBUG, "nl80211: Connect (ifindex=%d)", drv->ifindex); nl80211_cmd(drv, msg, 0, NL80211_CMD_CONNECT); ret = nl80211_connect_common(drv, params, msg); if (ret) goto nla_put_failure; algs = 0; if (params->auth_alg & WPA_AUTH_ALG_OPEN) algs++; if (params->auth_alg & WPA_AUTH_ALG_SHARED) algs++; if (params->auth_alg & WPA_AUTH_ALG_LEAP) algs++; if (algs > 1) { wpa_printf(MSG_DEBUG, " * Leave out Auth Type for automatic " "selection"); goto skip_auth_type; } if (params->auth_alg & WPA_AUTH_ALG_OPEN) type = NL80211_AUTHTYPE_OPEN_SYSTEM; else if (params->auth_alg & WPA_AUTH_ALG_SHARED) type = NL80211_AUTHTYPE_SHARED_KEY; else if (params->auth_alg & WPA_AUTH_ALG_LEAP) type = NL80211_AUTHTYPE_NETWORK_EAP; else if (params->auth_alg & WPA_AUTH_ALG_FT) type = NL80211_AUTHTYPE_FT; else goto nla_put_failure; wpa_printf(MSG_DEBUG, " * Auth Type %d", type); NLA_PUT_U32(msg, NL80211_ATTR_AUTH_TYPE, type); skip_auth_type: ret = nl80211_set_conn_keys(params, msg); if (ret) goto nla_put_failure; ret = send_and_recv_msgs(drv, msg, NULL, NULL); msg = NULL; if (ret) { wpa_printf(MSG_DEBUG, "nl80211: MLME connect failed: ret=%d " "(%s)", ret, strerror(-ret)); goto nla_put_failure; } ret = 0; wpa_printf(MSG_DEBUG, "nl80211: Connect request send successfully"); nla_put_failure: nlmsg_free(msg); return ret; } static int wpa_driver_nl80211_connect( struct wpa_driver_nl80211_data *drv, struct wpa_driver_associate_params *params) { int ret = wpa_driver_nl80211_try_connect(drv, params); if (ret == -EALREADY) { /* * cfg80211 does not currently accept new connections if * we are already connected. As a workaround, force * disconnection and try again. */ wpa_printf(MSG_DEBUG, "nl80211: Explicitly " "disconnecting before reassociation " "attempt"); if (wpa_driver_nl80211_disconnect( drv, WLAN_REASON_PREV_AUTH_NOT_VALID)) return -1; ret = wpa_driver_nl80211_try_connect(drv, params); } return ret; } static int wpa_driver_nl80211_associate( void *priv, struct wpa_driver_associate_params *params) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; int ret; struct nl_msg *msg; if (params->mode == IEEE80211_MODE_AP) return wpa_driver_nl80211_ap(drv, params); if (params->mode == IEEE80211_MODE_IBSS) return wpa_driver_nl80211_ibss(drv, params); if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) { enum nl80211_iftype nlmode = params->p2p ? NL80211_IFTYPE_P2P_CLIENT : NL80211_IFTYPE_STATION; if (wpa_driver_nl80211_set_mode(priv, nlmode) < 0) return -1; return wpa_driver_nl80211_connect(drv, params); } nl80211_mark_disconnected(drv); msg = nlmsg_alloc(); if (!msg) return -1; wpa_printf(MSG_DEBUG, "nl80211: Associate (ifindex=%d)", drv->ifindex); nl80211_cmd(drv, msg, 0, NL80211_CMD_ASSOCIATE); ret = nl80211_connect_common(drv, params, msg); if (ret) goto nla_put_failure; if (params->prev_bssid) { wpa_printf(MSG_DEBUG, " * prev_bssid=" MACSTR, MAC2STR(params->prev_bssid)); NLA_PUT(msg, NL80211_ATTR_PREV_BSSID, ETH_ALEN, params->prev_bssid); } ret = send_and_recv_msgs(drv, msg, NULL, NULL); msg = NULL; if (ret) { wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: MLME command failed (assoc): ret=%d (%s)", ret, strerror(-ret)); nl80211_dump_scan(drv); goto nla_put_failure; } ret = 0; wpa_printf(MSG_DEBUG, "nl80211: Association request send " "successfully"); nla_put_failure: nlmsg_free(msg); return ret; } static int nl80211_set_mode(struct wpa_driver_nl80211_data *drv, int ifindex, enum nl80211_iftype mode) { struct nl_msg *msg; int ret = -ENOBUFS; wpa_printf(MSG_DEBUG, "nl80211: Set mode ifindex %d iftype %d (%s)", ifindex, mode, nl80211_iftype_str(mode)); msg = nlmsg_alloc(); if (!msg) return -ENOMEM; nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_INTERFACE); if (nl80211_set_iface_id(msg, drv->first_bss) < 0) goto nla_put_failure; NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, mode); ret = send_and_recv_msgs(drv, msg, NULL, NULL); msg = NULL; if (!ret) return 0; nla_put_failure: nlmsg_free(msg); wpa_printf(MSG_DEBUG, "nl80211: Failed to set interface %d to mode %d:" " %d (%s)", ifindex, mode, ret, strerror(-ret)); return ret; } static int wpa_driver_nl80211_set_mode(struct i802_bss *bss, enum nl80211_iftype nlmode) { struct wpa_driver_nl80211_data *drv = bss->drv; int ret = -1; int i; int was_ap = is_ap_interface(drv->nlmode); int res; res = nl80211_set_mode(drv, drv->ifindex, nlmode); if (res && nlmode == nl80211_get_ifmode(bss)) res = 0; if (res == 0) { drv->nlmode = nlmode; ret = 0; goto done; } if (res == -ENODEV) return -1; if (nlmode == drv->nlmode) { wpa_printf(MSG_DEBUG, "nl80211: Interface already in " "requested mode - ignore error"); ret = 0; goto done; /* Already in the requested mode */ } /* mac80211 doesn't allow mode changes while the device is up, so * take the device down, try to set the mode again, and bring the * device back up. */ wpa_printf(MSG_DEBUG, "nl80211: Try mode change after setting " "interface down"); for (i = 0; i < 10; i++) { res = i802_set_iface_flags(bss, 0); if (res == -EACCES || res == -ENODEV) break; if (res == 0) { /* Try to set the mode again while the interface is * down */ ret = nl80211_set_mode(drv, drv->ifindex, nlmode); if (ret == -EACCES) break; res = i802_set_iface_flags(bss, 1); if (res && !ret) ret = -1; else if (ret != -EBUSY) break; } else wpa_printf(MSG_DEBUG, "nl80211: Failed to set " "interface down"); os_sleep(0, 100000); } if (!ret) { wpa_printf(MSG_DEBUG, "nl80211: Mode change succeeded while " "interface is down"); drv->nlmode = nlmode; drv->ignore_if_down_event = 1; } done: if (ret) { wpa_printf(MSG_DEBUG, "nl80211: Interface mode change to %d " "from %d failed", nlmode, drv->nlmode); return ret; } if (is_p2p_net_interface(nlmode)) nl80211_disable_11b_rates(drv, drv->ifindex, 1); else if (drv->disabled_11b_rates) nl80211_disable_11b_rates(drv, drv->ifindex, 0); if (is_ap_interface(nlmode)) { nl80211_mgmt_unsubscribe(bss, "start AP"); /* Setup additional AP mode functionality if needed */ if (nl80211_setup_ap(bss)) return -1; } else if (was_ap) { /* Remove additional AP mode functionality */ nl80211_teardown_ap(bss); } else { nl80211_mgmt_unsubscribe(bss, "mode change"); } if (!bss->in_deinit && !is_ap_interface(nlmode) && nl80211_mgmt_subscribe_non_ap(bss) < 0) wpa_printf(MSG_DEBUG, "nl80211: Failed to register Action " "frame processing - ignore for now"); return 0; } static int wpa_driver_nl80211_get_capa(void *priv, struct wpa_driver_capa *capa) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; if (!drv->has_capability) return -1; os_memcpy(capa, &drv->capa, sizeof(*capa)); if (drv->extended_capa && drv->extended_capa_mask) { capa->extended_capa = drv->extended_capa; capa->extended_capa_mask = drv->extended_capa_mask; capa->extended_capa_len = drv->extended_capa_len; } if ((capa->flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE) && !drv->allow_p2p_device) { wpa_printf(MSG_DEBUG, "nl80211: Do not indicate P2P_DEVICE support (p2p_device=1 driver param not specified)"); capa->flags &= ~WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE; } return 0; } static int wpa_driver_nl80211_set_operstate(void *priv, int state) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; wpa_printf(MSG_DEBUG, "nl80211: Set %s operstate %d->%d (%s)", bss->ifname, drv->operstate, state, state ? "UP" : "DORMANT"); drv->operstate = state; return netlink_send_oper_ifla(drv->global->netlink, drv->ifindex, -1, state ? IF_OPER_UP : IF_OPER_DORMANT); } static int wpa_driver_nl80211_set_supp_port(void *priv, int authorized) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; struct nl80211_sta_flag_update upd; int ret = -ENOBUFS; if (!drv->associated && is_zero_ether_addr(drv->bssid) && !authorized) { wpa_printf(MSG_DEBUG, "nl80211: Skip set_supp_port(unauthorized) while not associated"); return 0; } wpa_printf(MSG_DEBUG, "nl80211: Set supplicant port %sauthorized for " MACSTR, authorized ? "" : "un", MAC2STR(drv->bssid)); msg = nlmsg_alloc(); if (!msg) return -ENOMEM; nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_STATION); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname)); NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, drv->bssid); os_memset(&upd, 0, sizeof(upd)); upd.mask = BIT(NL80211_STA_FLAG_AUTHORIZED); if (authorized) upd.set = BIT(NL80211_STA_FLAG_AUTHORIZED); NLA_PUT(msg, NL80211_ATTR_STA_FLAGS2, sizeof(upd), &upd); ret = send_and_recv_msgs(drv, msg, NULL, NULL); msg = NULL; if (!ret) return 0; nla_put_failure: nlmsg_free(msg); wpa_printf(MSG_DEBUG, "nl80211: Failed to set STA flag: %d (%s)", ret, strerror(-ret)); return ret; } /* Set kernel driver on given frequency (MHz) */ static int i802_set_freq(void *priv, struct hostapd_freq_params *freq) { struct i802_bss *bss = priv; return wpa_driver_nl80211_set_freq(bss, freq); } static inline int min_int(int a, int b) { if (a < b) return a; return b; } static int get_key_handler(struct nl_msg *msg, void *arg) { struct nlattr *tb[NL80211_ATTR_MAX + 1]; struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL); /* * TODO: validate the key index and mac address! * Otherwise, there's a race condition as soon as * the kernel starts sending key notifications. */ if (tb[NL80211_ATTR_KEY_SEQ]) memcpy(arg, nla_data(tb[NL80211_ATTR_KEY_SEQ]), min_int(nla_len(tb[NL80211_ATTR_KEY_SEQ]), 6)); return NL_SKIP; } static int i802_get_seqnum(const char *iface, void *priv, const u8 *addr, int idx, u8 *seq) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; msg = nlmsg_alloc(); if (!msg) return -ENOMEM; nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_KEY); if (addr) NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, idx); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(iface)); memset(seq, 0, 6); return send_and_recv_msgs(drv, msg, get_key_handler, seq); nla_put_failure: nlmsg_free(msg); return -ENOBUFS; } static int i802_set_rts(void *priv, int rts) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; int ret = -ENOBUFS; u32 val; msg = nlmsg_alloc(); if (!msg) return -ENOMEM; if (rts >= 2347) val = (u32) -1; else val = rts; nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_WIPHY); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD, val); ret = send_and_recv_msgs(drv, msg, NULL, NULL); msg = NULL; if (!ret) return 0; nla_put_failure: nlmsg_free(msg); wpa_printf(MSG_DEBUG, "nl80211: Failed to set RTS threshold %d: " "%d (%s)", rts, ret, strerror(-ret)); return ret; } static int i802_set_frag(void *priv, int frag) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; int ret = -ENOBUFS; u32 val; msg = nlmsg_alloc(); if (!msg) return -ENOMEM; if (frag >= 2346) val = (u32) -1; else val = frag; nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_WIPHY); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FRAG_THRESHOLD, val); ret = send_and_recv_msgs(drv, msg, NULL, NULL); msg = NULL; if (!ret) return 0; nla_put_failure: nlmsg_free(msg); wpa_printf(MSG_DEBUG, "nl80211: Failed to set fragmentation threshold " "%d: %d (%s)", frag, ret, strerror(-ret)); return ret; } static int i802_flush(void *priv) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; int res; msg = nlmsg_alloc(); if (!msg) return -1; wpa_printf(MSG_DEBUG, "nl80211: flush -> DEL_STATION %s (all)", bss->ifname); nl80211_cmd(drv, msg, 0, NL80211_CMD_DEL_STATION); /* * XXX: FIX! this needs to flush all VLANs too */ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname)); res = send_and_recv_msgs(drv, msg, NULL, NULL); if (res) { wpa_printf(MSG_DEBUG, "nl80211: Station flush failed: ret=%d " "(%s)", res, strerror(-res)); } return res; nla_put_failure: nlmsg_free(msg); return -ENOBUFS; } static int get_sta_handler(struct nl_msg *msg, void *arg) { struct nlattr *tb[NL80211_ATTR_MAX + 1]; struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); struct hostap_sta_driver_data *data = arg; struct nlattr *stats[NL80211_STA_INFO_MAX + 1]; static struct nla_policy stats_policy[NL80211_STA_INFO_MAX + 1] = { [NL80211_STA_INFO_INACTIVE_TIME] = { .type = NLA_U32 }, [NL80211_STA_INFO_RX_BYTES] = { .type = NLA_U32 }, [NL80211_STA_INFO_TX_BYTES] = { .type = NLA_U32 }, [NL80211_STA_INFO_RX_PACKETS] = { .type = NLA_U32 }, [NL80211_STA_INFO_TX_PACKETS] = { .type = NLA_U32 }, [NL80211_STA_INFO_TX_FAILED] = { .type = NLA_U32 }, }; nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL); /* * TODO: validate the interface and mac address! * Otherwise, there's a race condition as soon as * the kernel starts sending station notifications. */ if (!tb[NL80211_ATTR_STA_INFO]) { wpa_printf(MSG_DEBUG, "sta stats missing!"); return NL_SKIP; } if (nla_parse_nested(stats, NL80211_STA_INFO_MAX, tb[NL80211_ATTR_STA_INFO], stats_policy)) { wpa_printf(MSG_DEBUG, "failed to parse nested attributes!"); return NL_SKIP; } if (stats[NL80211_STA_INFO_INACTIVE_TIME]) data->inactive_msec = nla_get_u32(stats[NL80211_STA_INFO_INACTIVE_TIME]); if (stats[NL80211_STA_INFO_RX_BYTES]) data->rx_bytes = nla_get_u32(stats[NL80211_STA_INFO_RX_BYTES]); if (stats[NL80211_STA_INFO_TX_BYTES]) data->tx_bytes = nla_get_u32(stats[NL80211_STA_INFO_TX_BYTES]); if (stats[NL80211_STA_INFO_RX_PACKETS]) data->rx_packets = nla_get_u32(stats[NL80211_STA_INFO_RX_PACKETS]); if (stats[NL80211_STA_INFO_TX_PACKETS]) data->tx_packets = nla_get_u32(stats[NL80211_STA_INFO_TX_PACKETS]); if (stats[NL80211_STA_INFO_TX_FAILED]) data->tx_retry_failed = nla_get_u32(stats[NL80211_STA_INFO_TX_FAILED]); return NL_SKIP; } static int i802_read_sta_data(struct i802_bss *bss, struct hostap_sta_driver_data *data, const u8 *addr) { struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; os_memset(data, 0, sizeof(*data)); msg = nlmsg_alloc(); if (!msg) return -ENOMEM; nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_STATION); NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname)); return send_and_recv_msgs(drv, msg, get_sta_handler, data); nla_put_failure: nlmsg_free(msg); return -ENOBUFS; } static int i802_set_tx_queue_params(void *priv, int queue, int aifs, int cw_min, int cw_max, int burst_time) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; struct nlattr *txq, *params; msg = nlmsg_alloc(); if (!msg) return -1; nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_WIPHY); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname)); txq = nla_nest_start(msg, NL80211_ATTR_WIPHY_TXQ_PARAMS); if (!txq) goto nla_put_failure; /* We are only sending parameters for a single TXQ at a time */ params = nla_nest_start(msg, 1); if (!params) goto nla_put_failure; switch (queue) { case 0: NLA_PUT_U8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_VO); break; case 1: NLA_PUT_U8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_VI); break; case 2: NLA_PUT_U8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_BE); break; case 3: NLA_PUT_U8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_BK); break; } /* Burst time is configured in units of 0.1 msec and TXOP parameter in * 32 usec, so need to convert the value here. */ NLA_PUT_U16(msg, NL80211_TXQ_ATTR_TXOP, (burst_time * 100 + 16) / 32); NLA_PUT_U16(msg, NL80211_TXQ_ATTR_CWMIN, cw_min); NLA_PUT_U16(msg, NL80211_TXQ_ATTR_CWMAX, cw_max); NLA_PUT_U8(msg, NL80211_TXQ_ATTR_AIFS, aifs); nla_nest_end(msg, params); nla_nest_end(msg, txq); if (send_and_recv_msgs(drv, msg, NULL, NULL) == 0) return 0; msg = NULL; nla_put_failure: nlmsg_free(msg); return -1; } static int i802_set_sta_vlan(struct i802_bss *bss, const u8 *addr, const char *ifname, int vlan_id) { struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; int ret = -ENOBUFS; msg = nlmsg_alloc(); if (!msg) return -ENOMEM; wpa_printf(MSG_DEBUG, "nl80211: %s[%d]: set_sta_vlan(" MACSTR ", ifname=%s[%d], vlan_id=%d)", bss->ifname, if_nametoindex(bss->ifname), MAC2STR(addr), ifname, if_nametoindex(ifname), vlan_id); nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_STATION); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname)); NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); NLA_PUT_U32(msg, NL80211_ATTR_STA_VLAN, if_nametoindex(ifname)); ret = send_and_recv_msgs(drv, msg, NULL, NULL); msg = NULL; if (ret < 0) { wpa_printf(MSG_ERROR, "nl80211: NL80211_ATTR_STA_VLAN (addr=" MACSTR " ifname=%s vlan_id=%d) failed: %d (%s)", MAC2STR(addr), ifname, vlan_id, ret, strerror(-ret)); } nla_put_failure: nlmsg_free(msg); return ret; } static int i802_get_inact_sec(void *priv, const u8 *addr) { struct hostap_sta_driver_data data; int ret; data.inactive_msec = (unsigned long) -1; ret = i802_read_sta_data(priv, &data, addr); if (ret || data.inactive_msec == (unsigned long) -1) return -1; return data.inactive_msec / 1000; } static int i802_sta_clear_stats(void *priv, const u8 *addr) { #if 0 /* TODO */ #endif return 0; } static int i802_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, int reason) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; struct ieee80211_mgmt mgmt; if (drv->device_ap_sme) return wpa_driver_nl80211_sta_remove(bss, addr); memset(&mgmt, 0, sizeof(mgmt)); mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_DEAUTH); memcpy(mgmt.da, addr, ETH_ALEN); memcpy(mgmt.sa, own_addr, ETH_ALEN); memcpy(mgmt.bssid, own_addr, ETH_ALEN); mgmt.u.deauth.reason_code = host_to_le16(reason); return wpa_driver_nl80211_send_mlme(bss, (u8 *) &mgmt, IEEE80211_HDRLEN + sizeof(mgmt.u.deauth), 0, 0, 0, 0, 0); } static int i802_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr, int reason) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; struct ieee80211_mgmt mgmt; if (drv->device_ap_sme) return wpa_driver_nl80211_sta_remove(bss, addr); memset(&mgmt, 0, sizeof(mgmt)); mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_DISASSOC); memcpy(mgmt.da, addr, ETH_ALEN); memcpy(mgmt.sa, own_addr, ETH_ALEN); memcpy(mgmt.bssid, own_addr, ETH_ALEN); mgmt.u.disassoc.reason_code = host_to_le16(reason); return wpa_driver_nl80211_send_mlme(bss, (u8 *) &mgmt, IEEE80211_HDRLEN + sizeof(mgmt.u.disassoc), 0, 0, 0, 0, 0); } static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx) { int i; int *old; wpa_printf(MSG_DEBUG, "nl80211: Add own interface ifindex %d", ifidx); for (i = 0; i < drv->num_if_indices; i++) { if (drv->if_indices[i] == 0) { drv->if_indices[i] = ifidx; return; } } if (drv->if_indices != drv->default_if_indices) old = drv->if_indices; else old = NULL; drv->if_indices = os_realloc_array(old, drv->num_if_indices + 1, sizeof(int)); if (!drv->if_indices) { if (!old) drv->if_indices = drv->default_if_indices; else drv->if_indices = old; wpa_printf(MSG_ERROR, "Failed to reallocate memory for " "interfaces"); wpa_printf(MSG_ERROR, "Ignoring EAPOL on interface %d", ifidx); return; } else if (!old) os_memcpy(drv->if_indices, drv->default_if_indices, sizeof(drv->default_if_indices)); drv->if_indices[drv->num_if_indices] = ifidx; drv->num_if_indices++; } static void del_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx) { int i; for (i = 0; i < drv->num_if_indices; i++) { if (drv->if_indices[i] == ifidx) { drv->if_indices[i] = 0; break; } } } static int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx) { int i; for (i = 0; i < drv->num_if_indices; i++) if (drv->if_indices[i] == ifidx) return 1; return 0; } static int i802_set_wds_sta(void *priv, const u8 *addr, int aid, int val, const char *bridge_ifname, char *ifname_wds) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; char name[IFNAMSIZ + 1]; os_snprintf(name, sizeof(name), "%s.sta%d", bss->ifname, aid); if (ifname_wds) os_strlcpy(ifname_wds, name, IFNAMSIZ + 1); wpa_printf(MSG_DEBUG, "nl80211: Set WDS STA addr=" MACSTR " aid=%d val=%d name=%s", MAC2STR(addr), aid, val, name); if (val) { if (!if_nametoindex(name)) { if (nl80211_create_iface(drv, name, NL80211_IFTYPE_AP_VLAN, bss->addr, 1, NULL, NULL, 0) < 0) return -1; if (bridge_ifname && linux_br_add_if(drv->global->ioctl_sock, bridge_ifname, name) < 0) return -1; } if (linux_set_iface_flags(drv->global->ioctl_sock, name, 1)) { wpa_printf(MSG_ERROR, "nl80211: Failed to set WDS STA " "interface %s up", name); } return i802_set_sta_vlan(priv, addr, name, 0); } else { if (bridge_ifname) linux_br_del_if(drv->global->ioctl_sock, bridge_ifname, name); i802_set_sta_vlan(priv, addr, bss->ifname, 0); return wpa_driver_nl80211_if_remove(priv, WPA_IF_AP_VLAN, name); } } static void handle_eapol(int sock, void *eloop_ctx, void *sock_ctx) { struct wpa_driver_nl80211_data *drv = eloop_ctx; struct sockaddr_ll lladdr; unsigned char buf[3000]; int len; socklen_t fromlen = sizeof(lladdr); len = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *)&lladdr, &fromlen); if (len < 0) { wpa_printf(MSG_ERROR, "nl80211: EAPOL recv failed: %s", strerror(errno)); return; } if (have_ifidx(drv, lladdr.sll_ifindex)) drv_event_eapol_rx(drv->ctx, lladdr.sll_addr, buf, len); } static int i802_check_bridge(struct wpa_driver_nl80211_data *drv, struct i802_bss *bss, const char *brname, const char *ifname) { int ifindex; char in_br[IFNAMSIZ]; os_strlcpy(bss->brname, brname, IFNAMSIZ); ifindex = if_nametoindex(brname); if (ifindex == 0) { /* * Bridge was configured, but the bridge device does * not exist. Try to add it now. */ if (linux_br_add(drv->global->ioctl_sock, brname) < 0) { wpa_printf(MSG_ERROR, "nl80211: Failed to add the " "bridge interface %s: %s", brname, strerror(errno)); return -1; } bss->added_bridge = 1; add_ifidx(drv, if_nametoindex(brname)); } if (linux_br_get(in_br, ifname) == 0) { if (os_strcmp(in_br, brname) == 0) return 0; /* already in the bridge */ wpa_printf(MSG_DEBUG, "nl80211: Removing interface %s from " "bridge %s", ifname, in_br); if (linux_br_del_if(drv->global->ioctl_sock, in_br, ifname) < 0) { wpa_printf(MSG_ERROR, "nl80211: Failed to " "remove interface %s from bridge " "%s: %s", ifname, brname, strerror(errno)); return -1; } } wpa_printf(MSG_DEBUG, "nl80211: Adding interface %s into bridge %s", ifname, brname); if (linux_br_add_if(drv->global->ioctl_sock, brname, ifname) < 0) { wpa_printf(MSG_ERROR, "nl80211: Failed to add interface %s " "into bridge %s: %s", ifname, brname, strerror(errno)); return -1; } bss->added_if_into_bridge = 1; return 0; } static void *i802_init(struct hostapd_data *hapd, struct wpa_init_params *params) { struct wpa_driver_nl80211_data *drv; struct i802_bss *bss; size_t i; char brname[IFNAMSIZ]; int ifindex, br_ifindex; int br_added = 0; bss = wpa_driver_nl80211_drv_init(hapd, params->ifname, params->global_priv, 1, params->bssid); if (bss == NULL) return NULL; drv = bss->drv; if (linux_br_get(brname, params->ifname) == 0) { wpa_printf(MSG_DEBUG, "nl80211: Interface %s is in bridge %s", params->ifname, brname); br_ifindex = if_nametoindex(brname); } else { brname[0] = '\0'; br_ifindex = 0; } for (i = 0; i < params->num_bridge; i++) { if (params->bridge[i]) { ifindex = if_nametoindex(params->bridge[i]); if (ifindex) add_ifidx(drv, ifindex); if (ifindex == br_ifindex) br_added = 1; } } if (!br_added && br_ifindex && (params->num_bridge == 0 || !params->bridge[0])) add_ifidx(drv, br_ifindex); /* start listening for EAPOL on the default AP interface */ add_ifidx(drv, drv->ifindex); if (params->num_bridge && params->bridge[0] && i802_check_bridge(drv, bss, params->bridge[0], params->ifname) < 0) goto failed; drv->eapol_sock = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_PAE)); if (drv->eapol_sock < 0) { wpa_printf(MSG_ERROR, "nl80211: socket(PF_PACKET, SOCK_DGRAM, ETH_P_PAE) failed: %s", strerror(errno)); goto failed; } if (eloop_register_read_sock(drv->eapol_sock, handle_eapol, drv, NULL)) { wpa_printf(MSG_INFO, "nl80211: Could not register read socket for eapol"); goto failed; } if (linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname, params->own_addr)) goto failed; memcpy(bss->addr, params->own_addr, ETH_ALEN); return bss; failed: wpa_driver_nl80211_deinit(bss); return NULL; } static void i802_deinit(void *priv) { struct i802_bss *bss = priv; wpa_driver_nl80211_deinit(bss); } static enum nl80211_iftype wpa_driver_nl80211_if_type( enum wpa_driver_if_type type) { switch (type) { case WPA_IF_STATION: return NL80211_IFTYPE_STATION; case WPA_IF_P2P_CLIENT: case WPA_IF_P2P_GROUP: return NL80211_IFTYPE_P2P_CLIENT; case WPA_IF_AP_VLAN: return NL80211_IFTYPE_AP_VLAN; case WPA_IF_AP_BSS: return NL80211_IFTYPE_AP; case WPA_IF_P2P_GO: return NL80211_IFTYPE_P2P_GO; case WPA_IF_P2P_DEVICE: return NL80211_IFTYPE_P2P_DEVICE; } return -1; } #ifdef CONFIG_P2P static int nl80211_addr_in_use(struct nl80211_global *global, const u8 *addr) { struct wpa_driver_nl80211_data *drv; dl_list_for_each(drv, &global->interfaces, struct wpa_driver_nl80211_data, list) { if (os_memcmp(addr, drv->first_bss->addr, ETH_ALEN) == 0) return 1; } return 0; } static int nl80211_p2p_interface_addr(struct wpa_driver_nl80211_data *drv, u8 *new_addr) { unsigned int idx; if (!drv->global) return -1; os_memcpy(new_addr, drv->first_bss->addr, ETH_ALEN); for (idx = 0; idx < 64; idx++) { new_addr[0] = drv->first_bss->addr[0] | 0x02; new_addr[0] ^= idx << 2; if (!nl80211_addr_in_use(drv->global, new_addr)) break; } if (idx == 64) return -1; wpa_printf(MSG_DEBUG, "nl80211: Assigned new P2P Interface Address " MACSTR, MAC2STR(new_addr)); return 0; } #endif /* CONFIG_P2P */ struct wdev_info { u64 wdev_id; int wdev_id_set; u8 macaddr[ETH_ALEN]; }; static int nl80211_wdev_handler(struct nl_msg *msg, void *arg) { struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); struct nlattr *tb[NL80211_ATTR_MAX + 1]; struct wdev_info *wi = arg; nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL); if (tb[NL80211_ATTR_WDEV]) { wi->wdev_id = nla_get_u64(tb[NL80211_ATTR_WDEV]); wi->wdev_id_set = 1; } if (tb[NL80211_ATTR_MAC]) os_memcpy(wi->macaddr, nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN); return NL_SKIP; } static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type, const char *ifname, const u8 *addr, void *bss_ctx, void **drv_priv, char *force_ifname, u8 *if_addr, const char *bridge, int use_existing) { enum nl80211_iftype nlmode; struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; int ifidx; int added = 1; if (addr) os_memcpy(if_addr, addr, ETH_ALEN); nlmode = wpa_driver_nl80211_if_type(type); if (nlmode == NL80211_IFTYPE_P2P_DEVICE) { struct wdev_info p2pdev_info; os_memset(&p2pdev_info, 0, sizeof(p2pdev_info)); ifidx = nl80211_create_iface(drv, ifname, nlmode, addr, 0, nl80211_wdev_handler, &p2pdev_info, use_existing); if (!p2pdev_info.wdev_id_set || ifidx != 0) { wpa_printf(MSG_ERROR, "nl80211: Failed to create a P2P Device interface %s", ifname); return -1; } drv->global->if_add_wdevid = p2pdev_info.wdev_id; drv->global->if_add_wdevid_set = p2pdev_info.wdev_id_set; if (!is_zero_ether_addr(p2pdev_info.macaddr)) os_memcpy(if_addr, p2pdev_info.macaddr, ETH_ALEN); wpa_printf(MSG_DEBUG, "nl80211: New P2P Device interface %s (0x%llx) created", ifname, (long long unsigned int) p2pdev_info.wdev_id); } else { ifidx = nl80211_create_iface(drv, ifname, nlmode, addr, 0, NULL, NULL, use_existing); if (use_existing && ifidx == -ENFILE) { added = 0; ifidx = if_nametoindex(ifname); } else if (ifidx < 0) { return -1; } } if (!addr) { if (drv->nlmode == NL80211_IFTYPE_P2P_DEVICE) os_memcpy(if_addr, bss->addr, ETH_ALEN); else if (linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname, if_addr) < 0) { if (added) nl80211_remove_iface(drv, ifidx); return -1; } } #ifdef CONFIG_P2P if (!addr && (type == WPA_IF_P2P_CLIENT || type == WPA_IF_P2P_GROUP || type == WPA_IF_P2P_GO)) { /* Enforce unique P2P Interface Address */ u8 new_addr[ETH_ALEN]; if (linux_get_ifhwaddr(drv->global->ioctl_sock, ifname, new_addr) < 0) { nl80211_remove_iface(drv, ifidx); return -1; } if (nl80211_addr_in_use(drv->global, new_addr)) { wpa_printf(MSG_DEBUG, "nl80211: Allocate new address " "for P2P group interface"); if (nl80211_p2p_interface_addr(drv, new_addr) < 0) { nl80211_remove_iface(drv, ifidx); return -1; } if (linux_set_ifhwaddr(drv->global->ioctl_sock, ifname, new_addr) < 0) { nl80211_remove_iface(drv, ifidx); return -1; } } os_memcpy(if_addr, new_addr, ETH_ALEN); } #endif /* CONFIG_P2P */ if (type == WPA_IF_AP_BSS) { struct i802_bss *new_bss = os_zalloc(sizeof(*new_bss)); if (new_bss == NULL) { if (added) nl80211_remove_iface(drv, ifidx); return -1; } if (bridge && i802_check_bridge(drv, new_bss, bridge, ifname) < 0) { wpa_printf(MSG_ERROR, "nl80211: Failed to add the new " "interface %s to a bridge %s", ifname, bridge); if (added) nl80211_remove_iface(drv, ifidx); os_free(new_bss); return -1; } if (linux_set_iface_flags(drv->global->ioctl_sock, ifname, 1)) { nl80211_remove_iface(drv, ifidx); os_free(new_bss); return -1; } os_strlcpy(new_bss->ifname, ifname, IFNAMSIZ); os_memcpy(new_bss->addr, if_addr, ETH_ALEN); new_bss->ifindex = ifidx; new_bss->drv = drv; new_bss->next = drv->first_bss->next; new_bss->freq = drv->first_bss->freq; new_bss->ctx = bss_ctx; new_bss->added_if = added; drv->first_bss->next = new_bss; if (drv_priv) *drv_priv = new_bss; nl80211_init_bss(new_bss); /* Subscribe management frames for this WPA_IF_AP_BSS */ if (nl80211_setup_ap(new_bss)) return -1; } if (drv->global) drv->global->if_add_ifindex = ifidx; return 0; } static int wpa_driver_nl80211_if_remove(struct i802_bss *bss, enum wpa_driver_if_type type, const char *ifname) { struct wpa_driver_nl80211_data *drv = bss->drv; int ifindex = if_nametoindex(ifname); wpa_printf(MSG_DEBUG, "nl80211: %s(type=%d ifname=%s) ifindex=%d added_if=%d", __func__, type, ifname, ifindex, bss->added_if); if (ifindex > 0 && (bss->added_if || bss->ifindex != ifindex)) nl80211_remove_iface(drv, ifindex); if (type != WPA_IF_AP_BSS) return 0; if (bss->added_if_into_bridge) { if (linux_br_del_if(drv->global->ioctl_sock, bss->brname, bss->ifname) < 0) wpa_printf(MSG_INFO, "nl80211: Failed to remove " "interface %s from bridge %s: %s", bss->ifname, bss->brname, strerror(errno)); } if (bss->added_bridge) { if (linux_br_del(drv->global->ioctl_sock, bss->brname) < 0) wpa_printf(MSG_INFO, "nl80211: Failed to remove " "bridge %s: %s", bss->brname, strerror(errno)); } if (bss != drv->first_bss) { struct i802_bss *tbss; wpa_printf(MSG_DEBUG, "nl80211: Not the first BSS - remove it"); for (tbss = drv->first_bss; tbss; tbss = tbss->next) { if (tbss->next == bss) { tbss->next = bss->next; /* Unsubscribe management frames */ nl80211_teardown_ap(bss); nl80211_destroy_bss(bss); os_free(bss); bss = NULL; break; } } if (bss) wpa_printf(MSG_INFO, "nl80211: %s - could not find " "BSS %p in the list", __func__, bss); } else { wpa_printf(MSG_DEBUG, "nl80211: First BSS - reassign context"); nl80211_teardown_ap(bss); if (!bss->added_if && !drv->first_bss->next) wpa_driver_nl80211_del_beacon(drv); nl80211_destroy_bss(bss); if (!bss->added_if) i802_set_iface_flags(bss, 0); if (drv->first_bss->next) { drv->first_bss = drv->first_bss->next; drv->ctx = drv->first_bss->ctx; os_free(bss); } else { wpa_printf(MSG_DEBUG, "nl80211: No second BSS to reassign context to"); } } return 0; } static int cookie_handler(struct nl_msg *msg, void *arg) { struct nlattr *tb[NL80211_ATTR_MAX + 1]; struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); u64 *cookie = arg; nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL); if (tb[NL80211_ATTR_COOKIE]) *cookie = nla_get_u64(tb[NL80211_ATTR_COOKIE]); return NL_SKIP; } static int nl80211_send_frame_cmd(struct i802_bss *bss, unsigned int freq, unsigned int wait, const u8 *buf, size_t buf_len, u64 *cookie_out, int no_cck, int no_ack, int offchanok) { struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; u64 cookie; int ret = -1; msg = nlmsg_alloc(); if (!msg) return -1; wpa_printf(MSG_MSGDUMP, "nl80211: CMD_FRAME freq=%u wait=%u no_cck=%d " "no_ack=%d offchanok=%d", freq, wait, no_cck, no_ack, offchanok); wpa_hexdump(MSG_MSGDUMP, "CMD_FRAME", buf, buf_len); nl80211_cmd(drv, msg, 0, NL80211_CMD_FRAME); if (nl80211_set_iface_id(msg, bss) < 0) goto nla_put_failure; if (freq) NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq); if (wait) NLA_PUT_U32(msg, NL80211_ATTR_DURATION, wait); if (offchanok && (drv->capa.flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX)) NLA_PUT_FLAG(msg, NL80211_ATTR_OFFCHANNEL_TX_OK); if (no_cck) NLA_PUT_FLAG(msg, NL80211_ATTR_TX_NO_CCK_RATE); if (no_ack) NLA_PUT_FLAG(msg, NL80211_ATTR_DONT_WAIT_FOR_ACK); NLA_PUT(msg, NL80211_ATTR_FRAME, buf_len, buf); cookie = 0; ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie); msg = NULL; if (ret) { wpa_printf(MSG_DEBUG, "nl80211: Frame command failed: ret=%d " "(%s) (freq=%u wait=%u)", ret, strerror(-ret), freq, wait); goto nla_put_failure; } wpa_printf(MSG_MSGDUMP, "nl80211: Frame TX command accepted%s; " "cookie 0x%llx", no_ack ? " (no ACK)" : "", (long long unsigned int) cookie); if (cookie_out) *cookie_out = no_ack ? (u64) -1 : cookie; nla_put_failure: nlmsg_free(msg); return ret; } static int wpa_driver_nl80211_send_action(struct i802_bss *bss, unsigned int freq, unsigned int wait_time, const u8 *dst, const u8 *src, const u8 *bssid, const u8 *data, size_t data_len, int no_cck) { struct wpa_driver_nl80211_data *drv = bss->drv; int ret = -1; u8 *buf; struct ieee80211_hdr *hdr; wpa_printf(MSG_DEBUG, "nl80211: Send Action frame (ifindex=%d, " "freq=%u MHz wait=%d ms no_cck=%d)", drv->ifindex, freq, wait_time, no_cck); buf = os_zalloc(24 + data_len); if (buf == NULL) return ret; os_memcpy(buf + 24, data, data_len); hdr = (struct ieee80211_hdr *) buf; hdr->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_ACTION); os_memcpy(hdr->addr1, dst, ETH_ALEN); os_memcpy(hdr->addr2, src, ETH_ALEN); os_memcpy(hdr->addr3, bssid, ETH_ALEN); if (is_ap_interface(drv->nlmode) && (!(drv->capa.flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX) || (int) freq == bss->freq || drv->device_ap_sme || !drv->use_monitor)) ret = wpa_driver_nl80211_send_mlme(bss, buf, 24 + data_len, 0, freq, no_cck, 1, wait_time); else ret = nl80211_send_frame_cmd(bss, freq, wait_time, buf, 24 + data_len, &drv->send_action_cookie, no_cck, 0, 1); os_free(buf); return ret; } static void wpa_driver_nl80211_send_action_cancel_wait(void *priv) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; int ret; msg = nlmsg_alloc(); if (!msg) return; wpa_printf(MSG_DEBUG, "nl80211: Cancel TX frame wait: cookie=0x%llx", (long long unsigned int) drv->send_action_cookie); nl80211_cmd(drv, msg, 0, NL80211_CMD_FRAME_WAIT_CANCEL); if (nl80211_set_iface_id(msg, bss) < 0) goto nla_put_failure; NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, drv->send_action_cookie); ret = send_and_recv_msgs(drv, msg, NULL, NULL); msg = NULL; if (ret) wpa_printf(MSG_DEBUG, "nl80211: wait cancel failed: ret=%d " "(%s)", ret, strerror(-ret)); nla_put_failure: nlmsg_free(msg); } static int wpa_driver_nl80211_remain_on_channel(void *priv, unsigned int freq, unsigned int duration) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; int ret; u64 cookie; msg = nlmsg_alloc(); if (!msg) return -1; nl80211_cmd(drv, msg, 0, NL80211_CMD_REMAIN_ON_CHANNEL); if (nl80211_set_iface_id(msg, bss) < 0) goto nla_put_failure; NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq); NLA_PUT_U32(msg, NL80211_ATTR_DURATION, duration); cookie = 0; ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie); msg = NULL; if (ret == 0) { wpa_printf(MSG_DEBUG, "nl80211: Remain-on-channel cookie " "0x%llx for freq=%u MHz duration=%u", (long long unsigned int) cookie, freq, duration); drv->remain_on_chan_cookie = cookie; drv->pending_remain_on_chan = 1; return 0; } wpa_printf(MSG_DEBUG, "nl80211: Failed to request remain-on-channel " "(freq=%d duration=%u): %d (%s)", freq, duration, ret, strerror(-ret)); nla_put_failure: nlmsg_free(msg); return -1; } static int wpa_driver_nl80211_cancel_remain_on_channel(void *priv) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; int ret; if (!drv->pending_remain_on_chan) { wpa_printf(MSG_DEBUG, "nl80211: No pending remain-on-channel " "to cancel"); return -1; } wpa_printf(MSG_DEBUG, "nl80211: Cancel remain-on-channel with cookie " "0x%llx", (long long unsigned int) drv->remain_on_chan_cookie); msg = nlmsg_alloc(); if (!msg) return -1; nl80211_cmd(drv, msg, 0, NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL); if (nl80211_set_iface_id(msg, bss) < 0) goto nla_put_failure; NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, drv->remain_on_chan_cookie); ret = send_and_recv_msgs(drv, msg, NULL, NULL); msg = NULL; if (ret == 0) return 0; wpa_printf(MSG_DEBUG, "nl80211: Failed to cancel remain-on-channel: " "%d (%s)", ret, strerror(-ret)); nla_put_failure: nlmsg_free(msg); return -1; } static int wpa_driver_nl80211_probe_req_report(struct i802_bss *bss, int report) { struct wpa_driver_nl80211_data *drv = bss->drv; if (!report) { if (bss->nl_preq && drv->device_ap_sme && is_ap_interface(drv->nlmode)) { /* * Do not disable Probe Request reporting that was * enabled in nl80211_setup_ap(). */ wpa_printf(MSG_DEBUG, "nl80211: Skip disabling of " "Probe Request reporting nl_preq=%p while " "in AP mode", bss->nl_preq); } else if (bss->nl_preq) { wpa_printf(MSG_DEBUG, "nl80211: Disable Probe Request " "reporting nl_preq=%p", bss->nl_preq); nl80211_destroy_eloop_handle(&bss->nl_preq); } return 0; } if (bss->nl_preq) { wpa_printf(MSG_DEBUG, "nl80211: Probe Request reporting " "already on! nl_preq=%p", bss->nl_preq); return 0; } bss->nl_preq = nl_create_handle(drv->global->nl_cb, "preq"); if (bss->nl_preq == NULL) return -1; wpa_printf(MSG_DEBUG, "nl80211: Enable Probe Request " "reporting nl_preq=%p", bss->nl_preq); if (nl80211_register_frame(bss, bss->nl_preq, (WLAN_FC_TYPE_MGMT << 2) | (WLAN_FC_STYPE_PROBE_REQ << 4), NULL, 0) < 0) goto out_err; nl80211_register_eloop_read(&bss->nl_preq, wpa_driver_nl80211_event_receive, bss->nl_cb); return 0; out_err: nl_destroy_handles(&bss->nl_preq); return -1; } static int nl80211_disable_11b_rates(struct wpa_driver_nl80211_data *drv, int ifindex, int disabled) { struct nl_msg *msg; struct nlattr *bands, *band; int ret; msg = nlmsg_alloc(); if (!msg) return -1; nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_TX_BITRATE_MASK); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex); bands = nla_nest_start(msg, NL80211_ATTR_TX_RATES); if (!bands) goto nla_put_failure; /* * Disable 2 GHz rates 1, 2, 5.5, 11 Mbps by masking out everything * else apart from 6, 9, 12, 18, 24, 36, 48, 54 Mbps from non-MCS * rates. All 5 GHz rates are left enabled. */ band = nla_nest_start(msg, NL80211_BAND_2GHZ); if (!band) goto nla_put_failure; if (disabled) { NLA_PUT(msg, NL80211_TXRATE_LEGACY, 8, "\x0c\x12\x18\x24\x30\x48\x60\x6c"); } nla_nest_end(msg, band); nla_nest_end(msg, bands); ret = send_and_recv_msgs(drv, msg, NULL, NULL); msg = NULL; if (ret) { wpa_printf(MSG_DEBUG, "nl80211: Set TX rates failed: ret=%d " "(%s)", ret, strerror(-ret)); } else drv->disabled_11b_rates = disabled; return ret; nla_put_failure: nlmsg_free(msg); return -1; } static int wpa_driver_nl80211_deinit_ap(void *priv) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; if (!is_ap_interface(drv->nlmode)) return -1; wpa_driver_nl80211_del_beacon(drv); /* * If the P2P GO interface was dynamically added, then it is * possible that the interface change to station is not possible. */ if (drv->nlmode == NL80211_IFTYPE_P2P_GO && bss->if_dynamic) return 0; return wpa_driver_nl80211_set_mode(priv, NL80211_IFTYPE_STATION); } static int wpa_driver_nl80211_stop_ap(void *priv) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; if (!is_ap_interface(drv->nlmode)) return -1; wpa_driver_nl80211_del_beacon(drv); bss->beacon_set = 0; return 0; } static int wpa_driver_nl80211_deinit_p2p_cli(void *priv) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; if (drv->nlmode != NL80211_IFTYPE_P2P_CLIENT) return -1; /* * If the P2P Client interface was dynamically added, then it is * possible that the interface change to station is not possible. */ if (bss->if_dynamic) return 0; return wpa_driver_nl80211_set_mode(priv, NL80211_IFTYPE_STATION); } static void wpa_driver_nl80211_resume(void *priv) { struct i802_bss *bss = priv; if (i802_set_iface_flags(bss, 1)) wpa_printf(MSG_DEBUG, "nl80211: Failed to set interface up on resume event"); } static int nl80211_send_ft_action(void *priv, u8 action, const u8 *target_ap, const u8 *ies, size_t ies_len) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; int ret; u8 *data, *pos; size_t data_len; const u8 *own_addr = bss->addr; if (action != 1) { wpa_printf(MSG_ERROR, "nl80211: Unsupported send_ft_action " "action %d", action); return -1; } /* * Action frame payload: * Category[1] = 6 (Fast BSS Transition) * Action[1] = 1 (Fast BSS Transition Request) * STA Address * Target AP Address * FT IEs */ data_len = 2 + 2 * ETH_ALEN + ies_len; data = os_malloc(data_len); if (data == NULL) return -1; pos = data; *pos++ = 0x06; /* FT Action category */ *pos++ = action; os_memcpy(pos, own_addr, ETH_ALEN); pos += ETH_ALEN; os_memcpy(pos, target_ap, ETH_ALEN); pos += ETH_ALEN; os_memcpy(pos, ies, ies_len); ret = wpa_driver_nl80211_send_action(bss, drv->assoc_freq, 0, drv->bssid, own_addr, drv->bssid, data, data_len, 0); os_free(data); return ret; } static int nl80211_signal_monitor(void *priv, int threshold, int hysteresis) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; struct nlattr *cqm; int ret = -1; wpa_printf(MSG_DEBUG, "nl80211: Signal monitor threshold=%d " "hysteresis=%d", threshold, hysteresis); msg = nlmsg_alloc(); if (!msg) return -1; nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_CQM); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex); cqm = nla_nest_start(msg, NL80211_ATTR_CQM); if (cqm == NULL) goto nla_put_failure; NLA_PUT_U32(msg, NL80211_ATTR_CQM_RSSI_THOLD, threshold); NLA_PUT_U32(msg, NL80211_ATTR_CQM_RSSI_HYST, hysteresis); nla_nest_end(msg, cqm); ret = send_and_recv_msgs(drv, msg, NULL, NULL); msg = NULL; nla_put_failure: nlmsg_free(msg); return ret; } static int get_channel_width(struct nl_msg *msg, void *arg) { struct nlattr *tb[NL80211_ATTR_MAX + 1]; struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); struct wpa_signal_info *sig_change = arg; nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL); sig_change->center_frq1 = -1; sig_change->center_frq2 = -1; sig_change->chanwidth = CHAN_WIDTH_UNKNOWN; if (tb[NL80211_ATTR_CHANNEL_WIDTH]) { sig_change->chanwidth = convert2width( nla_get_u32(tb[NL80211_ATTR_CHANNEL_WIDTH])); if (tb[NL80211_ATTR_CENTER_FREQ1]) sig_change->center_frq1 = nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ1]); if (tb[NL80211_ATTR_CENTER_FREQ2]) sig_change->center_frq2 = nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ2]); } return NL_SKIP; } static int nl80211_get_channel_width(struct wpa_driver_nl80211_data *drv, struct wpa_signal_info *sig) { struct nl_msg *msg; msg = nlmsg_alloc(); if (!msg) return -ENOMEM; nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_INTERFACE); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); return send_and_recv_msgs(drv, msg, get_channel_width, sig); nla_put_failure: nlmsg_free(msg); return -ENOBUFS; } static int nl80211_signal_poll(void *priv, struct wpa_signal_info *si) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; int res; os_memset(si, 0, sizeof(*si)); res = nl80211_get_link_signal(drv, si); if (res != 0) return res; res = nl80211_get_channel_width(drv, si); if (res != 0) return res; return nl80211_get_link_noise(drv, si); } static int wpa_driver_nl80211_shared_freq(void *priv) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; struct wpa_driver_nl80211_data *driver; int freq = 0; /* * If the same PHY is in connected state with some other interface, * then retrieve the assoc freq. */ wpa_printf(MSG_DEBUG, "nl80211: Get shared freq for PHY %s", drv->phyname); dl_list_for_each(driver, &drv->global->interfaces, struct wpa_driver_nl80211_data, list) { if (drv == driver || os_strcmp(drv->phyname, driver->phyname) != 0 || !driver->associated) continue; wpa_printf(MSG_DEBUG, "nl80211: Found a match for PHY %s - %s " MACSTR, driver->phyname, driver->first_bss->ifname, MAC2STR(driver->first_bss->addr)); if (is_ap_interface(driver->nlmode)) freq = driver->first_bss->freq; else freq = nl80211_get_assoc_freq(driver); wpa_printf(MSG_DEBUG, "nl80211: Shared freq for PHY %s: %d", drv->phyname, freq); } if (!freq) wpa_printf(MSG_DEBUG, "nl80211: No shared interface for " "PHY (%s) in associated state", drv->phyname); return freq; } static int nl80211_send_frame(void *priv, const u8 *data, size_t data_len, int encrypt) { struct i802_bss *bss = priv; return wpa_driver_nl80211_send_frame(bss, data, data_len, encrypt, 0, 0, 0, 0, 0); } static int nl80211_set_param(void *priv, const char *param) { wpa_printf(MSG_DEBUG, "nl80211: driver param='%s'", param); if (param == NULL) return 0; #ifdef CONFIG_P2P if (os_strstr(param, "use_p2p_group_interface=1")) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; wpa_printf(MSG_DEBUG, "nl80211: Use separate P2P group " "interface"); drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_CONCURRENT; drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_MGMT_AND_NON_P2P; } if (os_strstr(param, "p2p_device=1")) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; drv->allow_p2p_device = 1; } #endif /* CONFIG_P2P */ if (os_strstr(param, "use_monitor=1")) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; drv->use_monitor = 1; } if (os_strstr(param, "force_connect_cmd=1")) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; drv->capa.flags &= ~WPA_DRIVER_FLAGS_SME; } return 0; } static void * nl80211_global_init(void) { struct nl80211_global *global; struct netlink_config *cfg; global = os_zalloc(sizeof(*global)); if (global == NULL) return NULL; global->ioctl_sock = -1; dl_list_init(&global->interfaces); global->if_add_ifindex = -1; cfg = os_zalloc(sizeof(*cfg)); if (cfg == NULL) goto err; cfg->ctx = global; cfg->newlink_cb = wpa_driver_nl80211_event_rtm_newlink; cfg->dellink_cb = wpa_driver_nl80211_event_rtm_dellink; global->netlink = netlink_init(cfg); if (global->netlink == NULL) { os_free(cfg); goto err; } if (wpa_driver_nl80211_init_nl_global(global) < 0) goto err; global->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0); if (global->ioctl_sock < 0) { wpa_printf(MSG_ERROR, "nl80211: socket(PF_INET,SOCK_DGRAM) failed: %s", strerror(errno)); goto err; } return global; err: nl80211_global_deinit(global); return NULL; } static void nl80211_global_deinit(void *priv) { struct nl80211_global *global = priv; if (global == NULL) return; if (!dl_list_empty(&global->interfaces)) { wpa_printf(MSG_ERROR, "nl80211: %u interface(s) remain at " "nl80211_global_deinit", dl_list_len(&global->interfaces)); } if (global->netlink) netlink_deinit(global->netlink); nl_destroy_handles(&global->nl); if (global->nl_event) nl80211_destroy_eloop_handle(&global->nl_event); nl_cb_put(global->nl_cb); if (global->ioctl_sock >= 0) close(global->ioctl_sock); os_free(global); } static const char * nl80211_get_radio_name(void *priv) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; return drv->phyname; } static int nl80211_pmkid(struct i802_bss *bss, int cmd, const u8 *bssid, const u8 *pmkid) { struct nl_msg *msg; msg = nlmsg_alloc(); if (!msg) return -ENOMEM; nl80211_cmd(bss->drv, msg, 0, cmd); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname)); if (pmkid) NLA_PUT(msg, NL80211_ATTR_PMKID, 16, pmkid); if (bssid) NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid); return send_and_recv_msgs(bss->drv, msg, NULL, NULL); nla_put_failure: nlmsg_free(msg); return -ENOBUFS; } static int nl80211_add_pmkid(void *priv, const u8 *bssid, const u8 *pmkid) { struct i802_bss *bss = priv; wpa_printf(MSG_DEBUG, "nl80211: Add PMKID for " MACSTR, MAC2STR(bssid)); return nl80211_pmkid(bss, NL80211_CMD_SET_PMKSA, bssid, pmkid); } static int nl80211_remove_pmkid(void *priv, const u8 *bssid, const u8 *pmkid) { struct i802_bss *bss = priv; wpa_printf(MSG_DEBUG, "nl80211: Delete PMKID for " MACSTR, MAC2STR(bssid)); return nl80211_pmkid(bss, NL80211_CMD_DEL_PMKSA, bssid, pmkid); } static int nl80211_flush_pmkid(void *priv) { struct i802_bss *bss = priv; wpa_printf(MSG_DEBUG, "nl80211: Flush PMKIDs"); return nl80211_pmkid(bss, NL80211_CMD_FLUSH_PMKSA, NULL, NULL); } static void clean_survey_results(struct survey_results *survey_results) { struct freq_survey *survey, *tmp; if (dl_list_empty(&survey_results->survey_list)) return; dl_list_for_each_safe(survey, tmp, &survey_results->survey_list, struct freq_survey, list) { dl_list_del(&survey->list); os_free(survey); } } static void add_survey(struct nlattr **sinfo, u32 ifidx, struct dl_list *survey_list) { struct freq_survey *survey; survey = os_zalloc(sizeof(struct freq_survey)); if (!survey) return; survey->ifidx = ifidx; survey->freq = nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]); survey->filled = 0; if (sinfo[NL80211_SURVEY_INFO_NOISE]) { survey->nf = (int8_t) nla_get_u8(sinfo[NL80211_SURVEY_INFO_NOISE]); survey->filled |= SURVEY_HAS_NF; } if (sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME]) { survey->channel_time = nla_get_u64(sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME]); survey->filled |= SURVEY_HAS_CHAN_TIME; } if (sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY]) { survey->channel_time_busy = nla_get_u64(sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY]); survey->filled |= SURVEY_HAS_CHAN_TIME_BUSY; } if (sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_RX]) { survey->channel_time_rx = nla_get_u64(sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_RX]); survey->filled |= SURVEY_HAS_CHAN_TIME_RX; } if (sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_TX]) { survey->channel_time_tx = nla_get_u64(sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_TX]); survey->filled |= SURVEY_HAS_CHAN_TIME_TX; } wpa_printf(MSG_DEBUG, "nl80211: Freq survey dump event (freq=%d MHz noise=%d channel_time=%ld busy_time=%ld tx_time=%ld rx_time=%ld filled=%04x)", survey->freq, survey->nf, (unsigned long int) survey->channel_time, (unsigned long int) survey->channel_time_busy, (unsigned long int) survey->channel_time_tx, (unsigned long int) survey->channel_time_rx, survey->filled); dl_list_add_tail(survey_list, &survey->list); } static int check_survey_ok(struct nlattr **sinfo, u32 surveyed_freq, unsigned int freq_filter) { if (!freq_filter) return 1; return freq_filter == surveyed_freq; } static int survey_handler(struct nl_msg *msg, void *arg) { struct nlattr *tb[NL80211_ATTR_MAX + 1]; struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); struct nlattr *sinfo[NL80211_SURVEY_INFO_MAX + 1]; struct survey_results *survey_results; u32 surveyed_freq = 0; u32 ifidx; static struct nla_policy survey_policy[NL80211_SURVEY_INFO_MAX + 1] = { [NL80211_SURVEY_INFO_FREQUENCY] = { .type = NLA_U32 }, [NL80211_SURVEY_INFO_NOISE] = { .type = NLA_U8 }, }; survey_results = (struct survey_results *) arg; nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL); if (!tb[NL80211_ATTR_IFINDEX]) return NL_SKIP; ifidx = nla_get_u32(tb[NL80211_ATTR_IFINDEX]); if (!tb[NL80211_ATTR_SURVEY_INFO]) return NL_SKIP; if (nla_parse_nested(sinfo, NL80211_SURVEY_INFO_MAX, tb[NL80211_ATTR_SURVEY_INFO], survey_policy)) return NL_SKIP; if (!sinfo[NL80211_SURVEY_INFO_FREQUENCY]) { wpa_printf(MSG_ERROR, "nl80211: Invalid survey data"); return NL_SKIP; } surveyed_freq = nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]); if (!check_survey_ok(sinfo, surveyed_freq, survey_results->freq_filter)) return NL_SKIP; if (survey_results->freq_filter && survey_results->freq_filter != surveyed_freq) { wpa_printf(MSG_EXCESSIVE, "nl80211: Ignoring survey data for freq %d MHz", surveyed_freq); return NL_SKIP; } add_survey(sinfo, ifidx, &survey_results->survey_list); return NL_SKIP; } static int wpa_driver_nl80211_get_survey(void *priv, unsigned int freq) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; int err = -ENOBUFS; union wpa_event_data data; struct survey_results *survey_results; os_memset(&data, 0, sizeof(data)); survey_results = &data.survey_results; dl_list_init(&survey_results->survey_list); msg = nlmsg_alloc(); if (!msg) goto nla_put_failure; nl80211_cmd(drv, msg, NLM_F_DUMP, NL80211_CMD_GET_SURVEY); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); if (freq) data.survey_results.freq_filter = freq; do { wpa_printf(MSG_DEBUG, "nl80211: Fetch survey data"); err = send_and_recv_msgs(drv, msg, survey_handler, survey_results); } while (err > 0); if (err) { wpa_printf(MSG_ERROR, "nl80211: Failed to process survey data"); goto out_clean; } wpa_supplicant_event(drv->ctx, EVENT_SURVEY, &data); out_clean: clean_survey_results(survey_results); nla_put_failure: return err; } static void nl80211_set_rekey_info(void *priv, const u8 *kek, const u8 *kck, const u8 *replay_ctr) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; struct nlattr *replay_nested; struct nl_msg *msg; msg = nlmsg_alloc(); if (!msg) return; nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_REKEY_OFFLOAD); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex); replay_nested = nla_nest_start(msg, NL80211_ATTR_REKEY_DATA); if (!replay_nested) goto nla_put_failure; NLA_PUT(msg, NL80211_REKEY_DATA_KEK, NL80211_KEK_LEN, kek); NLA_PUT(msg, NL80211_REKEY_DATA_KCK, NL80211_KCK_LEN, kck); NLA_PUT(msg, NL80211_REKEY_DATA_REPLAY_CTR, NL80211_REPLAY_CTR_LEN, replay_ctr); nla_nest_end(msg, replay_nested); send_and_recv_msgs(drv, msg, NULL, NULL); return; nla_put_failure: nlmsg_free(msg); } static void nl80211_send_null_frame(struct i802_bss *bss, const u8 *own_addr, const u8 *addr, int qos) { /* send data frame to poll STA and check whether * this frame is ACKed */ struct { struct ieee80211_hdr hdr; u16 qos_ctl; } STRUCT_PACKED nulldata; size_t size; /* Send data frame to poll STA and check whether this frame is ACKed */ os_memset(&nulldata, 0, sizeof(nulldata)); if (qos) { nulldata.hdr.frame_control = IEEE80211_FC(WLAN_FC_TYPE_DATA, WLAN_FC_STYPE_QOS_NULL); size = sizeof(nulldata); } else { nulldata.hdr.frame_control = IEEE80211_FC(WLAN_FC_TYPE_DATA, WLAN_FC_STYPE_NULLFUNC); size = sizeof(struct ieee80211_hdr); } nulldata.hdr.frame_control |= host_to_le16(WLAN_FC_FROMDS); os_memcpy(nulldata.hdr.IEEE80211_DA_FROMDS, addr, ETH_ALEN); os_memcpy(nulldata.hdr.IEEE80211_BSSID_FROMDS, own_addr, ETH_ALEN); os_memcpy(nulldata.hdr.IEEE80211_SA_FROMDS, own_addr, ETH_ALEN); if (wpa_driver_nl80211_send_mlme(bss, (u8 *) &nulldata, size, 0, 0, 0, 0, 0) < 0) wpa_printf(MSG_DEBUG, "nl80211_send_null_frame: Failed to " "send poll frame"); } static void nl80211_poll_client(void *priv, const u8 *own_addr, const u8 *addr, int qos) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; if (!drv->poll_command_supported) { nl80211_send_null_frame(bss, own_addr, addr, qos); return; } msg = nlmsg_alloc(); if (!msg) return; nl80211_cmd(drv, msg, 0, NL80211_CMD_PROBE_CLIENT); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex); NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); send_and_recv_msgs(drv, msg, NULL, NULL); return; nla_put_failure: nlmsg_free(msg); } static int nl80211_set_power_save(struct i802_bss *bss, int enabled) { struct nl_msg *msg; msg = nlmsg_alloc(); if (!msg) return -ENOMEM; nl80211_cmd(bss->drv, msg, 0, NL80211_CMD_SET_POWER_SAVE); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex); NLA_PUT_U32(msg, NL80211_ATTR_PS_STATE, enabled ? NL80211_PS_ENABLED : NL80211_PS_DISABLED); return send_and_recv_msgs(bss->drv, msg, NULL, NULL); nla_put_failure: nlmsg_free(msg); return -ENOBUFS; } static int nl80211_set_p2p_powersave(void *priv, int legacy_ps, int opp_ps, int ctwindow) { struct i802_bss *bss = priv; wpa_printf(MSG_DEBUG, "nl80211: set_p2p_powersave (legacy_ps=%d " "opp_ps=%d ctwindow=%d)", legacy_ps, opp_ps, ctwindow); if (opp_ps != -1 || ctwindow != -1) { #ifdef ANDROID_P2P wpa_driver_set_p2p_ps(priv, legacy_ps, opp_ps, ctwindow); #else /* ANDROID_P2P */ return -1; /* Not yet supported */ #endif /* ANDROID_P2P */ } if (legacy_ps == -1) return 0; if (legacy_ps != 0 && legacy_ps != 1) return -1; /* Not yet supported */ return nl80211_set_power_save(bss, legacy_ps); } static int nl80211_start_radar_detection(void *priv, struct hostapd_freq_params *freq) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; int ret; wpa_printf(MSG_DEBUG, "nl80211: Start radar detection (CAC) %d MHz (ht_enabled=%d, vht_enabled=%d, bandwidth=%d MHz, cf1=%d MHz, cf2=%d MHz)", freq->freq, freq->ht_enabled, freq->vht_enabled, freq->bandwidth, freq->center_freq1, freq->center_freq2); if (!(drv->capa.flags & WPA_DRIVER_FLAGS_RADAR)) { wpa_printf(MSG_DEBUG, "nl80211: Driver does not support radar " "detection"); return -1; } msg = nlmsg_alloc(); if (!msg) return -1; nl80211_cmd(bss->drv, msg, 0, NL80211_CMD_RADAR_DETECT); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq->freq); if (freq->vht_enabled) { switch (freq->bandwidth) { case 20: NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH, NL80211_CHAN_WIDTH_20); break; case 40: NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH, NL80211_CHAN_WIDTH_40); break; case 80: if (freq->center_freq2) NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH, NL80211_CHAN_WIDTH_80P80); else NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH, NL80211_CHAN_WIDTH_80); break; case 160: NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH, NL80211_CHAN_WIDTH_160); break; default: return -1; } NLA_PUT_U32(msg, NL80211_ATTR_CENTER_FREQ1, freq->center_freq1); if (freq->center_freq2) NLA_PUT_U32(msg, NL80211_ATTR_CENTER_FREQ2, freq->center_freq2); } else if (freq->ht_enabled) { switch (freq->sec_channel_offset) { case -1: NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, NL80211_CHAN_HT40MINUS); break; case 1: NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, NL80211_CHAN_HT40PLUS); break; default: NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, NL80211_CHAN_HT20); break; } } ret = send_and_recv_msgs(drv, msg, NULL, NULL); if (ret == 0) return 0; wpa_printf(MSG_DEBUG, "nl80211: Failed to start radar detection: " "%d (%s)", ret, strerror(-ret)); nla_put_failure: return -1; } #ifdef CONFIG_TDLS static int nl80211_send_tdls_mgmt(void *priv, const u8 *dst, u8 action_code, u8 dialog_token, u16 status_code, const u8 *buf, size_t len) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; if (!(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT)) return -EOPNOTSUPP; if (!dst) return -EINVAL; msg = nlmsg_alloc(); if (!msg) return -ENOMEM; nl80211_cmd(drv, msg, 0, NL80211_CMD_TDLS_MGMT); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, dst); NLA_PUT_U8(msg, NL80211_ATTR_TDLS_ACTION, action_code); NLA_PUT_U8(msg, NL80211_ATTR_TDLS_DIALOG_TOKEN, dialog_token); NLA_PUT_U16(msg, NL80211_ATTR_STATUS_CODE, status_code); NLA_PUT(msg, NL80211_ATTR_IE, len, buf); return send_and_recv_msgs(drv, msg, NULL, NULL); nla_put_failure: nlmsg_free(msg); return -ENOBUFS; } static int nl80211_tdls_oper(void *priv, enum tdls_oper oper, const u8 *peer) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; enum nl80211_tdls_operation nl80211_oper; if (!(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT)) return -EOPNOTSUPP; switch (oper) { case TDLS_DISCOVERY_REQ: nl80211_oper = NL80211_TDLS_DISCOVERY_REQ; break; case TDLS_SETUP: nl80211_oper = NL80211_TDLS_SETUP; break; case TDLS_TEARDOWN: nl80211_oper = NL80211_TDLS_TEARDOWN; break; case TDLS_ENABLE_LINK: nl80211_oper = NL80211_TDLS_ENABLE_LINK; break; case TDLS_DISABLE_LINK: nl80211_oper = NL80211_TDLS_DISABLE_LINK; break; case TDLS_ENABLE: return 0; case TDLS_DISABLE: return 0; default: return -EINVAL; } msg = nlmsg_alloc(); if (!msg) return -ENOMEM; nl80211_cmd(drv, msg, 0, NL80211_CMD_TDLS_OPER); NLA_PUT_U8(msg, NL80211_ATTR_TDLS_OPERATION, nl80211_oper); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, peer); return send_and_recv_msgs(drv, msg, NULL, NULL); nla_put_failure: nlmsg_free(msg); return -ENOBUFS; } #endif /* CONFIG TDLS */ #ifdef ANDROID typedef struct android_wifi_priv_cmd { char *buf; int used_len; int total_len; } android_wifi_priv_cmd; static int drv_errors = 0; static void wpa_driver_send_hang_msg(struct wpa_driver_nl80211_data *drv) { drv_errors++; if (drv_errors > DRV_NUMBER_SEQUENTIAL_ERRORS) { drv_errors = 0; wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "HANGED"); } } static int android_priv_cmd(struct i802_bss *bss, const char *cmd) { struct wpa_driver_nl80211_data *drv = bss->drv; struct ifreq ifr; android_wifi_priv_cmd priv_cmd; char buf[MAX_DRV_CMD_SIZE]; int ret; os_memset(&ifr, 0, sizeof(ifr)); os_memset(&priv_cmd, 0, sizeof(priv_cmd)); os_strlcpy(ifr.ifr_name, bss->ifname, IFNAMSIZ); os_memset(buf, 0, sizeof(buf)); os_strlcpy(buf, cmd, sizeof(buf)); priv_cmd.buf = buf; priv_cmd.used_len = sizeof(buf); priv_cmd.total_len = sizeof(buf); ifr.ifr_data = &priv_cmd; ret = ioctl(drv->global->ioctl_sock, SIOCDEVPRIVATE + 1, &ifr); if (ret < 0) { wpa_printf(MSG_ERROR, "%s: failed to issue private commands", __func__); wpa_driver_send_hang_msg(drv); return ret; } drv_errors = 0; return 0; } static int android_pno_start(struct i802_bss *bss, struct wpa_driver_scan_params *params) { struct wpa_driver_nl80211_data *drv = bss->drv; struct ifreq ifr; android_wifi_priv_cmd priv_cmd; int ret = 0, i = 0, bp; char buf[WEXT_PNO_MAX_COMMAND_SIZE]; bp = WEXT_PNOSETUP_HEADER_SIZE; os_memcpy(buf, WEXT_PNOSETUP_HEADER, bp); buf[bp++] = WEXT_PNO_TLV_PREFIX; buf[bp++] = WEXT_PNO_TLV_VERSION; buf[bp++] = WEXT_PNO_TLV_SUBVERSION; buf[bp++] = WEXT_PNO_TLV_RESERVED; while (i < WEXT_PNO_AMOUNT && (size_t) i < params->num_ssids) { /* Check that there is enough space needed for 1 more SSID, the * other sections and null termination */ if ((bp + WEXT_PNO_SSID_HEADER_SIZE + MAX_SSID_LEN + WEXT_PNO_NONSSID_SECTIONS_SIZE + 1) >= (int) sizeof(buf)) break; wpa_hexdump_ascii(MSG_DEBUG, "For PNO Scan", params->ssids[i].ssid, params->ssids[i].ssid_len); buf[bp++] = WEXT_PNO_SSID_SECTION; buf[bp++] = params->ssids[i].ssid_len; os_memcpy(&buf[bp], params->ssids[i].ssid, params->ssids[i].ssid_len); bp += params->ssids[i].ssid_len; i++; } buf[bp++] = WEXT_PNO_SCAN_INTERVAL_SECTION; os_snprintf(&buf[bp], WEXT_PNO_SCAN_INTERVAL_LENGTH + 1, "%x", WEXT_PNO_SCAN_INTERVAL); bp += WEXT_PNO_SCAN_INTERVAL_LENGTH; buf[bp++] = WEXT_PNO_REPEAT_SECTION; os_snprintf(&buf[bp], WEXT_PNO_REPEAT_LENGTH + 1, "%x", WEXT_PNO_REPEAT); bp += WEXT_PNO_REPEAT_LENGTH; buf[bp++] = WEXT_PNO_MAX_REPEAT_SECTION; os_snprintf(&buf[bp], WEXT_PNO_MAX_REPEAT_LENGTH + 1, "%x", WEXT_PNO_MAX_REPEAT); bp += WEXT_PNO_MAX_REPEAT_LENGTH + 1; memset(&ifr, 0, sizeof(ifr)); memset(&priv_cmd, 0, sizeof(priv_cmd)); os_strlcpy(ifr.ifr_name, bss->ifname, IFNAMSIZ); priv_cmd.buf = buf; priv_cmd.used_len = bp; priv_cmd.total_len = bp; ifr.ifr_data = &priv_cmd; ret = ioctl(drv->global->ioctl_sock, SIOCDEVPRIVATE + 1, &ifr); if (ret < 0) { wpa_printf(MSG_ERROR, "ioctl[SIOCSIWPRIV] (pnosetup): %d", ret); wpa_driver_send_hang_msg(drv); return ret; } drv_errors = 0; return android_priv_cmd(bss, "PNOFORCE 1"); } static int android_pno_stop(struct i802_bss *bss) { return android_priv_cmd(bss, "PNOFORCE 0"); } #endif /* ANDROID */ static int driver_nl80211_set_key(const char *ifname, void *priv, enum wpa_alg alg, const u8 *addr, int key_idx, int set_tx, const u8 *seq, size_t seq_len, const u8 *key, size_t key_len) { struct i802_bss *bss = priv; return wpa_driver_nl80211_set_key(ifname, bss, alg, addr, key_idx, set_tx, seq, seq_len, key, key_len); } static int driver_nl80211_scan2(void *priv, struct wpa_driver_scan_params *params) { struct i802_bss *bss = priv; return wpa_driver_nl80211_scan(bss, params); } static int driver_nl80211_deauthenticate(void *priv, const u8 *addr, int reason_code) { struct i802_bss *bss = priv; return wpa_driver_nl80211_deauthenticate(bss, addr, reason_code); } static int driver_nl80211_authenticate(void *priv, struct wpa_driver_auth_params *params) { struct i802_bss *bss = priv; return wpa_driver_nl80211_authenticate(bss, params); } static void driver_nl80211_deinit(void *priv) { struct i802_bss *bss = priv; wpa_driver_nl80211_deinit(bss); } static int driver_nl80211_if_remove(void *priv, enum wpa_driver_if_type type, const char *ifname) { struct i802_bss *bss = priv; return wpa_driver_nl80211_if_remove(bss, type, ifname); } static int driver_nl80211_send_mlme(void *priv, const u8 *data, size_t data_len, int noack) { struct i802_bss *bss = priv; return wpa_driver_nl80211_send_mlme(bss, data, data_len, noack, 0, 0, 0, 0); } static int driver_nl80211_sta_remove(void *priv, const u8 *addr) { struct i802_bss *bss = priv; return wpa_driver_nl80211_sta_remove(bss, addr); } static int driver_nl80211_set_sta_vlan(void *priv, const u8 *addr, const char *ifname, int vlan_id) { struct i802_bss *bss = priv; return i802_set_sta_vlan(bss, addr, ifname, vlan_id); } static int driver_nl80211_read_sta_data(void *priv, struct hostap_sta_driver_data *data, const u8 *addr) { struct i802_bss *bss = priv; return i802_read_sta_data(bss, data, addr); } static int driver_nl80211_send_action(void *priv, unsigned int freq, unsigned int wait_time, const u8 *dst, const u8 *src, const u8 *bssid, const u8 *data, size_t data_len, int no_cck) { struct i802_bss *bss = priv; return wpa_driver_nl80211_send_action(bss, freq, wait_time, dst, src, bssid, data, data_len, no_cck); } static int driver_nl80211_probe_req_report(void *priv, int report) { struct i802_bss *bss = priv; return wpa_driver_nl80211_probe_req_report(bss, report); } static int wpa_driver_nl80211_update_ft_ies(void *priv, const u8 *md, const u8 *ies, size_t ies_len) { int ret; struct nl_msg *msg; struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; u16 mdid = WPA_GET_LE16(md); msg = nlmsg_alloc(); if (!msg) return -ENOMEM; wpa_printf(MSG_DEBUG, "nl80211: Updating FT IEs"); nl80211_cmd(drv, msg, 0, NL80211_CMD_UPDATE_FT_IES); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); NLA_PUT(msg, NL80211_ATTR_IE, ies_len, ies); NLA_PUT_U16(msg, NL80211_ATTR_MDID, mdid); ret = send_and_recv_msgs(drv, msg, NULL, NULL); if (ret) { wpa_printf(MSG_DEBUG, "nl80211: update_ft_ies failed " "err=%d (%s)", ret, strerror(-ret)); } return ret; nla_put_failure: nlmsg_free(msg); return -ENOBUFS; } const u8 * wpa_driver_nl80211_get_macaddr(void *priv) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; if (drv->nlmode != NL80211_IFTYPE_P2P_DEVICE) return NULL; return bss->addr; } static const char * scan_state_str(enum scan_states scan_state) { switch (scan_state) { case NO_SCAN: return "NO_SCAN"; case SCAN_REQUESTED: return "SCAN_REQUESTED"; case SCAN_STARTED: return "SCAN_STARTED"; case SCAN_COMPLETED: return "SCAN_COMPLETED"; case SCAN_ABORTED: return "SCAN_ABORTED"; case SCHED_SCAN_STARTED: return "SCHED_SCAN_STARTED"; case SCHED_SCAN_STOPPED: return "SCHED_SCAN_STOPPED"; case SCHED_SCAN_RESULTS: return "SCHED_SCAN_RESULTS"; } return "??"; } static int wpa_driver_nl80211_status(void *priv, char *buf, size_t buflen) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; int res; char *pos, *end; pos = buf; end = buf + buflen; res = os_snprintf(pos, end - pos, "ifindex=%d\n" "ifname=%s\n" "brname=%s\n" "addr=" MACSTR "\n" "freq=%d\n" "%s%s%s%s%s", bss->ifindex, bss->ifname, bss->brname, MAC2STR(bss->addr), bss->freq, bss->beacon_set ? "beacon_set=1\n" : "", bss->added_if_into_bridge ? "added_if_into_bridge=1\n" : "", bss->added_bridge ? "added_bridge=1\n" : "", bss->in_deinit ? "in_deinit=1\n" : "", bss->if_dynamic ? "if_dynamic=1\n" : ""); if (res < 0 || res >= end - pos) return pos - buf; pos += res; if (bss->wdev_id_set) { res = os_snprintf(pos, end - pos, "wdev_id=%llu\n", (unsigned long long) bss->wdev_id); if (res < 0 || res >= end - pos) return pos - buf; pos += res; } res = os_snprintf(pos, end - pos, "phyname=%s\n" "drv_ifindex=%d\n" "operstate=%d\n" "scan_state=%s\n" "auth_bssid=" MACSTR "\n" "auth_attempt_bssid=" MACSTR "\n" "bssid=" MACSTR "\n" "prev_bssid=" MACSTR "\n" "associated=%d\n" "assoc_freq=%u\n" "monitor_sock=%d\n" "monitor_ifidx=%d\n" "monitor_refcount=%d\n" "last_mgmt_freq=%u\n" "eapol_tx_sock=%d\n" "%s%s%s%s%s%s%s%s%s%s%s%s%s", drv->phyname, drv->ifindex, drv->operstate, scan_state_str(drv->scan_state), MAC2STR(drv->auth_bssid), MAC2STR(drv->auth_attempt_bssid), MAC2STR(drv->bssid), MAC2STR(drv->prev_bssid), drv->associated, drv->assoc_freq, drv->monitor_sock, drv->monitor_ifidx, drv->monitor_refcount, drv->last_mgmt_freq, drv->eapol_tx_sock, drv->ignore_if_down_event ? "ignore_if_down_event=1\n" : "", drv->scan_complete_events ? "scan_complete_events=1\n" : "", drv->disabled_11b_rates ? "disabled_11b_rates=1\n" : "", drv->pending_remain_on_chan ? "pending_remain_on_chan=1\n" : "", drv->in_interface_list ? "in_interface_list=1\n" : "", drv->device_ap_sme ? "device_ap_sme=1\n" : "", drv->poll_command_supported ? "poll_command_supported=1\n" : "", drv->data_tx_status ? "data_tx_status=1\n" : "", drv->scan_for_auth ? "scan_for_auth=1\n" : "", drv->retry_auth ? "retry_auth=1\n" : "", drv->use_monitor ? "use_monitor=1\n" : "", drv->ignore_next_local_disconnect ? "ignore_next_local_disconnect=1\n" : "", drv->allow_p2p_device ? "allow_p2p_device=1\n" : ""); if (res < 0 || res >= end - pos) return pos - buf; pos += res; if (drv->has_capability) { res = os_snprintf(pos, end - pos, "capa.key_mgmt=0x%x\n" "capa.enc=0x%x\n" "capa.auth=0x%x\n" "capa.flags=0x%x\n" "capa.max_scan_ssids=%d\n" "capa.max_sched_scan_ssids=%d\n" "capa.sched_scan_supported=%d\n" "capa.max_match_sets=%d\n" "capa.max_remain_on_chan=%u\n" "capa.max_stations=%u\n" "capa.probe_resp_offloads=0x%x\n" "capa.max_acl_mac_addrs=%u\n" "capa.num_multichan_concurrent=%u\n", drv->capa.key_mgmt, drv->capa.enc, drv->capa.auth, drv->capa.flags, drv->capa.max_scan_ssids, drv->capa.max_sched_scan_ssids, drv->capa.sched_scan_supported, drv->capa.max_match_sets, drv->capa.max_remain_on_chan, drv->capa.max_stations, drv->capa.probe_resp_offloads, drv->capa.max_acl_mac_addrs, drv->capa.num_multichan_concurrent); if (res < 0 || res >= end - pos) return pos - buf; pos += res; } return pos - buf; } static int set_beacon_data(struct nl_msg *msg, struct beacon_data *settings) { if (settings->head) NLA_PUT(msg, NL80211_ATTR_BEACON_HEAD, settings->head_len, settings->head); if (settings->tail) NLA_PUT(msg, NL80211_ATTR_BEACON_TAIL, settings->tail_len, settings->tail); if (settings->beacon_ies) NLA_PUT(msg, NL80211_ATTR_IE, settings->beacon_ies_len, settings->beacon_ies); if (settings->proberesp_ies) NLA_PUT(msg, NL80211_ATTR_IE_PROBE_RESP, settings->proberesp_ies_len, settings->proberesp_ies); if (settings->assocresp_ies) NLA_PUT(msg, NL80211_ATTR_IE_ASSOC_RESP, settings->assocresp_ies_len, settings->assocresp_ies); if (settings->probe_resp) NLA_PUT(msg, NL80211_ATTR_PROBE_RESP, settings->probe_resp_len, settings->probe_resp); return 0; nla_put_failure: return -ENOBUFS; } static int nl80211_switch_channel(void *priv, struct csa_settings *settings) { struct nl_msg *msg; struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; struct nlattr *beacon_csa; int ret = -ENOBUFS; wpa_printf(MSG_DEBUG, "nl80211: Channel switch request (cs_count=%u block_tx=%u freq=%d width=%d cf1=%d cf2=%d)", settings->cs_count, settings->block_tx, settings->freq_params.freq, settings->freq_params.bandwidth, settings->freq_params.center_freq1, settings->freq_params.center_freq2); if (!(drv->capa.flags & WPA_DRIVER_FLAGS_AP_CSA)) { wpa_printf(MSG_DEBUG, "nl80211: Driver does not support channel switch command"); return -EOPNOTSUPP; } if ((drv->nlmode != NL80211_IFTYPE_AP) && (drv->nlmode != NL80211_IFTYPE_P2P_GO)) return -EOPNOTSUPP; /* check settings validity */ if (!settings->beacon_csa.tail || ((settings->beacon_csa.tail_len <= settings->counter_offset_beacon) || (settings->beacon_csa.tail[settings->counter_offset_beacon] != settings->cs_count))) return -EINVAL; if (settings->beacon_csa.probe_resp && ((settings->beacon_csa.probe_resp_len <= settings->counter_offset_presp) || (settings->beacon_csa.probe_resp[settings->counter_offset_presp] != settings->cs_count))) return -EINVAL; msg = nlmsg_alloc(); if (!msg) return -ENOMEM; nl80211_cmd(drv, msg, 0, NL80211_CMD_CHANNEL_SWITCH); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); NLA_PUT_U32(msg, NL80211_ATTR_CH_SWITCH_COUNT, settings->cs_count); ret = nl80211_put_freq_params(msg, &settings->freq_params); if (ret) goto error; if (settings->block_tx) NLA_PUT_FLAG(msg, NL80211_ATTR_CH_SWITCH_BLOCK_TX); /* beacon_after params */ ret = set_beacon_data(msg, &settings->beacon_after); if (ret) goto error; /* beacon_csa params */ beacon_csa = nla_nest_start(msg, NL80211_ATTR_CSA_IES); if (!beacon_csa) goto nla_put_failure; ret = set_beacon_data(msg, &settings->beacon_csa); if (ret) goto error; NLA_PUT_U16(msg, NL80211_ATTR_CSA_C_OFF_BEACON, settings->counter_offset_beacon); if (settings->beacon_csa.probe_resp) NLA_PUT_U16(msg, NL80211_ATTR_CSA_C_OFF_PRESP, settings->counter_offset_presp); nla_nest_end(msg, beacon_csa); ret = send_and_recv_msgs(drv, msg, NULL, NULL); if (ret) { wpa_printf(MSG_DEBUG, "nl80211: switch_channel failed err=%d (%s)", ret, strerror(-ret)); } return ret; nla_put_failure: ret = -ENOBUFS; error: nlmsg_free(msg); wpa_printf(MSG_DEBUG, "nl80211: Could not build channel switch request"); return ret; } static int nl80211_set_qos_map(void *priv, const u8 *qos_map_set, u8 qos_map_set_len) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; int ret; msg = nlmsg_alloc(); if (!msg) return -ENOMEM; wpa_hexdump(MSG_DEBUG, "nl80211: Setting QoS Map", qos_map_set, qos_map_set_len); nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_QOS_MAP); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); NLA_PUT(msg, NL80211_ATTR_QOS_MAP, qos_map_set_len, qos_map_set); ret = send_and_recv_msgs(drv, msg, NULL, NULL); if (ret) wpa_printf(MSG_DEBUG, "nl80211: Setting QoS Map failed"); return ret; nla_put_failure: nlmsg_free(msg); return -ENOBUFS; } const struct wpa_driver_ops wpa_driver_nl80211_ops = { .name = "nl80211", .desc = "Linux nl80211/cfg80211", .get_bssid = wpa_driver_nl80211_get_bssid, .get_ssid = wpa_driver_nl80211_get_ssid, .set_key = driver_nl80211_set_key, .scan2 = driver_nl80211_scan2, .sched_scan = wpa_driver_nl80211_sched_scan, .stop_sched_scan = wpa_driver_nl80211_stop_sched_scan, .get_scan_results2 = wpa_driver_nl80211_get_scan_results, .deauthenticate = driver_nl80211_deauthenticate, .authenticate = driver_nl80211_authenticate, .associate = wpa_driver_nl80211_associate, .global_init = nl80211_global_init, .global_deinit = nl80211_global_deinit, .init2 = wpa_driver_nl80211_init, .deinit = driver_nl80211_deinit, .get_capa = wpa_driver_nl80211_get_capa, .set_operstate = wpa_driver_nl80211_set_operstate, .set_supp_port = wpa_driver_nl80211_set_supp_port, .set_country = wpa_driver_nl80211_set_country, .get_country = wpa_driver_nl80211_get_country, .set_ap = wpa_driver_nl80211_set_ap, .set_acl = wpa_driver_nl80211_set_acl, .if_add = wpa_driver_nl80211_if_add, .if_remove = driver_nl80211_if_remove, .send_mlme = driver_nl80211_send_mlme, .get_hw_feature_data = wpa_driver_nl80211_get_hw_feature_data, .sta_add = wpa_driver_nl80211_sta_add, .sta_remove = driver_nl80211_sta_remove, .hapd_send_eapol = wpa_driver_nl80211_hapd_send_eapol, .sta_set_flags = wpa_driver_nl80211_sta_set_flags, .hapd_init = i802_init, .hapd_deinit = i802_deinit, .set_wds_sta = i802_set_wds_sta, .get_seqnum = i802_get_seqnum, .flush = i802_flush, .get_inact_sec = i802_get_inact_sec, .sta_clear_stats = i802_sta_clear_stats, .set_rts = i802_set_rts, .set_frag = i802_set_frag, .set_tx_queue_params = i802_set_tx_queue_params, .set_sta_vlan = driver_nl80211_set_sta_vlan, .sta_deauth = i802_sta_deauth, .sta_disassoc = i802_sta_disassoc, .read_sta_data = driver_nl80211_read_sta_data, .set_freq = i802_set_freq, .send_action = driver_nl80211_send_action, .send_action_cancel_wait = wpa_driver_nl80211_send_action_cancel_wait, .remain_on_channel = wpa_driver_nl80211_remain_on_channel, .cancel_remain_on_channel = wpa_driver_nl80211_cancel_remain_on_channel, .probe_req_report = driver_nl80211_probe_req_report, .deinit_ap = wpa_driver_nl80211_deinit_ap, .deinit_p2p_cli = wpa_driver_nl80211_deinit_p2p_cli, .resume = wpa_driver_nl80211_resume, .send_ft_action = nl80211_send_ft_action, .signal_monitor = nl80211_signal_monitor, .signal_poll = nl80211_signal_poll, .send_frame = nl80211_send_frame, .shared_freq = wpa_driver_nl80211_shared_freq, .set_param = nl80211_set_param, .get_radio_name = nl80211_get_radio_name, .add_pmkid = nl80211_add_pmkid, .remove_pmkid = nl80211_remove_pmkid, .flush_pmkid = nl80211_flush_pmkid, .set_rekey_info = nl80211_set_rekey_info, .poll_client = nl80211_poll_client, .set_p2p_powersave = nl80211_set_p2p_powersave, .start_dfs_cac = nl80211_start_radar_detection, .stop_ap = wpa_driver_nl80211_stop_ap, #ifdef CONFIG_TDLS .send_tdls_mgmt = nl80211_send_tdls_mgmt, .tdls_oper = nl80211_tdls_oper, #endif /* CONFIG_TDLS */ .update_ft_ies = wpa_driver_nl80211_update_ft_ies, .get_mac_addr = wpa_driver_nl80211_get_macaddr, .get_survey = wpa_driver_nl80211_get_survey, .status = wpa_driver_nl80211_status, .switch_channel = nl80211_switch_channel, #ifdef ANDROID_P2P .set_noa = wpa_driver_set_p2p_noa, .get_noa = wpa_driver_get_p2p_noa, .set_ap_wps_ie = wpa_driver_set_ap_wps_p2p_ie, #endif /* ANDROID_P2P */ #ifdef ANDROID .driver_cmd = wpa_driver_nl80211_driver_cmd, #endif /* ANDROID */ .set_qos_map = nl80211_set_qos_map, }; wpa-2.1/src/drivers/driver_none.c000066400000000000000000000034441227414666700171120ustar00rootroot00000000000000/* * Driver interface for RADIUS server or WPS ER only (no driver) * Copyright (c) 2008, Atheros Communications * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "includes.h" #include "common.h" #include "driver.h" struct none_driver_data { struct hostapd_data *hapd; void *ctx; }; static void * none_driver_hapd_init(struct hostapd_data *hapd, struct wpa_init_params *params) { struct none_driver_data *drv; drv = os_zalloc(sizeof(struct none_driver_data)); if (drv == NULL) { wpa_printf(MSG_ERROR, "Could not allocate memory for none " "driver data"); return NULL; } drv->hapd = hapd; return drv; } static void none_driver_hapd_deinit(void *priv) { struct none_driver_data *drv = priv; os_free(drv); } static int none_driver_send_ether(void *priv, const u8 *dst, const u8 *src, u16 proto, const u8 *data, size_t data_len) { return 0; } static void * none_driver_init(void *ctx, const char *ifname) { struct none_driver_data *drv; drv = os_zalloc(sizeof(struct none_driver_data)); if (drv == NULL) { wpa_printf(MSG_ERROR, "Could not allocate memory for none " "driver data"); return NULL; } drv->ctx = ctx; return drv; } static void none_driver_deinit(void *priv) { struct none_driver_data *drv = priv; os_free(drv); } static int none_driver_send_eapol(void *priv, const u8 *dest, u16 proto, const u8 *data, size_t data_len) { return -1; } const struct wpa_driver_ops wpa_driver_none_ops = { .name = "none", .desc = "no driver (RADIUS server/WPS ER)", .hapd_init = none_driver_hapd_init, .hapd_deinit = none_driver_hapd_deinit, .send_ether = none_driver_send_ether, .init = none_driver_init, .deinit = none_driver_deinit, .send_eapol = none_driver_send_eapol, }; wpa-2.1/src/drivers/driver_openbsd.c000066400000000000000000000057301227414666700176050ustar00rootroot00000000000000/* * Driver interaction with OpenBSD net80211 layer * Copyright (c) 2013, Mark Kettenis * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "includes.h" #include #include #include #include #include #include "common.h" #include "driver.h" struct openbsd_driver_data { char ifname[IFNAMSIZ + 1]; void *ctx; int sock; /* open socket for 802.11 ioctls */ }; static int wpa_driver_openbsd_get_ssid(void *priv, u8 *ssid) { struct openbsd_driver_data *drv = priv; struct ieee80211_nwid nwid; struct ifreq ifr; os_memset(&ifr, 0, sizeof(ifr)); os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name)); ifr.ifr_data = (void *)&nwid; if (ioctl(drv->sock, SIOCG80211NWID, &ifr) < 0 || nwid.i_len > IEEE80211_NWID_LEN) return -1; os_memcpy(ssid, nwid.i_nwid, nwid.i_len); return nwid.i_len; } static int wpa_driver_openbsd_get_bssid(void *priv, u8 *bssid) { struct openbsd_driver_data *drv = priv; struct ieee80211_bssid id; os_strlcpy(id.i_name, drv->ifname, sizeof(id.i_name)); if (ioctl(drv->sock, SIOCG80211BSSID, &id) < 0) return -1; os_memcpy(bssid, id.i_bssid, IEEE80211_ADDR_LEN); return 0; } static int wpa_driver_openbsd_get_capa(void *priv, struct wpa_driver_capa *capa) { os_memset(capa, 0, sizeof(*capa)); capa->flags = WPA_DRIVER_FLAGS_4WAY_HANDSHAKE; return 0; } static int wpa_driver_openbsd_set_key(const char *ifname, void *priv, enum wpa_alg alg, const unsigned char *addr, int key_idx, int set_tx, const u8 *seq, size_t seq_len, const u8 *key, size_t key_len) { struct openbsd_driver_data *drv = priv; struct ieee80211_keyavail keyavail; if (alg != WPA_ALG_PMK || key_len > IEEE80211_PMK_LEN) return -1; memset(&keyavail, 0, sizeof(keyavail)); os_strlcpy(keyavail.i_name, drv->ifname, sizeof(keyavail.i_name)); if (wpa_driver_openbsd_get_bssid(priv, keyavail.i_macaddr) < 0) return -1; memcpy(keyavail.i_key, key, key_len); if (ioctl(drv->sock, SIOCS80211KEYAVAIL, &keyavail) < 0) return -1; return 0; } static void * wpa_driver_openbsd_init(void *ctx, const char *ifname) { struct openbsd_driver_data *drv; drv = os_zalloc(sizeof(*drv)); if (drv == NULL) return NULL; drv->sock = socket(PF_INET, SOCK_DGRAM, 0); if (drv->sock < 0) goto fail; drv->ctx = ctx; os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); return drv; fail: os_free(drv); return NULL; } static void wpa_driver_openbsd_deinit(void *priv) { struct openbsd_driver_data *drv = priv; close(drv->sock); os_free(drv); } const struct wpa_driver_ops wpa_driver_openbsd_ops = { .name = "openbsd", .desc = "OpenBSD 802.11 support", .get_ssid = wpa_driver_openbsd_get_ssid, .get_bssid = wpa_driver_openbsd_get_bssid, .get_capa = wpa_driver_openbsd_get_capa, .set_key = wpa_driver_openbsd_set_key, .init = wpa_driver_openbsd_init, .deinit = wpa_driver_openbsd_deinit, }; wpa-2.1/src/drivers/driver_privsep.c000066400000000000000000000427151227414666700176470ustar00rootroot00000000000000/* * WPA Supplicant - privilege separated driver interface * Copyright (c) 2007-2009, 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.h" #include "driver.h" #include "eloop.h" #include "common/privsep_commands.h" struct wpa_driver_privsep_data { void *ctx; u8 own_addr[ETH_ALEN]; int priv_socket; char *own_socket_path; int cmd_socket; char *own_cmd_path; struct sockaddr_un priv_addr; char ifname[16]; }; static int wpa_priv_reg_cmd(struct wpa_driver_privsep_data *drv, int cmd) { int res; res = sendto(drv->priv_socket, &cmd, sizeof(cmd), 0, (struct sockaddr *) &drv->priv_addr, sizeof(drv->priv_addr)); if (res < 0) perror("sendto"); return res < 0 ? -1 : 0; } static int wpa_priv_cmd(struct wpa_driver_privsep_data *drv, int cmd, const void *data, size_t data_len, void *reply, size_t *reply_len) { struct msghdr msg; struct iovec io[2]; io[0].iov_base = &cmd; io[0].iov_len = sizeof(cmd); io[1].iov_base = (u8 *) data; io[1].iov_len = data_len; os_memset(&msg, 0, sizeof(msg)); msg.msg_iov = io; msg.msg_iovlen = data ? 2 : 1; msg.msg_name = &drv->priv_addr; msg.msg_namelen = sizeof(drv->priv_addr); if (sendmsg(drv->cmd_socket, &msg, 0) < 0) { perror("sendmsg(cmd_socket)"); return -1; } if (reply) { fd_set rfds; struct timeval tv; int res; FD_ZERO(&rfds); FD_SET(drv->cmd_socket, &rfds); tv.tv_sec = 5; tv.tv_usec = 0; res = select(drv->cmd_socket + 1, &rfds, NULL, NULL, &tv); if (res < 0 && errno != EINTR) { perror("select"); return -1; } if (FD_ISSET(drv->cmd_socket, &rfds)) { res = recv(drv->cmd_socket, reply, *reply_len, 0); if (res < 0) { perror("recv"); return -1; } *reply_len = res; } else { wpa_printf(MSG_DEBUG, "PRIVSEP: Timeout while waiting " "for reply (cmd=%d)", cmd); return -1; } } return 0; } static int wpa_driver_privsep_scan(void *priv, struct wpa_driver_scan_params *params) { struct wpa_driver_privsep_data *drv = priv; const u8 *ssid = params->ssids[0].ssid; size_t ssid_len = params->ssids[0].ssid_len; wpa_printf(MSG_DEBUG, "%s: priv=%p", __func__, priv); return wpa_priv_cmd(drv, PRIVSEP_CMD_SCAN, ssid, ssid_len, NULL, NULL); } static struct wpa_scan_results * wpa_driver_privsep_get_scan_results2(void *priv) { struct wpa_driver_privsep_data *drv = priv; int res, num; u8 *buf, *pos, *end; size_t reply_len = 60000; struct wpa_scan_results *results; struct wpa_scan_res *r; buf = os_malloc(reply_len); if (buf == NULL) return NULL; res = wpa_priv_cmd(drv, PRIVSEP_CMD_GET_SCAN_RESULTS, NULL, 0, buf, &reply_len); if (res < 0) { os_free(buf); return NULL; } wpa_printf(MSG_DEBUG, "privsep: Received %lu bytes of scan results", (unsigned long) reply_len); if (reply_len < sizeof(int)) { wpa_printf(MSG_DEBUG, "privsep: Invalid scan result len %lu", (unsigned long) reply_len); os_free(buf); return NULL; } pos = buf; end = buf + reply_len; os_memcpy(&num, pos, sizeof(int)); if (num < 0 || num > 1000) { os_free(buf); return NULL; } pos += sizeof(int); results = os_zalloc(sizeof(*results)); if (results == NULL) { os_free(buf); return NULL; } results->res = os_calloc(num, sizeof(struct wpa_scan_res *)); if (results->res == NULL) { os_free(results); os_free(buf); return NULL; } while (results->num < (size_t) num && pos + sizeof(int) < end) { int len; os_memcpy(&len, pos, sizeof(int)); pos += sizeof(int); if (len < 0 || len > 10000 || pos + len > end) break; r = os_malloc(len); if (r == NULL) break; os_memcpy(r, pos, len); pos += len; if (sizeof(*r) + r->ie_len > (size_t) len) { os_free(r); break; } results->res[results->num++] = r; } os_free(buf); return results; } static int wpa_driver_privsep_set_key(const char *ifname, void *priv, enum wpa_alg alg, const u8 *addr, int key_idx, int set_tx, const u8 *seq, size_t seq_len, const u8 *key, size_t key_len) { struct wpa_driver_privsep_data *drv = priv; struct privsep_cmd_set_key cmd; wpa_printf(MSG_DEBUG, "%s: priv=%p alg=%d key_idx=%d set_tx=%d", __func__, priv, alg, key_idx, set_tx); os_memset(&cmd, 0, sizeof(cmd)); cmd.alg = alg; if (addr) os_memcpy(cmd.addr, addr, ETH_ALEN); else os_memset(cmd.addr, 0xff, ETH_ALEN); cmd.key_idx = key_idx; cmd.set_tx = set_tx; if (seq && seq_len > 0 && seq_len < sizeof(cmd.seq)) { os_memcpy(cmd.seq, seq, seq_len); cmd.seq_len = seq_len; } if (key && key_len > 0 && key_len < sizeof(cmd.key)) { os_memcpy(cmd.key, key, key_len); cmd.key_len = key_len; } return wpa_priv_cmd(drv, PRIVSEP_CMD_SET_KEY, &cmd, sizeof(cmd), NULL, NULL); } static int wpa_driver_privsep_associate( void *priv, struct wpa_driver_associate_params *params) { struct wpa_driver_privsep_data *drv = priv; struct privsep_cmd_associate *data; int res; size_t buflen; wpa_printf(MSG_DEBUG, "%s: priv=%p freq=%d pairwise_suite=%d " "group_suite=%d key_mgmt_suite=%d auth_alg=%d mode=%d", __func__, priv, params->freq, params->pairwise_suite, params->group_suite, params->key_mgmt_suite, params->auth_alg, params->mode); buflen = sizeof(*data) + params->wpa_ie_len; data = os_zalloc(buflen); if (data == NULL) return -1; if (params->bssid) os_memcpy(data->bssid, params->bssid, ETH_ALEN); os_memcpy(data->ssid, params->ssid, params->ssid_len); data->ssid_len = params->ssid_len; data->freq = params->freq; data->pairwise_suite = params->pairwise_suite; data->group_suite = params->group_suite; data->key_mgmt_suite = params->key_mgmt_suite; data->auth_alg = params->auth_alg; data->mode = params->mode; data->wpa_ie_len = params->wpa_ie_len; if (params->wpa_ie) os_memcpy(data + 1, params->wpa_ie, params->wpa_ie_len); /* TODO: add support for other assoc parameters */ res = wpa_priv_cmd(drv, PRIVSEP_CMD_ASSOCIATE, data, buflen, NULL, NULL); os_free(data); return res; } static int wpa_driver_privsep_get_bssid(void *priv, u8 *bssid) { struct wpa_driver_privsep_data *drv = priv; int res; size_t len = ETH_ALEN; res = wpa_priv_cmd(drv, PRIVSEP_CMD_GET_BSSID, NULL, 0, bssid, &len); if (res < 0 || len != ETH_ALEN) return -1; return 0; } static int wpa_driver_privsep_get_ssid(void *priv, u8 *ssid) { struct wpa_driver_privsep_data *drv = priv; int res, ssid_len; u8 reply[sizeof(int) + 32]; size_t len = sizeof(reply); res = wpa_priv_cmd(drv, PRIVSEP_CMD_GET_SSID, NULL, 0, reply, &len); if (res < 0 || len < sizeof(int)) return -1; os_memcpy(&ssid_len, reply, sizeof(int)); if (ssid_len < 0 || ssid_len > 32 || sizeof(int) + ssid_len > len) { wpa_printf(MSG_DEBUG, "privsep: Invalid get SSID reply"); return -1; } os_memcpy(ssid, &reply[sizeof(int)], ssid_len); return ssid_len; } static int wpa_driver_privsep_deauthenticate(void *priv, const u8 *addr, int reason_code) { //struct wpa_driver_privsep_data *drv = priv; wpa_printf(MSG_DEBUG, "%s addr=" MACSTR " reason_code=%d", __func__, MAC2STR(addr), reason_code); wpa_printf(MSG_DEBUG, "%s - TODO", __func__); return 0; } static void wpa_driver_privsep_event_assoc(void *ctx, enum wpa_event_type event, u8 *buf, size_t len) { union wpa_event_data data; int inc_data = 0; u8 *pos, *end; int ie_len; os_memset(&data, 0, sizeof(data)); pos = buf; end = buf + len; if (end - pos < (int) sizeof(int)) return; os_memcpy(&ie_len, pos, sizeof(int)); pos += sizeof(int); if (ie_len < 0 || ie_len > end - pos) return; if (ie_len) { data.assoc_info.req_ies = pos; data.assoc_info.req_ies_len = ie_len; pos += ie_len; inc_data = 1; } wpa_supplicant_event(ctx, event, inc_data ? &data : NULL); } static void wpa_driver_privsep_event_interface_status(void *ctx, u8 *buf, size_t len) { union wpa_event_data data; int ievent; if (len < sizeof(int) || len - sizeof(int) > sizeof(data.interface_status.ifname)) return; os_memcpy(&ievent, buf, sizeof(int)); os_memset(&data, 0, sizeof(data)); data.interface_status.ievent = ievent; os_memcpy(data.interface_status.ifname, buf + sizeof(int), len - sizeof(int)); wpa_supplicant_event(ctx, EVENT_INTERFACE_STATUS, &data); } static void wpa_driver_privsep_event_michael_mic_failure( void *ctx, u8 *buf, size_t len) { union wpa_event_data data; if (len != sizeof(int)) return; os_memset(&data, 0, sizeof(data)); os_memcpy(&data.michael_mic_failure.unicast, buf, sizeof(int)); wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &data); } static void wpa_driver_privsep_event_pmkid_candidate(void *ctx, u8 *buf, size_t len) { union wpa_event_data data; if (len != sizeof(struct pmkid_candidate)) return; os_memset(&data, 0, sizeof(data)); os_memcpy(&data.pmkid_candidate, buf, len); wpa_supplicant_event(ctx, EVENT_PMKID_CANDIDATE, &data); } static void wpa_driver_privsep_event_stkstart(void *ctx, u8 *buf, size_t len) { union wpa_event_data data; if (len != ETH_ALEN) return; os_memset(&data, 0, sizeof(data)); os_memcpy(data.stkstart.peer, buf, ETH_ALEN); wpa_supplicant_event(ctx, EVENT_STKSTART, &data); } static void wpa_driver_privsep_event_ft_response(void *ctx, u8 *buf, size_t len) { union wpa_event_data data; if (len < sizeof(int) + ETH_ALEN) return; os_memset(&data, 0, sizeof(data)); os_memcpy(&data.ft_ies.ft_action, buf, sizeof(int)); os_memcpy(data.ft_ies.target_ap, buf + sizeof(int), ETH_ALEN); data.ft_ies.ies = buf + sizeof(int) + ETH_ALEN; data.ft_ies.ies_len = len - sizeof(int) - ETH_ALEN; wpa_supplicant_event(ctx, EVENT_FT_RESPONSE, &data); } static void wpa_driver_privsep_event_rx_eapol(void *ctx, u8 *buf, size_t len) { if (len < ETH_ALEN) return; drv_event_eapol_rx(ctx, buf, buf + ETH_ALEN, len - ETH_ALEN); } static void wpa_driver_privsep_receive(int sock, void *eloop_ctx, void *sock_ctx) { struct wpa_driver_privsep_data *drv = eloop_ctx; u8 *buf, *event_buf; size_t event_len; int res, event; enum privsep_event e; struct sockaddr_un from; socklen_t fromlen = sizeof(from); const size_t buflen = 2000; buf = os_malloc(buflen); if (buf == NULL) return; res = recvfrom(sock, buf, buflen, 0, (struct sockaddr *) &from, &fromlen); if (res < 0) { perror("recvfrom(priv_socket)"); os_free(buf); return; } wpa_printf(MSG_DEBUG, "privsep_driver: received %u bytes", res); if (res < (int) sizeof(int)) { wpa_printf(MSG_DEBUG, "Too short event message (len=%d)", res); return; } os_memcpy(&event, buf, sizeof(int)); event_buf = &buf[sizeof(int)]; event_len = res - sizeof(int); wpa_printf(MSG_DEBUG, "privsep: Event %d received (len=%lu)", event, (unsigned long) event_len); e = event; switch (e) { case PRIVSEP_EVENT_SCAN_RESULTS: wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, NULL); break; case PRIVSEP_EVENT_ASSOC: wpa_driver_privsep_event_assoc(drv->ctx, EVENT_ASSOC, event_buf, event_len); break; case PRIVSEP_EVENT_DISASSOC: wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL); break; case PRIVSEP_EVENT_ASSOCINFO: wpa_driver_privsep_event_assoc(drv->ctx, EVENT_ASSOCINFO, event_buf, event_len); break; case PRIVSEP_EVENT_MICHAEL_MIC_FAILURE: wpa_driver_privsep_event_michael_mic_failure( drv->ctx, event_buf, event_len); break; case PRIVSEP_EVENT_INTERFACE_STATUS: wpa_driver_privsep_event_interface_status(drv->ctx, event_buf, event_len); break; case PRIVSEP_EVENT_PMKID_CANDIDATE: wpa_driver_privsep_event_pmkid_candidate(drv->ctx, event_buf, event_len); break; case PRIVSEP_EVENT_STKSTART: wpa_driver_privsep_event_stkstart(drv->ctx, event_buf, event_len); break; case PRIVSEP_EVENT_FT_RESPONSE: wpa_driver_privsep_event_ft_response(drv->ctx, event_buf, event_len); break; case PRIVSEP_EVENT_RX_EAPOL: wpa_driver_privsep_event_rx_eapol(drv->ctx, event_buf, event_len); break; } os_free(buf); } static void * wpa_driver_privsep_init(void *ctx, const char *ifname) { struct wpa_driver_privsep_data *drv; drv = os_zalloc(sizeof(*drv)); if (drv == NULL) return NULL; drv->ctx = ctx; drv->priv_socket = -1; drv->cmd_socket = -1; os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); return drv; } static void wpa_driver_privsep_deinit(void *priv) { struct wpa_driver_privsep_data *drv = priv; if (drv->priv_socket >= 0) { wpa_priv_reg_cmd(drv, PRIVSEP_CMD_UNREGISTER); eloop_unregister_read_sock(drv->priv_socket); close(drv->priv_socket); } if (drv->own_socket_path) { unlink(drv->own_socket_path); os_free(drv->own_socket_path); } if (drv->cmd_socket >= 0) { eloop_unregister_read_sock(drv->cmd_socket); close(drv->cmd_socket); } if (drv->own_cmd_path) { unlink(drv->own_cmd_path); os_free(drv->own_cmd_path); } os_free(drv); } static int wpa_driver_privsep_set_param(void *priv, const char *param) { struct wpa_driver_privsep_data *drv = priv; const char *pos; char *own_dir, *priv_dir; static unsigned int counter = 0; size_t len; struct sockaddr_un addr; wpa_printf(MSG_DEBUG, "%s: param='%s'", __func__, param); if (param == NULL) pos = NULL; else pos = os_strstr(param, "own_dir="); if (pos) { char *end; own_dir = os_strdup(pos + 8); if (own_dir == NULL) return -1; end = os_strchr(own_dir, ' '); if (end) *end = '\0'; } else { own_dir = os_strdup("/tmp"); if (own_dir == NULL) return -1; } if (param == NULL) pos = NULL; else pos = os_strstr(param, "priv_dir="); if (pos) { char *end; priv_dir = os_strdup(pos + 9); if (priv_dir == NULL) { os_free(own_dir); return -1; } end = os_strchr(priv_dir, ' '); if (end) *end = '\0'; } else { priv_dir = os_strdup("/var/run/wpa_priv"); if (priv_dir == NULL) { os_free(own_dir); return -1; } } len = os_strlen(own_dir) + 50; drv->own_socket_path = os_malloc(len); if (drv->own_socket_path == NULL) { os_free(priv_dir); os_free(own_dir); return -1; } os_snprintf(drv->own_socket_path, len, "%s/wpa_privsep-%d-%d", own_dir, getpid(), counter++); len = os_strlen(own_dir) + 50; drv->own_cmd_path = os_malloc(len); if (drv->own_cmd_path == NULL) { os_free(drv->own_socket_path); drv->own_socket_path = NULL; os_free(priv_dir); os_free(own_dir); return -1; } os_snprintf(drv->own_cmd_path, len, "%s/wpa_privsep-%d-%d", own_dir, getpid(), counter++); os_free(own_dir); drv->priv_addr.sun_family = AF_UNIX; os_snprintf(drv->priv_addr.sun_path, sizeof(drv->priv_addr.sun_path), "%s/%s", priv_dir, drv->ifname); os_free(priv_dir); drv->priv_socket = socket(PF_UNIX, SOCK_DGRAM, 0); if (drv->priv_socket < 0) { perror("socket(PF_UNIX)"); os_free(drv->own_socket_path); drv->own_socket_path = NULL; return -1; } os_memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; os_strlcpy(addr.sun_path, drv->own_socket_path, sizeof(addr.sun_path)); if (bind(drv->priv_socket, (struct sockaddr *) &addr, sizeof(addr)) < 0) { perror("privsep-set-params priv-sock: bind(PF_UNIX)"); close(drv->priv_socket); drv->priv_socket = -1; unlink(drv->own_socket_path); os_free(drv->own_socket_path); drv->own_socket_path = NULL; return -1; } eloop_register_read_sock(drv->priv_socket, wpa_driver_privsep_receive, drv, NULL); drv->cmd_socket = socket(PF_UNIX, SOCK_DGRAM, 0); if (drv->cmd_socket < 0) { perror("socket(PF_UNIX)"); os_free(drv->own_cmd_path); drv->own_cmd_path = NULL; return -1; } os_memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; os_strlcpy(addr.sun_path, drv->own_cmd_path, sizeof(addr.sun_path)); if (bind(drv->cmd_socket, (struct sockaddr *) &addr, sizeof(addr)) < 0) { perror("privsep-set-params cmd-sock: bind(PF_UNIX)"); close(drv->cmd_socket); drv->cmd_socket = -1; unlink(drv->own_cmd_path); os_free(drv->own_cmd_path); drv->own_cmd_path = NULL; return -1; } if (wpa_priv_reg_cmd(drv, PRIVSEP_CMD_REGISTER) < 0) { wpa_printf(MSG_ERROR, "Failed to register with wpa_priv"); return -1; } return 0; } static int wpa_driver_privsep_get_capa(void *priv, struct wpa_driver_capa *capa) { struct wpa_driver_privsep_data *drv = priv; int res; size_t len = sizeof(*capa); res = wpa_priv_cmd(drv, PRIVSEP_CMD_GET_CAPA, NULL, 0, capa, &len); if (res < 0 || len != sizeof(*capa)) return -1; return 0; } static const u8 * wpa_driver_privsep_get_mac_addr(void *priv) { struct wpa_driver_privsep_data *drv = priv; wpa_printf(MSG_DEBUG, "%s", __func__); return drv->own_addr; } static int wpa_driver_privsep_set_country(void *priv, const char *alpha2) { struct wpa_driver_privsep_data *drv = priv; wpa_printf(MSG_DEBUG, "%s country='%s'", __func__, alpha2); return wpa_priv_cmd(drv, PRIVSEP_CMD_SET_COUNTRY, alpha2, os_strlen(alpha2), NULL, NULL); } struct wpa_driver_ops wpa_driver_privsep_ops = { "privsep", "wpa_supplicant privilege separated driver", .get_bssid = wpa_driver_privsep_get_bssid, .get_ssid = wpa_driver_privsep_get_ssid, .set_key = wpa_driver_privsep_set_key, .init = wpa_driver_privsep_init, .deinit = wpa_driver_privsep_deinit, .set_param = wpa_driver_privsep_set_param, .scan2 = wpa_driver_privsep_scan, .deauthenticate = wpa_driver_privsep_deauthenticate, .associate = wpa_driver_privsep_associate, .get_capa = wpa_driver_privsep_get_capa, .get_mac_addr = wpa_driver_privsep_get_mac_addr, .get_scan_results2 = wpa_driver_privsep_get_scan_results2, .set_country = wpa_driver_privsep_set_country, }; struct wpa_driver_ops *wpa_drivers[] = { &wpa_driver_privsep_ops, NULL }; wpa-2.1/src/drivers/driver_roboswitch.c000066400000000000000000000323161227414666700203360ustar00rootroot00000000000000/* * WPA Supplicant - roboswitch driver interface * Copyright (c) 2008-2009 Jouke Witteveen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "includes.h" #include #include #include #include #include #include "common.h" #include "driver.h" #include "l2_packet/l2_packet.h" #define ROBO_PHY_ADDR 0x1e /* RoboSwitch PHY address */ /* MII access registers */ #define ROBO_MII_PAGE 0x10 /* MII page register */ #define ROBO_MII_ADDR 0x11 /* MII address register */ #define ROBO_MII_DATA_OFFSET 0x18 /* Start of MII data registers */ #define ROBO_MII_PAGE_ENABLE 0x01 /* MII page op code */ #define ROBO_MII_ADDR_WRITE 0x01 /* MII address write op code */ #define ROBO_MII_ADDR_READ 0x02 /* MII address read op code */ #define ROBO_MII_DATA_MAX 4 /* Consecutive MII data registers */ #define ROBO_MII_RETRY_MAX 10 /* Read attempts before giving up */ /* Page numbers */ #define ROBO_ARLCTRL_PAGE 0x04 /* ARL control page */ #define ROBO_VLAN_PAGE 0x34 /* VLAN page */ /* ARL control page registers */ #define ROBO_ARLCTRL_CONF 0x00 /* ARL configuration register */ #define ROBO_ARLCTRL_ADDR_1 0x10 /* Multiport address 1 */ #define ROBO_ARLCTRL_VEC_1 0x16 /* Multiport vector 1 */ #define ROBO_ARLCTRL_ADDR_2 0x20 /* Multiport address 2 */ #define ROBO_ARLCTRL_VEC_2 0x26 /* Multiport vector 2 */ /* VLAN page registers */ #define ROBO_VLAN_ACCESS 0x08 /* VLAN table access register */ #define ROBO_VLAN_ACCESS_5350 0x06 /* VLAN table access register (5350) */ #define ROBO_VLAN_READ 0x0c /* VLAN read register */ #define ROBO_VLAN_MAX 0xff /* Maximum number of VLANs */ static const u8 pae_group_addr[ETH_ALEN] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 }; struct wpa_driver_roboswitch_data { void *ctx; struct l2_packet_data *l2; char ifname[IFNAMSIZ + 1]; u8 own_addr[ETH_ALEN]; struct ifreq ifr; int fd, is_5350; u16 ports; }; /* Copied from the kernel-only part of mii.h. */ static inline struct mii_ioctl_data *if_mii(struct ifreq *rq) { return (struct mii_ioctl_data *) &rq->ifr_ifru; } /* * RoboSwitch uses 16-bit Big Endian addresses. * The ordering of the words is reversed in the MII registers. */ static void wpa_driver_roboswitch_addr_be16(const u8 addr[ETH_ALEN], u16 *be) { int i; for (i = 0; i < ETH_ALEN; i += 2) be[(ETH_ALEN - i) / 2 - 1] = WPA_GET_BE16(addr + i); } static u16 wpa_driver_roboswitch_mdio_read( struct wpa_driver_roboswitch_data *drv, u8 reg) { struct mii_ioctl_data *mii = if_mii(&drv->ifr); mii->phy_id = ROBO_PHY_ADDR; mii->reg_num = reg; if (ioctl(drv->fd, SIOCGMIIREG, &drv->ifr) < 0) { perror("ioctl[SIOCGMIIREG]"); return 0x00; } return mii->val_out; } static void wpa_driver_roboswitch_mdio_write( struct wpa_driver_roboswitch_data *drv, u8 reg, u16 val) { struct mii_ioctl_data *mii = if_mii(&drv->ifr); mii->phy_id = ROBO_PHY_ADDR; mii->reg_num = reg; mii->val_in = val; if (ioctl(drv->fd, SIOCSMIIREG, &drv->ifr) < 0) { perror("ioctl[SIOCSMIIREG"); } } static int wpa_driver_roboswitch_reg(struct wpa_driver_roboswitch_data *drv, u8 page, u8 reg, u8 op) { int i; /* set page number */ wpa_driver_roboswitch_mdio_write(drv, ROBO_MII_PAGE, (page << 8) | ROBO_MII_PAGE_ENABLE); /* set register address */ wpa_driver_roboswitch_mdio_write(drv, ROBO_MII_ADDR, (reg << 8) | op); /* check if operation completed */ for (i = 0; i < ROBO_MII_RETRY_MAX; ++i) { if ((wpa_driver_roboswitch_mdio_read(drv, ROBO_MII_ADDR) & 3) == 0) return 0; } /* timeout */ return -1; } static int wpa_driver_roboswitch_read(struct wpa_driver_roboswitch_data *drv, u8 page, u8 reg, u16 *val, int len) { int i; if (len > ROBO_MII_DATA_MAX || wpa_driver_roboswitch_reg(drv, page, reg, ROBO_MII_ADDR_READ) < 0) return -1; for (i = 0; i < len; ++i) { val[i] = wpa_driver_roboswitch_mdio_read( drv, ROBO_MII_DATA_OFFSET + i); } return 0; } static int wpa_driver_roboswitch_write(struct wpa_driver_roboswitch_data *drv, u8 page, u8 reg, u16 *val, int len) { int i; if (len > ROBO_MII_DATA_MAX) return -1; for (i = 0; i < len; ++i) { wpa_driver_roboswitch_mdio_write(drv, ROBO_MII_DATA_OFFSET + i, val[i]); } return wpa_driver_roboswitch_reg(drv, page, reg, ROBO_MII_ADDR_WRITE); } static void wpa_driver_roboswitch_receive(void *priv, const u8 *src_addr, const u8 *buf, size_t len) { struct wpa_driver_roboswitch_data *drv = priv; if (len > 14 && WPA_GET_BE16(buf + 12) == ETH_P_EAPOL && os_memcmp(buf, drv->own_addr, ETH_ALEN) == 0) drv_event_eapol_rx(drv->ctx, src_addr, buf + 14, len - 14); } static int wpa_driver_roboswitch_get_ssid(void *priv, u8 *ssid) { ssid[0] = 0; return 0; } static int wpa_driver_roboswitch_get_bssid(void *priv, u8 *bssid) { /* Report PAE group address as the "BSSID" for wired connection. */ os_memcpy(bssid, pae_group_addr, ETH_ALEN); return 0; } static int wpa_driver_roboswitch_get_capa(void *priv, struct wpa_driver_capa *capa) { os_memset(capa, 0, sizeof(*capa)); capa->flags = WPA_DRIVER_FLAGS_WIRED; return 0; } static int wpa_driver_roboswitch_set_param(void *priv, const char *param) { struct wpa_driver_roboswitch_data *drv = priv; char *sep; if (param == NULL || os_strstr(param, "multicast_only=1") == NULL) { sep = drv->ifname + os_strlen(drv->ifname); *sep = '.'; drv->l2 = l2_packet_init(drv->ifname, NULL, ETH_P_ALL, wpa_driver_roboswitch_receive, drv, 1); if (drv->l2 == NULL) { wpa_printf(MSG_INFO, "%s: Unable to listen on %s", __func__, drv->ifname); return -1; } *sep = '\0'; l2_packet_get_own_addr(drv->l2, drv->own_addr); } else { wpa_printf(MSG_DEBUG, "%s: Ignoring unicast frames", __func__); drv->l2 = NULL; } return 0; } static const char * wpa_driver_roboswitch_get_ifname(void *priv) { struct wpa_driver_roboswitch_data *drv = priv; return drv->ifname; } static int wpa_driver_roboswitch_join(struct wpa_driver_roboswitch_data *drv, u16 ports, const u8 *addr) { u16 read1[3], read2[3], addr_be16[3]; wpa_driver_roboswitch_addr_be16(addr, addr_be16); if (wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, ROBO_ARLCTRL_CONF, read1, 1) < 0) return -1; if (!(read1[0] & (1 << 4))) { /* multiport addresses are not yet enabled */ read1[0] |= 1 << 4; wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, ROBO_ARLCTRL_ADDR_1, addr_be16, 3); wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, ROBO_ARLCTRL_VEC_1, &ports, 1); wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, ROBO_ARLCTRL_ADDR_2, addr_be16, 3); wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, ROBO_ARLCTRL_VEC_2, &ports, 1); wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, ROBO_ARLCTRL_CONF, read1, 1); } else { /* if both multiport addresses are the same we can add */ wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, ROBO_ARLCTRL_ADDR_1, read1, 3); wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, ROBO_ARLCTRL_ADDR_2, read2, 3); if (os_memcmp(read1, read2, 6) != 0) return -1; wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, ROBO_ARLCTRL_VEC_1, read1, 1); wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, ROBO_ARLCTRL_VEC_2, read2, 1); if (read1[0] != read2[0]) return -1; wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, ROBO_ARLCTRL_ADDR_1, addr_be16, 3); wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, ROBO_ARLCTRL_VEC_1, &ports, 1); } return 0; } static int wpa_driver_roboswitch_leave(struct wpa_driver_roboswitch_data *drv, u16 ports, const u8 *addr) { u16 _read, addr_be16[3], addr_read[3], ports_read; wpa_driver_roboswitch_addr_be16(addr, addr_be16); wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, ROBO_ARLCTRL_CONF, &_read, 1); /* If ARL control is disabled, there is nothing to leave. */ if (!(_read & (1 << 4))) return -1; wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, ROBO_ARLCTRL_ADDR_1, addr_read, 3); wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, ROBO_ARLCTRL_VEC_1, &ports_read, 1); /* check if we occupy multiport address 1 */ if (os_memcmp(addr_read, addr_be16, 6) == 0 && ports_read == ports) { wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, ROBO_ARLCTRL_ADDR_2, addr_read, 3); wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, ROBO_ARLCTRL_VEC_2, &ports_read, 1); /* and multiport address 2 */ if (os_memcmp(addr_read, addr_be16, 6) == 0 && ports_read == ports) { _read &= ~(1 << 4); wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, ROBO_ARLCTRL_CONF, &_read, 1); } else { wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, ROBO_ARLCTRL_ADDR_1, addr_read, 3); wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, ROBO_ARLCTRL_VEC_1, &ports_read, 1); wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, ROBO_ARLCTRL_ADDR_2, addr_read, 3); wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, ROBO_ARLCTRL_VEC_2, &ports_read, 1); } } else { wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, ROBO_ARLCTRL_ADDR_2, addr_read, 3); wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, ROBO_ARLCTRL_VEC_2, &ports_read, 1); /* or multiport address 2 */ if (os_memcmp(addr_read, addr_be16, 6) == 0 && ports_read == ports) { wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, ROBO_ARLCTRL_ADDR_1, addr_read, 3); wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, ROBO_ARLCTRL_VEC_1, &ports_read, 1); } else return -1; } return 0; } static void * wpa_driver_roboswitch_init(void *ctx, const char *ifname) { struct wpa_driver_roboswitch_data *drv; char *sep; u16 vlan = 0, _read[2]; drv = os_zalloc(sizeof(*drv)); if (drv == NULL) return NULL; drv->ctx = ctx; drv->own_addr[0] = '\0'; /* copy ifname and take a pointer to the second to last character */ sep = drv->ifname + os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)) - 2; /* find the '.' separating and */ while (sep > drv->ifname && *sep != '.') sep--; if (sep <= drv->ifname) { wpa_printf(MSG_INFO, "%s: No . pair in " "interface name %s", __func__, drv->ifname); os_free(drv); return NULL; } *sep = '\0'; while (*++sep) { if (*sep < '0' || *sep > '9') { wpa_printf(MSG_INFO, "%s: Invalid vlan specification " "in interface name %s", __func__, ifname); os_free(drv); return NULL; } vlan *= 10; vlan += *sep - '0'; if (vlan > ROBO_VLAN_MAX) { wpa_printf(MSG_INFO, "%s: VLAN out of range in " "interface name %s", __func__, ifname); os_free(drv); return NULL; } } drv->fd = socket(PF_INET, SOCK_DGRAM, 0); if (drv->fd < 0) { wpa_printf(MSG_INFO, "%s: Unable to create socket", __func__); os_free(drv); return NULL; } os_memset(&drv->ifr, 0, sizeof(drv->ifr)); os_strlcpy(drv->ifr.ifr_name, drv->ifname, IFNAMSIZ); if (ioctl(drv->fd, SIOCGMIIPHY, &drv->ifr) < 0) { perror("ioctl[SIOCGMIIPHY]"); os_free(drv); return NULL; } if (if_mii(&drv->ifr)->phy_id != ROBO_PHY_ADDR) { wpa_printf(MSG_INFO, "%s: Invalid phy address (not a " "RoboSwitch?)", __func__); os_free(drv); return NULL; } /* set and read back to see if the register can be used */ _read[0] = ROBO_VLAN_MAX; wpa_driver_roboswitch_write(drv, ROBO_VLAN_PAGE, ROBO_VLAN_ACCESS_5350, _read, 1); wpa_driver_roboswitch_read(drv, ROBO_VLAN_PAGE, ROBO_VLAN_ACCESS_5350, _read + 1, 1); drv->is_5350 = _read[0] == _read[1]; /* set the read bit */ vlan |= 1 << 13; wpa_driver_roboswitch_write(drv, ROBO_VLAN_PAGE, drv->is_5350 ? ROBO_VLAN_ACCESS_5350 : ROBO_VLAN_ACCESS, &vlan, 1); wpa_driver_roboswitch_read(drv, ROBO_VLAN_PAGE, ROBO_VLAN_READ, _read, drv->is_5350 ? 2 : 1); if (!(drv->is_5350 ? _read[1] & (1 << 4) : _read[0] & (1 << 14))) { wpa_printf(MSG_INFO, "%s: Could not get port information for " "VLAN %d", __func__, vlan & ~(1 << 13)); os_free(drv); return NULL; } drv->ports = _read[0] & 0x001F; /* add the MII port */ drv->ports |= 1 << 8; if (wpa_driver_roboswitch_join(drv, drv->ports, pae_group_addr) < 0) { wpa_printf(MSG_INFO, "%s: Unable to join PAE group", __func__); os_free(drv); return NULL; } else { wpa_printf(MSG_DEBUG, "%s: Added PAE group address to " "RoboSwitch ARL", __func__); } return drv; } static void wpa_driver_roboswitch_deinit(void *priv) { struct wpa_driver_roboswitch_data *drv = priv; if (drv->l2) { l2_packet_deinit(drv->l2); drv->l2 = NULL; } if (wpa_driver_roboswitch_leave(drv, drv->ports, pae_group_addr) < 0) { wpa_printf(MSG_DEBUG, "%s: Unable to leave PAE group", __func__); } close(drv->fd); os_free(drv); } const struct wpa_driver_ops wpa_driver_roboswitch_ops = { .name = "roboswitch", .desc = "wpa_supplicant roboswitch driver", .get_ssid = wpa_driver_roboswitch_get_ssid, .get_bssid = wpa_driver_roboswitch_get_bssid, .get_capa = wpa_driver_roboswitch_get_capa, .init = wpa_driver_roboswitch_init, .deinit = wpa_driver_roboswitch_deinit, .set_param = wpa_driver_roboswitch_set_param, .get_ifname = wpa_driver_roboswitch_get_ifname, }; wpa-2.1/src/drivers/driver_test.c000066400000000000000000002100241227414666700171240ustar00rootroot00000000000000/* * Testing driver interface for a simulated network driver * Copyright (c) 2004-2010, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ /* Make sure we get winsock2.h for Windows build to get sockaddr_storage */ #include "build_config.h" #ifdef CONFIG_NATIVE_WINDOWS #include #endif /* CONFIG_NATIVE_WINDOWS */ #include "utils/includes.h" #ifndef CONFIG_NATIVE_WINDOWS #include #include #include #define DRIVER_TEST_UNIX #endif /* CONFIG_NATIVE_WINDOWS */ #include "utils/common.h" #include "utils/eloop.h" #include "utils/list.h" #include "utils/trace.h" #include "common/ieee802_11_defs.h" #include "crypto/sha1.h" #include "l2_packet/l2_packet.h" #include "wps/wps.h" #include "driver.h" struct test_client_socket { struct test_client_socket *next; u8 addr[ETH_ALEN]; struct sockaddr_un un; socklen_t unlen; struct test_driver_bss *bss; }; struct test_driver_bss { struct wpa_driver_test_data *drv; struct dl_list list; void *bss_ctx; char ifname[IFNAMSIZ]; u8 bssid[ETH_ALEN]; u8 *ie; size_t ielen; u8 *wps_beacon_ie; size_t wps_beacon_ie_len; u8 *wps_probe_resp_ie; size_t wps_probe_resp_ie_len; u8 ssid[32]; size_t ssid_len; int privacy; }; struct wpa_driver_test_global { int bss_add_used; u8 req_addr[ETH_ALEN]; }; struct wpa_driver_test_data { struct wpa_driver_test_global *global; void *ctx; WPA_TRACE_REF(ctx); u8 own_addr[ETH_ALEN]; int test_socket; #ifdef DRIVER_TEST_UNIX struct sockaddr_un hostapd_addr; #endif /* DRIVER_TEST_UNIX */ int hostapd_addr_set; struct sockaddr_in hostapd_addr_udp; int hostapd_addr_udp_set; char *own_socket_path; char *test_dir; #define MAX_SCAN_RESULTS 30 struct wpa_scan_res *scanres[MAX_SCAN_RESULTS]; size_t num_scanres; int use_associnfo; u8 assoc_wpa_ie[80]; size_t assoc_wpa_ie_len; int associated; u8 *probe_req_ie; size_t probe_req_ie_len; u8 probe_req_ssid[32]; size_t probe_req_ssid_len; int ibss; int ap; struct test_client_socket *cli; struct dl_list bss; int udp_port; int alloc_iface_idx; int probe_req_report; unsigned int remain_on_channel_freq; unsigned int remain_on_channel_duration; int current_freq; }; static void wpa_driver_test_deinit(void *priv); static int wpa_driver_test_attach(struct wpa_driver_test_data *drv, const char *dir, int ap); static void wpa_driver_test_close_test_socket( struct wpa_driver_test_data *drv); static void test_remain_on_channel_timeout(void *eloop_ctx, void *timeout_ctx); static void test_driver_free_bss(struct test_driver_bss *bss) { os_free(bss->ie); os_free(bss->wps_beacon_ie); os_free(bss->wps_probe_resp_ie); os_free(bss); } static void test_driver_free_bsses(struct wpa_driver_test_data *drv) { struct test_driver_bss *bss, *tmp; dl_list_for_each_safe(bss, tmp, &drv->bss, struct test_driver_bss, list) { dl_list_del(&bss->list); test_driver_free_bss(bss); } } static struct test_client_socket * test_driver_get_cli(struct wpa_driver_test_data *drv, struct sockaddr_un *from, socklen_t fromlen) { struct test_client_socket *cli = drv->cli; while (cli) { if (cli->unlen == fromlen && strncmp(cli->un.sun_path, from->sun_path, fromlen - sizeof(cli->un.sun_family)) == 0) return cli; cli = cli->next; } return NULL; } static int test_driver_send_eapol(void *priv, const u8 *addr, const u8 *data, size_t data_len, int encrypt, const u8 *own_addr, u32 flags) { struct test_driver_bss *dbss = priv; struct wpa_driver_test_data *drv = dbss->drv; struct test_client_socket *cli; struct msghdr msg; struct iovec io[3]; struct l2_ethhdr eth; if (drv->test_socket < 0) return -1; cli = drv->cli; while (cli) { if (memcmp(cli->addr, addr, ETH_ALEN) == 0) break; cli = cli->next; } if (!cli) { wpa_printf(MSG_DEBUG, "%s: no destination client entry", __func__); return -1; } memcpy(eth.h_dest, addr, ETH_ALEN); memcpy(eth.h_source, own_addr, ETH_ALEN); eth.h_proto = host_to_be16(ETH_P_EAPOL); io[0].iov_base = "EAPOL "; io[0].iov_len = 6; io[1].iov_base = ð io[1].iov_len = sizeof(eth); io[2].iov_base = (u8 *) data; io[2].iov_len = data_len; memset(&msg, 0, sizeof(msg)); msg.msg_iov = io; msg.msg_iovlen = 3; msg.msg_name = &cli->un; msg.msg_namelen = cli->unlen; return sendmsg(drv->test_socket, &msg, 0); } static int test_driver_send_ether(void *priv, const u8 *dst, const u8 *src, u16 proto, const u8 *data, size_t data_len) { struct test_driver_bss *dbss = priv; struct wpa_driver_test_data *drv = dbss->drv; struct msghdr msg; struct iovec io[3]; struct l2_ethhdr eth; char desttxt[30]; struct sockaddr_un addr; struct dirent *dent; DIR *dir; int ret = 0, broadcast = 0, count = 0; if (drv->test_socket < 0 || drv->test_dir == NULL) { wpa_printf(MSG_DEBUG, "%s: invalid parameters (sock=%d " "test_dir=%p)", __func__, drv->test_socket, drv->test_dir); return -1; } broadcast = memcmp(dst, "\xff\xff\xff\xff\xff\xff", ETH_ALEN) == 0; snprintf(desttxt, sizeof(desttxt), MACSTR, MAC2STR(dst)); memcpy(eth.h_dest, dst, ETH_ALEN); memcpy(eth.h_source, src, ETH_ALEN); eth.h_proto = host_to_be16(proto); io[0].iov_base = "ETHER "; io[0].iov_len = 6; io[1].iov_base = ð io[1].iov_len = sizeof(eth); io[2].iov_base = (u8 *) data; io[2].iov_len = data_len; memset(&msg, 0, sizeof(msg)); msg.msg_iov = io; msg.msg_iovlen = 3; dir = opendir(drv->test_dir); if (dir == NULL) { perror("test_driver: opendir"); return -1; } while ((dent = readdir(dir))) { #ifdef _DIRENT_HAVE_D_TYPE /* Skip the file if it is not a socket. Also accept * DT_UNKNOWN (0) in case the C library or underlying file * system does not support d_type. */ if (dent->d_type != DT_SOCK && dent->d_type != DT_UNKNOWN) continue; #endif /* _DIRENT_HAVE_D_TYPE */ if (strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..") == 0) continue; memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; snprintf(addr.sun_path, sizeof(addr.sun_path), "%s/%s", drv->test_dir, dent->d_name); if (strcmp(addr.sun_path, drv->own_socket_path) == 0) continue; if (!broadcast && strstr(dent->d_name, desttxt) == NULL) continue; wpa_printf(MSG_DEBUG, "%s: Send ether frame to %s", __func__, dent->d_name); msg.msg_name = &addr; msg.msg_namelen = sizeof(addr); ret = sendmsg(drv->test_socket, &msg, 0); if (ret < 0) perror("driver_test: sendmsg"); count++; } closedir(dir); if (!broadcast && count == 0) { wpa_printf(MSG_DEBUG, "%s: Destination " MACSTR " not found", __func__, MAC2STR(dst)); return -1; } return ret; } static int wpa_driver_test_send_mlme(void *priv, const u8 *data, size_t data_len, int noack) { struct test_driver_bss *dbss = priv; struct wpa_driver_test_data *drv = dbss->drv; struct msghdr msg; struct iovec io[2]; const u8 *dest; struct sockaddr_un addr; struct dirent *dent; DIR *dir; int broadcast; int ret = 0; struct ieee80211_hdr *hdr; u16 fc; char cmd[50]; int freq; #ifdef HOSTAPD char desttxt[30]; #endif /* HOSTAPD */ union wpa_event_data event; wpa_hexdump(MSG_MSGDUMP, "test_send_mlme", data, data_len); if (drv->test_socket < 0 || data_len < 10) { wpa_printf(MSG_DEBUG, "%s: invalid parameters (sock=%d len=%lu" " test_dir=%p)", __func__, drv->test_socket, (unsigned long) data_len, drv->test_dir); return -1; } dest = data + 4; broadcast = os_memcmp(dest, "\xff\xff\xff\xff\xff\xff", ETH_ALEN) == 0; #ifdef HOSTAPD snprintf(desttxt, sizeof(desttxt), MACSTR, MAC2STR(dest)); #endif /* HOSTAPD */ if (drv->remain_on_channel_freq) freq = drv->remain_on_channel_freq; else freq = drv->current_freq; wpa_printf(MSG_DEBUG, "test_driver(%s): MLME TX on freq %d MHz", dbss->ifname, freq); os_snprintf(cmd, sizeof(cmd), "MLME freq=%d ", freq); io[0].iov_base = cmd; io[0].iov_len = os_strlen(cmd); io[1].iov_base = (void *) data; io[1].iov_len = data_len; os_memset(&msg, 0, sizeof(msg)); msg.msg_iov = io; msg.msg_iovlen = 2; #ifdef HOSTAPD if (drv->test_dir == NULL) { wpa_printf(MSG_DEBUG, "%s: test_dir == NULL", __func__); return -1; } dir = opendir(drv->test_dir); if (dir == NULL) { perror("test_driver: opendir"); return -1; } while ((dent = readdir(dir))) { #ifdef _DIRENT_HAVE_D_TYPE /* Skip the file if it is not a socket. Also accept * DT_UNKNOWN (0) in case the C library or underlying file * system does not support d_type. */ if (dent->d_type != DT_SOCK && dent->d_type != DT_UNKNOWN) continue; #endif /* _DIRENT_HAVE_D_TYPE */ if (os_strcmp(dent->d_name, ".") == 0 || os_strcmp(dent->d_name, "..") == 0) continue; os_memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; os_snprintf(addr.sun_path, sizeof(addr.sun_path), "%s/%s", drv->test_dir, dent->d_name); if (os_strcmp(addr.sun_path, drv->own_socket_path) == 0) continue; if (!broadcast && os_strstr(dent->d_name, desttxt) == NULL) continue; wpa_printf(MSG_DEBUG, "%s: Send management frame to %s", __func__, dent->d_name); msg.msg_name = &addr; msg.msg_namelen = sizeof(addr); ret = sendmsg(drv->test_socket, &msg, 0); if (ret < 0) perror("driver_test: sendmsg(test_socket)"); } closedir(dir); #else /* HOSTAPD */ if (os_memcmp(dest, dbss->bssid, ETH_ALEN) == 0 || drv->test_dir == NULL) { if (drv->hostapd_addr_udp_set) { msg.msg_name = &drv->hostapd_addr_udp; msg.msg_namelen = sizeof(drv->hostapd_addr_udp); } else { #ifdef DRIVER_TEST_UNIX msg.msg_name = &drv->hostapd_addr; msg.msg_namelen = sizeof(drv->hostapd_addr); #endif /* DRIVER_TEST_UNIX */ } } else if (broadcast) { dir = opendir(drv->test_dir); if (dir == NULL) return -1; while ((dent = readdir(dir))) { #ifdef _DIRENT_HAVE_D_TYPE /* Skip the file if it is not a socket. * Also accept DT_UNKNOWN (0) in case * the C library or underlying file * system does not support d_type. */ if (dent->d_type != DT_SOCK && dent->d_type != DT_UNKNOWN) continue; #endif /* _DIRENT_HAVE_D_TYPE */ if (os_strcmp(dent->d_name, ".") == 0 || os_strcmp(dent->d_name, "..") == 0) continue; wpa_printf(MSG_DEBUG, "%s: Send broadcast MLME to %s", __func__, dent->d_name); os_memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; os_snprintf(addr.sun_path, sizeof(addr.sun_path), "%s/%s", drv->test_dir, dent->d_name); msg.msg_name = &addr; msg.msg_namelen = sizeof(addr); ret = sendmsg(drv->test_socket, &msg, 0); if (ret < 0) perror("driver_test: sendmsg(test_socket)"); } closedir(dir); return ret; } else { struct stat st; os_memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; os_snprintf(addr.sun_path, sizeof(addr.sun_path), "%s/AP-" MACSTR, drv->test_dir, MAC2STR(dest)); if (stat(addr.sun_path, &st) < 0) { os_snprintf(addr.sun_path, sizeof(addr.sun_path), "%s/STA-" MACSTR, drv->test_dir, MAC2STR(dest)); } msg.msg_name = &addr; msg.msg_namelen = sizeof(addr); } if (sendmsg(drv->test_socket, &msg, 0) < 0) { perror("sendmsg(test_socket)"); return -1; } #endif /* HOSTAPD */ hdr = (struct ieee80211_hdr *) data; fc = le_to_host16(hdr->frame_control); os_memset(&event, 0, sizeof(event)); event.tx_status.type = WLAN_FC_GET_TYPE(fc); event.tx_status.stype = WLAN_FC_GET_STYPE(fc); event.tx_status.dst = hdr->addr1; event.tx_status.data = data; event.tx_status.data_len = data_len; event.tx_status.ack = ret >= 0; wpa_supplicant_event(drv->ctx, EVENT_TX_STATUS, &event); return ret; } static void test_driver_scan(struct wpa_driver_test_data *drv, struct sockaddr_un *from, socklen_t fromlen, char *data) { char buf[512], *pos, *end; int ret; struct test_driver_bss *bss; u8 sa[ETH_ALEN]; u8 ie[512]; size_t ielen; union wpa_event_data event; /* data: optional [ ' ' | STA-addr | ' ' | IEs(hex) ] */ wpa_printf(MSG_DEBUG, "test_driver: SCAN"); if (*data) { if (*data != ' ' || hwaddr_aton(data + 1, sa)) { wpa_printf(MSG_DEBUG, "test_driver: Unexpected SCAN " "command format"); return; } data += 18; while (*data == ' ') data++; ielen = os_strlen(data) / 2; if (ielen > sizeof(ie)) ielen = sizeof(ie); if (hexstr2bin(data, ie, ielen) < 0) ielen = 0; wpa_printf(MSG_DEBUG, "test_driver: Scan from " MACSTR, MAC2STR(sa)); wpa_hexdump(MSG_MSGDUMP, "test_driver: scan IEs", ie, ielen); os_memset(&event, 0, sizeof(event)); event.rx_probe_req.sa = sa; event.rx_probe_req.ie = ie; event.rx_probe_req.ie_len = ielen; wpa_supplicant_event(drv->ctx, EVENT_RX_PROBE_REQ, &event); } dl_list_for_each(bss, &drv->bss, struct test_driver_bss, list) { pos = buf; end = buf + sizeof(buf); /* reply: SCANRESP BSSID SSID IEs */ ret = snprintf(pos, end - pos, "SCANRESP " MACSTR " ", MAC2STR(bss->bssid)); if (ret < 0 || ret >= end - pos) return; pos += ret; pos += wpa_snprintf_hex(pos, end - pos, bss->ssid, bss->ssid_len); ret = snprintf(pos, end - pos, " "); if (ret < 0 || ret >= end - pos) return; pos += ret; pos += wpa_snprintf_hex(pos, end - pos, bss->ie, bss->ielen); pos += wpa_snprintf_hex(pos, end - pos, bss->wps_probe_resp_ie, bss->wps_probe_resp_ie_len); if (bss->privacy) { ret = snprintf(pos, end - pos, " PRIVACY"); if (ret < 0 || ret >= end - pos) return; pos += ret; } sendto(drv->test_socket, buf, pos - buf, 0, (struct sockaddr *) from, fromlen); } } static void test_driver_assoc(struct wpa_driver_test_data *drv, struct sockaddr_un *from, socklen_t fromlen, char *data) { struct test_client_socket *cli; u8 ie[256], ssid[32]; size_t ielen, ssid_len = 0; char *pos, *pos2, cmd[50]; struct test_driver_bss *bss, *tmp; /* data: STA-addr SSID(hex) IEs(hex) */ cli = os_zalloc(sizeof(*cli)); if (cli == NULL) return; if (hwaddr_aton(data, cli->addr)) { printf("test_socket: Invalid MAC address '%s' in ASSOC\n", data); os_free(cli); return; } pos = data + 17; while (*pos == ' ') pos++; pos2 = strchr(pos, ' '); ielen = 0; if (pos2) { ssid_len = (pos2 - pos) / 2; if (hexstr2bin(pos, ssid, ssid_len) < 0) { wpa_printf(MSG_DEBUG, "%s: Invalid SSID", __func__); os_free(cli); return; } wpa_hexdump_ascii(MSG_DEBUG, "test_driver_assoc: SSID", ssid, ssid_len); pos = pos2 + 1; ielen = strlen(pos) / 2; if (ielen > sizeof(ie)) ielen = sizeof(ie); if (hexstr2bin(pos, ie, ielen) < 0) ielen = 0; } bss = NULL; dl_list_for_each(tmp, &drv->bss, struct test_driver_bss, list) { if (tmp->ssid_len == ssid_len && os_memcmp(tmp->ssid, ssid, ssid_len) == 0) { bss = tmp; break; } } if (bss == NULL) { wpa_printf(MSG_DEBUG, "%s: No matching SSID found from " "configured BSSes", __func__); os_free(cli); return; } cli->bss = bss; memcpy(&cli->un, from, sizeof(cli->un)); cli->unlen = fromlen; cli->next = drv->cli; drv->cli = cli; wpa_hexdump_ascii(MSG_DEBUG, "test_socket: ASSOC sun_path", (const u8 *) cli->un.sun_path, cli->unlen - sizeof(cli->un.sun_family)); snprintf(cmd, sizeof(cmd), "ASSOCRESP " MACSTR " 0", MAC2STR(bss->bssid)); sendto(drv->test_socket, cmd, strlen(cmd), 0, (struct sockaddr *) from, fromlen); drv_event_assoc(bss->bss_ctx, cli->addr, ie, ielen, 0); } static void test_driver_disassoc(struct wpa_driver_test_data *drv, struct sockaddr_un *from, socklen_t fromlen) { struct test_client_socket *cli; cli = test_driver_get_cli(drv, from, fromlen); if (!cli) return; drv_event_disassoc(drv->ctx, cli->addr); } static void test_driver_eapol(struct wpa_driver_test_data *drv, struct sockaddr_un *from, socklen_t fromlen, u8 *data, size_t datalen) { #ifdef HOSTAPD struct test_client_socket *cli; #endif /* HOSTAPD */ const u8 *src = NULL; if (datalen > 14) { /* Skip Ethernet header */ src = data + ETH_ALEN; wpa_printf(MSG_DEBUG, "test_driver: dst=" MACSTR " src=" MACSTR " proto=%04x", MAC2STR(data), MAC2STR(src), WPA_GET_BE16(data + 2 * ETH_ALEN)); data += 14; datalen -= 14; } #ifdef HOSTAPD cli = test_driver_get_cli(drv, from, fromlen); if (cli) { drv_event_eapol_rx(cli->bss->bss_ctx, cli->addr, data, datalen); } else { wpa_printf(MSG_DEBUG, "test_socket: EAPOL from unknown " "client"); } #else /* HOSTAPD */ if (src) drv_event_eapol_rx(drv->ctx, src, data, datalen); #endif /* HOSTAPD */ } static void test_driver_ether(struct wpa_driver_test_data *drv, struct sockaddr_un *from, socklen_t fromlen, u8 *data, size_t datalen) { struct l2_ethhdr *eth; if (datalen < sizeof(*eth)) return; eth = (struct l2_ethhdr *) data; wpa_printf(MSG_DEBUG, "test_driver: RX ETHER dst=" MACSTR " src=" MACSTR " proto=%04x", MAC2STR(eth->h_dest), MAC2STR(eth->h_source), be_to_host16(eth->h_proto)); #ifdef CONFIG_IEEE80211R if (be_to_host16(eth->h_proto) == ETH_P_RRB) { union wpa_event_data ev; os_memset(&ev, 0, sizeof(ev)); ev.ft_rrb_rx.src = eth->h_source; ev.ft_rrb_rx.data = data + sizeof(*eth); ev.ft_rrb_rx.data_len = datalen - sizeof(*eth); } #endif /* CONFIG_IEEE80211R */ } static void test_driver_mlme(struct wpa_driver_test_data *drv, struct sockaddr_un *from, socklen_t fromlen, u8 *data, size_t datalen) { struct ieee80211_hdr *hdr; u16 fc; union wpa_event_data event; int freq = 0, own_freq; struct test_driver_bss *bss; bss = dl_list_first(&drv->bss, struct test_driver_bss, list); if (datalen > 6 && os_memcmp(data, "freq=", 5) == 0) { size_t pos; for (pos = 5; pos < datalen; pos++) { if (data[pos] == ' ') break; } if (pos < datalen) { freq = atoi((const char *) &data[5]); wpa_printf(MSG_DEBUG, "test_driver(%s): MLME RX on " "freq %d MHz", bss->ifname, freq); pos++; data += pos; datalen -= pos; } } if (drv->remain_on_channel_freq) own_freq = drv->remain_on_channel_freq; else own_freq = drv->current_freq; if (freq && own_freq && freq != own_freq) { wpa_printf(MSG_DEBUG, "test_driver(%s): Ignore MLME RX on " "another frequency %d MHz (own %d MHz)", bss->ifname, freq, own_freq); return; } hdr = (struct ieee80211_hdr *) data; if (test_driver_get_cli(drv, from, fromlen) == NULL && datalen >= 16) { struct test_client_socket *cli; cli = os_zalloc(sizeof(*cli)); if (cli == NULL) return; wpa_printf(MSG_DEBUG, "Adding client entry for " MACSTR, MAC2STR(hdr->addr2)); memcpy(cli->addr, hdr->addr2, ETH_ALEN); memcpy(&cli->un, from, sizeof(cli->un)); cli->unlen = fromlen; cli->next = drv->cli; drv->cli = cli; } wpa_hexdump(MSG_MSGDUMP, "test_driver_mlme: received frame", data, datalen); fc = le_to_host16(hdr->frame_control); if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT) { wpa_printf(MSG_ERROR, "%s: received non-mgmt frame", __func__); return; } os_memset(&event, 0, sizeof(event)); event.rx_mgmt.frame = data; event.rx_mgmt.frame_len = datalen; wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event); } static void test_driver_receive_unix(int sock, void *eloop_ctx, void *sock_ctx) { struct wpa_driver_test_data *drv = eloop_ctx; char buf[2000]; int res; struct sockaddr_un from; socklen_t fromlen = sizeof(from); res = recvfrom(sock, buf, sizeof(buf) - 1, 0, (struct sockaddr *) &from, &fromlen); if (res < 0) { perror("recvfrom(test_socket)"); return; } buf[res] = '\0'; wpa_printf(MSG_DEBUG, "test_driver: received %u bytes", res); if (strncmp(buf, "SCAN", 4) == 0) { test_driver_scan(drv, &from, fromlen, buf + 4); } else if (strncmp(buf, "ASSOC ", 6) == 0) { test_driver_assoc(drv, &from, fromlen, buf + 6); } else if (strcmp(buf, "DISASSOC") == 0) { test_driver_disassoc(drv, &from, fromlen); } else if (strncmp(buf, "EAPOL ", 6) == 0) { test_driver_eapol(drv, &from, fromlen, (u8 *) buf + 6, res - 6); } else if (strncmp(buf, "ETHER ", 6) == 0) { test_driver_ether(drv, &from, fromlen, (u8 *) buf + 6, res - 6); } else if (strncmp(buf, "MLME ", 5) == 0) { test_driver_mlme(drv, &from, fromlen, (u8 *) buf + 5, res - 5); } else { wpa_hexdump_ascii(MSG_DEBUG, "Unknown test_socket command", (u8 *) buf, res); } } static int test_driver_set_generic_elem(void *priv, const u8 *elem, size_t elem_len) { struct test_driver_bss *bss = priv; os_free(bss->ie); if (elem == NULL) { bss->ie = NULL; bss->ielen = 0; return 0; } bss->ie = os_malloc(elem_len); if (bss->ie == NULL) { bss->ielen = 0; return -1; } memcpy(bss->ie, elem, elem_len); bss->ielen = elem_len; return 0; } static int test_driver_set_ap_wps_ie(void *priv, const struct wpabuf *beacon, const struct wpabuf *proberesp, const struct wpabuf *assocresp) { struct test_driver_bss *bss = priv; if (beacon == NULL) wpa_printf(MSG_DEBUG, "test_driver: Clear Beacon WPS IE"); else wpa_hexdump_buf(MSG_DEBUG, "test_driver: Beacon WPS IE", beacon); os_free(bss->wps_beacon_ie); if (beacon == NULL) { bss->wps_beacon_ie = NULL; bss->wps_beacon_ie_len = 0; } else { bss->wps_beacon_ie = os_malloc(wpabuf_len(beacon)); if (bss->wps_beacon_ie == NULL) { bss->wps_beacon_ie_len = 0; return -1; } os_memcpy(bss->wps_beacon_ie, wpabuf_head(beacon), wpabuf_len(beacon)); bss->wps_beacon_ie_len = wpabuf_len(beacon); } if (proberesp == NULL) wpa_printf(MSG_DEBUG, "test_driver: Clear Probe Response WPS " "IE"); else wpa_hexdump_buf(MSG_DEBUG, "test_driver: Probe Response WPS " "IE", proberesp); os_free(bss->wps_probe_resp_ie); if (proberesp == NULL) { bss->wps_probe_resp_ie = NULL; bss->wps_probe_resp_ie_len = 0; } else { bss->wps_probe_resp_ie = os_malloc(wpabuf_len(proberesp)); if (bss->wps_probe_resp_ie == NULL) { bss->wps_probe_resp_ie_len = 0; return -1; } os_memcpy(bss->wps_probe_resp_ie, wpabuf_head(proberesp), wpabuf_len(proberesp)); bss->wps_probe_resp_ie_len = wpabuf_len(proberesp); } return 0; } static int test_driver_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, int reason) { struct test_driver_bss *dbss = priv; struct wpa_driver_test_data *drv = dbss->drv; struct test_client_socket *cli; if (drv->test_socket < 0) return -1; cli = drv->cli; while (cli) { if (memcmp(cli->addr, addr, ETH_ALEN) == 0) break; cli = cli->next; } if (!cli) return -1; return sendto(drv->test_socket, "DEAUTH", 6, 0, (struct sockaddr *) &cli->un, cli->unlen); } static int test_driver_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr, int reason) { struct test_driver_bss *dbss = priv; struct wpa_driver_test_data *drv = dbss->drv; struct test_client_socket *cli; if (drv->test_socket < 0) return -1; cli = drv->cli; while (cli) { if (memcmp(cli->addr, addr, ETH_ALEN) == 0) break; cli = cli->next; } if (!cli) return -1; return sendto(drv->test_socket, "DISASSOC", 8, 0, (struct sockaddr *) &cli->un, cli->unlen); } static int test_driver_bss_add(void *priv, const char *ifname, const u8 *bssid, void *bss_ctx, void **drv_priv) { struct test_driver_bss *dbss = priv; struct wpa_driver_test_data *drv = dbss->drv; struct test_driver_bss *bss; wpa_printf(MSG_DEBUG, "%s(ifname=%s bssid=" MACSTR ")", __func__, ifname, MAC2STR(bssid)); bss = os_zalloc(sizeof(*bss)); if (bss == NULL) return -1; bss->bss_ctx = bss_ctx; bss->drv = drv; os_strlcpy(bss->ifname, ifname, IFNAMSIZ); os_memcpy(bss->bssid, bssid, ETH_ALEN); dl_list_add(&drv->bss, &bss->list); if (drv->global) { drv->global->bss_add_used = 1; os_memcpy(drv->global->req_addr, bssid, ETH_ALEN); } if (drv_priv) *drv_priv = bss; return 0; } static int test_driver_bss_remove(void *priv, const char *ifname) { struct test_driver_bss *dbss = priv; struct wpa_driver_test_data *drv = dbss->drv; struct test_driver_bss *bss; struct test_client_socket *cli, *prev_c; wpa_printf(MSG_DEBUG, "%s(ifname=%s)", __func__, ifname); dl_list_for_each(bss, &drv->bss, struct test_driver_bss, list) { if (strcmp(bss->ifname, ifname) != 0) continue; for (prev_c = NULL, cli = drv->cli; cli; prev_c = cli, cli = cli->next) { if (cli->bss != bss) continue; if (prev_c) prev_c->next = cli->next; else drv->cli = cli->next; os_free(cli); break; } dl_list_del(&bss->list); test_driver_free_bss(bss); return 0; } return -1; } static int test_driver_if_add(void *priv, enum wpa_driver_if_type type, const char *ifname, const u8 *addr, void *bss_ctx, void **drv_priv, char *force_ifname, u8 *if_addr, const char *bridge, int use_existing) { struct test_driver_bss *dbss = priv; struct wpa_driver_test_data *drv = dbss->drv; wpa_printf(MSG_DEBUG, "%s(type=%d ifname=%s bss_ctx=%p)", __func__, type, ifname, bss_ctx); if (addr) os_memcpy(if_addr, addr, ETH_ALEN); else { drv->alloc_iface_idx++; if_addr[0] = 0x02; /* locally administered */ sha1_prf(drv->own_addr, ETH_ALEN, "hostapd test addr generation", (const u8 *) &drv->alloc_iface_idx, sizeof(drv->alloc_iface_idx), if_addr + 1, ETH_ALEN - 1); } if (type == WPA_IF_AP_BSS || type == WPA_IF_P2P_GO || type == WPA_IF_P2P_CLIENT || type == WPA_IF_P2P_GROUP) return test_driver_bss_add(priv, ifname, if_addr, bss_ctx, drv_priv); return 0; } static int test_driver_if_remove(void *priv, enum wpa_driver_if_type type, const char *ifname) { wpa_printf(MSG_DEBUG, "%s(type=%d ifname=%s)", __func__, type, ifname); if (type == WPA_IF_AP_BSS || type == WPA_IF_P2P_GO || type == WPA_IF_P2P_CLIENT || type == WPA_IF_P2P_GROUP) return test_driver_bss_remove(priv, ifname); return 0; } static int test_driver_set_ssid(void *priv, const u8 *buf, int len) { struct test_driver_bss *bss = priv; wpa_printf(MSG_DEBUG, "%s(ifname=%s)", __func__, bss->ifname); if (len < 0) return -1; wpa_hexdump_ascii(MSG_DEBUG, "test_driver_set_ssid: SSID", buf, len); if ((size_t) len > sizeof(bss->ssid)) return -1; os_memcpy(bss->ssid, buf, len); bss->ssid_len = len; return 0; } static int test_driver_set_privacy(void *priv, int enabled) { struct test_driver_bss *dbss = priv; wpa_printf(MSG_DEBUG, "%s(enabled=%d)", __func__, enabled); dbss->privacy = enabled; return 0; } static int test_driver_set_sta_vlan(void *priv, const u8 *addr, const char *ifname, int vlan_id) { wpa_printf(MSG_DEBUG, "%s(addr=" MACSTR " ifname=%s vlan_id=%d)", __func__, MAC2STR(addr), ifname, vlan_id); return 0; } static int test_driver_sta_add(void *priv, struct hostapd_sta_add_params *params) { struct test_driver_bss *bss = priv; struct wpa_driver_test_data *drv = bss->drv; struct test_client_socket *cli; wpa_printf(MSG_DEBUG, "%s(ifname=%s addr=" MACSTR " aid=%d " "capability=0x%x listen_interval=%d)", __func__, bss->ifname, MAC2STR(params->addr), params->aid, params->capability, params->listen_interval); wpa_hexdump(MSG_DEBUG, "test_driver_sta_add - supp_rates", params->supp_rates, params->supp_rates_len); cli = drv->cli; while (cli) { if (os_memcmp(cli->addr, params->addr, ETH_ALEN) == 0) break; cli = cli->next; } if (!cli) { wpa_printf(MSG_DEBUG, "%s: no matching client entry", __func__); return -1; } cli->bss = bss; return 0; } static struct wpa_driver_test_data * test_alloc_data(void *ctx, const char *ifname) { struct wpa_driver_test_data *drv; struct test_driver_bss *bss; drv = os_zalloc(sizeof(struct wpa_driver_test_data)); if (drv == NULL) { wpa_printf(MSG_ERROR, "Could not allocate memory for test " "driver data"); return NULL; } bss = os_zalloc(sizeof(struct test_driver_bss)); if (bss == NULL) { os_free(drv); return NULL; } drv->ctx = ctx; wpa_trace_add_ref(drv, ctx, ctx); dl_list_init(&drv->bss); dl_list_add(&drv->bss, &bss->list); os_strlcpy(bss->ifname, ifname, IFNAMSIZ); bss->bss_ctx = ctx; bss->drv = drv; /* Generate a MAC address to help testing with multiple STAs */ drv->own_addr[0] = 0x02; /* locally administered */ sha1_prf((const u8 *) ifname, os_strlen(ifname), "test mac addr generation", NULL, 0, drv->own_addr + 1, ETH_ALEN - 1); return drv; } static void * test_driver_init(struct hostapd_data *hapd, struct wpa_init_params *params) { struct wpa_driver_test_data *drv; struct sockaddr_un addr_un; struct sockaddr_in addr_in; struct sockaddr *addr; socklen_t alen; struct test_driver_bss *bss; drv = test_alloc_data(hapd, params->ifname); if (drv == NULL) return NULL; drv->ap = 1; bss = dl_list_first(&drv->bss, struct test_driver_bss, list); drv->global = params->global_priv; bss->bss_ctx = hapd; os_memcpy(bss->bssid, drv->own_addr, ETH_ALEN); os_memcpy(params->own_addr, drv->own_addr, ETH_ALEN); if (params->test_socket) { if (os_strlen(params->test_socket) >= sizeof(addr_un.sun_path)) { printf("Too long test_socket path\n"); wpa_driver_test_deinit(bss); return NULL; } if (strncmp(params->test_socket, "DIR:", 4) == 0) { size_t len = strlen(params->test_socket) + 30; drv->test_dir = os_strdup(params->test_socket + 4); drv->own_socket_path = os_malloc(len); if (drv->own_socket_path) { snprintf(drv->own_socket_path, len, "%s/AP-" MACSTR, params->test_socket + 4, MAC2STR(params->own_addr)); } } else if (strncmp(params->test_socket, "UDP:", 4) == 0) { drv->udp_port = atoi(params->test_socket + 4); } else { drv->own_socket_path = os_strdup(params->test_socket); } if (drv->own_socket_path == NULL && drv->udp_port == 0) { wpa_driver_test_deinit(bss); return NULL; } drv->test_socket = socket(drv->udp_port ? PF_INET : PF_UNIX, SOCK_DGRAM, 0); if (drv->test_socket < 0) { perror("socket"); wpa_driver_test_deinit(bss); return NULL; } if (drv->udp_port) { os_memset(&addr_in, 0, sizeof(addr_in)); addr_in.sin_family = AF_INET; addr_in.sin_port = htons(drv->udp_port); addr = (struct sockaddr *) &addr_in; alen = sizeof(addr_in); } else { os_memset(&addr_un, 0, sizeof(addr_un)); addr_un.sun_family = AF_UNIX; os_strlcpy(addr_un.sun_path, drv->own_socket_path, sizeof(addr_un.sun_path)); addr = (struct sockaddr *) &addr_un; alen = sizeof(addr_un); } if (bind(drv->test_socket, addr, alen) < 0) { perror("test-driver-init: bind(PF_UNIX)"); close(drv->test_socket); if (drv->own_socket_path) unlink(drv->own_socket_path); wpa_driver_test_deinit(bss); return NULL; } eloop_register_read_sock(drv->test_socket, test_driver_receive_unix, drv, NULL); } else drv->test_socket = -1; return bss; } static void wpa_driver_test_poll(void *eloop_ctx, void *timeout_ctx) { struct wpa_driver_test_data *drv = eloop_ctx; #ifdef DRIVER_TEST_UNIX if (drv->associated && drv->hostapd_addr_set) { struct stat st; if (stat(drv->hostapd_addr.sun_path, &st) < 0) { wpa_printf(MSG_DEBUG, "%s: lost connection to AP: %s", __func__, strerror(errno)); drv->associated = 0; wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL); } } #endif /* DRIVER_TEST_UNIX */ eloop_register_timeout(1, 0, wpa_driver_test_poll, drv, NULL); } static void wpa_driver_test_scan_timeout(void *eloop_ctx, void *timeout_ctx) { wpa_printf(MSG_DEBUG, "Scan timeout - try to get results"); wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL); } #ifdef DRIVER_TEST_UNIX static void wpa_driver_scan_dir(struct wpa_driver_test_data *drv, const char *path) { struct dirent *dent; DIR *dir; struct sockaddr_un addr; char cmd[512], *pos, *end; int ret; dir = opendir(path); if (dir == NULL) return; end = cmd + sizeof(cmd); pos = cmd; ret = os_snprintf(pos, end - pos, "SCAN " MACSTR, MAC2STR(drv->own_addr)); if (ret >= 0 && ret < end - pos) pos += ret; if (drv->probe_req_ie) { ret = os_snprintf(pos, end - pos, " "); if (ret >= 0 && ret < end - pos) pos += ret; pos += wpa_snprintf_hex(pos, end - pos, drv->probe_req_ie, drv->probe_req_ie_len); } if (drv->probe_req_ssid_len) { /* Add SSID IE */ ret = os_snprintf(pos, end - pos, "%02x%02x", WLAN_EID_SSID, (unsigned int) drv->probe_req_ssid_len); if (ret >= 0 && ret < end - pos) pos += ret; pos += wpa_snprintf_hex(pos, end - pos, drv->probe_req_ssid, drv->probe_req_ssid_len); } end[-1] = '\0'; while ((dent = readdir(dir))) { if (os_strncmp(dent->d_name, "AP-", 3) != 0 && os_strncmp(dent->d_name, "STA-", 4) != 0) continue; if (drv->own_socket_path) { size_t olen, dlen; olen = os_strlen(drv->own_socket_path); dlen = os_strlen(dent->d_name); if (olen >= dlen && os_strcmp(dent->d_name, drv->own_socket_path + olen - dlen) == 0) continue; } wpa_printf(MSG_DEBUG, "%s: SCAN %s", __func__, dent->d_name); os_memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; os_snprintf(addr.sun_path, sizeof(addr.sun_path), "%s/%s", path, dent->d_name); if (sendto(drv->test_socket, cmd, os_strlen(cmd), 0, (struct sockaddr *) &addr, sizeof(addr)) < 0) { perror("sendto(test_socket)"); } } closedir(dir); } #endif /* DRIVER_TEST_UNIX */ static int wpa_driver_test_scan(void *priv, struct wpa_driver_scan_params *params) { struct test_driver_bss *dbss = priv; struct wpa_driver_test_data *drv = dbss->drv; size_t i; wpa_printf(MSG_DEBUG, "%s: priv=%p", __func__, priv); os_free(drv->probe_req_ie); if (params->extra_ies) { drv->probe_req_ie = os_malloc(params->extra_ies_len); if (drv->probe_req_ie == NULL) { drv->probe_req_ie_len = 0; return -1; } os_memcpy(drv->probe_req_ie, params->extra_ies, params->extra_ies_len); drv->probe_req_ie_len = params->extra_ies_len; } else { drv->probe_req_ie = NULL; drv->probe_req_ie_len = 0; } for (i = 0; i < params->num_ssids; i++) wpa_hexdump(MSG_DEBUG, "Scan SSID", params->ssids[i].ssid, params->ssids[i].ssid_len); drv->probe_req_ssid_len = 0; if (params->num_ssids) { os_memcpy(drv->probe_req_ssid, params->ssids[0].ssid, params->ssids[0].ssid_len); drv->probe_req_ssid_len = params->ssids[0].ssid_len; } wpa_hexdump(MSG_DEBUG, "Scan extra IE(s)", params->extra_ies, params->extra_ies_len); drv->num_scanres = 0; #ifdef DRIVER_TEST_UNIX if (drv->test_socket >= 0 && drv->test_dir) wpa_driver_scan_dir(drv, drv->test_dir); if (drv->test_socket >= 0 && drv->hostapd_addr_set && sendto(drv->test_socket, "SCAN", 4, 0, (struct sockaddr *) &drv->hostapd_addr, sizeof(drv->hostapd_addr)) < 0) { perror("sendto(test_socket)"); } #endif /* DRIVER_TEST_UNIX */ if (drv->test_socket >= 0 && drv->hostapd_addr_udp_set && sendto(drv->test_socket, "SCAN", 4, 0, (struct sockaddr *) &drv->hostapd_addr_udp, sizeof(drv->hostapd_addr_udp)) < 0) { perror("sendto(test_socket)"); } eloop_cancel_timeout(wpa_driver_test_scan_timeout, drv, drv->ctx); eloop_register_timeout(1, 0, wpa_driver_test_scan_timeout, drv, drv->ctx); return 0; } static struct wpa_scan_results * wpa_driver_test_get_scan_results2(void *priv) { struct test_driver_bss *dbss = priv; struct wpa_driver_test_data *drv = dbss->drv; struct wpa_scan_results *res; size_t i; res = os_zalloc(sizeof(*res)); if (res == NULL) return NULL; res->res = os_calloc(drv->num_scanres, sizeof(struct wpa_scan_res *)); if (res->res == NULL) { os_free(res); return NULL; } for (i = 0; i < drv->num_scanres; i++) { struct wpa_scan_res *r; if (drv->scanres[i] == NULL) continue; r = os_malloc(sizeof(*r) + drv->scanres[i]->ie_len); if (r == NULL) break; os_memcpy(r, drv->scanres[i], sizeof(*r) + drv->scanres[i]->ie_len); res->res[res->num++] = r; } return res; } static int wpa_driver_test_set_key(const char *ifname, void *priv, enum wpa_alg alg, const u8 *addr, int key_idx, int set_tx, const u8 *seq, size_t seq_len, const u8 *key, size_t key_len) { wpa_printf(MSG_DEBUG, "%s: ifname=%s priv=%p alg=%d key_idx=%d " "set_tx=%d", __func__, ifname, priv, alg, key_idx, set_tx); if (addr) wpa_printf(MSG_DEBUG, " addr=" MACSTR, MAC2STR(addr)); if (seq) wpa_hexdump(MSG_DEBUG, " seq", seq, seq_len); if (key) wpa_hexdump_key(MSG_DEBUG, " key", key, key_len); return 0; } static int wpa_driver_update_mode(struct wpa_driver_test_data *drv, int ap) { if (ap && !drv->ap) { wpa_driver_test_close_test_socket(drv); wpa_driver_test_attach(drv, drv->test_dir, 1); drv->ap = 1; } else if (!ap && drv->ap) { wpa_driver_test_close_test_socket(drv); wpa_driver_test_attach(drv, drv->test_dir, 0); drv->ap = 0; } return 0; } static int wpa_driver_test_associate( void *priv, struct wpa_driver_associate_params *params) { struct test_driver_bss *dbss = priv; struct wpa_driver_test_data *drv = dbss->drv; wpa_printf(MSG_DEBUG, "%s: priv=%p freq=%d pairwise_suite=%d " "group_suite=%d key_mgmt_suite=%d auth_alg=%d mode=%d", __func__, priv, params->freq, params->pairwise_suite, params->group_suite, params->key_mgmt_suite, params->auth_alg, params->mode); wpa_driver_update_mode(drv, params->mode == IEEE80211_MODE_AP); if (params->bssid) { wpa_printf(MSG_DEBUG, " bssid=" MACSTR, MAC2STR(params->bssid)); } if (params->ssid) { wpa_hexdump_ascii(MSG_DEBUG, " ssid", params->ssid, params->ssid_len); } if (params->wpa_ie) { wpa_hexdump(MSG_DEBUG, " wpa_ie", params->wpa_ie, params->wpa_ie_len); drv->assoc_wpa_ie_len = params->wpa_ie_len; if (drv->assoc_wpa_ie_len > sizeof(drv->assoc_wpa_ie)) drv->assoc_wpa_ie_len = sizeof(drv->assoc_wpa_ie); os_memcpy(drv->assoc_wpa_ie, params->wpa_ie, drv->assoc_wpa_ie_len); } else drv->assoc_wpa_ie_len = 0; wpa_driver_update_mode(drv, params->mode == IEEE80211_MODE_AP); drv->ibss = params->mode == IEEE80211_MODE_IBSS; dbss->privacy = params->key_mgmt_suite & (WPA_KEY_MGMT_IEEE8021X | WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_WPA_NONE | WPA_KEY_MGMT_FT_IEEE8021X | WPA_KEY_MGMT_FT_PSK | WPA_KEY_MGMT_IEEE8021X_SHA256 | WPA_KEY_MGMT_PSK_SHA256); if (params->wep_key_len[params->wep_tx_keyidx]) dbss->privacy = 1; #ifdef DRIVER_TEST_UNIX if (drv->test_dir && params->bssid && params->mode != IEEE80211_MODE_IBSS) { os_memset(&drv->hostapd_addr, 0, sizeof(drv->hostapd_addr)); drv->hostapd_addr.sun_family = AF_UNIX; os_snprintf(drv->hostapd_addr.sun_path, sizeof(drv->hostapd_addr.sun_path), "%s/AP-" MACSTR, drv->test_dir, MAC2STR(params->bssid)); drv->hostapd_addr_set = 1; } #endif /* DRIVER_TEST_UNIX */ if (params->mode == IEEE80211_MODE_AP) { os_memcpy(dbss->ssid, params->ssid, params->ssid_len); dbss->ssid_len = params->ssid_len; os_memcpy(dbss->bssid, drv->own_addr, ETH_ALEN); if (params->wpa_ie && params->wpa_ie_len) { dbss->ie = os_malloc(params->wpa_ie_len); if (dbss->ie) { os_memcpy(dbss->ie, params->wpa_ie, params->wpa_ie_len); dbss->ielen = params->wpa_ie_len; } } } else if (drv->test_socket >= 0 && (drv->hostapd_addr_set || drv->hostapd_addr_udp_set)) { char cmd[200], *pos, *end; int ret; end = cmd + sizeof(cmd); pos = cmd; ret = os_snprintf(pos, end - pos, "ASSOC " MACSTR " ", MAC2STR(drv->own_addr)); if (ret >= 0 && ret < end - pos) pos += ret; pos += wpa_snprintf_hex(pos, end - pos, params->ssid, params->ssid_len); ret = os_snprintf(pos, end - pos, " "); if (ret >= 0 && ret < end - pos) pos += ret; pos += wpa_snprintf_hex(pos, end - pos, params->wpa_ie, params->wpa_ie_len); end[-1] = '\0'; #ifdef DRIVER_TEST_UNIX if (drv->hostapd_addr_set && sendto(drv->test_socket, cmd, os_strlen(cmd), 0, (struct sockaddr *) &drv->hostapd_addr, sizeof(drv->hostapd_addr)) < 0) { perror("sendto(test_socket)"); return -1; } #endif /* DRIVER_TEST_UNIX */ if (drv->hostapd_addr_udp_set && sendto(drv->test_socket, cmd, os_strlen(cmd), 0, (struct sockaddr *) &drv->hostapd_addr_udp, sizeof(drv->hostapd_addr_udp)) < 0) { perror("sendto(test_socket)"); return -1; } os_memcpy(dbss->ssid, params->ssid, params->ssid_len); dbss->ssid_len = params->ssid_len; } else { drv->associated = 1; if (params->mode == IEEE80211_MODE_IBSS) { os_memcpy(dbss->ssid, params->ssid, params->ssid_len); dbss->ssid_len = params->ssid_len; if (params->bssid) os_memcpy(dbss->bssid, params->bssid, ETH_ALEN); else { os_get_random(dbss->bssid, ETH_ALEN); dbss->bssid[0] &= ~0x01; dbss->bssid[0] |= 0x02; } } wpa_supplicant_event(drv->ctx, EVENT_ASSOC, NULL); } return 0; } static int wpa_driver_test_get_bssid(void *priv, u8 *bssid) { struct test_driver_bss *dbss = priv; os_memcpy(bssid, dbss->bssid, ETH_ALEN); return 0; } static int wpa_driver_test_get_ssid(void *priv, u8 *ssid) { struct test_driver_bss *dbss = priv; os_memcpy(ssid, dbss->ssid, 32); return dbss->ssid_len; } static int wpa_driver_test_send_disassoc(struct wpa_driver_test_data *drv) { #ifdef DRIVER_TEST_UNIX if (drv->test_socket >= 0 && sendto(drv->test_socket, "DISASSOC", 8, 0, (struct sockaddr *) &drv->hostapd_addr, sizeof(drv->hostapd_addr)) < 0) { perror("sendto(test_socket)"); return -1; } #endif /* DRIVER_TEST_UNIX */ if (drv->test_socket >= 0 && drv->hostapd_addr_udp_set && sendto(drv->test_socket, "DISASSOC", 8, 0, (struct sockaddr *) &drv->hostapd_addr_udp, sizeof(drv->hostapd_addr_udp)) < 0) { perror("sendto(test_socket)"); return -1; } return 0; } static int wpa_driver_test_deauthenticate(void *priv, const u8 *addr, int reason_code) { struct test_driver_bss *dbss = priv; struct wpa_driver_test_data *drv = dbss->drv; wpa_printf(MSG_DEBUG, "%s addr=" MACSTR " reason_code=%d", __func__, MAC2STR(addr), reason_code); os_memset(dbss->bssid, 0, ETH_ALEN); drv->associated = 0; wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL); return wpa_driver_test_send_disassoc(drv); } static const u8 * wpa_scan_get_ie(const struct wpa_scan_res *res, u8 ie) { const u8 *end, *pos; pos = (const u8 *) (res + 1); end = pos + res->ie_len; while (pos + 1 < end) { if (pos + 2 + pos[1] > end) break; if (pos[0] == ie) return pos; pos += 2 + pos[1]; } return NULL; } static void wpa_driver_test_scanresp(struct wpa_driver_test_data *drv, struct sockaddr *from, socklen_t fromlen, const char *data) { struct wpa_scan_res *res; const char *pos, *pos2; size_t len; u8 *ie_pos, *ie_start, *ie_end; #define MAX_IE_LEN 1000 const u8 *ds_params; wpa_printf(MSG_DEBUG, "test_driver: SCANRESP %s", data); if (drv->num_scanres >= MAX_SCAN_RESULTS) { wpa_printf(MSG_DEBUG, "test_driver: No room for the new scan " "result"); return; } /* SCANRESP BSSID SSID IEs */ res = os_zalloc(sizeof(*res) + MAX_IE_LEN); if (res == NULL) return; ie_start = ie_pos = (u8 *) (res + 1); ie_end = ie_pos + MAX_IE_LEN; if (hwaddr_aton(data, res->bssid)) { wpa_printf(MSG_DEBUG, "test_driver: invalid BSSID in scanres"); os_free(res); return; } pos = data + 17; while (*pos == ' ') pos++; pos2 = os_strchr(pos, ' '); if (pos2 == NULL) { wpa_printf(MSG_DEBUG, "test_driver: invalid SSID termination " "in scanres"); os_free(res); return; } len = (pos2 - pos) / 2; if (len > 32) len = 32; /* * Generate SSID IE from the SSID field since this IE is not included * in the main IE field. */ *ie_pos++ = WLAN_EID_SSID; *ie_pos++ = len; if (hexstr2bin(pos, ie_pos, len) < 0) { wpa_printf(MSG_DEBUG, "test_driver: invalid SSID in scanres"); os_free(res); return; } ie_pos += len; pos = pos2 + 1; pos2 = os_strchr(pos, ' '); if (pos2 == NULL) len = os_strlen(pos) / 2; else len = (pos2 - pos) / 2; if ((int) len > ie_end - ie_pos) len = ie_end - ie_pos; if (hexstr2bin(pos, ie_pos, len) < 0) { wpa_printf(MSG_DEBUG, "test_driver: invalid IEs in scanres"); os_free(res); return; } ie_pos += len; res->ie_len = ie_pos - ie_start; if (pos2) { pos = pos2 + 1; while (*pos == ' ') pos++; if (os_strstr(pos, "PRIVACY")) res->caps |= IEEE80211_CAP_PRIVACY; if (os_strstr(pos, "IBSS")) res->caps |= IEEE80211_CAP_IBSS; } ds_params = wpa_scan_get_ie(res, WLAN_EID_DS_PARAMS); if (ds_params && ds_params[1] > 0) { if (ds_params[2] >= 1 && ds_params[2] <= 13) res->freq = 2407 + ds_params[2] * 5; } os_free(drv->scanres[drv->num_scanres]); drv->scanres[drv->num_scanres++] = res; } static void wpa_driver_test_assocresp(struct wpa_driver_test_data *drv, struct sockaddr *from, socklen_t fromlen, const char *data) { struct test_driver_bss *bss; bss = dl_list_first(&drv->bss, struct test_driver_bss, list); /* ASSOCRESP BSSID */ if (hwaddr_aton(data, bss->bssid)) { wpa_printf(MSG_DEBUG, "test_driver: invalid BSSID in " "assocresp"); } if (drv->use_associnfo) { union wpa_event_data event; os_memset(&event, 0, sizeof(event)); event.assoc_info.req_ies = drv->assoc_wpa_ie; event.assoc_info.req_ies_len = drv->assoc_wpa_ie_len; wpa_supplicant_event(drv->ctx, EVENT_ASSOCINFO, &event); } drv->associated = 1; wpa_supplicant_event(drv->ctx, EVENT_ASSOC, NULL); } static void wpa_driver_test_disassoc(struct wpa_driver_test_data *drv, struct sockaddr *from, socklen_t fromlen) { drv->associated = 0; wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL); } static void wpa_driver_test_eapol(struct wpa_driver_test_data *drv, struct sockaddr *from, socklen_t fromlen, const u8 *data, size_t data_len) { const u8 *src; struct test_driver_bss *bss; bss = dl_list_first(&drv->bss, struct test_driver_bss, list); if (data_len > 14) { /* Skip Ethernet header */ src = data + ETH_ALEN; data += 14; data_len -= 14; } else src = bss->bssid; drv_event_eapol_rx(drv->ctx, src, data, data_len); } static void wpa_driver_test_mlme(struct wpa_driver_test_data *drv, struct sockaddr *from, socklen_t fromlen, const u8 *data, size_t data_len) { int freq = 0, own_freq; union wpa_event_data event; const struct ieee80211_mgmt *mgmt; u16 fc; struct test_driver_bss *bss; bss = dl_list_first(&drv->bss, struct test_driver_bss, list); if (data_len > 6 && os_memcmp(data, "freq=", 5) == 0) { size_t pos; for (pos = 5; pos < data_len; pos++) { if (data[pos] == ' ') break; } if (pos < data_len) { freq = atoi((const char *) &data[5]); wpa_printf(MSG_DEBUG, "test_driver(%s): MLME RX on " "freq %d MHz", bss->ifname, freq); pos++; data += pos; data_len -= pos; } } if (drv->remain_on_channel_freq) own_freq = drv->remain_on_channel_freq; else own_freq = drv->current_freq; if (freq && own_freq && freq != own_freq) { wpa_printf(MSG_DEBUG, "test_driver(%s): Ignore MLME RX on " "another frequency %d MHz (own %d MHz)", bss->ifname, freq, own_freq); return; } os_memset(&event, 0, sizeof(event)); event.mlme_rx.buf = data; event.mlme_rx.len = data_len; event.mlme_rx.freq = freq; wpa_supplicant_event(drv->ctx, EVENT_MLME_RX, &event); mgmt = (const struct ieee80211_mgmt *) data; fc = le_to_host16(mgmt->frame_control); if (drv->probe_req_report && data_len >= 24) { if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT && WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_PROBE_REQ) { os_memset(&event, 0, sizeof(event)); event.rx_probe_req.sa = mgmt->sa; event.rx_probe_req.da = mgmt->da; event.rx_probe_req.bssid = mgmt->bssid; event.rx_probe_req.ie = mgmt->u.probe_req.variable; event.rx_probe_req.ie_len = data_len - (mgmt->u.probe_req.variable - data); wpa_supplicant_event(drv->ctx, EVENT_RX_PROBE_REQ, &event); } } } static void wpa_driver_test_scan_cmd(struct wpa_driver_test_data *drv, struct sockaddr *from, socklen_t fromlen, const u8 *data, size_t data_len) { char buf[512], *pos, *end; int ret; struct test_driver_bss *bss; bss = dl_list_first(&drv->bss, struct test_driver_bss, list); /* data: optional [ STA-addr | ' ' | IEs(hex) ] */ if (!drv->ibss) return; pos = buf; end = buf + sizeof(buf); /* reply: SCANRESP BSSID SSID IEs */ ret = snprintf(pos, end - pos, "SCANRESP " MACSTR " ", MAC2STR(bss->bssid)); if (ret < 0 || ret >= end - pos) return; pos += ret; pos += wpa_snprintf_hex(pos, end - pos, bss->ssid, bss->ssid_len); ret = snprintf(pos, end - pos, " "); if (ret < 0 || ret >= end - pos) return; pos += ret; pos += wpa_snprintf_hex(pos, end - pos, drv->assoc_wpa_ie, drv->assoc_wpa_ie_len); if (bss->privacy) { ret = snprintf(pos, end - pos, " PRIVACY"); if (ret < 0 || ret >= end - pos) return; pos += ret; } ret = snprintf(pos, end - pos, " IBSS"); if (ret < 0 || ret >= end - pos) return; pos += ret; sendto(drv->test_socket, buf, pos - buf, 0, (struct sockaddr *) from, fromlen); } static void wpa_driver_test_receive_unix(int sock, void *eloop_ctx, void *sock_ctx) { struct wpa_driver_test_data *drv = eloop_ctx; char *buf; int res; struct sockaddr_storage from; socklen_t fromlen = sizeof(from); const size_t buflen = 2000; if (drv->ap) { test_driver_receive_unix(sock, eloop_ctx, sock_ctx); return; } buf = os_malloc(buflen); if (buf == NULL) return; res = recvfrom(sock, buf, buflen - 1, 0, (struct sockaddr *) &from, &fromlen); if (res < 0) { perror("recvfrom(test_socket)"); os_free(buf); return; } buf[res] = '\0'; wpa_printf(MSG_DEBUG, "test_driver: received %u bytes", res); if (os_strncmp(buf, "SCANRESP ", 9) == 0) { wpa_driver_test_scanresp(drv, (struct sockaddr *) &from, fromlen, buf + 9); } else if (os_strncmp(buf, "ASSOCRESP ", 10) == 0) { wpa_driver_test_assocresp(drv, (struct sockaddr *) &from, fromlen, buf + 10); } else if (os_strcmp(buf, "DISASSOC") == 0) { wpa_driver_test_disassoc(drv, (struct sockaddr *) &from, fromlen); } else if (os_strcmp(buf, "DEAUTH") == 0) { wpa_driver_test_disassoc(drv, (struct sockaddr *) &from, fromlen); } else if (os_strncmp(buf, "EAPOL ", 6) == 0) { wpa_driver_test_eapol(drv, (struct sockaddr *) &from, fromlen, (const u8 *) buf + 6, res - 6); } else if (os_strncmp(buf, "MLME ", 5) == 0) { wpa_driver_test_mlme(drv, (struct sockaddr *) &from, fromlen, (const u8 *) buf + 5, res - 5); } else if (os_strncmp(buf, "SCAN ", 5) == 0) { wpa_driver_test_scan_cmd(drv, (struct sockaddr *) &from, fromlen, (const u8 *) buf + 5, res - 5); } else { wpa_hexdump_ascii(MSG_DEBUG, "Unknown test_socket command", (u8 *) buf, res); } os_free(buf); } static void * wpa_driver_test_init2(void *ctx, const char *ifname, void *global_priv) { struct wpa_driver_test_data *drv; struct wpa_driver_test_global *global = global_priv; struct test_driver_bss *bss; drv = test_alloc_data(ctx, ifname); if (drv == NULL) return NULL; bss = dl_list_first(&drv->bss, struct test_driver_bss, list); drv->global = global_priv; drv->test_socket = -1; /* Set dummy BSSID and SSID for testing. */ bss->bssid[0] = 0x02; bss->bssid[1] = 0x00; bss->bssid[2] = 0x00; bss->bssid[3] = 0x00; bss->bssid[4] = 0x00; bss->bssid[5] = 0x01; os_memcpy(bss->ssid, "test", 5); bss->ssid_len = 4; if (global->bss_add_used) { os_memcpy(drv->own_addr, global->req_addr, ETH_ALEN); global->bss_add_used = 0; } eloop_register_timeout(1, 0, wpa_driver_test_poll, drv, NULL); return bss; } static void wpa_driver_test_close_test_socket(struct wpa_driver_test_data *drv) { if (drv->test_socket >= 0) { eloop_unregister_read_sock(drv->test_socket); close(drv->test_socket); drv->test_socket = -1; } if (drv->own_socket_path) { unlink(drv->own_socket_path); os_free(drv->own_socket_path); drv->own_socket_path = NULL; } } static void wpa_driver_test_deinit(void *priv) { struct test_driver_bss *dbss = priv; struct wpa_driver_test_data *drv = dbss->drv; struct test_client_socket *cli, *prev; int i; cli = drv->cli; while (cli) { prev = cli; cli = cli->next; os_free(prev); } #ifdef HOSTAPD /* There should be only one BSS remaining at this point. */ if (dl_list_len(&drv->bss) != 1) wpa_printf(MSG_ERROR, "%s: %u remaining BSS entries", __func__, dl_list_len(&drv->bss)); #endif /* HOSTAPD */ test_driver_free_bsses(drv); wpa_driver_test_close_test_socket(drv); eloop_cancel_timeout(wpa_driver_test_scan_timeout, drv, drv->ctx); eloop_cancel_timeout(wpa_driver_test_poll, drv, NULL); eloop_cancel_timeout(test_remain_on_channel_timeout, drv, NULL); os_free(drv->test_dir); for (i = 0; i < MAX_SCAN_RESULTS; i++) os_free(drv->scanres[i]); os_free(drv->probe_req_ie); wpa_trace_remove_ref(drv, ctx, drv->ctx); os_free(drv); } static int wpa_driver_test_attach(struct wpa_driver_test_data *drv, const char *dir, int ap) { #ifdef DRIVER_TEST_UNIX static unsigned int counter = 0; struct sockaddr_un addr; size_t len; os_free(drv->own_socket_path); if (dir) { len = os_strlen(dir) + 30; drv->own_socket_path = os_malloc(len); if (drv->own_socket_path == NULL) return -1; os_snprintf(drv->own_socket_path, len, "%s/%s-" MACSTR, dir, ap ? "AP" : "STA", MAC2STR(drv->own_addr)); } else { drv->own_socket_path = os_malloc(100); if (drv->own_socket_path == NULL) return -1; os_snprintf(drv->own_socket_path, 100, "/tmp/wpa_supplicant_test-%d-%d", getpid(), counter++); } drv->test_socket = socket(PF_UNIX, SOCK_DGRAM, 0); if (drv->test_socket < 0) { perror("socket(PF_UNIX)"); os_free(drv->own_socket_path); drv->own_socket_path = NULL; return -1; } os_memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; os_strlcpy(addr.sun_path, drv->own_socket_path, sizeof(addr.sun_path)); if (bind(drv->test_socket, (struct sockaddr *) &addr, sizeof(addr)) < 0) { perror("test-driver-attach: bind(PF_UNIX)"); close(drv->test_socket); unlink(drv->own_socket_path); os_free(drv->own_socket_path); drv->own_socket_path = NULL; return -1; } eloop_register_read_sock(drv->test_socket, wpa_driver_test_receive_unix, drv, NULL); return 0; #else /* DRIVER_TEST_UNIX */ return -1; #endif /* DRIVER_TEST_UNIX */ } static int wpa_driver_test_attach_udp(struct wpa_driver_test_data *drv, char *dst) { char *pos; pos = os_strchr(dst, ':'); if (pos == NULL) return -1; *pos++ = '\0'; wpa_printf(MSG_DEBUG, "%s: addr=%s port=%s", __func__, dst, pos); drv->test_socket = socket(PF_INET, SOCK_DGRAM, 0); if (drv->test_socket < 0) { perror("socket(PF_INET)"); return -1; } os_memset(&drv->hostapd_addr_udp, 0, sizeof(drv->hostapd_addr_udp)); drv->hostapd_addr_udp.sin_family = AF_INET; #if defined(CONFIG_NATIVE_WINDOWS) || defined(CONFIG_ANSI_C_EXTRA) { int a[4]; u8 *pos; sscanf(dst, "%d.%d.%d.%d", &a[0], &a[1], &a[2], &a[3]); pos = (u8 *) &drv->hostapd_addr_udp.sin_addr; *pos++ = a[0]; *pos++ = a[1]; *pos++ = a[2]; *pos++ = a[3]; } #else /* CONFIG_NATIVE_WINDOWS or CONFIG_ANSI_C_EXTRA */ inet_aton(dst, &drv->hostapd_addr_udp.sin_addr); #endif /* CONFIG_NATIVE_WINDOWS or CONFIG_ANSI_C_EXTRA */ drv->hostapd_addr_udp.sin_port = htons(atoi(pos)); drv->hostapd_addr_udp_set = 1; eloop_register_read_sock(drv->test_socket, wpa_driver_test_receive_unix, drv, NULL); return 0; } static int wpa_driver_test_set_param(void *priv, const char *param) { struct test_driver_bss *dbss = priv; struct wpa_driver_test_data *drv = dbss->drv; const char *pos; wpa_printf(MSG_DEBUG, "%s: param='%s'", __func__, param); if (param == NULL) return 0; wpa_driver_test_close_test_socket(drv); #ifdef DRIVER_TEST_UNIX pos = os_strstr(param, "test_socket="); if (pos) { const char *pos2; size_t len; pos += 12; pos2 = os_strchr(pos, ' '); if (pos2) len = pos2 - pos; else len = os_strlen(pos); if (len > sizeof(drv->hostapd_addr.sun_path)) return -1; os_memset(&drv->hostapd_addr, 0, sizeof(drv->hostapd_addr)); drv->hostapd_addr.sun_family = AF_UNIX; os_memcpy(drv->hostapd_addr.sun_path, pos, len); drv->hostapd_addr_set = 1; } #endif /* DRIVER_TEST_UNIX */ pos = os_strstr(param, "test_dir="); if (pos) { char *end; os_free(drv->test_dir); drv->test_dir = os_strdup(pos + 9); if (drv->test_dir == NULL) return -1; end = os_strchr(drv->test_dir, ' '); if (end) *end = '\0'; if (wpa_driver_test_attach(drv, drv->test_dir, 0)) return -1; } else { pos = os_strstr(param, "test_udp="); if (pos) { char *dst, *epos; dst = os_strdup(pos + 9); if (dst == NULL) return -1; epos = os_strchr(dst, ' '); if (epos) *epos = '\0'; if (wpa_driver_test_attach_udp(drv, dst)) return -1; os_free(dst); } else if (wpa_driver_test_attach(drv, NULL, 0)) return -1; } if (os_strstr(param, "use_associnfo=1")) { wpa_printf(MSG_DEBUG, "test_driver: Use AssocInfo events"); drv->use_associnfo = 1; } return 0; } static const u8 * wpa_driver_test_get_mac_addr(void *priv) { struct test_driver_bss *dbss = priv; struct wpa_driver_test_data *drv = dbss->drv; wpa_printf(MSG_DEBUG, "%s", __func__); return drv->own_addr; } static int wpa_driver_test_send_eapol(void *priv, const u8 *dest, u16 proto, const u8 *data, size_t data_len) { struct test_driver_bss *dbss = priv; struct wpa_driver_test_data *drv = dbss->drv; char *msg; size_t msg_len; struct l2_ethhdr eth; struct sockaddr *addr; socklen_t alen; #ifdef DRIVER_TEST_UNIX struct sockaddr_un addr_un; #endif /* DRIVER_TEST_UNIX */ wpa_hexdump(MSG_MSGDUMP, "test_send_eapol TX frame", data, data_len); os_memset(ð, 0, sizeof(eth)); os_memcpy(eth.h_dest, dest, ETH_ALEN); os_memcpy(eth.h_source, drv->own_addr, ETH_ALEN); eth.h_proto = host_to_be16(proto); msg_len = 6 + sizeof(eth) + data_len; msg = os_malloc(msg_len); if (msg == NULL) return -1; os_memcpy(msg, "EAPOL ", 6); os_memcpy(msg + 6, ð, sizeof(eth)); os_memcpy(msg + 6 + sizeof(eth), data, data_len); if (os_memcmp(dest, dbss->bssid, ETH_ALEN) == 0 || drv->test_dir == NULL) { if (drv->hostapd_addr_udp_set) { addr = (struct sockaddr *) &drv->hostapd_addr_udp; alen = sizeof(drv->hostapd_addr_udp); } else { #ifdef DRIVER_TEST_UNIX addr = (struct sockaddr *) &drv->hostapd_addr; alen = sizeof(drv->hostapd_addr); #else /* DRIVER_TEST_UNIX */ os_free(msg); return -1; #endif /* DRIVER_TEST_UNIX */ } } else { #ifdef DRIVER_TEST_UNIX struct stat st; os_memset(&addr_un, 0, sizeof(addr_un)); addr_un.sun_family = AF_UNIX; os_snprintf(addr_un.sun_path, sizeof(addr_un.sun_path), "%s/STA-" MACSTR, drv->test_dir, MAC2STR(dest)); if (stat(addr_un.sun_path, &st) < 0) { os_snprintf(addr_un.sun_path, sizeof(addr_un.sun_path), "%s/AP-" MACSTR, drv->test_dir, MAC2STR(dest)); } addr = (struct sockaddr *) &addr_un; alen = sizeof(addr_un); #else /* DRIVER_TEST_UNIX */ os_free(msg); return -1; #endif /* DRIVER_TEST_UNIX */ } if (sendto(drv->test_socket, msg, msg_len, 0, addr, alen) < 0) { perror("sendmsg(test_socket)"); os_free(msg); return -1; } os_free(msg); return 0; } static int wpa_driver_test_get_capa(void *priv, struct wpa_driver_capa *capa) { os_memset(capa, 0, sizeof(*capa)); capa->key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA | WPA_DRIVER_CAPA_KEY_MGMT_WPA2 | WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK | WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK | WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE | WPA_DRIVER_CAPA_KEY_MGMT_FT | WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK; capa->enc = WPA_DRIVER_CAPA_ENC_WEP40 | WPA_DRIVER_CAPA_ENC_WEP104 | WPA_DRIVER_CAPA_ENC_TKIP | WPA_DRIVER_CAPA_ENC_CCMP; capa->auth = WPA_DRIVER_AUTH_OPEN | WPA_DRIVER_AUTH_SHARED | WPA_DRIVER_AUTH_LEAP; capa->flags |= WPA_DRIVER_FLAGS_AP; capa->flags |= WPA_DRIVER_FLAGS_P2P_CONCURRENT; capa->flags |= WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE; capa->flags |= WPA_DRIVER_FLAGS_P2P_CAPABLE; capa->max_scan_ssids = 2; capa->max_remain_on_chan = 60000; return 0; } static int wpa_driver_test_mlme_setprotection(void *priv, const u8 *addr, int protect_type, int key_type) { wpa_printf(MSG_DEBUG, "%s: protect_type=%d key_type=%d", __func__, protect_type, key_type); if (addr) { wpa_printf(MSG_DEBUG, "%s: addr=" MACSTR, __func__, MAC2STR(addr)); } return 0; } static void * wpa_driver_test_global_init(void) { struct wpa_driver_test_global *global; global = os_zalloc(sizeof(*global)); return global; } static void wpa_driver_test_global_deinit(void *priv) { struct wpa_driver_test_global *global = priv; os_free(global); } static struct wpa_interface_info * wpa_driver_test_get_interfaces(void *global_priv) { /* struct wpa_driver_test_global *global = priv; */ struct wpa_interface_info *iface; iface = os_zalloc(sizeof(*iface)); if (iface == NULL) return iface; iface->ifname = os_strdup("sta0"); iface->desc = os_strdup("test interface 0"); iface->drv_name = "test"; iface->next = os_zalloc(sizeof(*iface)); if (iface->next) { iface->next->ifname = os_strdup("sta1"); iface->next->desc = os_strdup("test interface 1"); iface->next->drv_name = "test"; } return iface; } static struct hostapd_hw_modes * wpa_driver_test_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags) { struct hostapd_hw_modes *modes; size_t i; *num_modes = 3; *flags = 0; modes = os_calloc(*num_modes, sizeof(struct hostapd_hw_modes)); if (modes == NULL) return NULL; modes[0].mode = HOSTAPD_MODE_IEEE80211G; modes[0].num_channels = 11; modes[0].num_rates = 12; modes[0].channels = os_calloc(11, sizeof(struct hostapd_channel_data)); modes[0].rates = os_calloc(modes[0].num_rates, sizeof(int)); if (modes[0].channels == NULL || modes[0].rates == NULL) goto fail; for (i = 0; i < 11; i++) { modes[0].channels[i].chan = i + 1; modes[0].channels[i].freq = 2412 + 5 * i; modes[0].channels[i].flag = 0; } modes[0].rates[0] = 10; modes[0].rates[1] = 20; modes[0].rates[2] = 55; modes[0].rates[3] = 110; modes[0].rates[4] = 60; modes[0].rates[5] = 90; modes[0].rates[6] = 120; modes[0].rates[7] = 180; modes[0].rates[8] = 240; modes[0].rates[9] = 360; modes[0].rates[10] = 480; modes[0].rates[11] = 540; modes[1].mode = HOSTAPD_MODE_IEEE80211B; modes[1].num_channels = 11; modes[1].num_rates = 4; modes[1].channels = os_calloc(11, sizeof(struct hostapd_channel_data)); modes[1].rates = os_calloc(modes[1].num_rates, sizeof(int)); if (modes[1].channels == NULL || modes[1].rates == NULL) goto fail; for (i = 0; i < 11; i++) { modes[1].channels[i].chan = i + 1; modes[1].channels[i].freq = 2412 + 5 * i; modes[1].channels[i].flag = 0; } modes[1].rates[0] = 10; modes[1].rates[1] = 20; modes[1].rates[2] = 55; modes[1].rates[3] = 110; modes[2].mode = HOSTAPD_MODE_IEEE80211A; modes[2].num_channels = 1; modes[2].num_rates = 8; modes[2].channels = os_calloc(1, sizeof(struct hostapd_channel_data)); modes[2].rates = os_calloc(modes[2].num_rates, sizeof(int)); if (modes[2].channels == NULL || modes[2].rates == NULL) goto fail; modes[2].channels[0].chan = 60; modes[2].channels[0].freq = 5300; modes[2].channels[0].flag = 0; modes[2].rates[0] = 60; modes[2].rates[1] = 90; modes[2].rates[2] = 120; modes[2].rates[3] = 180; modes[2].rates[4] = 240; modes[2].rates[5] = 360; modes[2].rates[6] = 480; modes[2].rates[7] = 540; return modes; fail: if (modes) { for (i = 0; i < *num_modes; i++) { os_free(modes[i].channels); os_free(modes[i].rates); } os_free(modes); } return NULL; } static int wpa_driver_test_set_freq(void *priv, struct hostapd_freq_params *freq) { struct test_driver_bss *dbss = priv; struct wpa_driver_test_data *drv = dbss->drv; wpa_printf(MSG_DEBUG, "test: set_freq %u MHz", freq->freq); drv->current_freq = freq->freq; return 0; } static int wpa_driver_test_send_action(void *priv, unsigned int freq, unsigned int wait, const u8 *dst, const u8 *src, const u8 *bssid, const u8 *data, size_t data_len, int no_cck) { struct test_driver_bss *dbss = priv; struct wpa_driver_test_data *drv = dbss->drv; int ret = -1; u8 *buf; struct ieee80211_hdr *hdr; wpa_printf(MSG_DEBUG, "test: Send Action frame"); if ((drv->remain_on_channel_freq && freq != drv->remain_on_channel_freq) || (drv->remain_on_channel_freq == 0 && freq != (unsigned int) drv->current_freq)) { wpa_printf(MSG_DEBUG, "test: Reject Action frame TX on " "unexpected channel: freq=%u MHz (current_freq=%u " "MHz, remain-on-channel freq=%u MHz)", freq, drv->current_freq, drv->remain_on_channel_freq); return -1; } buf = os_zalloc(24 + data_len); if (buf == NULL) return ret; os_memcpy(buf + 24, data, data_len); hdr = (struct ieee80211_hdr *) buf; hdr->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_ACTION); os_memcpy(hdr->addr1, dst, ETH_ALEN); os_memcpy(hdr->addr2, src, ETH_ALEN); os_memcpy(hdr->addr3, bssid, ETH_ALEN); ret = wpa_driver_test_send_mlme(priv, buf, 24 + data_len, 0); os_free(buf); return ret; } static void test_remain_on_channel_timeout(void *eloop_ctx, void *timeout_ctx) { struct wpa_driver_test_data *drv = eloop_ctx; union wpa_event_data data; wpa_printf(MSG_DEBUG, "test: Remain-on-channel timeout"); os_memset(&data, 0, sizeof(data)); data.remain_on_channel.freq = drv->remain_on_channel_freq; data.remain_on_channel.duration = drv->remain_on_channel_duration; drv->remain_on_channel_freq = 0; wpa_supplicant_event(drv->ctx, EVENT_CANCEL_REMAIN_ON_CHANNEL, &data); } static int wpa_driver_test_remain_on_channel(void *priv, unsigned int freq, unsigned int duration) { struct test_driver_bss *dbss = priv; struct wpa_driver_test_data *drv = dbss->drv; union wpa_event_data data; wpa_printf(MSG_DEBUG, "%s(freq=%u, duration=%u)", __func__, freq, duration); if (drv->remain_on_channel_freq && drv->remain_on_channel_freq != freq) { wpa_printf(MSG_DEBUG, "test: Refuse concurrent " "remain_on_channel request"); return -1; } drv->remain_on_channel_freq = freq; drv->remain_on_channel_duration = duration; eloop_cancel_timeout(test_remain_on_channel_timeout, drv, NULL); eloop_register_timeout(duration / 1000, (duration % 1000) * 1000, test_remain_on_channel_timeout, drv, NULL); os_memset(&data, 0, sizeof(data)); data.remain_on_channel.freq = freq; data.remain_on_channel.duration = duration; wpa_supplicant_event(drv->ctx, EVENT_REMAIN_ON_CHANNEL, &data); return 0; } static int wpa_driver_test_cancel_remain_on_channel(void *priv) { struct test_driver_bss *dbss = priv; struct wpa_driver_test_data *drv = dbss->drv; wpa_printf(MSG_DEBUG, "%s", __func__); if (!drv->remain_on_channel_freq) return -1; drv->remain_on_channel_freq = 0; eloop_cancel_timeout(test_remain_on_channel_timeout, drv, NULL); return 0; } static int wpa_driver_test_probe_req_report(void *priv, int report) { struct test_driver_bss *dbss = priv; struct wpa_driver_test_data *drv = dbss->drv; wpa_printf(MSG_DEBUG, "%s(report=%d)", __func__, report); drv->probe_req_report = report; return 0; } const struct wpa_driver_ops wpa_driver_test_ops = { "test", "wpa_supplicant test driver", .hapd_init = test_driver_init, .hapd_deinit = wpa_driver_test_deinit, .hapd_send_eapol = test_driver_send_eapol, .send_mlme = wpa_driver_test_send_mlme, .set_generic_elem = test_driver_set_generic_elem, .sta_deauth = test_driver_sta_deauth, .sta_disassoc = test_driver_sta_disassoc, .get_hw_feature_data = wpa_driver_test_get_hw_feature_data, .if_add = test_driver_if_add, .if_remove = test_driver_if_remove, .hapd_set_ssid = test_driver_set_ssid, .set_privacy = test_driver_set_privacy, .set_sta_vlan = test_driver_set_sta_vlan, .sta_add = test_driver_sta_add, .send_ether = test_driver_send_ether, .set_ap_wps_ie = test_driver_set_ap_wps_ie, .get_bssid = wpa_driver_test_get_bssid, .get_ssid = wpa_driver_test_get_ssid, .set_key = wpa_driver_test_set_key, .deinit = wpa_driver_test_deinit, .set_param = wpa_driver_test_set_param, .deauthenticate = wpa_driver_test_deauthenticate, .associate = wpa_driver_test_associate, .get_capa = wpa_driver_test_get_capa, .get_mac_addr = wpa_driver_test_get_mac_addr, .send_eapol = wpa_driver_test_send_eapol, .mlme_setprotection = wpa_driver_test_mlme_setprotection, .get_scan_results2 = wpa_driver_test_get_scan_results2, .global_init = wpa_driver_test_global_init, .global_deinit = wpa_driver_test_global_deinit, .init2 = wpa_driver_test_init2, .get_interfaces = wpa_driver_test_get_interfaces, .scan2 = wpa_driver_test_scan, .set_freq = wpa_driver_test_set_freq, .send_action = wpa_driver_test_send_action, .remain_on_channel = wpa_driver_test_remain_on_channel, .cancel_remain_on_channel = wpa_driver_test_cancel_remain_on_channel, .probe_req_report = wpa_driver_test_probe_req_report, }; wpa-2.1/src/drivers/driver_wext.c000066400000000000000000001707241227414666700171500ustar00rootroot00000000000000/* * Driver interaction with generic Linux Wireless Extensions * Copyright (c) 2003-2010, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. * * This file implements a driver interface for the Linux Wireless Extensions. * When used with WE-18 or newer, this interface can be used as-is with number * of drivers. In addition to this, some of the common functions in this file * can be used by other driver interface implementations that use generic WE * ioctls, but require private ioctls for some of the functionality. */ #include "includes.h" #include #include #include #include #include #include "linux_wext.h" #include "common.h" #include "eloop.h" #include "common/ieee802_11_defs.h" #include "common/wpa_common.h" #include "priv_netlink.h" #include "netlink.h" #include "linux_ioctl.h" #include "rfkill.h" #include "driver.h" #include "driver_wext.h" static int wpa_driver_wext_flush_pmkid(void *priv); static int wpa_driver_wext_get_range(void *priv); static int wpa_driver_wext_finish_drv_init(struct wpa_driver_wext_data *drv); static void wpa_driver_wext_disconnect(struct wpa_driver_wext_data *drv); static int wpa_driver_wext_set_auth_alg(void *priv, int auth_alg); int wpa_driver_wext_set_auth_param(struct wpa_driver_wext_data *drv, int idx, u32 value) { struct iwreq iwr; int ret = 0; os_memset(&iwr, 0, sizeof(iwr)); os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); iwr.u.param.flags = idx & IW_AUTH_INDEX; iwr.u.param.value = value; if (ioctl(drv->ioctl_sock, SIOCSIWAUTH, &iwr) < 0) { if (errno != EOPNOTSUPP) { wpa_printf(MSG_DEBUG, "WEXT: SIOCSIWAUTH(param %d " "value 0x%x) failed: %s)", idx, value, strerror(errno)); } ret = errno == EOPNOTSUPP ? -2 : -1; } return ret; } /** * wpa_driver_wext_get_bssid - Get BSSID, SIOCGIWAP * @priv: Pointer to private wext data from wpa_driver_wext_init() * @bssid: Buffer for BSSID * Returns: 0 on success, -1 on failure */ int wpa_driver_wext_get_bssid(void *priv, u8 *bssid) { struct wpa_driver_wext_data *drv = priv; struct iwreq iwr; int ret = 0; os_memset(&iwr, 0, sizeof(iwr)); os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); if (ioctl(drv->ioctl_sock, SIOCGIWAP, &iwr) < 0) { perror("ioctl[SIOCGIWAP]"); ret = -1; } os_memcpy(bssid, iwr.u.ap_addr.sa_data, ETH_ALEN); return ret; } /** * wpa_driver_wext_set_bssid - Set BSSID, SIOCSIWAP * @priv: Pointer to private wext data from wpa_driver_wext_init() * @bssid: BSSID * Returns: 0 on success, -1 on failure */ int wpa_driver_wext_set_bssid(void *priv, const u8 *bssid) { struct wpa_driver_wext_data *drv = priv; struct iwreq iwr; int ret = 0; os_memset(&iwr, 0, sizeof(iwr)); os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); iwr.u.ap_addr.sa_family = ARPHRD_ETHER; if (bssid) os_memcpy(iwr.u.ap_addr.sa_data, bssid, ETH_ALEN); else os_memset(iwr.u.ap_addr.sa_data, 0, ETH_ALEN); if (ioctl(drv->ioctl_sock, SIOCSIWAP, &iwr) < 0) { perror("ioctl[SIOCSIWAP]"); ret = -1; } return ret; } /** * wpa_driver_wext_get_ssid - Get SSID, SIOCGIWESSID * @priv: Pointer to private wext data from wpa_driver_wext_init() * @ssid: Buffer for the SSID; must be at least 32 bytes long * Returns: SSID length on success, -1 on failure */ int wpa_driver_wext_get_ssid(void *priv, u8 *ssid) { struct wpa_driver_wext_data *drv = priv; struct iwreq iwr; int ret = 0; os_memset(&iwr, 0, sizeof(iwr)); os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); iwr.u.essid.pointer = (caddr_t) ssid; iwr.u.essid.length = 32; if (ioctl(drv->ioctl_sock, SIOCGIWESSID, &iwr) < 0) { perror("ioctl[SIOCGIWESSID]"); ret = -1; } else { ret = iwr.u.essid.length; if (ret > 32) ret = 32; /* Some drivers include nul termination in the SSID, so let's * remove it here before further processing. WE-21 changes this * to explicitly require the length _not_ to include nul * termination. */ if (ret > 0 && ssid[ret - 1] == '\0' && drv->we_version_compiled < 21) ret--; } return ret; } /** * wpa_driver_wext_set_ssid - Set SSID, SIOCSIWESSID * @priv: Pointer to private wext data from wpa_driver_wext_init() * @ssid: SSID * @ssid_len: Length of SSID (0..32) * Returns: 0 on success, -1 on failure */ int wpa_driver_wext_set_ssid(void *priv, const u8 *ssid, size_t ssid_len) { struct wpa_driver_wext_data *drv = priv; struct iwreq iwr; int ret = 0; char buf[33]; if (ssid_len > 32) return -1; os_memset(&iwr, 0, sizeof(iwr)); os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); /* flags: 1 = ESSID is active, 0 = not (promiscuous) */ iwr.u.essid.flags = (ssid_len != 0); os_memset(buf, 0, sizeof(buf)); os_memcpy(buf, ssid, ssid_len); iwr.u.essid.pointer = (caddr_t) buf; if (drv->we_version_compiled < 21) { /* For historic reasons, set SSID length to include one extra * character, C string nul termination, even though SSID is * really an octet string that should not be presented as a C * string. Some Linux drivers decrement the length by one and * can thus end up missing the last octet of the SSID if the * length is not incremented here. WE-21 changes this to * explicitly require the length _not_ to include nul * termination. */ if (ssid_len) ssid_len++; } iwr.u.essid.length = ssid_len; if (ioctl(drv->ioctl_sock, SIOCSIWESSID, &iwr) < 0) { perror("ioctl[SIOCSIWESSID]"); ret = -1; } return ret; } /** * wpa_driver_wext_set_freq - Set frequency/channel, SIOCSIWFREQ * @priv: Pointer to private wext data from wpa_driver_wext_init() * @freq: Frequency in MHz * Returns: 0 on success, -1 on failure */ int wpa_driver_wext_set_freq(void *priv, int freq) { struct wpa_driver_wext_data *drv = priv; struct iwreq iwr; int ret = 0; os_memset(&iwr, 0, sizeof(iwr)); os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); iwr.u.freq.m = freq * 100000; iwr.u.freq.e = 1; if (ioctl(drv->ioctl_sock, SIOCSIWFREQ, &iwr) < 0) { perror("ioctl[SIOCSIWFREQ]"); ret = -1; } return ret; } static void wpa_driver_wext_event_wireless_custom(void *ctx, char *custom) { union wpa_event_data data; wpa_printf(MSG_MSGDUMP, "WEXT: Custom wireless event: '%s'", custom); os_memset(&data, 0, sizeof(data)); /* Host AP driver */ if (os_strncmp(custom, "MLME-MICHAELMICFAILURE.indication", 33) == 0) { data.michael_mic_failure.unicast = os_strstr(custom, " unicast ") != NULL; /* TODO: parse parameters(?) */ wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &data); } else if (os_strncmp(custom, "ASSOCINFO(ReqIEs=", 17) == 0) { char *spos; int bytes; u8 *req_ies = NULL, *resp_ies = NULL; spos = custom + 17; bytes = strspn(spos, "0123456789abcdefABCDEF"); if (!bytes || (bytes & 1)) return; bytes /= 2; req_ies = os_malloc(bytes); if (req_ies == NULL || hexstr2bin(spos, req_ies, bytes) < 0) goto done; data.assoc_info.req_ies = req_ies; data.assoc_info.req_ies_len = bytes; spos += bytes * 2; data.assoc_info.resp_ies = NULL; data.assoc_info.resp_ies_len = 0; if (os_strncmp(spos, " RespIEs=", 9) == 0) { spos += 9; bytes = strspn(spos, "0123456789abcdefABCDEF"); if (!bytes || (bytes & 1)) goto done; bytes /= 2; resp_ies = os_malloc(bytes); if (resp_ies == NULL || hexstr2bin(spos, resp_ies, bytes) < 0) goto done; data.assoc_info.resp_ies = resp_ies; data.assoc_info.resp_ies_len = bytes; } wpa_supplicant_event(ctx, EVENT_ASSOCINFO, &data); done: os_free(resp_ies); os_free(req_ies); #ifdef CONFIG_PEERKEY } else if (os_strncmp(custom, "STKSTART.request=", 17) == 0) { if (hwaddr_aton(custom + 17, data.stkstart.peer)) { wpa_printf(MSG_DEBUG, "WEXT: unrecognized " "STKSTART.request '%s'", custom + 17); return; } wpa_supplicant_event(ctx, EVENT_STKSTART, &data); #endif /* CONFIG_PEERKEY */ } } static int wpa_driver_wext_event_wireless_michaelmicfailure( void *ctx, const char *ev, size_t len) { const struct iw_michaelmicfailure *mic; union wpa_event_data data; if (len < sizeof(*mic)) return -1; mic = (const struct iw_michaelmicfailure *) ev; wpa_printf(MSG_DEBUG, "Michael MIC failure wireless event: " "flags=0x%x src_addr=" MACSTR, mic->flags, MAC2STR(mic->src_addr.sa_data)); os_memset(&data, 0, sizeof(data)); data.michael_mic_failure.unicast = !(mic->flags & IW_MICFAILURE_GROUP); wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &data); return 0; } static int wpa_driver_wext_event_wireless_pmkidcand( struct wpa_driver_wext_data *drv, const char *ev, size_t len) { const struct iw_pmkid_cand *cand; union wpa_event_data data; const u8 *addr; if (len < sizeof(*cand)) return -1; cand = (const struct iw_pmkid_cand *) ev; addr = (const u8 *) cand->bssid.sa_data; wpa_printf(MSG_DEBUG, "PMKID candidate wireless event: " "flags=0x%x index=%d bssid=" MACSTR, cand->flags, cand->index, MAC2STR(addr)); os_memset(&data, 0, sizeof(data)); os_memcpy(data.pmkid_candidate.bssid, addr, ETH_ALEN); data.pmkid_candidate.index = cand->index; data.pmkid_candidate.preauth = cand->flags & IW_PMKID_CAND_PREAUTH; wpa_supplicant_event(drv->ctx, EVENT_PMKID_CANDIDATE, &data); return 0; } static int wpa_driver_wext_event_wireless_assocreqie( struct wpa_driver_wext_data *drv, const char *ev, int len) { if (len < 0) return -1; wpa_hexdump(MSG_DEBUG, "AssocReq IE wireless event", (const u8 *) ev, len); os_free(drv->assoc_req_ies); drv->assoc_req_ies = os_malloc(len); if (drv->assoc_req_ies == NULL) { drv->assoc_req_ies_len = 0; return -1; } os_memcpy(drv->assoc_req_ies, ev, len); drv->assoc_req_ies_len = len; return 0; } static int wpa_driver_wext_event_wireless_assocrespie( struct wpa_driver_wext_data *drv, const char *ev, int len) { if (len < 0) return -1; wpa_hexdump(MSG_DEBUG, "AssocResp IE wireless event", (const u8 *) ev, len); os_free(drv->assoc_resp_ies); drv->assoc_resp_ies = os_malloc(len); if (drv->assoc_resp_ies == NULL) { drv->assoc_resp_ies_len = 0; return -1; } os_memcpy(drv->assoc_resp_ies, ev, len); drv->assoc_resp_ies_len = len; return 0; } static void wpa_driver_wext_event_assoc_ies(struct wpa_driver_wext_data *drv) { union wpa_event_data data; if (drv->assoc_req_ies == NULL && drv->assoc_resp_ies == NULL) return; os_memset(&data, 0, sizeof(data)); if (drv->assoc_req_ies) { data.assoc_info.req_ies = drv->assoc_req_ies; data.assoc_info.req_ies_len = drv->assoc_req_ies_len; } if (drv->assoc_resp_ies) { data.assoc_info.resp_ies = drv->assoc_resp_ies; data.assoc_info.resp_ies_len = drv->assoc_resp_ies_len; } wpa_supplicant_event(drv->ctx, EVENT_ASSOCINFO, &data); os_free(drv->assoc_req_ies); drv->assoc_req_ies = NULL; os_free(drv->assoc_resp_ies); drv->assoc_resp_ies = NULL; } static void wpa_driver_wext_event_wireless(struct wpa_driver_wext_data *drv, char *data, int len) { struct iw_event iwe_buf, *iwe = &iwe_buf; char *pos, *end, *custom, *buf; pos = data; end = data + len; while (pos + IW_EV_LCP_LEN <= end) { /* Event data may be unaligned, so make a local, aligned copy * before processing. */ os_memcpy(&iwe_buf, pos, IW_EV_LCP_LEN); wpa_printf(MSG_DEBUG, "Wireless event: cmd=0x%x len=%d", iwe->cmd, iwe->len); if (iwe->len <= IW_EV_LCP_LEN) return; custom = pos + IW_EV_POINT_LEN; if (drv->we_version_compiled > 18 && (iwe->cmd == IWEVMICHAELMICFAILURE || iwe->cmd == IWEVCUSTOM || iwe->cmd == IWEVASSOCREQIE || iwe->cmd == IWEVASSOCRESPIE || iwe->cmd == IWEVPMKIDCAND)) { /* WE-19 removed the pointer from struct iw_point */ char *dpos = (char *) &iwe_buf.u.data.length; int dlen = dpos - (char *) &iwe_buf; os_memcpy(dpos, pos + IW_EV_LCP_LEN, sizeof(struct iw_event) - dlen); } else { os_memcpy(&iwe_buf, pos, sizeof(struct iw_event)); custom += IW_EV_POINT_OFF; } switch (iwe->cmd) { case SIOCGIWAP: wpa_printf(MSG_DEBUG, "Wireless event: new AP: " MACSTR, MAC2STR((u8 *) iwe->u.ap_addr.sa_data)); if (is_zero_ether_addr( (const u8 *) iwe->u.ap_addr.sa_data) || os_memcmp(iwe->u.ap_addr.sa_data, "\x44\x44\x44\x44\x44\x44", ETH_ALEN) == 0) { os_free(drv->assoc_req_ies); drv->assoc_req_ies = NULL; os_free(drv->assoc_resp_ies); drv->assoc_resp_ies = NULL; wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL); } else { wpa_driver_wext_event_assoc_ies(drv); wpa_supplicant_event(drv->ctx, EVENT_ASSOC, NULL); } break; case IWEVMICHAELMICFAILURE: if (custom + iwe->u.data.length > end) { wpa_printf(MSG_DEBUG, "WEXT: Invalid " "IWEVMICHAELMICFAILURE length"); return; } wpa_driver_wext_event_wireless_michaelmicfailure( drv->ctx, custom, iwe->u.data.length); break; case IWEVCUSTOM: if (custom + iwe->u.data.length > end) { wpa_printf(MSG_DEBUG, "WEXT: Invalid " "IWEVCUSTOM length"); return; } buf = dup_binstr(custom, iwe->u.data.length); if (buf == NULL) return; wpa_driver_wext_event_wireless_custom(drv->ctx, buf); os_free(buf); break; case SIOCGIWSCAN: drv->scan_complete_events = 1; eloop_cancel_timeout(wpa_driver_wext_scan_timeout, drv, drv->ctx); wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, NULL); break; case IWEVASSOCREQIE: if (custom + iwe->u.data.length > end) { wpa_printf(MSG_DEBUG, "WEXT: Invalid " "IWEVASSOCREQIE length"); return; } wpa_driver_wext_event_wireless_assocreqie( drv, custom, iwe->u.data.length); break; case IWEVASSOCRESPIE: if (custom + iwe->u.data.length > end) { wpa_printf(MSG_DEBUG, "WEXT: Invalid " "IWEVASSOCRESPIE length"); return; } wpa_driver_wext_event_wireless_assocrespie( drv, custom, iwe->u.data.length); break; case IWEVPMKIDCAND: if (custom + iwe->u.data.length > end) { wpa_printf(MSG_DEBUG, "WEXT: Invalid " "IWEVPMKIDCAND length"); return; } wpa_driver_wext_event_wireless_pmkidcand( drv, custom, iwe->u.data.length); break; } pos += iwe->len; } } static void wpa_driver_wext_event_link(struct wpa_driver_wext_data *drv, char *buf, size_t len, int del) { union wpa_event_data event; os_memset(&event, 0, sizeof(event)); if (len > sizeof(event.interface_status.ifname)) len = sizeof(event.interface_status.ifname) - 1; os_memcpy(event.interface_status.ifname, buf, len); event.interface_status.ievent = del ? EVENT_INTERFACE_REMOVED : EVENT_INTERFACE_ADDED; wpa_printf(MSG_DEBUG, "RTM_%sLINK, IFLA_IFNAME: Interface '%s' %s", del ? "DEL" : "NEW", event.interface_status.ifname, del ? "removed" : "added"); if (os_strcmp(drv->ifname, event.interface_status.ifname) == 0) { if (del) { if (drv->if_removed) { wpa_printf(MSG_DEBUG, "WEXT: if_removed " "already set - ignore event"); return; } drv->if_removed = 1; } else { if (if_nametoindex(drv->ifname) == 0) { wpa_printf(MSG_DEBUG, "WEXT: Interface %s " "does not exist - ignore " "RTM_NEWLINK", drv->ifname); return; } if (!drv->if_removed) { wpa_printf(MSG_DEBUG, "WEXT: if_removed " "already cleared - ignore event"); return; } drv->if_removed = 0; } } wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event); } static int wpa_driver_wext_own_ifname(struct wpa_driver_wext_data *drv, u8 *buf, size_t len) { int attrlen, rta_len; struct rtattr *attr; attrlen = len; attr = (struct rtattr *) buf; rta_len = RTA_ALIGN(sizeof(struct rtattr)); while (RTA_OK(attr, attrlen)) { if (attr->rta_type == IFLA_IFNAME) { if (os_strcmp(((char *) attr) + rta_len, drv->ifname) == 0) return 1; else break; } attr = RTA_NEXT(attr, attrlen); } return 0; } static int wpa_driver_wext_own_ifindex(struct wpa_driver_wext_data *drv, int ifindex, u8 *buf, size_t len) { if (drv->ifindex == ifindex || drv->ifindex2 == ifindex) return 1; if (drv->if_removed && wpa_driver_wext_own_ifname(drv, buf, len)) { drv->ifindex = if_nametoindex(drv->ifname); wpa_printf(MSG_DEBUG, "WEXT: Update ifindex for a removed " "interface"); wpa_driver_wext_finish_drv_init(drv); return 1; } return 0; } static void wpa_driver_wext_event_rtm_newlink(void *ctx, struct ifinfomsg *ifi, u8 *buf, size_t len) { struct wpa_driver_wext_data *drv = ctx; int attrlen, rta_len; struct rtattr *attr; char namebuf[IFNAMSIZ]; if (!wpa_driver_wext_own_ifindex(drv, ifi->ifi_index, buf, len)) { wpa_printf(MSG_DEBUG, "Ignore event for foreign ifindex %d", ifi->ifi_index); return; } wpa_printf(MSG_DEBUG, "RTM_NEWLINK: operstate=%d ifi_flags=0x%x " "(%s%s%s%s)", drv->operstate, ifi->ifi_flags, (ifi->ifi_flags & IFF_UP) ? "[UP]" : "", (ifi->ifi_flags & IFF_RUNNING) ? "[RUNNING]" : "", (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "", (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : ""); if (!drv->if_disabled && !(ifi->ifi_flags & IFF_UP)) { wpa_printf(MSG_DEBUG, "WEXT: Interface down"); drv->if_disabled = 1; wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_DISABLED, NULL); } if (drv->if_disabled && (ifi->ifi_flags & IFF_UP)) { if (if_indextoname(ifi->ifi_index, namebuf) && linux_iface_up(drv->ioctl_sock, drv->ifname) == 0) { wpa_printf(MSG_DEBUG, "WEXT: Ignore interface up " "event since interface %s is down", namebuf); } else if (if_nametoindex(drv->ifname) == 0) { wpa_printf(MSG_DEBUG, "WEXT: Ignore interface up " "event since interface %s does not exist", drv->ifname); } else if (drv->if_removed) { wpa_printf(MSG_DEBUG, "WEXT: Ignore interface up " "event since interface %s is marked " "removed", drv->ifname); } else { wpa_printf(MSG_DEBUG, "WEXT: Interface up"); drv->if_disabled = 0; wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_ENABLED, NULL); } } /* * Some drivers send the association event before the operup event--in * this case, lifting operstate in wpa_driver_wext_set_operstate() * fails. This will hit us when wpa_supplicant does not need to do * IEEE 802.1X authentication */ if (drv->operstate == 1 && (ifi->ifi_flags & (IFF_LOWER_UP | IFF_DORMANT)) == IFF_LOWER_UP && !(ifi->ifi_flags & IFF_RUNNING)) netlink_send_oper_ifla(drv->netlink, drv->ifindex, -1, IF_OPER_UP); attrlen = len; attr = (struct rtattr *) buf; rta_len = RTA_ALIGN(sizeof(struct rtattr)); while (RTA_OK(attr, attrlen)) { if (attr->rta_type == IFLA_WIRELESS) { wpa_driver_wext_event_wireless( drv, ((char *) attr) + rta_len, attr->rta_len - rta_len); } else if (attr->rta_type == IFLA_IFNAME) { wpa_driver_wext_event_link(drv, ((char *) attr) + rta_len, attr->rta_len - rta_len, 0); } attr = RTA_NEXT(attr, attrlen); } } static void wpa_driver_wext_event_rtm_dellink(void *ctx, struct ifinfomsg *ifi, u8 *buf, size_t len) { struct wpa_driver_wext_data *drv = ctx; int attrlen, rta_len; struct rtattr *attr; attrlen = len; attr = (struct rtattr *) buf; rta_len = RTA_ALIGN(sizeof(struct rtattr)); while (RTA_OK(attr, attrlen)) { if (attr->rta_type == IFLA_IFNAME) { wpa_driver_wext_event_link(drv, ((char *) attr) + rta_len, attr->rta_len - rta_len, 1); } attr = RTA_NEXT(attr, attrlen); } } static void wpa_driver_wext_rfkill_blocked(void *ctx) { wpa_printf(MSG_DEBUG, "WEXT: RFKILL blocked"); /* * This may be for any interface; use ifdown event to disable * interface. */ } static void wpa_driver_wext_rfkill_unblocked(void *ctx) { struct wpa_driver_wext_data *drv = ctx; wpa_printf(MSG_DEBUG, "WEXT: RFKILL unblocked"); if (linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 1)) { wpa_printf(MSG_DEBUG, "WEXT: Could not set interface UP " "after rfkill unblock"); return; } /* rtnetlink ifup handler will report interface as enabled */ } static void wext_get_phy_name(struct wpa_driver_wext_data *drv) { /* Find phy (radio) to which this interface belongs */ char buf[90], *pos; int f, rv; drv->phyname[0] = '\0'; snprintf(buf, sizeof(buf) - 1, "/sys/class/net/%s/phy80211/name", drv->ifname); f = open(buf, O_RDONLY); if (f < 0) { wpa_printf(MSG_DEBUG, "Could not open file %s: %s", buf, strerror(errno)); return; } rv = read(f, drv->phyname, sizeof(drv->phyname) - 1); close(f); if (rv < 0) { wpa_printf(MSG_DEBUG, "Could not read file %s: %s", buf, strerror(errno)); return; } drv->phyname[rv] = '\0'; pos = os_strchr(drv->phyname, '\n'); if (pos) *pos = '\0'; wpa_printf(MSG_DEBUG, "wext: interface %s phy: %s", drv->ifname, drv->phyname); } /** * wpa_driver_wext_init - Initialize WE driver interface * @ctx: context to be used when calling wpa_supplicant functions, * e.g., wpa_supplicant_event() * @ifname: interface name, e.g., wlan0 * Returns: Pointer to private data, %NULL on failure */ void * wpa_driver_wext_init(void *ctx, const char *ifname) { struct wpa_driver_wext_data *drv; struct netlink_config *cfg; struct rfkill_config *rcfg; char path[128]; struct stat buf; drv = os_zalloc(sizeof(*drv)); if (drv == NULL) return NULL; drv->ctx = ctx; os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); os_snprintf(path, sizeof(path), "/sys/class/net/%s/phy80211", ifname); if (stat(path, &buf) == 0) { wpa_printf(MSG_DEBUG, "WEXT: cfg80211-based driver detected"); drv->cfg80211 = 1; wext_get_phy_name(drv); } drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0); if (drv->ioctl_sock < 0) { perror("socket(PF_INET,SOCK_DGRAM)"); goto err1; } cfg = os_zalloc(sizeof(*cfg)); if (cfg == NULL) goto err1; cfg->ctx = drv; cfg->newlink_cb = wpa_driver_wext_event_rtm_newlink; cfg->dellink_cb = wpa_driver_wext_event_rtm_dellink; drv->netlink = netlink_init(cfg); if (drv->netlink == NULL) { os_free(cfg); goto err2; } rcfg = os_zalloc(sizeof(*rcfg)); if (rcfg == NULL) goto err3; rcfg->ctx = drv; os_strlcpy(rcfg->ifname, ifname, sizeof(rcfg->ifname)); rcfg->blocked_cb = wpa_driver_wext_rfkill_blocked; rcfg->unblocked_cb = wpa_driver_wext_rfkill_unblocked; drv->rfkill = rfkill_init(rcfg); if (drv->rfkill == NULL) { wpa_printf(MSG_DEBUG, "WEXT: RFKILL status not available"); os_free(rcfg); } drv->mlme_sock = -1; if (wpa_driver_wext_finish_drv_init(drv) < 0) goto err3; wpa_driver_wext_set_auth_param(drv, IW_AUTH_WPA_ENABLED, 1); return drv; err3: rfkill_deinit(drv->rfkill); netlink_deinit(drv->netlink); err2: close(drv->ioctl_sock); err1: os_free(drv); return NULL; } static void wpa_driver_wext_send_rfkill(void *eloop_ctx, void *timeout_ctx) { wpa_supplicant_event(timeout_ctx, EVENT_INTERFACE_DISABLED, NULL); } static int wpa_driver_wext_finish_drv_init(struct wpa_driver_wext_data *drv) { int send_rfkill_event = 0; if (linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 1) < 0) { if (rfkill_is_blocked(drv->rfkill)) { wpa_printf(MSG_DEBUG, "WEXT: Could not yet enable " "interface '%s' due to rfkill", drv->ifname); drv->if_disabled = 1; send_rfkill_event = 1; } else { wpa_printf(MSG_ERROR, "WEXT: Could not set " "interface '%s' UP", drv->ifname); return -1; } } /* * Make sure that the driver does not have any obsolete PMKID entries. */ wpa_driver_wext_flush_pmkid(drv); if (wpa_driver_wext_set_mode(drv, 0) < 0) { wpa_printf(MSG_DEBUG, "Could not configure driver to use " "managed mode"); /* Try to use it anyway */ } wpa_driver_wext_get_range(drv); /* * Unlock the driver's BSSID and force to a random SSID to clear any * previous association the driver might have when the supplicant * starts up. */ wpa_driver_wext_disconnect(drv); drv->ifindex = if_nametoindex(drv->ifname); if (os_strncmp(drv->ifname, "wlan", 4) == 0) { /* * Host AP driver may use both wlan# and wifi# interface in * wireless events. Since some of the versions included WE-18 * support, let's add the alternative ifindex also from * driver_wext.c for the time being. This may be removed at * some point once it is believed that old versions of the * driver are not in use anymore. */ char ifname2[IFNAMSIZ + 1]; os_strlcpy(ifname2, drv->ifname, sizeof(ifname2)); os_memcpy(ifname2, "wifi", 4); wpa_driver_wext_alternative_ifindex(drv, ifname2); } netlink_send_oper_ifla(drv->netlink, drv->ifindex, 1, IF_OPER_DORMANT); if (send_rfkill_event) { eloop_register_timeout(0, 0, wpa_driver_wext_send_rfkill, drv, drv->ctx); } return 0; } /** * wpa_driver_wext_deinit - Deinitialize WE driver interface * @priv: Pointer to private wext data from wpa_driver_wext_init() * * Shut down driver interface and processing of driver events. Free * private data buffer if one was allocated in wpa_driver_wext_init(). */ void wpa_driver_wext_deinit(void *priv) { struct wpa_driver_wext_data *drv = priv; wpa_driver_wext_set_auth_param(drv, IW_AUTH_WPA_ENABLED, 0); eloop_cancel_timeout(wpa_driver_wext_scan_timeout, drv, drv->ctx); /* * Clear possibly configured driver parameters in order to make it * easier to use the driver after wpa_supplicant has been terminated. */ wpa_driver_wext_disconnect(drv); netlink_send_oper_ifla(drv->netlink, drv->ifindex, 0, IF_OPER_UP); netlink_deinit(drv->netlink); rfkill_deinit(drv->rfkill); if (drv->mlme_sock >= 0) eloop_unregister_read_sock(drv->mlme_sock); (void) linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 0); close(drv->ioctl_sock); if (drv->mlme_sock >= 0) close(drv->mlme_sock); os_free(drv->assoc_req_ies); os_free(drv->assoc_resp_ies); os_free(drv); } /** * wpa_driver_wext_scan_timeout - Scan timeout to report scan completion * @eloop_ctx: Unused * @timeout_ctx: ctx argument given to wpa_driver_wext_init() * * This function can be used as registered timeout when starting a scan to * generate a scan completed event if the driver does not report this. */ void wpa_driver_wext_scan_timeout(void *eloop_ctx, void *timeout_ctx) { wpa_printf(MSG_DEBUG, "Scan timeout - try to get results"); wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL); } /** * wpa_driver_wext_scan - Request the driver to initiate scan * @priv: Pointer to private wext data from wpa_driver_wext_init() * @param: Scan parameters (specific SSID to scan for (ProbeReq), etc.) * Returns: 0 on success, -1 on failure */ int wpa_driver_wext_scan(void *priv, struct wpa_driver_scan_params *params) { struct wpa_driver_wext_data *drv = priv; struct iwreq iwr; int ret = 0, timeout; struct iw_scan_req req; const u8 *ssid = params->ssids[0].ssid; size_t ssid_len = params->ssids[0].ssid_len; if (ssid_len > IW_ESSID_MAX_SIZE) { wpa_printf(MSG_DEBUG, "%s: too long SSID (%lu)", __FUNCTION__, (unsigned long) ssid_len); return -1; } os_memset(&iwr, 0, sizeof(iwr)); os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); if (ssid && ssid_len) { os_memset(&req, 0, sizeof(req)); req.essid_len = ssid_len; req.bssid.sa_family = ARPHRD_ETHER; os_memset(req.bssid.sa_data, 0xff, ETH_ALEN); os_memcpy(req.essid, ssid, ssid_len); iwr.u.data.pointer = (caddr_t) &req; iwr.u.data.length = sizeof(req); iwr.u.data.flags = IW_SCAN_THIS_ESSID; } if (ioctl(drv->ioctl_sock, SIOCSIWSCAN, &iwr) < 0) { perror("ioctl[SIOCSIWSCAN]"); ret = -1; } /* Not all drivers generate "scan completed" wireless event, so try to * read results after a timeout. */ timeout = 10; if (drv->scan_complete_events) { /* * The driver seems to deliver SIOCGIWSCAN events to notify * when scan is complete, so use longer timeout to avoid race * conditions with scanning and following association request. */ timeout = 30; } wpa_printf(MSG_DEBUG, "Scan requested (ret=%d) - scan timeout %d " "seconds", ret, timeout); eloop_cancel_timeout(wpa_driver_wext_scan_timeout, drv, drv->ctx); eloop_register_timeout(timeout, 0, wpa_driver_wext_scan_timeout, drv, drv->ctx); return ret; } static u8 * wpa_driver_wext_giwscan(struct wpa_driver_wext_data *drv, size_t *len) { struct iwreq iwr; u8 *res_buf; size_t res_buf_len; res_buf_len = IW_SCAN_MAX_DATA; for (;;) { res_buf = os_malloc(res_buf_len); if (res_buf == NULL) return NULL; os_memset(&iwr, 0, sizeof(iwr)); os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); iwr.u.data.pointer = res_buf; iwr.u.data.length = res_buf_len; if (ioctl(drv->ioctl_sock, SIOCGIWSCAN, &iwr) == 0) break; if (errno == E2BIG && res_buf_len < 65535) { os_free(res_buf); res_buf = NULL; res_buf_len *= 2; if (res_buf_len > 65535) res_buf_len = 65535; /* 16-bit length field */ wpa_printf(MSG_DEBUG, "Scan results did not fit - " "trying larger buffer (%lu bytes)", (unsigned long) res_buf_len); } else { perror("ioctl[SIOCGIWSCAN]"); os_free(res_buf); return NULL; } } if (iwr.u.data.length > res_buf_len) { os_free(res_buf); return NULL; } *len = iwr.u.data.length; return res_buf; } /* * Data structure for collecting WEXT scan results. This is needed to allow * the various methods of reporting IEs to be combined into a single IE buffer. */ struct wext_scan_data { struct wpa_scan_res res; u8 *ie; size_t ie_len; u8 ssid[32]; size_t ssid_len; int maxrate; }; static void wext_get_scan_mode(struct iw_event *iwe, struct wext_scan_data *res) { if (iwe->u.mode == IW_MODE_ADHOC) res->res.caps |= IEEE80211_CAP_IBSS; else if (iwe->u.mode == IW_MODE_MASTER || iwe->u.mode == IW_MODE_INFRA) res->res.caps |= IEEE80211_CAP_ESS; } static void wext_get_scan_ssid(struct iw_event *iwe, struct wext_scan_data *res, char *custom, char *end) { int ssid_len = iwe->u.essid.length; if (custom + ssid_len > end) return; if (iwe->u.essid.flags && ssid_len > 0 && ssid_len <= IW_ESSID_MAX_SIZE) { os_memcpy(res->ssid, custom, ssid_len); res->ssid_len = ssid_len; } } static void wext_get_scan_freq(struct iw_event *iwe, struct wext_scan_data *res) { int divi = 1000000, i; if (iwe->u.freq.e == 0) { /* * Some drivers do not report frequency, but a channel. * Try to map this to frequency by assuming they are using * IEEE 802.11b/g. But don't overwrite a previously parsed * frequency if the driver sends both frequency and channel, * since the driver may be sending an A-band channel that we * don't handle here. */ if (res->res.freq) return; if (iwe->u.freq.m >= 1 && iwe->u.freq.m <= 13) { res->res.freq = 2407 + 5 * iwe->u.freq.m; return; } else if (iwe->u.freq.m == 14) { res->res.freq = 2484; return; } } if (iwe->u.freq.e > 6) { wpa_printf(MSG_DEBUG, "Invalid freq in scan results (BSSID=" MACSTR " m=%d e=%d)", MAC2STR(res->res.bssid), iwe->u.freq.m, iwe->u.freq.e); return; } for (i = 0; i < iwe->u.freq.e; i++) divi /= 10; res->res.freq = iwe->u.freq.m / divi; } static void wext_get_scan_qual(struct wpa_driver_wext_data *drv, struct iw_event *iwe, struct wext_scan_data *res) { res->res.qual = iwe->u.qual.qual; res->res.noise = iwe->u.qual.noise; res->res.level = iwe->u.qual.level; if (iwe->u.qual.updated & IW_QUAL_QUAL_INVALID) res->res.flags |= WPA_SCAN_QUAL_INVALID; if (iwe->u.qual.updated & IW_QUAL_LEVEL_INVALID) res->res.flags |= WPA_SCAN_LEVEL_INVALID; if (iwe->u.qual.updated & IW_QUAL_NOISE_INVALID) res->res.flags |= WPA_SCAN_NOISE_INVALID; if (iwe->u.qual.updated & IW_QUAL_DBM) res->res.flags |= WPA_SCAN_LEVEL_DBM; if ((iwe->u.qual.updated & IW_QUAL_DBM) || ((iwe->u.qual.level != 0) && (iwe->u.qual.level > drv->max_level))) { if (iwe->u.qual.level >= 64) res->res.level -= 0x100; if (iwe->u.qual.noise >= 64) res->res.noise -= 0x100; } } static void wext_get_scan_encode(struct iw_event *iwe, struct wext_scan_data *res) { if (!(iwe->u.data.flags & IW_ENCODE_DISABLED)) res->res.caps |= IEEE80211_CAP_PRIVACY; } static void wext_get_scan_rate(struct iw_event *iwe, struct wext_scan_data *res, char *pos, char *end) { int maxrate; char *custom = pos + IW_EV_LCP_LEN; struct iw_param p; size_t clen; clen = iwe->len; if (custom + clen > end) return; maxrate = 0; while (((ssize_t) clen) >= (ssize_t) sizeof(struct iw_param)) { /* Note: may be misaligned, make a local, aligned copy */ os_memcpy(&p, custom, sizeof(struct iw_param)); if (p.value > maxrate) maxrate = p.value; clen -= sizeof(struct iw_param); custom += sizeof(struct iw_param); } /* Convert the maxrate from WE-style (b/s units) to * 802.11 rates (500000 b/s units). */ res->maxrate = maxrate / 500000; } static void wext_get_scan_iwevgenie(struct iw_event *iwe, struct wext_scan_data *res, char *custom, char *end) { char *genie, *gpos, *gend; u8 *tmp; if (iwe->u.data.length == 0) return; gpos = genie = custom; gend = genie + iwe->u.data.length; if (gend > end) { wpa_printf(MSG_INFO, "IWEVGENIE overflow"); return; } tmp = os_realloc(res->ie, res->ie_len + gend - gpos); if (tmp == NULL) return; os_memcpy(tmp + res->ie_len, gpos, gend - gpos); res->ie = tmp; res->ie_len += gend - gpos; } static void wext_get_scan_custom(struct iw_event *iwe, struct wext_scan_data *res, char *custom, char *end) { size_t clen; u8 *tmp; clen = iwe->u.data.length; if (custom + clen > end) return; if (clen > 7 && os_strncmp(custom, "wpa_ie=", 7) == 0) { char *spos; int bytes; spos = custom + 7; bytes = custom + clen - spos; if (bytes & 1 || bytes == 0) return; bytes /= 2; tmp = os_realloc(res->ie, res->ie_len + bytes); if (tmp == NULL) return; res->ie = tmp; if (hexstr2bin(spos, tmp + res->ie_len, bytes) < 0) return; res->ie_len += bytes; } else if (clen > 7 && os_strncmp(custom, "rsn_ie=", 7) == 0) { char *spos; int bytes; spos = custom + 7; bytes = custom + clen - spos; if (bytes & 1 || bytes == 0) return; bytes /= 2; tmp = os_realloc(res->ie, res->ie_len + bytes); if (tmp == NULL) return; res->ie = tmp; if (hexstr2bin(spos, tmp + res->ie_len, bytes) < 0) return; res->ie_len += bytes; } else if (clen > 4 && os_strncmp(custom, "tsf=", 4) == 0) { char *spos; int bytes; u8 bin[8]; spos = custom + 4; bytes = custom + clen - spos; if (bytes != 16) { wpa_printf(MSG_INFO, "Invalid TSF length (%d)", bytes); return; } bytes /= 2; if (hexstr2bin(spos, bin, bytes) < 0) { wpa_printf(MSG_DEBUG, "WEXT: Invalid TSF value"); return; } res->res.tsf += WPA_GET_BE64(bin); } } static int wext_19_iw_point(struct wpa_driver_wext_data *drv, u16 cmd) { return drv->we_version_compiled > 18 && (cmd == SIOCGIWESSID || cmd == SIOCGIWENCODE || cmd == IWEVGENIE || cmd == IWEVCUSTOM); } static void wpa_driver_wext_add_scan_entry(struct wpa_scan_results *res, struct wext_scan_data *data) { struct wpa_scan_res **tmp; struct wpa_scan_res *r; size_t extra_len; u8 *pos, *end, *ssid_ie = NULL, *rate_ie = NULL; /* Figure out whether we need to fake any IEs */ pos = data->ie; end = pos + data->ie_len; while (pos && pos + 1 < end) { if (pos + 2 + pos[1] > end) break; if (pos[0] == WLAN_EID_SSID) ssid_ie = pos; else if (pos[0] == WLAN_EID_SUPP_RATES) rate_ie = pos; else if (pos[0] == WLAN_EID_EXT_SUPP_RATES) rate_ie = pos; pos += 2 + pos[1]; } extra_len = 0; if (ssid_ie == NULL) extra_len += 2 + data->ssid_len; if (rate_ie == NULL && data->maxrate) extra_len += 3; r = os_zalloc(sizeof(*r) + extra_len + data->ie_len); if (r == NULL) return; os_memcpy(r, &data->res, sizeof(*r)); r->ie_len = extra_len + data->ie_len; pos = (u8 *) (r + 1); if (ssid_ie == NULL) { /* * Generate a fake SSID IE since the driver did not report * a full IE list. */ *pos++ = WLAN_EID_SSID; *pos++ = data->ssid_len; os_memcpy(pos, data->ssid, data->ssid_len); pos += data->ssid_len; } if (rate_ie == NULL && data->maxrate) { /* * Generate a fake Supported Rates IE since the driver did not * report a full IE list. */ *pos++ = WLAN_EID_SUPP_RATES; *pos++ = 1; *pos++ = data->maxrate; } if (data->ie) os_memcpy(pos, data->ie, data->ie_len); tmp = os_realloc_array(res->res, res->num + 1, sizeof(struct wpa_scan_res *)); if (tmp == NULL) { os_free(r); return; } tmp[res->num++] = r; res->res = tmp; } /** * wpa_driver_wext_get_scan_results - Fetch the latest scan results * @priv: Pointer to private wext data from wpa_driver_wext_init() * Returns: Scan results on success, -1 on failure */ struct wpa_scan_results * wpa_driver_wext_get_scan_results(void *priv) { struct wpa_driver_wext_data *drv = priv; size_t len; int first; u8 *res_buf; struct iw_event iwe_buf, *iwe = &iwe_buf; char *pos, *end, *custom; struct wpa_scan_results *res; struct wext_scan_data data; res_buf = wpa_driver_wext_giwscan(drv, &len); if (res_buf == NULL) return NULL; first = 1; res = os_zalloc(sizeof(*res)); if (res == NULL) { os_free(res_buf); return NULL; } pos = (char *) res_buf; end = (char *) res_buf + len; os_memset(&data, 0, sizeof(data)); while (pos + IW_EV_LCP_LEN <= end) { /* Event data may be unaligned, so make a local, aligned copy * before processing. */ os_memcpy(&iwe_buf, pos, IW_EV_LCP_LEN); if (iwe->len <= IW_EV_LCP_LEN) break; custom = pos + IW_EV_POINT_LEN; if (wext_19_iw_point(drv, iwe->cmd)) { /* WE-19 removed the pointer from struct iw_point */ char *dpos = (char *) &iwe_buf.u.data.length; int dlen = dpos - (char *) &iwe_buf; os_memcpy(dpos, pos + IW_EV_LCP_LEN, sizeof(struct iw_event) - dlen); } else { os_memcpy(&iwe_buf, pos, sizeof(struct iw_event)); custom += IW_EV_POINT_OFF; } switch (iwe->cmd) { case SIOCGIWAP: if (!first) wpa_driver_wext_add_scan_entry(res, &data); first = 0; os_free(data.ie); os_memset(&data, 0, sizeof(data)); os_memcpy(data.res.bssid, iwe->u.ap_addr.sa_data, ETH_ALEN); break; case SIOCGIWMODE: wext_get_scan_mode(iwe, &data); break; case SIOCGIWESSID: wext_get_scan_ssid(iwe, &data, custom, end); break; case SIOCGIWFREQ: wext_get_scan_freq(iwe, &data); break; case IWEVQUAL: wext_get_scan_qual(drv, iwe, &data); break; case SIOCGIWENCODE: wext_get_scan_encode(iwe, &data); break; case SIOCGIWRATE: wext_get_scan_rate(iwe, &data, pos, end); break; case IWEVGENIE: wext_get_scan_iwevgenie(iwe, &data, custom, end); break; case IWEVCUSTOM: wext_get_scan_custom(iwe, &data, custom, end); break; } pos += iwe->len; } os_free(res_buf); res_buf = NULL; if (!first) wpa_driver_wext_add_scan_entry(res, &data); os_free(data.ie); wpa_printf(MSG_DEBUG, "Received %lu bytes of scan results (%lu BSSes)", (unsigned long) len, (unsigned long) res->num); return res; } static int wpa_driver_wext_get_range(void *priv) { struct wpa_driver_wext_data *drv = priv; struct iw_range *range; struct iwreq iwr; int minlen; size_t buflen; /* * Use larger buffer than struct iw_range in order to allow the * structure to grow in the future. */ buflen = sizeof(struct iw_range) + 500; range = os_zalloc(buflen); if (range == NULL) return -1; os_memset(&iwr, 0, sizeof(iwr)); os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); iwr.u.data.pointer = (caddr_t) range; iwr.u.data.length = buflen; minlen = ((char *) &range->enc_capa) - (char *) range + sizeof(range->enc_capa); if (ioctl(drv->ioctl_sock, SIOCGIWRANGE, &iwr) < 0) { perror("ioctl[SIOCGIWRANGE]"); os_free(range); return -1; } else if (iwr.u.data.length >= minlen && range->we_version_compiled >= 18) { wpa_printf(MSG_DEBUG, "SIOCGIWRANGE: WE(compiled)=%d " "WE(source)=%d enc_capa=0x%x", range->we_version_compiled, range->we_version_source, range->enc_capa); drv->has_capability = 1; drv->we_version_compiled = range->we_version_compiled; if (range->enc_capa & IW_ENC_CAPA_WPA) { drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA | WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK; } if (range->enc_capa & IW_ENC_CAPA_WPA2) { drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA2 | WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK; } drv->capa.enc |= WPA_DRIVER_CAPA_ENC_WEP40 | WPA_DRIVER_CAPA_ENC_WEP104; drv->capa.enc |= WPA_DRIVER_CAPA_ENC_WEP128; if (range->enc_capa & IW_ENC_CAPA_CIPHER_TKIP) drv->capa.enc |= WPA_DRIVER_CAPA_ENC_TKIP; if (range->enc_capa & IW_ENC_CAPA_CIPHER_CCMP) drv->capa.enc |= WPA_DRIVER_CAPA_ENC_CCMP; if (range->enc_capa & IW_ENC_CAPA_4WAY_HANDSHAKE) drv->capa.flags |= WPA_DRIVER_FLAGS_4WAY_HANDSHAKE; drv->capa.auth = WPA_DRIVER_AUTH_OPEN | WPA_DRIVER_AUTH_SHARED | WPA_DRIVER_AUTH_LEAP; drv->capa.max_scan_ssids = 1; wpa_printf(MSG_DEBUG, " capabilities: key_mgmt 0x%x enc 0x%x " "flags 0x%x", drv->capa.key_mgmt, drv->capa.enc, drv->capa.flags); } else { wpa_printf(MSG_DEBUG, "SIOCGIWRANGE: too old (short) data - " "assuming WPA is not supported"); } drv->max_level = range->max_qual.level; os_free(range); return 0; } static int wpa_driver_wext_set_psk(struct wpa_driver_wext_data *drv, const u8 *psk) { struct iw_encode_ext *ext; struct iwreq iwr; int ret; wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); if (!(drv->capa.flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE)) return 0; if (!psk) return 0; os_memset(&iwr, 0, sizeof(iwr)); os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); ext = os_zalloc(sizeof(*ext) + PMK_LEN); if (ext == NULL) return -1; iwr.u.encoding.pointer = (caddr_t) ext; iwr.u.encoding.length = sizeof(*ext) + PMK_LEN; ext->key_len = PMK_LEN; os_memcpy(&ext->key, psk, ext->key_len); ext->alg = IW_ENCODE_ALG_PMK; ret = ioctl(drv->ioctl_sock, SIOCSIWENCODEEXT, &iwr); if (ret < 0) perror("ioctl[SIOCSIWENCODEEXT] PMK"); os_free(ext); return ret; } static int wpa_driver_wext_set_key_ext(void *priv, enum wpa_alg alg, const u8 *addr, int key_idx, int set_tx, const u8 *seq, size_t seq_len, const u8 *key, size_t key_len) { struct wpa_driver_wext_data *drv = priv; struct iwreq iwr; int ret = 0; struct iw_encode_ext *ext; if (seq_len > IW_ENCODE_SEQ_MAX_SIZE) { wpa_printf(MSG_DEBUG, "%s: Invalid seq_len %lu", __FUNCTION__, (unsigned long) seq_len); return -1; } ext = os_zalloc(sizeof(*ext) + key_len); if (ext == NULL) return -1; os_memset(&iwr, 0, sizeof(iwr)); os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); iwr.u.encoding.flags = key_idx + 1; iwr.u.encoding.flags |= IW_ENCODE_TEMP; if (alg == WPA_ALG_NONE) iwr.u.encoding.flags |= IW_ENCODE_DISABLED; iwr.u.encoding.pointer = (caddr_t) ext; iwr.u.encoding.length = sizeof(*ext) + key_len; if (addr == NULL || is_broadcast_ether_addr(addr)) ext->ext_flags |= IW_ENCODE_EXT_GROUP_KEY; if (set_tx) ext->ext_flags |= IW_ENCODE_EXT_SET_TX_KEY; ext->addr.sa_family = ARPHRD_ETHER; if (addr) os_memcpy(ext->addr.sa_data, addr, ETH_ALEN); else os_memset(ext->addr.sa_data, 0xff, ETH_ALEN); if (key && key_len) { os_memcpy(ext + 1, key, key_len); ext->key_len = key_len; } switch (alg) { case WPA_ALG_NONE: ext->alg = IW_ENCODE_ALG_NONE; break; case WPA_ALG_WEP: ext->alg = IW_ENCODE_ALG_WEP; break; case WPA_ALG_TKIP: ext->alg = IW_ENCODE_ALG_TKIP; break; case WPA_ALG_CCMP: ext->alg = IW_ENCODE_ALG_CCMP; break; case WPA_ALG_PMK: ext->alg = IW_ENCODE_ALG_PMK; break; #ifdef CONFIG_IEEE80211W case WPA_ALG_IGTK: ext->alg = IW_ENCODE_ALG_AES_CMAC; break; #endif /* CONFIG_IEEE80211W */ default: wpa_printf(MSG_DEBUG, "%s: Unknown algorithm %d", __FUNCTION__, alg); os_free(ext); return -1; } if (seq && seq_len) { ext->ext_flags |= IW_ENCODE_EXT_RX_SEQ_VALID; os_memcpy(ext->rx_seq, seq, seq_len); } if (ioctl(drv->ioctl_sock, SIOCSIWENCODEEXT, &iwr) < 0) { ret = errno == EOPNOTSUPP ? -2 : -1; if (errno == ENODEV) { /* * ndiswrapper seems to be returning incorrect error * code.. */ ret = -2; } perror("ioctl[SIOCSIWENCODEEXT]"); } os_free(ext); return ret; } /** * wpa_driver_wext_set_key - Configure encryption key * @priv: Pointer to private wext data from wpa_driver_wext_init() * @priv: Private driver interface data * @alg: Encryption algorithm (%WPA_ALG_NONE, %WPA_ALG_WEP, * %WPA_ALG_TKIP, %WPA_ALG_CCMP); %WPA_ALG_NONE clears the key. * @addr: Address of the peer STA or ff:ff:ff:ff:ff:ff for * broadcast/default keys * @key_idx: key index (0..3), usually 0 for unicast keys * @set_tx: Configure this key as the default Tx key (only used when * driver does not support separate unicast/individual key * @seq: Sequence number/packet number, seq_len octets, the next * packet number to be used for in replay protection; configured * for Rx keys (in most cases, this is only used with broadcast * keys and set to zero for unicast keys) * @seq_len: Length of the seq, depends on the algorithm: * TKIP: 6 octets, CCMP: 6 octets * @key: Key buffer; TKIP: 16-byte temporal key, 8-byte Tx Mic key, * 8-byte Rx Mic Key * @key_len: Length of the key buffer in octets (WEP: 5 or 13, * TKIP: 32, CCMP: 16) * Returns: 0 on success, -1 on failure * * This function uses SIOCSIWENCODEEXT by default, but tries to use * SIOCSIWENCODE if the extended ioctl fails when configuring a WEP key. */ int wpa_driver_wext_set_key(const char *ifname, void *priv, enum wpa_alg alg, const u8 *addr, int key_idx, int set_tx, const u8 *seq, size_t seq_len, const u8 *key, size_t key_len) { struct wpa_driver_wext_data *drv = priv; struct iwreq iwr; int ret = 0; wpa_printf(MSG_DEBUG, "%s: alg=%d key_idx=%d set_tx=%d seq_len=%lu " "key_len=%lu", __FUNCTION__, alg, key_idx, set_tx, (unsigned long) seq_len, (unsigned long) key_len); ret = wpa_driver_wext_set_key_ext(drv, alg, addr, key_idx, set_tx, seq, seq_len, key, key_len); if (ret == 0) return 0; if (ret == -2 && (alg == WPA_ALG_NONE || alg == WPA_ALG_WEP)) { wpa_printf(MSG_DEBUG, "Driver did not support " "SIOCSIWENCODEEXT, trying SIOCSIWENCODE"); ret = 0; } else { wpa_printf(MSG_DEBUG, "Driver did not support " "SIOCSIWENCODEEXT"); return ret; } os_memset(&iwr, 0, sizeof(iwr)); os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); iwr.u.encoding.flags = key_idx + 1; iwr.u.encoding.flags |= IW_ENCODE_TEMP; if (alg == WPA_ALG_NONE) iwr.u.encoding.flags |= IW_ENCODE_DISABLED; iwr.u.encoding.pointer = (caddr_t) key; iwr.u.encoding.length = key_len; if (ioctl(drv->ioctl_sock, SIOCSIWENCODE, &iwr) < 0) { perror("ioctl[SIOCSIWENCODE]"); ret = -1; } if (set_tx && alg != WPA_ALG_NONE) { os_memset(&iwr, 0, sizeof(iwr)); os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); iwr.u.encoding.flags = key_idx + 1; iwr.u.encoding.flags |= IW_ENCODE_TEMP; iwr.u.encoding.pointer = (caddr_t) NULL; iwr.u.encoding.length = 0; if (ioctl(drv->ioctl_sock, SIOCSIWENCODE, &iwr) < 0) { perror("ioctl[SIOCSIWENCODE] (set_tx)"); ret = -1; } } return ret; } static int wpa_driver_wext_set_countermeasures(void *priv, int enabled) { struct wpa_driver_wext_data *drv = priv; wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); return wpa_driver_wext_set_auth_param(drv, IW_AUTH_TKIP_COUNTERMEASURES, enabled); } static int wpa_driver_wext_set_drop_unencrypted(void *priv, int enabled) { struct wpa_driver_wext_data *drv = priv; wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); drv->use_crypt = enabled; return wpa_driver_wext_set_auth_param(drv, IW_AUTH_DROP_UNENCRYPTED, enabled); } static int wpa_driver_wext_mlme(struct wpa_driver_wext_data *drv, const u8 *addr, int cmd, int reason_code) { struct iwreq iwr; struct iw_mlme mlme; int ret = 0; os_memset(&iwr, 0, sizeof(iwr)); os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); os_memset(&mlme, 0, sizeof(mlme)); mlme.cmd = cmd; mlme.reason_code = reason_code; mlme.addr.sa_family = ARPHRD_ETHER; os_memcpy(mlme.addr.sa_data, addr, ETH_ALEN); iwr.u.data.pointer = (caddr_t) &mlme; iwr.u.data.length = sizeof(mlme); if (ioctl(drv->ioctl_sock, SIOCSIWMLME, &iwr) < 0) { perror("ioctl[SIOCSIWMLME]"); ret = -1; } return ret; } static void wpa_driver_wext_disconnect(struct wpa_driver_wext_data *drv) { struct iwreq iwr; const u8 null_bssid[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 }; u8 ssid[32]; int i; /* * Only force-disconnect when the card is in infrastructure mode, * otherwise the driver might interpret the cleared BSSID and random * SSID as an attempt to create a new ad-hoc network. */ os_memset(&iwr, 0, sizeof(iwr)); os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); if (ioctl(drv->ioctl_sock, SIOCGIWMODE, &iwr) < 0) { perror("ioctl[SIOCGIWMODE]"); iwr.u.mode = IW_MODE_INFRA; } if (iwr.u.mode == IW_MODE_INFRA) { /* Clear the BSSID selection */ if (wpa_driver_wext_set_bssid(drv, null_bssid) < 0) { wpa_printf(MSG_DEBUG, "WEXT: Failed to clear BSSID " "selection on disconnect"); } if (drv->cfg80211) { /* * cfg80211 supports SIOCSIWMLME commands, so there is * no need for the random SSID hack, but clear the * SSID. */ if (wpa_driver_wext_set_ssid(drv, (u8 *) "", 0) < 0) { wpa_printf(MSG_DEBUG, "WEXT: Failed to clear " "SSID on disconnect"); } return; } /* * Set a random SSID to make sure the driver will not be trying * to associate with something even if it does not understand * SIOCSIWMLME commands (or tries to associate automatically * after deauth/disassoc). */ for (i = 0; i < 32; i++) ssid[i] = rand() & 0xFF; if (wpa_driver_wext_set_ssid(drv, ssid, 32) < 0) { wpa_printf(MSG_DEBUG, "WEXT: Failed to set bogus " "SSID to disconnect"); } } } static int wpa_driver_wext_deauthenticate(void *priv, const u8 *addr, int reason_code) { struct wpa_driver_wext_data *drv = priv; int ret; wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); ret = wpa_driver_wext_mlme(drv, addr, IW_MLME_DEAUTH, reason_code); wpa_driver_wext_disconnect(drv); return ret; } static int wpa_driver_wext_set_gen_ie(void *priv, const u8 *ie, size_t ie_len) { struct wpa_driver_wext_data *drv = priv; struct iwreq iwr; int ret = 0; os_memset(&iwr, 0, sizeof(iwr)); os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); iwr.u.data.pointer = (caddr_t) ie; iwr.u.data.length = ie_len; if (ioctl(drv->ioctl_sock, SIOCSIWGENIE, &iwr) < 0) { perror("ioctl[SIOCSIWGENIE]"); ret = -1; } return ret; } int wpa_driver_wext_cipher2wext(int cipher) { switch (cipher) { case WPA_CIPHER_NONE: return IW_AUTH_CIPHER_NONE; case WPA_CIPHER_WEP40: return IW_AUTH_CIPHER_WEP40; case WPA_CIPHER_TKIP: return IW_AUTH_CIPHER_TKIP; case WPA_CIPHER_CCMP: return IW_AUTH_CIPHER_CCMP; case WPA_CIPHER_WEP104: return IW_AUTH_CIPHER_WEP104; default: return 0; } } int wpa_driver_wext_keymgmt2wext(int keymgmt) { switch (keymgmt) { case WPA_KEY_MGMT_IEEE8021X: case WPA_KEY_MGMT_IEEE8021X_NO_WPA: return IW_AUTH_KEY_MGMT_802_1X; case WPA_KEY_MGMT_PSK: return IW_AUTH_KEY_MGMT_PSK; default: return 0; } } static int wpa_driver_wext_auth_alg_fallback(struct wpa_driver_wext_data *drv, struct wpa_driver_associate_params *params) { struct iwreq iwr; int ret = 0; wpa_printf(MSG_DEBUG, "WEXT: Driver did not support " "SIOCSIWAUTH for AUTH_ALG, trying SIOCSIWENCODE"); os_memset(&iwr, 0, sizeof(iwr)); os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); /* Just changing mode, not actual keys */ iwr.u.encoding.flags = 0; iwr.u.encoding.pointer = (caddr_t) NULL; iwr.u.encoding.length = 0; /* * Note: IW_ENCODE_{OPEN,RESTRICTED} can be interpreted to mean two * different things. Here they are used to indicate Open System vs. * Shared Key authentication algorithm. However, some drivers may use * them to select between open/restricted WEP encrypted (open = allow * both unencrypted and encrypted frames; restricted = only allow * encrypted frames). */ if (!drv->use_crypt) { iwr.u.encoding.flags |= IW_ENCODE_DISABLED; } else { if (params->auth_alg & WPA_AUTH_ALG_OPEN) iwr.u.encoding.flags |= IW_ENCODE_OPEN; if (params->auth_alg & WPA_AUTH_ALG_SHARED) iwr.u.encoding.flags |= IW_ENCODE_RESTRICTED; } if (ioctl(drv->ioctl_sock, SIOCSIWENCODE, &iwr) < 0) { perror("ioctl[SIOCSIWENCODE]"); ret = -1; } return ret; } int wpa_driver_wext_associate(void *priv, struct wpa_driver_associate_params *params) { struct wpa_driver_wext_data *drv = priv; int ret = 0; int allow_unencrypted_eapol; int value; wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); if (drv->cfg80211) { /* * Stop cfg80211 from trying to associate before we are done * with all parameters. */ wpa_driver_wext_set_ssid(drv, (u8 *) "", 0); } if (wpa_driver_wext_set_drop_unencrypted(drv, params->drop_unencrypted) < 0) ret = -1; if (wpa_driver_wext_set_auth_alg(drv, params->auth_alg) < 0) ret = -1; if (wpa_driver_wext_set_mode(drv, params->mode) < 0) ret = -1; /* * If the driver did not support SIOCSIWAUTH, fallback to * SIOCSIWENCODE here. */ if (drv->auth_alg_fallback && wpa_driver_wext_auth_alg_fallback(drv, params) < 0) ret = -1; if (!params->bssid && wpa_driver_wext_set_bssid(drv, NULL) < 0) ret = -1; /* TODO: should consider getting wpa version and cipher/key_mgmt suites * from configuration, not from here, where only the selected suite is * available */ if (wpa_driver_wext_set_gen_ie(drv, params->wpa_ie, params->wpa_ie_len) < 0) ret = -1; if (params->wpa_ie == NULL || params->wpa_ie_len == 0) value = IW_AUTH_WPA_VERSION_DISABLED; else if (params->wpa_ie[0] == WLAN_EID_RSN) value = IW_AUTH_WPA_VERSION_WPA2; else value = IW_AUTH_WPA_VERSION_WPA; if (wpa_driver_wext_set_auth_param(drv, IW_AUTH_WPA_VERSION, value) < 0) ret = -1; value = wpa_driver_wext_cipher2wext(params->pairwise_suite); if (wpa_driver_wext_set_auth_param(drv, IW_AUTH_CIPHER_PAIRWISE, value) < 0) ret = -1; value = wpa_driver_wext_cipher2wext(params->group_suite); if (wpa_driver_wext_set_auth_param(drv, IW_AUTH_CIPHER_GROUP, value) < 0) ret = -1; value = wpa_driver_wext_keymgmt2wext(params->key_mgmt_suite); if (wpa_driver_wext_set_auth_param(drv, IW_AUTH_KEY_MGMT, value) < 0) ret = -1; value = params->key_mgmt_suite != WPA_KEY_MGMT_NONE || params->pairwise_suite != WPA_CIPHER_NONE || params->group_suite != WPA_CIPHER_NONE || params->wpa_ie_len; if (wpa_driver_wext_set_auth_param(drv, IW_AUTH_PRIVACY_INVOKED, value) < 0) ret = -1; /* Allow unencrypted EAPOL messages even if pairwise keys are set when * not using WPA. IEEE 802.1X specifies that these frames are not * encrypted, but WPA encrypts them when pairwise keys are in use. */ if (params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X || params->key_mgmt_suite == WPA_KEY_MGMT_PSK) allow_unencrypted_eapol = 0; else allow_unencrypted_eapol = 1; if (wpa_driver_wext_set_psk(drv, params->psk) < 0) ret = -1; if (wpa_driver_wext_set_auth_param(drv, IW_AUTH_RX_UNENCRYPTED_EAPOL, allow_unencrypted_eapol) < 0) ret = -1; #ifdef CONFIG_IEEE80211W switch (params->mgmt_frame_protection) { case NO_MGMT_FRAME_PROTECTION: value = IW_AUTH_MFP_DISABLED; break; case MGMT_FRAME_PROTECTION_OPTIONAL: value = IW_AUTH_MFP_OPTIONAL; break; case MGMT_FRAME_PROTECTION_REQUIRED: value = IW_AUTH_MFP_REQUIRED; break; }; if (wpa_driver_wext_set_auth_param(drv, IW_AUTH_MFP, value) < 0) ret = -1; #endif /* CONFIG_IEEE80211W */ if (params->freq && wpa_driver_wext_set_freq(drv, params->freq) < 0) ret = -1; if (!drv->cfg80211 && wpa_driver_wext_set_ssid(drv, params->ssid, params->ssid_len) < 0) ret = -1; if (params->bssid && wpa_driver_wext_set_bssid(drv, params->bssid) < 0) ret = -1; if (drv->cfg80211 && wpa_driver_wext_set_ssid(drv, params->ssid, params->ssid_len) < 0) ret = -1; return ret; } static int wpa_driver_wext_set_auth_alg(void *priv, int auth_alg) { struct wpa_driver_wext_data *drv = priv; int algs = 0, res; if (auth_alg & WPA_AUTH_ALG_OPEN) algs |= IW_AUTH_ALG_OPEN_SYSTEM; if (auth_alg & WPA_AUTH_ALG_SHARED) algs |= IW_AUTH_ALG_SHARED_KEY; if (auth_alg & WPA_AUTH_ALG_LEAP) algs |= IW_AUTH_ALG_LEAP; if (algs == 0) { /* at least one algorithm should be set */ algs = IW_AUTH_ALG_OPEN_SYSTEM; } res = wpa_driver_wext_set_auth_param(drv, IW_AUTH_80211_AUTH_ALG, algs); drv->auth_alg_fallback = res == -2; return res; } /** * wpa_driver_wext_set_mode - Set wireless mode (infra/adhoc), SIOCSIWMODE * @priv: Pointer to private wext data from wpa_driver_wext_init() * @mode: 0 = infra/BSS (associate with an AP), 1 = adhoc/IBSS * Returns: 0 on success, -1 on failure */ int wpa_driver_wext_set_mode(void *priv, int mode) { struct wpa_driver_wext_data *drv = priv; struct iwreq iwr; int ret = -1; unsigned int new_mode = mode ? IW_MODE_ADHOC : IW_MODE_INFRA; os_memset(&iwr, 0, sizeof(iwr)); os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); iwr.u.mode = new_mode; if (ioctl(drv->ioctl_sock, SIOCSIWMODE, &iwr) == 0) { ret = 0; goto done; } if (errno != EBUSY) { perror("ioctl[SIOCSIWMODE]"); goto done; } /* mac80211 doesn't allow mode changes while the device is up, so if * the device isn't in the mode we're about to change to, take device * down, try to set the mode again, and bring it back up. */ if (ioctl(drv->ioctl_sock, SIOCGIWMODE, &iwr) < 0) { perror("ioctl[SIOCGIWMODE]"); goto done; } if (iwr.u.mode == new_mode) { ret = 0; goto done; } if (linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 0) == 0) { /* Try to set the mode again while the interface is down */ iwr.u.mode = new_mode; if (ioctl(drv->ioctl_sock, SIOCSIWMODE, &iwr) < 0) perror("ioctl[SIOCSIWMODE]"); else ret = 0; (void) linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 1); } done: return ret; } static int wpa_driver_wext_pmksa(struct wpa_driver_wext_data *drv, u32 cmd, const u8 *bssid, const u8 *pmkid) { struct iwreq iwr; struct iw_pmksa pmksa; int ret = 0; os_memset(&iwr, 0, sizeof(iwr)); os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); os_memset(&pmksa, 0, sizeof(pmksa)); pmksa.cmd = cmd; pmksa.bssid.sa_family = ARPHRD_ETHER; if (bssid) os_memcpy(pmksa.bssid.sa_data, bssid, ETH_ALEN); if (pmkid) os_memcpy(pmksa.pmkid, pmkid, IW_PMKID_LEN); iwr.u.data.pointer = (caddr_t) &pmksa; iwr.u.data.length = sizeof(pmksa); if (ioctl(drv->ioctl_sock, SIOCSIWPMKSA, &iwr) < 0) { if (errno != EOPNOTSUPP) perror("ioctl[SIOCSIWPMKSA]"); ret = -1; } return ret; } static int wpa_driver_wext_add_pmkid(void *priv, const u8 *bssid, const u8 *pmkid) { struct wpa_driver_wext_data *drv = priv; return wpa_driver_wext_pmksa(drv, IW_PMKSA_ADD, bssid, pmkid); } static int wpa_driver_wext_remove_pmkid(void *priv, const u8 *bssid, const u8 *pmkid) { struct wpa_driver_wext_data *drv = priv; return wpa_driver_wext_pmksa(drv, IW_PMKSA_REMOVE, bssid, pmkid); } static int wpa_driver_wext_flush_pmkid(void *priv) { struct wpa_driver_wext_data *drv = priv; return wpa_driver_wext_pmksa(drv, IW_PMKSA_FLUSH, NULL, NULL); } int wpa_driver_wext_get_capa(void *priv, struct wpa_driver_capa *capa) { struct wpa_driver_wext_data *drv = priv; if (!drv->has_capability) return -1; os_memcpy(capa, &drv->capa, sizeof(*capa)); return 0; } int wpa_driver_wext_alternative_ifindex(struct wpa_driver_wext_data *drv, const char *ifname) { if (ifname == NULL) { drv->ifindex2 = -1; return 0; } drv->ifindex2 = if_nametoindex(ifname); if (drv->ifindex2 <= 0) return -1; wpa_printf(MSG_DEBUG, "Added alternative ifindex %d (%s) for " "wireless events", drv->ifindex2, ifname); return 0; } int wpa_driver_wext_set_operstate(void *priv, int state) { struct wpa_driver_wext_data *drv = priv; wpa_printf(MSG_DEBUG, "%s: operstate %d->%d (%s)", __func__, drv->operstate, state, state ? "UP" : "DORMANT"); drv->operstate = state; return netlink_send_oper_ifla(drv->netlink, drv->ifindex, -1, state ? IF_OPER_UP : IF_OPER_DORMANT); } int wpa_driver_wext_get_version(struct wpa_driver_wext_data *drv) { return drv->we_version_compiled; } static const char * wext_get_radio_name(void *priv) { struct wpa_driver_wext_data *drv = priv; return drv->phyname; } const struct wpa_driver_ops wpa_driver_wext_ops = { .name = "wext", .desc = "Linux wireless extensions (generic)", .get_bssid = wpa_driver_wext_get_bssid, .get_ssid = wpa_driver_wext_get_ssid, .set_key = wpa_driver_wext_set_key, .set_countermeasures = wpa_driver_wext_set_countermeasures, .scan2 = wpa_driver_wext_scan, .get_scan_results2 = wpa_driver_wext_get_scan_results, .deauthenticate = wpa_driver_wext_deauthenticate, .associate = wpa_driver_wext_associate, .init = wpa_driver_wext_init, .deinit = wpa_driver_wext_deinit, .add_pmkid = wpa_driver_wext_add_pmkid, .remove_pmkid = wpa_driver_wext_remove_pmkid, .flush_pmkid = wpa_driver_wext_flush_pmkid, .get_capa = wpa_driver_wext_get_capa, .set_operstate = wpa_driver_wext_set_operstate, .get_radio_name = wext_get_radio_name, }; wpa-2.1/src/drivers/driver_wext.h000066400000000000000000000045531227414666700171510ustar00rootroot00000000000000/* * WPA Supplicant - driver_wext exported functions * Copyright (c) 2003-2005, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef DRIVER_WEXT_H #define DRIVER_WEXT_H #include struct wpa_driver_wext_data { void *ctx; struct netlink_data *netlink; int ioctl_sock; int mlme_sock; char ifname[IFNAMSIZ + 1]; char phyname[32]; int ifindex; int ifindex2; int if_removed; int if_disabled; struct rfkill_data *rfkill; u8 *assoc_req_ies; size_t assoc_req_ies_len; u8 *assoc_resp_ies; size_t assoc_resp_ies_len; struct wpa_driver_capa capa; int has_capability; int we_version_compiled; /* for set_auth_alg fallback */ int use_crypt; int auth_alg_fallback; int operstate; char mlmedev[IFNAMSIZ + 1]; int scan_complete_events; int cfg80211; /* whether driver is using cfg80211 */ u8 max_level; }; int wpa_driver_wext_get_bssid(void *priv, u8 *bssid); int wpa_driver_wext_set_bssid(void *priv, const u8 *bssid); int wpa_driver_wext_get_ssid(void *priv, u8 *ssid); int wpa_driver_wext_set_ssid(void *priv, const u8 *ssid, size_t ssid_len); int wpa_driver_wext_set_freq(void *priv, int freq); int wpa_driver_wext_set_mode(void *priv, int mode); int wpa_driver_wext_set_key(const char *ifname, void *priv, enum wpa_alg alg, const u8 *addr, int key_idx, int set_tx, const u8 *seq, size_t seq_len, const u8 *key, size_t key_len); int wpa_driver_wext_scan(void *priv, struct wpa_driver_scan_params *params); struct wpa_scan_results * wpa_driver_wext_get_scan_results(void *priv); void wpa_driver_wext_scan_timeout(void *eloop_ctx, void *timeout_ctx); int wpa_driver_wext_alternative_ifindex(struct wpa_driver_wext_data *drv, const char *ifname); void * wpa_driver_wext_init(void *ctx, const char *ifname); void wpa_driver_wext_deinit(void *priv); int wpa_driver_wext_set_operstate(void *priv, int state); int wpa_driver_wext_get_version(struct wpa_driver_wext_data *drv); int wpa_driver_wext_associate(void *priv, struct wpa_driver_associate_params *params); int wpa_driver_wext_get_capa(void *priv, struct wpa_driver_capa *capa); int wpa_driver_wext_set_auth_param(struct wpa_driver_wext_data *drv, int idx, u32 value); int wpa_driver_wext_cipher2wext(int cipher); int wpa_driver_wext_keymgmt2wext(int keymgmt); #endif /* DRIVER_WEXT_H */ wpa-2.1/src/drivers/driver_wired.c000066400000000000000000000367371227414666700173000ustar00rootroot00000000000000/* * Wired Ethernet driver interface * Copyright (c) 2005-2009, Jouni Malinen * Copyright (c) 2004, Gunter Burchardt * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "includes.h" #include #include #ifdef __linux__ #include #include #include #endif /* __linux__ */ #if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) #include #include #endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) */ #ifdef __sun__ #include #endif /* __sun__ */ #include "common.h" #include "eloop.h" #include "driver.h" #ifdef _MSC_VER #pragma pack(push, 1) #endif /* _MSC_VER */ struct ieee8023_hdr { u8 dest[6]; u8 src[6]; u16 ethertype; } STRUCT_PACKED; #ifdef _MSC_VER #pragma pack(pop) #endif /* _MSC_VER */ static const u8 pae_group_addr[ETH_ALEN] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 }; struct wpa_driver_wired_data { char ifname[IFNAMSIZ + 1]; void *ctx; int sock; /* raw packet socket for driver access */ int dhcp_sock; /* socket for dhcp packets */ int use_pae_group_addr; int pf_sock; int membership, multi, iff_allmulti, iff_up; }; /* TODO: detecting new devices should eventually be changed from using DHCP * snooping to trigger on any packet from a new layer 2 MAC address, e.g., * based on ebtables, etc. */ struct dhcp_message { u_int8_t op; u_int8_t htype; u_int8_t hlen; u_int8_t hops; u_int32_t xid; u_int16_t secs; u_int16_t flags; u_int32_t ciaddr; u_int32_t yiaddr; u_int32_t siaddr; u_int32_t giaddr; u_int8_t chaddr[16]; u_int8_t sname[64]; u_int8_t file[128]; u_int32_t cookie; u_int8_t options[308]; /* 312 - cookie */ }; static int wired_multicast_membership(int sock, int ifindex, const u8 *addr, int add) { #ifdef __linux__ struct packet_mreq mreq; if (sock < 0) return -1; os_memset(&mreq, 0, sizeof(mreq)); mreq.mr_ifindex = ifindex; mreq.mr_type = PACKET_MR_MULTICAST; mreq.mr_alen = ETH_ALEN; os_memcpy(mreq.mr_address, addr, ETH_ALEN); if (setsockopt(sock, SOL_PACKET, add ? PACKET_ADD_MEMBERSHIP : PACKET_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) { perror("setsockopt"); return -1; } return 0; #else /* __linux__ */ return -1; #endif /* __linux__ */ } #ifdef __linux__ static void handle_data(void *ctx, unsigned char *buf, size_t len) { #ifdef HOSTAPD struct ieee8023_hdr *hdr; u8 *pos, *sa; size_t left; union wpa_event_data event; /* must contain at least ieee8023_hdr 6 byte source, 6 byte dest, * 2 byte ethertype */ if (len < 14) { wpa_printf(MSG_MSGDUMP, "handle_data: too short (%lu)", (unsigned long) len); return; } hdr = (struct ieee8023_hdr *) buf; switch (ntohs(hdr->ethertype)) { case ETH_P_PAE: wpa_printf(MSG_MSGDUMP, "Received EAPOL packet"); sa = hdr->src; os_memset(&event, 0, sizeof(event)); event.new_sta.addr = sa; wpa_supplicant_event(ctx, EVENT_NEW_STA, &event); pos = (u8 *) (hdr + 1); left = len - sizeof(*hdr); drv_event_eapol_rx(ctx, sa, pos, left); break; default: wpa_printf(MSG_DEBUG, "Unknown ethertype 0x%04x in data frame", ntohs(hdr->ethertype)); break; } #endif /* HOSTAPD */ } static void handle_read(int sock, void *eloop_ctx, void *sock_ctx) { int len; unsigned char buf[3000]; len = recv(sock, buf, sizeof(buf), 0); if (len < 0) { perror("recv"); return; } handle_data(eloop_ctx, buf, len); } static void handle_dhcp(int sock, void *eloop_ctx, void *sock_ctx) { int len; unsigned char buf[3000]; struct dhcp_message *msg; u8 *mac_address; union wpa_event_data event; len = recv(sock, buf, sizeof(buf), 0); if (len < 0) { perror("recv"); return; } /* must contain at least dhcp_message->chaddr */ if (len < 44) { wpa_printf(MSG_MSGDUMP, "handle_dhcp: too short (%d)", len); return; } msg = (struct dhcp_message *) buf; mac_address = (u8 *) &(msg->chaddr); wpa_printf(MSG_MSGDUMP, "Got DHCP broadcast packet from " MACSTR, MAC2STR(mac_address)); os_memset(&event, 0, sizeof(event)); event.new_sta.addr = mac_address; wpa_supplicant_event(eloop_ctx, EVENT_NEW_STA, &event); } #endif /* __linux__ */ static int wired_init_sockets(struct wpa_driver_wired_data *drv, u8 *own_addr) { #ifdef __linux__ struct ifreq ifr; struct sockaddr_ll addr; struct sockaddr_in addr2; int n = 1; drv->sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_PAE)); if (drv->sock < 0) { perror("socket[PF_PACKET,SOCK_RAW]"); return -1; } if (eloop_register_read_sock(drv->sock, handle_read, drv->ctx, NULL)) { printf("Could not register read socket\n"); return -1; } os_memset(&ifr, 0, sizeof(ifr)); os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name)); if (ioctl(drv->sock, SIOCGIFINDEX, &ifr) != 0) { perror("ioctl(SIOCGIFINDEX)"); return -1; } os_memset(&addr, 0, sizeof(addr)); addr.sll_family = AF_PACKET; addr.sll_ifindex = ifr.ifr_ifindex; wpa_printf(MSG_DEBUG, "Opening raw packet socket for ifindex %d", addr.sll_ifindex); if (bind(drv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { perror("bind"); return -1; } /* filter multicast address */ if (wired_multicast_membership(drv->sock, ifr.ifr_ifindex, pae_group_addr, 1) < 0) { wpa_printf(MSG_ERROR, "wired: Failed to add multicast group " "membership"); return -1; } os_memset(&ifr, 0, sizeof(ifr)); os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name)); if (ioctl(drv->sock, SIOCGIFHWADDR, &ifr) != 0) { perror("ioctl(SIOCGIFHWADDR)"); return -1; } if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) { printf("Invalid HW-addr family 0x%04x\n", ifr.ifr_hwaddr.sa_family); return -1; } os_memcpy(own_addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN); /* setup dhcp listen socket for sta detection */ if ((drv->dhcp_sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { perror("socket call failed for dhcp"); return -1; } if (eloop_register_read_sock(drv->dhcp_sock, handle_dhcp, drv->ctx, NULL)) { printf("Could not register read socket\n"); return -1; } os_memset(&addr2, 0, sizeof(addr2)); addr2.sin_family = AF_INET; addr2.sin_port = htons(67); addr2.sin_addr.s_addr = INADDR_ANY; if (setsockopt(drv->dhcp_sock, SOL_SOCKET, SO_REUSEADDR, (char *) &n, sizeof(n)) == -1) { perror("setsockopt[SOL_SOCKET,SO_REUSEADDR]"); return -1; } if (setsockopt(drv->dhcp_sock, SOL_SOCKET, SO_BROADCAST, (char *) &n, sizeof(n)) == -1) { perror("setsockopt[SOL_SOCKET,SO_BROADCAST]"); return -1; } os_memset(&ifr, 0, sizeof(ifr)); os_strlcpy(ifr.ifr_ifrn.ifrn_name, drv->ifname, IFNAMSIZ); if (setsockopt(drv->dhcp_sock, SOL_SOCKET, SO_BINDTODEVICE, (char *) &ifr, sizeof(ifr)) < 0) { perror("setsockopt[SOL_SOCKET,SO_BINDTODEVICE]"); return -1; } if (bind(drv->dhcp_sock, (struct sockaddr *) &addr2, sizeof(struct sockaddr)) == -1) { perror("bind"); return -1; } return 0; #else /* __linux__ */ return -1; #endif /* __linux__ */ } static int wired_send_eapol(void *priv, const u8 *addr, const u8 *data, size_t data_len, int encrypt, const u8 *own_addr, u32 flags) { struct wpa_driver_wired_data *drv = priv; struct ieee8023_hdr *hdr; size_t len; u8 *pos; int res; len = sizeof(*hdr) + data_len; hdr = os_zalloc(len); if (hdr == NULL) { printf("malloc() failed for wired_send_eapol(len=%lu)\n", (unsigned long) len); return -1; } os_memcpy(hdr->dest, drv->use_pae_group_addr ? pae_group_addr : addr, ETH_ALEN); os_memcpy(hdr->src, own_addr, ETH_ALEN); hdr->ethertype = htons(ETH_P_PAE); pos = (u8 *) (hdr + 1); os_memcpy(pos, data, data_len); res = send(drv->sock, (u8 *) hdr, len, 0); os_free(hdr); if (res < 0) { perror("wired_send_eapol: send"); printf("wired_send_eapol - packet len: %lu - failed\n", (unsigned long) len); } return res; } static void * wired_driver_hapd_init(struct hostapd_data *hapd, struct wpa_init_params *params) { struct wpa_driver_wired_data *drv; drv = os_zalloc(sizeof(struct wpa_driver_wired_data)); if (drv == NULL) { printf("Could not allocate memory for wired driver data\n"); return NULL; } drv->ctx = hapd; os_strlcpy(drv->ifname, params->ifname, sizeof(drv->ifname)); drv->use_pae_group_addr = params->use_pae_group_addr; if (wired_init_sockets(drv, params->own_addr)) { os_free(drv); return NULL; } return drv; } static void wired_driver_hapd_deinit(void *priv) { struct wpa_driver_wired_data *drv = priv; if (drv->sock >= 0) close(drv->sock); if (drv->dhcp_sock >= 0) close(drv->dhcp_sock); os_free(drv); } static int wpa_driver_wired_get_ssid(void *priv, u8 *ssid) { ssid[0] = 0; return 0; } static int wpa_driver_wired_get_bssid(void *priv, u8 *bssid) { /* Report PAE group address as the "BSSID" for wired connection. */ os_memcpy(bssid, pae_group_addr, ETH_ALEN); return 0; } static int wpa_driver_wired_get_capa(void *priv, struct wpa_driver_capa *capa) { os_memset(capa, 0, sizeof(*capa)); capa->flags = WPA_DRIVER_FLAGS_WIRED; return 0; } static int wpa_driver_wired_get_ifflags(const char *ifname, int *flags) { struct ifreq ifr; int s; s = socket(PF_INET, SOCK_DGRAM, 0); if (s < 0) { perror("socket"); return -1; } os_memset(&ifr, 0, sizeof(ifr)); os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); if (ioctl(s, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) { perror("ioctl[SIOCGIFFLAGS]"); close(s); return -1; } close(s); *flags = ifr.ifr_flags & 0xffff; return 0; } static int wpa_driver_wired_set_ifflags(const char *ifname, int flags) { struct ifreq ifr; int s; s = socket(PF_INET, SOCK_DGRAM, 0); if (s < 0) { perror("socket"); return -1; } os_memset(&ifr, 0, sizeof(ifr)); os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); ifr.ifr_flags = flags & 0xffff; if (ioctl(s, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) { perror("ioctl[SIOCSIFFLAGS]"); close(s); return -1; } close(s); return 0; } #if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) static int wpa_driver_wired_get_ifstatus(const char *ifname, int *status) { struct ifmediareq ifmr; int s; s = socket(PF_INET, SOCK_DGRAM, 0); if (s < 0) { perror("socket"); return -1; } os_memset(&ifmr, 0, sizeof(ifmr)); os_strlcpy(ifmr.ifm_name, ifname, IFNAMSIZ); if (ioctl(s, SIOCGIFMEDIA, (caddr_t) &ifmr) < 0) { perror("ioctl[SIOCGIFMEDIA]"); close(s); return -1; } close(s); *status = (ifmr.ifm_status & (IFM_ACTIVE | IFM_AVALID)) == (IFM_ACTIVE | IFM_AVALID); return 0; } #endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(FreeBSD_kernel__) */ static int wpa_driver_wired_multi(const char *ifname, const u8 *addr, int add) { struct ifreq ifr; int s; #ifdef __sun__ return -1; #endif /* __sun__ */ s = socket(PF_INET, SOCK_DGRAM, 0); if (s < 0) { perror("socket"); return -1; } os_memset(&ifr, 0, sizeof(ifr)); os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); #ifdef __linux__ ifr.ifr_hwaddr.sa_family = AF_UNSPEC; os_memcpy(ifr.ifr_hwaddr.sa_data, addr, ETH_ALEN); #endif /* __linux__ */ #if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) { struct sockaddr_dl *dlp; dlp = (struct sockaddr_dl *) &ifr.ifr_addr; dlp->sdl_len = sizeof(struct sockaddr_dl); dlp->sdl_family = AF_LINK; dlp->sdl_index = 0; dlp->sdl_nlen = 0; dlp->sdl_alen = ETH_ALEN; dlp->sdl_slen = 0; os_memcpy(LLADDR(dlp), addr, ETH_ALEN); } #endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(FreeBSD_kernel__) */ #if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__) { struct sockaddr *sap; sap = (struct sockaddr *) &ifr.ifr_addr; sap->sa_len = sizeof(struct sockaddr); sap->sa_family = AF_UNSPEC; os_memcpy(sap->sa_data, addr, ETH_ALEN); } #endif /* defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__) */ if (ioctl(s, add ? SIOCADDMULTI : SIOCDELMULTI, (caddr_t) &ifr) < 0) { perror("ioctl[SIOC{ADD/DEL}MULTI]"); close(s); return -1; } close(s); return 0; } static void * wpa_driver_wired_init(void *ctx, const char *ifname) { struct wpa_driver_wired_data *drv; int flags; drv = os_zalloc(sizeof(*drv)); if (drv == NULL) return NULL; os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); drv->ctx = ctx; #ifdef __linux__ drv->pf_sock = socket(PF_PACKET, SOCK_DGRAM, 0); if (drv->pf_sock < 0) perror("socket(PF_PACKET)"); #else /* __linux__ */ drv->pf_sock = -1; #endif /* __linux__ */ if (wpa_driver_wired_get_ifflags(ifname, &flags) == 0 && !(flags & IFF_UP) && wpa_driver_wired_set_ifflags(ifname, flags | IFF_UP) == 0) { drv->iff_up = 1; } if (wired_multicast_membership(drv->pf_sock, if_nametoindex(drv->ifname), pae_group_addr, 1) == 0) { wpa_printf(MSG_DEBUG, "%s: Added multicast membership with " "packet socket", __func__); drv->membership = 1; } else if (wpa_driver_wired_multi(ifname, pae_group_addr, 1) == 0) { wpa_printf(MSG_DEBUG, "%s: Added multicast membership with " "SIOCADDMULTI", __func__); drv->multi = 1; } else if (wpa_driver_wired_get_ifflags(ifname, &flags) < 0) { wpa_printf(MSG_INFO, "%s: Could not get interface " "flags", __func__); os_free(drv); return NULL; } else if (flags & IFF_ALLMULTI) { wpa_printf(MSG_DEBUG, "%s: Interface is already configured " "for multicast", __func__); } else if (wpa_driver_wired_set_ifflags(ifname, flags | IFF_ALLMULTI) < 0) { wpa_printf(MSG_INFO, "%s: Failed to enable allmulti", __func__); os_free(drv); return NULL; } else { wpa_printf(MSG_DEBUG, "%s: Enabled allmulti mode", __func__); drv->iff_allmulti = 1; } #if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) { int status; wpa_printf(MSG_DEBUG, "%s: waiting for link to become active", __func__); while (wpa_driver_wired_get_ifstatus(ifname, &status) == 0 && status == 0) sleep(1); } #endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(FreeBSD_kernel__) */ return drv; } static void wpa_driver_wired_deinit(void *priv) { struct wpa_driver_wired_data *drv = priv; int flags; if (drv->membership && wired_multicast_membership(drv->pf_sock, if_nametoindex(drv->ifname), pae_group_addr, 0) < 0) { wpa_printf(MSG_DEBUG, "%s: Failed to remove PAE multicast " "group (PACKET)", __func__); } if (drv->multi && wpa_driver_wired_multi(drv->ifname, pae_group_addr, 0) < 0) { wpa_printf(MSG_DEBUG, "%s: Failed to remove PAE multicast " "group (SIOCDELMULTI)", __func__); } if (drv->iff_allmulti && (wpa_driver_wired_get_ifflags(drv->ifname, &flags) < 0 || wpa_driver_wired_set_ifflags(drv->ifname, flags & ~IFF_ALLMULTI) < 0)) { wpa_printf(MSG_DEBUG, "%s: Failed to disable allmulti mode", __func__); } if (drv->iff_up && wpa_driver_wired_get_ifflags(drv->ifname, &flags) == 0 && (flags & IFF_UP) && wpa_driver_wired_set_ifflags(drv->ifname, flags & ~IFF_UP) < 0) { wpa_printf(MSG_DEBUG, "%s: Failed to set the interface down", __func__); } if (drv->pf_sock != -1) close(drv->pf_sock); os_free(drv); } const struct wpa_driver_ops wpa_driver_wired_ops = { .name = "wired", .desc = "Wired Ethernet driver", .hapd_init = wired_driver_hapd_init, .hapd_deinit = wired_driver_hapd_deinit, .hapd_send_eapol = wired_send_eapol, .get_ssid = wpa_driver_wired_get_ssid, .get_bssid = wpa_driver_wired_get_bssid, .get_capa = wpa_driver_wired_get_capa, .init = wpa_driver_wired_init, .deinit = wpa_driver_wired_deinit, }; wpa-2.1/src/drivers/drivers.c000066400000000000000000000056271227414666700162630ustar00rootroot00000000000000/* * Driver interface list * Copyright (c) 2004-2005, 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 "driver.h" #ifdef CONFIG_DRIVER_WEXT extern struct wpa_driver_ops wpa_driver_wext_ops; /* driver_wext.c */ #endif /* CONFIG_DRIVER_WEXT */ #ifdef CONFIG_DRIVER_NL80211 extern struct wpa_driver_ops wpa_driver_nl80211_ops; /* driver_nl80211.c */ #endif /* CONFIG_DRIVER_NL80211 */ #ifdef CONFIG_DRIVER_HOSTAP extern struct wpa_driver_ops wpa_driver_hostap_ops; /* driver_hostap.c */ #endif /* CONFIG_DRIVER_HOSTAP */ #ifdef CONFIG_DRIVER_MADWIFI extern struct wpa_driver_ops wpa_driver_madwifi_ops; /* driver_madwifi.c */ #endif /* CONFIG_DRIVER_MADWIFI */ #ifdef CONFIG_DRIVER_BSD extern struct wpa_driver_ops wpa_driver_bsd_ops; /* driver_bsd.c */ #endif /* CONFIG_DRIVER_BSD */ #ifdef CONFIG_DRIVER_OPENBSD extern struct wpa_driver_ops wpa_driver_openbsd_ops; /* driver_openbsd.c */ #endif /* CONFIG_DRIVER_OPENBSD */ #ifdef CONFIG_DRIVER_NDIS extern struct wpa_driver_ops wpa_driver_ndis_ops; /* driver_ndis.c */ #endif /* CONFIG_DRIVER_NDIS */ #ifdef CONFIG_DRIVER_WIRED extern struct wpa_driver_ops wpa_driver_wired_ops; /* driver_wired.c */ #endif /* CONFIG_DRIVER_WIRED */ #ifdef CONFIG_DRIVER_TEST extern struct wpa_driver_ops wpa_driver_test_ops; /* driver_test.c */ #endif /* CONFIG_DRIVER_TEST */ #ifdef CONFIG_DRIVER_ROBOSWITCH /* driver_roboswitch.c */ extern struct wpa_driver_ops wpa_driver_roboswitch_ops; #endif /* CONFIG_DRIVER_ROBOSWITCH */ #ifdef CONFIG_DRIVER_ATHEROS extern struct wpa_driver_ops wpa_driver_atheros_ops; /* driver_atheros.c */ #endif /* CONFIG_DRIVER_ATHEROS */ #ifdef CONFIG_DRIVER_NONE extern struct wpa_driver_ops wpa_driver_none_ops; /* driver_none.c */ #endif /* CONFIG_DRIVER_NONE */ struct wpa_driver_ops *wpa_drivers[] = { #ifdef CONFIG_DRIVER_NL80211 &wpa_driver_nl80211_ops, #endif /* CONFIG_DRIVER_NL80211 */ #ifdef CONFIG_DRIVER_WEXT &wpa_driver_wext_ops, #endif /* CONFIG_DRIVER_WEXT */ #ifdef CONFIG_DRIVER_HOSTAP &wpa_driver_hostap_ops, #endif /* CONFIG_DRIVER_HOSTAP */ #ifdef CONFIG_DRIVER_MADWIFI &wpa_driver_madwifi_ops, #endif /* CONFIG_DRIVER_MADWIFI */ #ifdef CONFIG_DRIVER_BSD &wpa_driver_bsd_ops, #endif /* CONFIG_DRIVER_BSD */ #ifdef CONFIG_DRIVER_OPENBSD &wpa_driver_openbsd_ops, #endif /* CONFIG_DRIVER_OPENBSD */ #ifdef CONFIG_DRIVER_NDIS &wpa_driver_ndis_ops, #endif /* CONFIG_DRIVER_NDIS */ #ifdef CONFIG_DRIVER_WIRED &wpa_driver_wired_ops, #endif /* CONFIG_DRIVER_WIRED */ #ifdef CONFIG_DRIVER_TEST &wpa_driver_test_ops, #endif /* CONFIG_DRIVER_TEST */ #ifdef CONFIG_DRIVER_ROBOSWITCH &wpa_driver_roboswitch_ops, #endif /* CONFIG_DRIVER_ROBOSWITCH */ #ifdef CONFIG_DRIVER_ATHEROS &wpa_driver_atheros_ops, #endif /* CONFIG_DRIVER_ATHEROS */ #ifdef CONFIG_DRIVER_NONE &wpa_driver_none_ops, #endif /* CONFIG_DRIVER_NONE */ NULL }; wpa-2.1/src/drivers/drivers.mak000066400000000000000000000075671227414666700166160ustar00rootroot00000000000000##### CLEAR VARS DRV_CFLAGS = DRV_WPA_CFLAGS = DRV_AP_CFLAGS = DRV_OBJS = DRV_WPA_OBJS = DRV_AP_OBJS = DRV_LIBS = DRV_WPA_LIBS = DRV_AP_LIBS = ##### COMMON DRIVERS ifdef CONFIG_DRIVER_WIRED DRV_CFLAGS += -DCONFIG_DRIVER_WIRED DRV_OBJS += ../src/drivers/driver_wired.o endif ifdef CONFIG_DRIVER_NL80211 DRV_CFLAGS += -DCONFIG_DRIVER_NL80211 DRV_OBJS += ../src/drivers/driver_nl80211.o DRV_OBJS += ../src/utils/radiotap.o NEED_SME=y NEED_AP_MLME=y NEED_NETLINK=y NEED_LINUX_IOCTL=y NEED_RFKILL=y ifdef CONFIG_LIBNL32 DRV_LIBS += -lnl-3 DRV_LIBS += -lnl-genl-3 DRV_CFLAGS += -DCONFIG_LIBNL20 -I/usr/include/libnl3 else ifdef CONFIG_LIBNL_TINY DRV_LIBS += -lnl-tiny else DRV_LIBS += -lnl endif ifdef CONFIG_LIBNL20 DRV_LIBS += -lnl-genl DRV_CFLAGS += -DCONFIG_LIBNL20 endif endif endif ifdef CONFIG_DRIVER_BSD ifndef CONFIG_L2_PACKET CONFIG_L2_PACKET=freebsd endif DRV_CFLAGS += -DCONFIG_DRIVER_BSD DRV_OBJS += ../src/drivers/driver_bsd.o CONFIG_L2_FREEBSD=y CONFIG_DNET_PCAP=y endif ifdef CONFIG_DRIVER_OPENBSD ifndef CONFIG_L2_PACKET CONFIG_L2_PACKET=freebsd endif DRV_CFLAGS += -DCONFIG_DRIVER_OPENBSD DRV_OBJS += ../src/drivers/driver_openbsd.o endif ifdef CONFIG_DRIVER_TEST DRV_CFLAGS += -DCONFIG_DRIVER_TEST DRV_OBJS += ../src/drivers/driver_test.o NEED_AP_MLME=y endif ifdef CONFIG_DRIVER_NONE DRV_CFLAGS += -DCONFIG_DRIVER_NONE DRV_OBJS += ../src/drivers/driver_none.o endif ##### PURE AP DRIVERS ifdef CONFIG_DRIVER_HOSTAP DRV_AP_CFLAGS += -DCONFIG_DRIVER_HOSTAP DRV_AP_OBJS += ../src/drivers/driver_hostap.o CONFIG_WIRELESS_EXTENSION=y NEED_AP_MLME=y NEED_NETLINK=y NEED_LINUX_IOCTL=y endif ifdef CONFIG_DRIVER_MADWIFI DRV_AP_CFLAGS += -DCONFIG_DRIVER_MADWIFI DRV_AP_OBJS += ../src/drivers/driver_madwifi.o CONFIG_WIRELESS_EXTENSION=y CONFIG_L2_PACKET=linux NEED_NETLINK=y NEED_LINUX_IOCTL=y endif ifdef CONFIG_DRIVER_ATHEROS DRV_AP_CFLAGS += -DCONFIG_DRIVER_ATHEROS DRV_AP_OBJS += ../src/drivers/driver_atheros.o CONFIG_L2_PACKET=linux NEED_NETLINK=y NEED_LINUX_IOCTL=y endif ##### PURE CLIENT DRIVERS ifdef CONFIG_DRIVER_WEXT DRV_WPA_CFLAGS += -DCONFIG_DRIVER_WEXT CONFIG_WIRELESS_EXTENSION=y NEED_NETLINK=y NEED_LINUX_IOCTL=y NEED_RFKILL=y endif ifdef CONFIG_DRIVER_NDIS DRV_WPA_CFLAGS += -DCONFIG_DRIVER_NDIS DRV_WPA_OBJS += ../src/drivers/driver_ndis.o ifdef CONFIG_NDIS_EVENTS_INTEGRATED DRV_WPA_OBJS += ../src/drivers/driver_ndis_.o endif ifndef CONFIG_L2_PACKET CONFIG_L2_PACKET=pcap endif CONFIG_WINPCAP=y ifdef CONFIG_USE_NDISUIO DRV_WPA_CFLAGS += -DCONFIG_USE_NDISUIO endif endif ifdef CONFIG_DRIVER_ROBOSWITCH DRV_WPA_CFLAGS += -DCONFIG_DRIVER_ROBOSWITCH DRV_WPA_OBJS += ../src/drivers/driver_roboswitch.o endif ifdef CONFIG_WIRELESS_EXTENSION DRV_WPA_CFLAGS += -DCONFIG_WIRELESS_EXTENSION DRV_WPA_OBJS += ../src/drivers/driver_wext.o NEED_RFKILL=y endif ifdef NEED_NETLINK DRV_OBJS += ../src/drivers/netlink.o endif ifdef NEED_LINUX_IOCTL DRV_OBJS += ../src/drivers/linux_ioctl.o endif ifdef NEED_RFKILL DRV_OBJS += ../src/drivers/rfkill.o endif ifdef CONFIG_VLAN_NETLINK ifdef CONFIG_FULL_DYNAMIC_VLAN ifdef CONFIG_LIBNL32 DRV_LIBS += -lnl-3 DRV_LIBS += -lnl-genl-3 DRV_LIBS += -lnl-route-3 DRV_CFLAGS += -DCONFIG_LIBNL20 else ifdef CONFIG_LIBNL_TINY DRV_LIBS += -lnl-tiny else DRV_LIBS += -lnl endif ifdef CONFIG_LIBNL20 DRV_LIBS += -lnl-genl DRV_LIBS += -lnl-route DRV_CFLAGS += -DCONFIG_LIBNL20 endif endif endif endif ##### COMMON VARS DRV_BOTH_CFLAGS := $(DRV_CFLAGS) $(DRV_WPA_CFLAGS) $(DRV_AP_CFLAGS) DRV_WPA_CFLAGS += $(DRV_CFLAGS) DRV_AP_CFLAGS += $(DRV_CFLAGS) DRV_BOTH_LIBS := $(DRV_LIBS) $(DRV_WPA_LIBS) $(DRV_AP_LIBS) DRV_WPA_LIBS += $(DRV_LIBS) DRV_AP_LIBS += $(DRV_LIBS) DRV_BOTH_OBJS := $(DRV_OBJS) $(DRV_WPA_OBJS) $(DRV_AP_OBJS) DRV_WPA_OBJS += $(DRV_OBJS) DRV_AP_OBJS += $(DRV_OBJS) DRV_BOTH_LDFLAGS := $(DRV_LDFLAGS) $(DRV_WPA_LDFLAGS) $(DRV_AP_LDFLAGS) DRV_WPA_LDFLAGS += $(DRV_LDFLAGS) DRV_AP_LDFLAGS += $(DRV_LDFLAGS) wpa-2.1/src/drivers/drivers.mk000066400000000000000000000076131227414666700164450ustar00rootroot00000000000000##### CLEAR VARS DRV_CFLAGS = DRV_WPA_CFLAGS = DRV_AP_CFLAGS = DRV_OBJS = DRV_WPA_OBJS = DRV_AP_OBJS = DRV_LIBS = DRV_WPA_LIBS = DRV_AP_LIBS = ##### COMMON DRIVERS ifdef CONFIG_DRIVER_WIRED DRV_CFLAGS += -DCONFIG_DRIVER_WIRED DRV_OBJS += src/drivers/driver_wired.c endif ifdef CONFIG_DRIVER_NL80211 DRV_CFLAGS += -DCONFIG_DRIVER_NL80211 DRV_OBJS += src/drivers/driver_nl80211.c DRV_OBJS += src/utils/radiotap.c NEED_SME=y NEED_AP_MLME=y NEED_NETLINK=y NEED_LINUX_IOCTL=y NEED_RFKILL=y ifdef CONFIG_LIBNL32 DRV_LIBS += -lnl-3 DRV_LIBS += -lnl-genl-3 DRV_CFLAGS += -DCONFIG_LIBNL20 -I/usr/include/libnl3 else ifdef CONFIG_LIBNL_TINY DRV_LIBS += -lnl-tiny else DRV_LIBS += -lnl endif ifdef CONFIG_LIBNL20 DRV_LIBS += -lnl-genl DRV_CFLAGS += -DCONFIG_LIBNL20 endif endif endif ifdef CONFIG_DRIVER_BSD ifndef CONFIG_L2_PACKET CONFIG_L2_PACKET=freebsd endif DRV_CFLAGS += -DCONFIG_DRIVER_BSD DRV_OBJS += src/drivers/driver_bsd.c CONFIG_L2_FREEBSD=y CONFIG_DNET_PCAP=y endif ifdef CONFIG_DRIVER_OPENBSD ifndef CONFIG_L2_PACKET CONFIG_L2_PACKET=freebsd endif DRV_CFLAGS += -DCONFIG_DRIVER_OPENBSD DRV_OBJS += src/drivers/driver_openbsd.c endif ifdef CONFIG_DRIVER_TEST DRV_CFLAGS += -DCONFIG_DRIVER_TEST DRV_OBJS += src/drivers/driver_test.c NEED_AP_MLME=y endif ifdef CONFIG_DRIVER_NONE DRV_CFLAGS += -DCONFIG_DRIVER_NONE DRV_OBJS += src/drivers/driver_none.c endif ##### PURE AP DRIVERS ifdef CONFIG_DRIVER_HOSTAP DRV_AP_CFLAGS += -DCONFIG_DRIVER_HOSTAP DRV_AP_OBJS += src/drivers/driver_hostap.c CONFIG_WIRELESS_EXTENSION=y NEED_AP_MLME=y NEED_NETLINK=y NEED_LINUX_IOCTL=y endif ifdef CONFIG_DRIVER_MADWIFI DRV_AP_CFLAGS += -DCONFIG_DRIVER_MADWIFI DRV_AP_OBJS += src/drivers/driver_madwifi.c CONFIG_WIRELESS_EXTENSION=y CONFIG_L2_PACKET=linux NEED_NETLINK=y NEED_LINUX_IOCTL=y endif ifdef CONFIG_DRIVER_ATHEROS DRV_AP_CFLAGS += -DCONFIG_DRIVER_ATHEROS DRV_AP_OBJS += src/drivers/driver_atheros.c CONFIG_L2_PACKET=linux NEED_NETLINK=y NEED_LINUX_IOCTL=y endif ##### PURE CLIENT DRIVERS ifdef CONFIG_DRIVER_WEXT DRV_WPA_CFLAGS += -DCONFIG_DRIVER_WEXT CONFIG_WIRELESS_EXTENSION=y NEED_NETLINK=y NEED_LINUX_IOCTL=y NEED_RFKILL=y endif ifdef CONFIG_DRIVER_NDIS DRV_WPA_CFLAGS += -DCONFIG_DRIVER_NDIS DRV_WPA_OBJS += src/drivers/driver_ndis.c ifdef CONFIG_NDIS_EVENTS_INTEGRATED DRV_WPA_OBJS += src/drivers/driver_ndis_.c endif ifndef CONFIG_L2_PACKET CONFIG_L2_PACKET=pcap endif CONFIG_WINPCAP=y ifdef CONFIG_USE_NDISUIO DRV_WPA_CFLAGS += -DCONFIG_USE_NDISUIO endif endif ifdef CONFIG_DRIVER_ROBOSWITCH DRV_WPA_CFLAGS += -DCONFIG_DRIVER_ROBOSWITCH DRV_WPA_OBJS += src/drivers/driver_roboswitch.c endif ifdef CONFIG_WIRELESS_EXTENSION DRV_WPA_CFLAGS += -DCONFIG_WIRELESS_EXTENSION DRV_WPA_OBJS += src/drivers/driver_wext.c NEED_RFKILL=y endif ifdef NEED_NETLINK DRV_OBJS += src/drivers/netlink.c endif ifdef NEED_LINUX_IOCTL DRV_OBJS += src/drivers/linux_ioctl.c endif ifdef NEED_RFKILL DRV_OBJS += src/drivers/rfkill.c endif ifdef CONFIG_DRIVER_CUSTOM DRV_CFLAGS += -DCONFIG_DRIVER_CUSTOM endif ifdef CONFIG_VLAN_NETLINK ifdef CONFIG_FULL_DYNAMIC_VLAN ifdef CONFIG_LIBNL32 DRV_LIBS += -lnl-3 DRV_LIBS += -lnl-genl-3 DRV_LIBS += -lnl-route-3 DRV_CFLAGS += -DCONFIG_LIBNL20 else ifdef CONFIG_LIBNL_TINY DRV_LIBS += -lnl-tiny else DRV_LIBS += -lnl endif ifdef CONFIG_LIBNL20 DRV_LIBS += -lnl-genl DRV_LIBS += -lnl-route DRV_CFLAGS += -DCONFIG_LIBNL20 endif endif endif endif ##### COMMON VARS DRV_BOTH_CFLAGS := $(DRV_CFLAGS) $(DRV_WPA_CFLAGS) $(DRV_AP_CFLAGS) DRV_WPA_CFLAGS += $(DRV_CFLAGS) DRV_AP_CFLAGS += $(DRV_CFLAGS) DRV_BOTH_LIBS := $(DRV_LIBS) $(DRV_WPA_LIBS) $(DRV_AP_LIBS) DRV_WPA_LIBS += $(DRV_LIBS) DRV_AP_LIBS += $(DRV_LIBS) DRV_BOTH_OBJS := $(DRV_OBJS) $(DRV_WPA_OBJS) $(DRV_AP_OBJS) DRV_WPA_OBJS += $(DRV_OBJS) DRV_AP_OBJS += $(DRV_OBJS) DRV_BOTH_LDFLAGS := $(DRV_LDFLAGS) $(DRV_WPA_LDFLAGS) $(DRV_AP_LDFLAGS) DRV_WPA_LDFLAGS += $(DRV_LDFLAGS) DRV_AP_LDFLAGS += $(DRV_LDFLAGS) wpa-2.1/src/drivers/linux_ioctl.c000066400000000000000000000110201227414666700171160ustar00rootroot00000000000000/* * Linux ioctl helper functions for driver wrappers * Copyright (c) 2002-2010, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "utils/includes.h" #include #include #include #include "utils/common.h" #include "linux_ioctl.h" int linux_set_iface_flags(int sock, const char *ifname, int dev_up) { struct ifreq ifr; int ret; if (sock < 0) return -1; os_memset(&ifr, 0, sizeof(ifr)); os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); if (ioctl(sock, SIOCGIFFLAGS, &ifr) != 0) { ret = errno ? -errno : -999; wpa_printf(MSG_ERROR, "Could not read interface %s flags: %s", ifname, strerror(errno)); return ret; } if (dev_up) { if (ifr.ifr_flags & IFF_UP) return 0; ifr.ifr_flags |= IFF_UP; } else { if (!(ifr.ifr_flags & IFF_UP)) return 0; ifr.ifr_flags &= ~IFF_UP; } if (ioctl(sock, SIOCSIFFLAGS, &ifr) != 0) { ret = errno ? -errno : -999; wpa_printf(MSG_ERROR, "Could not set interface %s flags (%s): " "%s", ifname, dev_up ? "UP" : "DOWN", strerror(errno)); return ret; } return 0; } int linux_iface_up(int sock, const char *ifname) { struct ifreq ifr; int ret; if (sock < 0) return -1; os_memset(&ifr, 0, sizeof(ifr)); os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); if (ioctl(sock, SIOCGIFFLAGS, &ifr) != 0) { ret = errno ? -errno : -999; wpa_printf(MSG_ERROR, "Could not read interface %s flags: %s", ifname, strerror(errno)); return ret; } return !!(ifr.ifr_flags & IFF_UP); } int linux_get_ifhwaddr(int sock, const char *ifname, u8 *addr) { struct ifreq ifr; os_memset(&ifr, 0, sizeof(ifr)); os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); if (ioctl(sock, SIOCGIFHWADDR, &ifr)) { wpa_printf(MSG_ERROR, "Could not get interface %s hwaddr: %s", ifname, strerror(errno)); return -1; } if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) { wpa_printf(MSG_ERROR, "%s: Invalid HW-addr family 0x%04x", ifname, ifr.ifr_hwaddr.sa_family); return -1; } os_memcpy(addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN); return 0; } int linux_set_ifhwaddr(int sock, const char *ifname, const u8 *addr) { struct ifreq ifr; os_memset(&ifr, 0, sizeof(ifr)); os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); os_memcpy(ifr.ifr_hwaddr.sa_data, addr, ETH_ALEN); ifr.ifr_hwaddr.sa_family = ARPHRD_ETHER; if (ioctl(sock, SIOCSIFHWADDR, &ifr)) { wpa_printf(MSG_DEBUG, "Could not set interface %s hwaddr: %s", ifname, strerror(errno)); return -1; } return 0; } #ifndef SIOCBRADDBR #define SIOCBRADDBR 0x89a0 #endif #ifndef SIOCBRDELBR #define SIOCBRDELBR 0x89a1 #endif #ifndef SIOCBRADDIF #define SIOCBRADDIF 0x89a2 #endif #ifndef SIOCBRDELIF #define SIOCBRDELIF 0x89a3 #endif int linux_br_add(int sock, const char *brname) { if (ioctl(sock, SIOCBRADDBR, brname) < 0) { wpa_printf(MSG_DEBUG, "Could not add bridge %s: %s", brname, strerror(errno)); return -1; } return 0; } int linux_br_del(int sock, const char *brname) { if (ioctl(sock, SIOCBRDELBR, brname) < 0) { wpa_printf(MSG_DEBUG, "Could not remove bridge %s: %s", brname, strerror(errno)); return -1; } return 0; } int linux_br_add_if(int sock, const char *brname, const char *ifname) { struct ifreq ifr; int ifindex; ifindex = if_nametoindex(ifname); if (ifindex == 0) return -1; os_memset(&ifr, 0, sizeof(ifr)); os_strlcpy(ifr.ifr_name, brname, IFNAMSIZ); ifr.ifr_ifindex = ifindex; if (ioctl(sock, SIOCBRADDIF, &ifr) < 0) { wpa_printf(MSG_DEBUG, "Could not add interface %s into bridge " "%s: %s", ifname, brname, strerror(errno)); return -1; } return 0; } int linux_br_del_if(int sock, const char *brname, const char *ifname) { struct ifreq ifr; int ifindex; ifindex = if_nametoindex(ifname); if (ifindex == 0) return -1; os_memset(&ifr, 0, sizeof(ifr)); os_strlcpy(ifr.ifr_name, brname, IFNAMSIZ); ifr.ifr_ifindex = ifindex; if (ioctl(sock, SIOCBRDELIF, &ifr) < 0) { wpa_printf(MSG_DEBUG, "Could not remove interface %s from " "bridge %s: %s", ifname, brname, strerror(errno)); return -1; } return 0; } int linux_br_get(char *brname, const char *ifname) { char path[128], brlink[128], *pos; ssize_t res; os_snprintf(path, sizeof(path), "/sys/class/net/%s/brport/bridge", ifname); res = readlink(path, brlink, sizeof(brlink)); if (res < 0 || (size_t) res >= sizeof(brlink)) return -1; brlink[res] = '\0'; pos = os_strrchr(brlink, '/'); if (pos == NULL) return -1; pos++; os_strlcpy(brname, pos, IFNAMSIZ); return 0; } wpa-2.1/src/drivers/linux_ioctl.h000066400000000000000000000015031227414666700171300ustar00rootroot00000000000000/* * Linux ioctl helper functions for driver wrappers * Copyright (c) 2002-2010, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef LINUX_IOCTL_H #define LINUX_IOCTL_H int linux_set_iface_flags(int sock, const char *ifname, int dev_up); int linux_iface_up(int sock, const char *ifname); int linux_get_ifhwaddr(int sock, const char *ifname, u8 *addr); int linux_set_ifhwaddr(int sock, const char *ifname, const u8 *addr); int linux_br_add(int sock, const char *brname); int linux_br_del(int sock, const char *brname); int linux_br_add_if(int sock, const char *brname, const char *ifname); int linux_br_del_if(int sock, const char *brname, const char *ifname); int linux_br_get(char *brname, const char *ifname); #endif /* LINUX_IOCTL_H */ wpa-2.1/src/drivers/linux_wext.h000066400000000000000000000016001227414666700170030ustar00rootroot00000000000000/* * Driver interaction with generic Linux Wireless Extensions * Copyright (c) 2003-2011, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef LINUX_WEXT_H #define LINUX_WEXT_H #ifndef ANDROID /* * Avoid including other kernel header to avoid conflicts with C library * headers. */ #define _LINUX_TYPES_H #define _LINUX_SOCKET_H #define _LINUX_IF_H #include #include typedef __uint32_t __u32; typedef __int32_t __s32; typedef __uint16_t __u16; typedef __int16_t __s16; typedef __uint8_t __u8; #ifndef __user #define __user #endif /* __user */ #endif /* ANDROID */ #include #ifndef IW_ENCODE_ALG_PMK #define IW_ENCODE_ALG_PMK 4 #endif #ifndef IW_ENC_CAPA_4WAY_HANDSHAKE #define IW_ENC_CAPA_4WAY_HANDSHAKE 0x00000010 #endif #endif /* LINUX_WEXT_H */ wpa-2.1/src/drivers/ndis_events.c000066400000000000000000000523231227414666700171210ustar00rootroot00000000000000/* * ndis_events - Receive NdisMIndicateStatus() events using WMI * Copyright (c) 2004-2006, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #define _WIN32_WINNT 0x0400 #include "includes.h" #ifndef COBJMACROS #define COBJMACROS #endif /* COBJMACROS */ #include #include "common.h" static int wmi_refcnt = 0; static int wmi_first = 1; struct ndis_events_data { IWbemObjectSink sink; IWbemObjectSinkVtbl sink_vtbl; IWbemServices *pSvc; IWbemLocator *pLoc; HANDLE read_pipe, write_pipe, event_avail; UINT ref; int terminating; char *ifname; /* {GUID..} */ WCHAR *adapter_desc; }; #define BstrAlloc(x) (x) ? SysAllocString(x) : NULL #define BstrFree(x) if (x) SysFreeString(x) /* WBEM / WMI wrapper functions, to perform in-place conversion of WCHARs to * BSTRs */ HRESULT STDMETHODCALLTYPE call_IWbemServices_ExecQuery( IWbemServices *pSvc, LPCWSTR strQueryLanguage, LPCWSTR strQuery, long lFlags, IWbemContext *pCtx, IEnumWbemClassObject **ppEnum) { BSTR bsQueryLanguage, bsQuery; HRESULT hr; bsQueryLanguage = BstrAlloc(strQueryLanguage); bsQuery = BstrAlloc(strQuery); hr = IWbemServices_ExecQuery(pSvc, bsQueryLanguage, bsQuery, lFlags, pCtx, ppEnum); BstrFree(bsQueryLanguage); BstrFree(bsQuery); return hr; } HRESULT STDMETHODCALLTYPE call_IWbemServices_ExecNotificationQueryAsync( IWbemServices *pSvc, LPCWSTR strQueryLanguage, LPCWSTR strQuery, long lFlags, IWbemContext *pCtx, IWbemObjectSink *pResponseHandler) { BSTR bsQueryLanguage, bsQuery; HRESULT hr; bsQueryLanguage = BstrAlloc(strQueryLanguage); bsQuery = BstrAlloc(strQuery); hr = IWbemServices_ExecNotificationQueryAsync(pSvc, bsQueryLanguage, bsQuery, lFlags, pCtx, pResponseHandler); BstrFree(bsQueryLanguage); BstrFree(bsQuery); return hr; } HRESULT STDMETHODCALLTYPE call_IWbemLocator_ConnectServer( IWbemLocator *pLoc, LPCWSTR strNetworkResource, LPCWSTR strUser, LPCWSTR strPassword, LPCWSTR strLocale, long lSecurityFlags, LPCWSTR strAuthority, IWbemContext *pCtx, IWbemServices **ppNamespace) { BSTR bsNetworkResource, bsUser, bsPassword, bsLocale, bsAuthority; HRESULT hr; bsNetworkResource = BstrAlloc(strNetworkResource); bsUser = BstrAlloc(strUser); bsPassword = BstrAlloc(strPassword); bsLocale = BstrAlloc(strLocale); bsAuthority = BstrAlloc(strAuthority); hr = IWbemLocator_ConnectServer(pLoc, bsNetworkResource, bsUser, bsPassword, bsLocale, lSecurityFlags, bsAuthority, pCtx, ppNamespace); BstrFree(bsNetworkResource); BstrFree(bsUser); BstrFree(bsPassword); BstrFree(bsLocale); BstrFree(bsAuthority); return hr; } enum event_types { EVENT_CONNECT, EVENT_DISCONNECT, EVENT_MEDIA_SPECIFIC, EVENT_ADAPTER_ARRIVAL, EVENT_ADAPTER_REMOVAL }; static int ndis_events_get_adapter(struct ndis_events_data *events, const char *ifname, const char *desc); static int ndis_events_constructor(struct ndis_events_data *events) { events->ref = 1; if (!CreatePipe(&events->read_pipe, &events->write_pipe, NULL, 512)) { wpa_printf(MSG_ERROR, "CreatePipe() failed: %d", (int) GetLastError()); return -1; } events->event_avail = CreateEvent(NULL, TRUE, FALSE, NULL); if (events->event_avail == NULL) { wpa_printf(MSG_ERROR, "CreateEvent() failed: %d", (int) GetLastError()); CloseHandle(events->read_pipe); CloseHandle(events->write_pipe); return -1; } return 0; } static void ndis_events_destructor(struct ndis_events_data *events) { CloseHandle(events->read_pipe); CloseHandle(events->write_pipe); CloseHandle(events->event_avail); IWbemServices_Release(events->pSvc); IWbemLocator_Release(events->pLoc); if (--wmi_refcnt == 0) CoUninitialize(); } static HRESULT STDMETHODCALLTYPE ndis_events_query_interface(IWbemObjectSink *this, REFIID riid, void **obj) { *obj = NULL; if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IWbemObjectSink)) { *obj = this; IWbemObjectSink_AddRef(this); return NOERROR; } return E_NOINTERFACE; } static ULONG STDMETHODCALLTYPE ndis_events_add_ref(IWbemObjectSink *this) { struct ndis_events_data *events = (struct ndis_events_data *) this; return ++events->ref; } static ULONG STDMETHODCALLTYPE ndis_events_release(IWbemObjectSink *this) { struct ndis_events_data *events = (struct ndis_events_data *) this; if (--events->ref != 0) return events->ref; ndis_events_destructor(events); wpa_printf(MSG_DEBUG, "ndis_events: terminated"); os_free(events->adapter_desc); os_free(events->ifname); os_free(events); return 0; } static int ndis_events_send_event(struct ndis_events_data *events, enum event_types type, char *data, size_t data_len) { char buf[512], *pos, *end; int _type; DWORD written; end = buf + sizeof(buf); _type = (int) type; os_memcpy(buf, &_type, sizeof(_type)); pos = buf + sizeof(_type); if (data) { if (2 + data_len > (size_t) (end - pos)) { wpa_printf(MSG_DEBUG, "Not enough room for send_event " "data (%d)", data_len); return -1; } *pos++ = data_len >> 8; *pos++ = data_len & 0xff; os_memcpy(pos, data, data_len); pos += data_len; } if (WriteFile(events->write_pipe, buf, pos - buf, &written, NULL)) { SetEvent(events->event_avail); return 0; } wpa_printf(MSG_INFO, "WriteFile() failed: %d", (int) GetLastError()); return -1; } static void ndis_events_media_connect(struct ndis_events_data *events) { wpa_printf(MSG_DEBUG, "MSNdis_StatusMediaConnect"); ndis_events_send_event(events, EVENT_CONNECT, NULL, 0); } static void ndis_events_media_disconnect(struct ndis_events_data *events) { wpa_printf(MSG_DEBUG, "MSNdis_StatusMediaDisconnect"); ndis_events_send_event(events, EVENT_DISCONNECT, NULL, 0); } static void ndis_events_media_specific(struct ndis_events_data *events, IWbemClassObject *pObj) { VARIANT vt; HRESULT hr; LONG lower, upper, k; UCHAR ch; char *data, *pos; size_t data_len; wpa_printf(MSG_DEBUG, "MSNdis_StatusMediaSpecificIndication"); /* This is the StatusBuffer from NdisMIndicateStatus() call */ hr = IWbemClassObject_Get(pObj, L"NdisStatusMediaSpecificIndication", 0, &vt, NULL, NULL); if (FAILED(hr)) { wpa_printf(MSG_DEBUG, "Could not get " "NdisStatusMediaSpecificIndication from " "the object?!"); return; } SafeArrayGetLBound(V_ARRAY(&vt), 1, &lower); SafeArrayGetUBound(V_ARRAY(&vt), 1, &upper); data_len = upper - lower + 1; data = os_malloc(data_len); if (data == NULL) { wpa_printf(MSG_DEBUG, "Failed to allocate buffer for event " "data"); VariantClear(&vt); return; } pos = data; for (k = lower; k <= upper; k++) { SafeArrayGetElement(V_ARRAY(&vt), &k, &ch); *pos++ = ch; } wpa_hexdump(MSG_DEBUG, "MediaSpecificEvent", (u8 *) data, data_len); VariantClear(&vt); ndis_events_send_event(events, EVENT_MEDIA_SPECIFIC, data, data_len); os_free(data); } static void ndis_events_adapter_arrival(struct ndis_events_data *events) { wpa_printf(MSG_DEBUG, "MSNdis_NotifyAdapterArrival"); ndis_events_send_event(events, EVENT_ADAPTER_ARRIVAL, NULL, 0); } static void ndis_events_adapter_removal(struct ndis_events_data *events) { wpa_printf(MSG_DEBUG, "MSNdis_NotifyAdapterRemoval"); ndis_events_send_event(events, EVENT_ADAPTER_REMOVAL, NULL, 0); } static HRESULT STDMETHODCALLTYPE ndis_events_indicate(IWbemObjectSink *this, long lObjectCount, IWbemClassObject __RPC_FAR *__RPC_FAR *ppObjArray) { struct ndis_events_data *events = (struct ndis_events_data *) this; long i; if (events->terminating) { wpa_printf(MSG_DEBUG, "ndis_events_indicate: Ignore " "indication - terminating"); return WBEM_NO_ERROR; } /* wpa_printf(MSG_DEBUG, "Notification received - %d object(s)", lObjectCount); */ for (i = 0; i < lObjectCount; i++) { IWbemClassObject *pObj = ppObjArray[i]; HRESULT hr; VARIANT vtClass, vt; hr = IWbemClassObject_Get(pObj, L"__CLASS", 0, &vtClass, NULL, NULL); if (FAILED(hr)) { wpa_printf(MSG_DEBUG, "Failed to get __CLASS from " "event."); break; } /* wpa_printf(MSG_DEBUG, "CLASS: '%S'", vtClass.bstrVal); */ hr = IWbemClassObject_Get(pObj, L"InstanceName", 0, &vt, NULL, NULL); if (FAILED(hr)) { wpa_printf(MSG_DEBUG, "Failed to get InstanceName " "from event."); VariantClear(&vtClass); break; } if (wcscmp(vtClass.bstrVal, L"MSNdis_NotifyAdapterArrival") == 0) { wpa_printf(MSG_DEBUG, "ndis_events_indicate: Try to " "update adapter description since it may " "have changed with new adapter instance"); ndis_events_get_adapter(events, events->ifname, NULL); } if (wcscmp(events->adapter_desc, vt.bstrVal) != 0) { wpa_printf(MSG_DEBUG, "ndis_events_indicate: Ignore " "indication for foreign adapter: " "InstanceName: '%S' __CLASS: '%S'", vt.bstrVal, vtClass.bstrVal); VariantClear(&vtClass); VariantClear(&vt); continue; } VariantClear(&vt); if (wcscmp(vtClass.bstrVal, L"MSNdis_StatusMediaSpecificIndication") == 0) { ndis_events_media_specific(events, pObj); } else if (wcscmp(vtClass.bstrVal, L"MSNdis_StatusMediaConnect") == 0) { ndis_events_media_connect(events); } else if (wcscmp(vtClass.bstrVal, L"MSNdis_StatusMediaDisconnect") == 0) { ndis_events_media_disconnect(events); } else if (wcscmp(vtClass.bstrVal, L"MSNdis_NotifyAdapterArrival") == 0) { ndis_events_adapter_arrival(events); } else if (wcscmp(vtClass.bstrVal, L"MSNdis_NotifyAdapterRemoval") == 0) { ndis_events_adapter_removal(events); } else { wpa_printf(MSG_DEBUG, "Unepected event - __CLASS: " "'%S'", vtClass.bstrVal); } VariantClear(&vtClass); } return WBEM_NO_ERROR; } static HRESULT STDMETHODCALLTYPE ndis_events_set_status(IWbemObjectSink *this, long lFlags, HRESULT hResult, BSTR strParam, IWbemClassObject __RPC_FAR *pObjParam) { return WBEM_NO_ERROR; } static int notification_query(IWbemObjectSink *pDestSink, IWbemServices *pSvc, const char *class_name) { HRESULT hr; WCHAR query[256]; _snwprintf(query, 256, L"SELECT * FROM %S", class_name); wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query); hr = call_IWbemServices_ExecNotificationQueryAsync( pSvc, L"WQL", query, 0, 0, pDestSink); if (FAILED(hr)) { wpa_printf(MSG_DEBUG, "ExecNotificationQueryAsync for %s " "failed with hresult of 0x%x", class_name, (int) hr); return -1; } return 0; } static int register_async_notification(IWbemObjectSink *pDestSink, IWbemServices *pSvc) { int i; const char *class_list[] = { "MSNdis_StatusMediaConnect", "MSNdis_StatusMediaDisconnect", "MSNdis_StatusMediaSpecificIndication", "MSNdis_NotifyAdapterArrival", "MSNdis_NotifyAdapterRemoval", NULL }; for (i = 0; class_list[i]; i++) { if (notification_query(pDestSink, pSvc, class_list[i]) < 0) return -1; } return 0; } void ndis_events_deinit(struct ndis_events_data *events) { events->terminating = 1; IWbemServices_CancelAsyncCall(events->pSvc, &events->sink); IWbemObjectSink_Release(&events->sink); /* * Rest of deinitialization is done in ndis_events_destructor() once * all reference count drops to zero. */ } static int ndis_events_use_desc(struct ndis_events_data *events, const char *desc) { char *tmp, *pos; size_t len; if (desc == NULL) { if (events->adapter_desc == NULL) return -1; /* Continue using old description */ return 0; } tmp = os_strdup(desc); if (tmp == NULL) return -1; pos = os_strstr(tmp, " (Microsoft's Packet Scheduler)"); if (pos) *pos = '\0'; len = os_strlen(tmp); events->adapter_desc = os_malloc((len + 1) * sizeof(WCHAR)); if (events->adapter_desc == NULL) { os_free(tmp); return -1; } _snwprintf(events->adapter_desc, len + 1, L"%S", tmp); os_free(tmp); return 0; } static int ndis_events_get_adapter(struct ndis_events_data *events, const char *ifname, const char *desc) { HRESULT hr; IWbemServices *pSvc; #define MAX_QUERY_LEN 256 WCHAR query[MAX_QUERY_LEN]; IEnumWbemClassObject *pEnumerator; IWbemClassObject *pObj; ULONG uReturned; VARIANT vt; int len, pos; /* * Try to get adapter descriptor through WMI CIMv2 Win32_NetworkAdapter * to have better probability of matching with InstanceName from * MSNdis events. If this fails, use the provided description. */ os_free(events->adapter_desc); events->adapter_desc = NULL; hr = call_IWbemLocator_ConnectServer( events->pLoc, L"ROOT\\CIMV2", NULL, NULL, 0, 0, 0, 0, &pSvc); if (FAILED(hr)) { wpa_printf(MSG_ERROR, "ndis_events: Could not connect to WMI " "server (ROOT\\CIMV2) - error 0x%x", (int) hr); return ndis_events_use_desc(events, desc); } wpa_printf(MSG_DEBUG, "ndis_events: Connected to ROOT\\CIMV2."); _snwprintf(query, MAX_QUERY_LEN, L"SELECT Index FROM Win32_NetworkAdapterConfiguration " L"WHERE SettingID='%S'", ifname); wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query); hr = call_IWbemServices_ExecQuery( pSvc, L"WQL", query, WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &pEnumerator); if (!SUCCEEDED(hr)) { wpa_printf(MSG_DEBUG, "ndis_events: Failed to query interface " "GUID from Win32_NetworkAdapterConfiguration: " "0x%x", (int) hr); IWbemServices_Release(pSvc); return ndis_events_use_desc(events, desc); } uReturned = 0; hr = IEnumWbemClassObject_Next(pEnumerator, WBEM_INFINITE, 1, &pObj, &uReturned); if (!SUCCEEDED(hr) || uReturned == 0) { wpa_printf(MSG_DEBUG, "ndis_events: Failed to find interface " "GUID from Win32_NetworkAdapterConfiguration: " "0x%x", (int) hr); IEnumWbemClassObject_Release(pEnumerator); IWbemServices_Release(pSvc); return ndis_events_use_desc(events, desc); } IEnumWbemClassObject_Release(pEnumerator); VariantInit(&vt); hr = IWbemClassObject_Get(pObj, L"Index", 0, &vt, NULL, NULL); if (!SUCCEEDED(hr)) { wpa_printf(MSG_DEBUG, "ndis_events: Failed to get Index from " "Win32_NetworkAdapterConfiguration: 0x%x", (int) hr); IWbemServices_Release(pSvc); return ndis_events_use_desc(events, desc); } _snwprintf(query, MAX_QUERY_LEN, L"SELECT Name,PNPDeviceID FROM Win32_NetworkAdapter WHERE " L"Index=%d", vt.uintVal); wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query); VariantClear(&vt); IWbemClassObject_Release(pObj); hr = call_IWbemServices_ExecQuery( pSvc, L"WQL", query, WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &pEnumerator); if (!SUCCEEDED(hr)) { wpa_printf(MSG_DEBUG, "ndis_events: Failed to query interface " "from Win32_NetworkAdapter: 0x%x", (int) hr); IWbemServices_Release(pSvc); return ndis_events_use_desc(events, desc); } uReturned = 0; hr = IEnumWbemClassObject_Next(pEnumerator, WBEM_INFINITE, 1, &pObj, &uReturned); if (!SUCCEEDED(hr) || uReturned == 0) { wpa_printf(MSG_DEBUG, "ndis_events: Failed to find interface " "from Win32_NetworkAdapter: 0x%x", (int) hr); IEnumWbemClassObject_Release(pEnumerator); IWbemServices_Release(pSvc); return ndis_events_use_desc(events, desc); } IEnumWbemClassObject_Release(pEnumerator); hr = IWbemClassObject_Get(pObj, L"Name", 0, &vt, NULL, NULL); if (!SUCCEEDED(hr)) { wpa_printf(MSG_DEBUG, "ndis_events: Failed to get Name from " "Win32_NetworkAdapter: 0x%x", (int) hr); IWbemClassObject_Release(pObj); IWbemServices_Release(pSvc); return ndis_events_use_desc(events, desc); } wpa_printf(MSG_DEBUG, "ndis_events: Win32_NetworkAdapter::Name='%S'", vt.bstrVal); events->adapter_desc = _wcsdup(vt.bstrVal); VariantClear(&vt); /* * Try to get even better candidate for matching with InstanceName * from Win32_PnPEntity. This is needed at least for some USB cards * that can change the InstanceName whenever being unplugged and * plugged again. */ hr = IWbemClassObject_Get(pObj, L"PNPDeviceID", 0, &vt, NULL, NULL); if (!SUCCEEDED(hr)) { wpa_printf(MSG_DEBUG, "ndis_events: Failed to get PNPDeviceID " "from Win32_NetworkAdapter: 0x%x", (int) hr); IWbemClassObject_Release(pObj); IWbemServices_Release(pSvc); if (events->adapter_desc == NULL) return ndis_events_use_desc(events, desc); return 0; /* use Win32_NetworkAdapter::Name */ } wpa_printf(MSG_DEBUG, "ndis_events: Win32_NetworkAdapter::PNPDeviceID=" "'%S'", vt.bstrVal); len = _snwprintf(query, MAX_QUERY_LEN, L"SELECT Name FROM Win32_PnPEntity WHERE DeviceID='"); if (len < 0 || len >= MAX_QUERY_LEN - 1) { VariantClear(&vt); IWbemClassObject_Release(pObj); IWbemServices_Release(pSvc); if (events->adapter_desc == NULL) return ndis_events_use_desc(events, desc); return 0; /* use Win32_NetworkAdapter::Name */ } /* Escape \ as \\ */ for (pos = 0; vt.bstrVal[pos] && len < MAX_QUERY_LEN - 2; pos++) { if (vt.bstrVal[pos] == '\\') { if (len >= MAX_QUERY_LEN - 3) break; query[len++] = '\\'; } query[len++] = vt.bstrVal[pos]; } query[len++] = L'\''; query[len] = L'\0'; VariantClear(&vt); IWbemClassObject_Release(pObj); wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query); hr = call_IWbemServices_ExecQuery( pSvc, L"WQL", query, WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &pEnumerator); if (!SUCCEEDED(hr)) { wpa_printf(MSG_DEBUG, "ndis_events: Failed to query interface " "Name from Win32_PnPEntity: 0x%x", (int) hr); IWbemServices_Release(pSvc); if (events->adapter_desc == NULL) return ndis_events_use_desc(events, desc); return 0; /* use Win32_NetworkAdapter::Name */ } uReturned = 0; hr = IEnumWbemClassObject_Next(pEnumerator, WBEM_INFINITE, 1, &pObj, &uReturned); if (!SUCCEEDED(hr) || uReturned == 0) { wpa_printf(MSG_DEBUG, "ndis_events: Failed to find interface " "from Win32_PnPEntity: 0x%x", (int) hr); IEnumWbemClassObject_Release(pEnumerator); IWbemServices_Release(pSvc); if (events->adapter_desc == NULL) return ndis_events_use_desc(events, desc); return 0; /* use Win32_NetworkAdapter::Name */ } IEnumWbemClassObject_Release(pEnumerator); hr = IWbemClassObject_Get(pObj, L"Name", 0, &vt, NULL, NULL); if (!SUCCEEDED(hr)) { wpa_printf(MSG_DEBUG, "ndis_events: Failed to get Name from " "Win32_PnPEntity: 0x%x", (int) hr); IWbemClassObject_Release(pObj); IWbemServices_Release(pSvc); if (events->adapter_desc == NULL) return ndis_events_use_desc(events, desc); return 0; /* use Win32_NetworkAdapter::Name */ } wpa_printf(MSG_DEBUG, "ndis_events: Win32_PnPEntity::Name='%S'", vt.bstrVal); os_free(events->adapter_desc); events->adapter_desc = _wcsdup(vt.bstrVal); VariantClear(&vt); IWbemClassObject_Release(pObj); IWbemServices_Release(pSvc); if (events->adapter_desc == NULL) return ndis_events_use_desc(events, desc); return 0; } struct ndis_events_data * ndis_events_init(HANDLE *read_pipe, HANDLE *event_avail, const char *ifname, const char *desc) { HRESULT hr; IWbemObjectSink *pSink; struct ndis_events_data *events; events = os_zalloc(sizeof(*events)); if (events == NULL) { wpa_printf(MSG_ERROR, "Could not allocate sink for events."); return NULL; } events->ifname = os_strdup(ifname); if (events->ifname == NULL) { os_free(events); return NULL; } if (wmi_refcnt++ == 0) { hr = CoInitializeEx(0, COINIT_MULTITHREADED); if (FAILED(hr)) { wpa_printf(MSG_ERROR, "CoInitializeEx() failed - " "returned 0x%x", (int) hr); os_free(events); return NULL; } } if (wmi_first) { /* CoInitializeSecurity() must be called once and only once * per process, so let's use wmi_first flag to protect against * multiple calls. */ wmi_first = 0; hr = CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_PKT_PRIVACY, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_SECURE_REFS, NULL); if (FAILED(hr)) { wpa_printf(MSG_ERROR, "CoInitializeSecurity() failed " "- returned 0x%x", (int) hr); os_free(events); return NULL; } } hr = CoCreateInstance(&CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, &IID_IWbemLocator, (LPVOID *) (void *) &events->pLoc); if (FAILED(hr)) { wpa_printf(MSG_ERROR, "CoCreateInstance() failed - returned " "0x%x", (int) hr); CoUninitialize(); os_free(events); return NULL; } if (ndis_events_get_adapter(events, ifname, desc) < 0) { CoUninitialize(); os_free(events); return NULL; } wpa_printf(MSG_DEBUG, "ndis_events: use adapter descriptor '%S'", events->adapter_desc); hr = call_IWbemLocator_ConnectServer( events->pLoc, L"ROOT\\WMI", NULL, NULL, 0, 0, 0, 0, &events->pSvc); if (FAILED(hr)) { wpa_printf(MSG_ERROR, "Could not connect to server - error " "0x%x", (int) hr); CoUninitialize(); os_free(events->adapter_desc); os_free(events); return NULL; } wpa_printf(MSG_DEBUG, "Connected to ROOT\\WMI."); ndis_events_constructor(events); pSink = &events->sink; pSink->lpVtbl = &events->sink_vtbl; events->sink_vtbl.QueryInterface = ndis_events_query_interface; events->sink_vtbl.AddRef = ndis_events_add_ref; events->sink_vtbl.Release = ndis_events_release; events->sink_vtbl.Indicate = ndis_events_indicate; events->sink_vtbl.SetStatus = ndis_events_set_status; if (register_async_notification(pSink, events->pSvc) < 0) { wpa_printf(MSG_DEBUG, "Failed to register async " "notifications"); ndis_events_destructor(events); os_free(events->adapter_desc); os_free(events); return NULL; } *read_pipe = events->read_pipe; *event_avail = events->event_avail; return events; } wpa-2.1/src/drivers/netlink.c000066400000000000000000000123221227414666700162370ustar00rootroot00000000000000/* * Netlink helper functions for driver wrappers * Copyright (c) 2002-2014, 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 "eloop.h" #include "priv_netlink.h" #include "netlink.h" struct netlink_data { struct netlink_config *cfg; int sock; }; static void netlink_receive_link(struct netlink_data *netlink, void (*cb)(void *ctx, struct ifinfomsg *ifi, u8 *buf, size_t len), struct nlmsghdr *h) { if (cb == NULL || NLMSG_PAYLOAD(h, 0) < sizeof(struct ifinfomsg)) return; cb(netlink->cfg->ctx, NLMSG_DATA(h), (u8 *) NLMSG_DATA(h) + NLMSG_ALIGN(sizeof(struct ifinfomsg)), NLMSG_PAYLOAD(h, sizeof(struct ifinfomsg))); } static void netlink_receive(int sock, void *eloop_ctx, void *sock_ctx) { struct netlink_data *netlink = eloop_ctx; char buf[8192]; int left; struct sockaddr_nl from; socklen_t fromlen; struct nlmsghdr *h; int max_events = 10; try_again: fromlen = sizeof(from); left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT, (struct sockaddr *) &from, &fromlen); if (left < 0) { if (errno != EINTR && errno != EAGAIN) wpa_printf(MSG_INFO, "netlink: recvfrom failed: %s", strerror(errno)); return; } h = (struct nlmsghdr *) buf; while (NLMSG_OK(h, left)) { switch (h->nlmsg_type) { case RTM_NEWLINK: netlink_receive_link(netlink, netlink->cfg->newlink_cb, h); break; case RTM_DELLINK: netlink_receive_link(netlink, netlink->cfg->dellink_cb, h); break; } h = NLMSG_NEXT(h, left); } if (left > 0) { wpa_printf(MSG_DEBUG, "netlink: %d extra bytes in the end of " "netlink message", left); } if (--max_events > 0) { /* * Try to receive all events in one eloop call in order to * limit race condition on cases where AssocInfo event, Assoc * event, and EAPOL frames are received more or less at the * same time. We want to process the event messages first * before starting EAPOL processing. */ goto try_again; } } struct netlink_data * netlink_init(struct netlink_config *cfg) { struct netlink_data *netlink; struct sockaddr_nl local; netlink = os_zalloc(sizeof(*netlink)); if (netlink == NULL) return NULL; netlink->sock = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (netlink->sock < 0) { wpa_printf(MSG_ERROR, "netlink: Failed to open netlink " "socket: %s", strerror(errno)); netlink_deinit(netlink); return NULL; } os_memset(&local, 0, sizeof(local)); local.nl_family = AF_NETLINK; local.nl_groups = RTMGRP_LINK; if (bind(netlink->sock, (struct sockaddr *) &local, sizeof(local)) < 0) { wpa_printf(MSG_ERROR, "netlink: Failed to bind netlink " "socket: %s", strerror(errno)); netlink_deinit(netlink); return NULL; } eloop_register_read_sock(netlink->sock, netlink_receive, netlink, NULL); netlink->cfg = cfg; return netlink; } void netlink_deinit(struct netlink_data *netlink) { if (netlink == NULL) return; if (netlink->sock >= 0) { eloop_unregister_read_sock(netlink->sock); close(netlink->sock); } os_free(netlink->cfg); os_free(netlink); } static const char * linkmode_str(int mode) { switch (mode) { case -1: return "no change"; case 0: return "kernel-control"; case 1: return "userspace-control"; } return "?"; } static const char * operstate_str(int state) { switch (state) { case -1: return "no change"; case IF_OPER_DORMANT: return "IF_OPER_DORMANT"; case IF_OPER_UP: return "IF_OPER_UP"; } return "?"; } int netlink_send_oper_ifla(struct netlink_data *netlink, int ifindex, int linkmode, int operstate) { struct { struct nlmsghdr hdr; struct ifinfomsg ifinfo; char opts[16]; } req; struct rtattr *rta; static int nl_seq; ssize_t ret; os_memset(&req, 0, sizeof(req)); req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); req.hdr.nlmsg_type = RTM_SETLINK; req.hdr.nlmsg_flags = NLM_F_REQUEST; req.hdr.nlmsg_seq = ++nl_seq; req.hdr.nlmsg_pid = 0; req.ifinfo.ifi_family = AF_UNSPEC; req.ifinfo.ifi_type = 0; req.ifinfo.ifi_index = ifindex; req.ifinfo.ifi_flags = 0; req.ifinfo.ifi_change = 0; if (linkmode != -1) { rta = aliasing_hide_typecast( ((char *) &req + NLMSG_ALIGN(req.hdr.nlmsg_len)), struct rtattr); rta->rta_type = IFLA_LINKMODE; rta->rta_len = RTA_LENGTH(sizeof(char)); *((char *) RTA_DATA(rta)) = linkmode; req.hdr.nlmsg_len = NLMSG_ALIGN(req.hdr.nlmsg_len) + RTA_LENGTH(sizeof(char)); } if (operstate != -1) { rta = aliasing_hide_typecast( ((char *) &req + NLMSG_ALIGN(req.hdr.nlmsg_len)), struct rtattr); rta->rta_type = IFLA_OPERSTATE; rta->rta_len = RTA_LENGTH(sizeof(char)); *((char *) RTA_DATA(rta)) = operstate; req.hdr.nlmsg_len = NLMSG_ALIGN(req.hdr.nlmsg_len) + RTA_LENGTH(sizeof(char)); } wpa_printf(MSG_DEBUG, "netlink: Operstate: ifindex=%d linkmode=%d (%s), operstate=%d (%s)", ifindex, linkmode, linkmode_str(linkmode), operstate, operstate_str(operstate)); ret = send(netlink->sock, &req, req.hdr.nlmsg_len, 0); if (ret < 0) { wpa_printf(MSG_DEBUG, "netlink: Sending operstate IFLA " "failed: %s (assume operstate is not supported)", strerror(errno)); } return ret < 0 ? -1 : 0; } wpa-2.1/src/drivers/netlink.h000066400000000000000000000013451227414666700162470ustar00rootroot00000000000000/* * Netlink helper functions for driver wrappers * Copyright (c) 2002-2009, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef NETLINK_H #define NETLINK_H struct netlink_data; struct ifinfomsg; struct netlink_config { void *ctx; void (*newlink_cb)(void *ctx, struct ifinfomsg *ifi, u8 *buf, size_t len); void (*dellink_cb)(void *ctx, struct ifinfomsg *ifi, u8 *buf, size_t len); }; struct netlink_data * netlink_init(struct netlink_config *cfg); void netlink_deinit(struct netlink_data *netlink); int netlink_send_oper_ifla(struct netlink_data *netlink, int ifindex, int linkmode, int operstate); #endif /* NETLINK_H */ wpa-2.1/src/drivers/nl80211_copy.h000066400000000000000000005045521227414666700166520ustar00rootroot00000000000000#ifndef __LINUX_NL80211_H #define __LINUX_NL80211_H /* * 802.11 netlink interface public header * * Copyright 2006-2010 Johannes Berg * Copyright 2008 Michael Wu * Copyright 2008 Luis Carlos Cobo * Copyright 2008 Michael Buesch * Copyright 2008, 2009 Luis R. Rodriguez * Copyright 2008 Jouni Malinen * Copyright 2008 Colin McCabe * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * */ #include #define NL80211_GENL_NAME "nl80211" /** * DOC: Station handling * * Stations are added per interface, but a special case exists with VLAN * interfaces. When a station is bound to an AP interface, it may be moved * into a VLAN identified by a VLAN interface index (%NL80211_ATTR_STA_VLAN). * The station is still assumed to belong to the AP interface it was added * to. * * Station handling varies per interface type and depending on the driver's * capabilities. * * For drivers supporting TDLS with external setup (WIPHY_FLAG_SUPPORTS_TDLS * and WIPHY_FLAG_TDLS_EXTERNAL_SETUP), the station lifetime is as follows: * - a setup station entry is added, not yet authorized, without any rate * or capability information, this just exists to avoid race conditions * - when the TDLS setup is done, a single NL80211_CMD_SET_STATION is valid * to add rate and capability information to the station and at the same * time mark it authorized. * - %NL80211_TDLS_ENABLE_LINK is then used * - after this, the only valid operation is to remove it by tearing down * the TDLS link (%NL80211_TDLS_DISABLE_LINK) * * TODO: need more info for other interface types */ /** * DOC: Frame transmission/registration support * * Frame transmission and registration support exists to allow userspace * management entities such as wpa_supplicant react to management frames * that are not being handled by the kernel. This includes, for example, * certain classes of action frames that cannot be handled in the kernel * for various reasons. * * Frame registration is done on a per-interface basis and registrations * cannot be removed other than by closing the socket. It is possible to * specify a registration filter to register, for example, only for a * certain type of action frame. In particular with action frames, those * that userspace registers for will not be returned as unhandled by the * driver, so that the registered application has to take responsibility * for doing that. * * The type of frame that can be registered for is also dependent on the * driver and interface type. The frame types are advertised in wiphy * attributes so applications know what to expect. * * NOTE: When an interface changes type while registrations are active, * these registrations are ignored until the interface type is * changed again. This means that changing the interface type can * lead to a situation that couldn't otherwise be produced, but * any such registrations will be dormant in the sense that they * will not be serviced, i.e. they will not receive any frames. * * Frame transmission allows userspace to send for example the required * responses to action frames. It is subject to some sanity checking, * but many frames can be transmitted. When a frame was transmitted, its * status is indicated to the sending socket. * * For more technical details, see the corresponding command descriptions * below. */ /** * DOC: Virtual interface / concurrency capabilities * * Some devices are able to operate with virtual MACs, they can have * more than one virtual interface. The capability handling for this * is a bit complex though, as there may be a number of restrictions * on the types of concurrency that are supported. * * To start with, each device supports the interface types listed in * the %NL80211_ATTR_SUPPORTED_IFTYPES attribute, but by listing the * types there no concurrency is implied. * * Once concurrency is desired, more attributes must be observed: * To start with, since some interface types are purely managed in * software, like the AP-VLAN type in mac80211 for example, there's * an additional list of these, they can be added at any time and * are only restricted by some semantic restrictions (e.g. AP-VLAN * cannot be added without a corresponding AP interface). This list * is exported in the %NL80211_ATTR_SOFTWARE_IFTYPES attribute. * * Further, the list of supported combinations is exported. This is * in the %NL80211_ATTR_INTERFACE_COMBINATIONS attribute. Basically, * it exports a list of "groups", and at any point in time the * interfaces that are currently active must fall into any one of * the advertised groups. Within each group, there are restrictions * on the number of interfaces of different types that are supported * and also the number of different channels, along with potentially * some other restrictions. See &enum nl80211_if_combination_attrs. * * All together, these attributes define the concurrency of virtual * interfaces that a given device supports. */ /** * DOC: packet coalesce support * * In most cases, host that receives IPv4 and IPv6 multicast/broadcast * packets does not do anything with these packets. Therefore the * reception of these unwanted packets causes unnecessary processing * and power consumption. * * Packet coalesce feature helps to reduce number of received interrupts * to host by buffering these packets in firmware/hardware for some * predefined time. Received interrupt will be generated when one of the * following events occur. * a) Expiration of hardware timer whose expiration time is set to maximum * coalescing delay of matching coalesce rule. * b) Coalescing buffer in hardware reaches it's limit. * c) Packet doesn't match any of the configured coalesce rules. * * User needs to configure following parameters for creating a coalesce * rule. * a) Maximum coalescing delay * b) List of packet patterns which needs to be matched * c) Condition for coalescence. pattern 'match' or 'no match' * Multiple such rules can be created. */ /** * enum nl80211_commands - supported nl80211 commands * * @NL80211_CMD_UNSPEC: unspecified command to catch errors * * @NL80211_CMD_GET_WIPHY: request information about a wiphy or dump request * to get a list of all present wiphys. * @NL80211_CMD_SET_WIPHY: set wiphy parameters, needs %NL80211_ATTR_WIPHY or * %NL80211_ATTR_IFINDEX; can be used to set %NL80211_ATTR_WIPHY_NAME, * %NL80211_ATTR_WIPHY_TXQ_PARAMS, %NL80211_ATTR_WIPHY_FREQ (and the * attributes determining the channel width; this is used for setting * monitor mode channel), %NL80211_ATTR_WIPHY_RETRY_SHORT, * %NL80211_ATTR_WIPHY_RETRY_LONG, %NL80211_ATTR_WIPHY_FRAG_THRESHOLD, * and/or %NL80211_ATTR_WIPHY_RTS_THRESHOLD. * However, for setting the channel, see %NL80211_CMD_SET_CHANNEL * instead, the support here is for backward compatibility only. * @NL80211_CMD_NEW_WIPHY: Newly created wiphy, response to get request * or rename notification. Has attributes %NL80211_ATTR_WIPHY and * %NL80211_ATTR_WIPHY_NAME. * @NL80211_CMD_DEL_WIPHY: Wiphy deleted. Has attributes * %NL80211_ATTR_WIPHY and %NL80211_ATTR_WIPHY_NAME. * * @NL80211_CMD_GET_INTERFACE: Request an interface's configuration; * either a dump request on a %NL80211_ATTR_WIPHY or a specific get * on an %NL80211_ATTR_IFINDEX is supported. * @NL80211_CMD_SET_INTERFACE: Set type of a virtual interface, requires * %NL80211_ATTR_IFINDEX and %NL80211_ATTR_IFTYPE. * @NL80211_CMD_NEW_INTERFACE: Newly created virtual interface or response * to %NL80211_CMD_GET_INTERFACE. Has %NL80211_ATTR_IFINDEX, * %NL80211_ATTR_WIPHY and %NL80211_ATTR_IFTYPE attributes. Can also * be sent from userspace to request creation of a new virtual interface, * then requires attributes %NL80211_ATTR_WIPHY, %NL80211_ATTR_IFTYPE and * %NL80211_ATTR_IFNAME. * @NL80211_CMD_DEL_INTERFACE: Virtual interface was deleted, has attributes * %NL80211_ATTR_IFINDEX and %NL80211_ATTR_WIPHY. Can also be sent from * userspace to request deletion of a virtual interface, then requires * attribute %NL80211_ATTR_IFINDEX. * * @NL80211_CMD_GET_KEY: Get sequence counter information for a key specified * by %NL80211_ATTR_KEY_IDX and/or %NL80211_ATTR_MAC. * @NL80211_CMD_SET_KEY: Set key attributes %NL80211_ATTR_KEY_DEFAULT, * %NL80211_ATTR_KEY_DEFAULT_MGMT, or %NL80211_ATTR_KEY_THRESHOLD. * @NL80211_CMD_NEW_KEY: add a key with given %NL80211_ATTR_KEY_DATA, * %NL80211_ATTR_KEY_IDX, %NL80211_ATTR_MAC, %NL80211_ATTR_KEY_CIPHER, * and %NL80211_ATTR_KEY_SEQ attributes. * @NL80211_CMD_DEL_KEY: delete a key identified by %NL80211_ATTR_KEY_IDX * or %NL80211_ATTR_MAC. * * @NL80211_CMD_GET_BEACON: (not used) * @NL80211_CMD_SET_BEACON: change the beacon on an access point interface * using the %NL80211_ATTR_BEACON_HEAD and %NL80211_ATTR_BEACON_TAIL * attributes. For drivers that generate the beacon and probe responses * internally, the following attributes must be provided: %NL80211_ATTR_IE, * %NL80211_ATTR_IE_PROBE_RESP and %NL80211_ATTR_IE_ASSOC_RESP. * @NL80211_CMD_START_AP: Start AP operation on an AP interface, parameters * are like for %NL80211_CMD_SET_BEACON, and additionally parameters that * do not change are used, these include %NL80211_ATTR_BEACON_INTERVAL, * %NL80211_ATTR_DTIM_PERIOD, %NL80211_ATTR_SSID, * %NL80211_ATTR_HIDDEN_SSID, %NL80211_ATTR_CIPHERS_PAIRWISE, * %NL80211_ATTR_CIPHER_GROUP, %NL80211_ATTR_WPA_VERSIONS, * %NL80211_ATTR_AKM_SUITES, %NL80211_ATTR_PRIVACY, * %NL80211_ATTR_AUTH_TYPE, %NL80211_ATTR_INACTIVITY_TIMEOUT, * %NL80211_ATTR_ACL_POLICY and %NL80211_ATTR_MAC_ADDRS. * The channel to use can be set on the interface or be given using the * %NL80211_ATTR_WIPHY_FREQ and the attributes determining channel width. * @NL80211_CMD_NEW_BEACON: old alias for %NL80211_CMD_START_AP * @NL80211_CMD_STOP_AP: Stop AP operation on the given interface * @NL80211_CMD_DEL_BEACON: old alias for %NL80211_CMD_STOP_AP * * @NL80211_CMD_GET_STATION: Get station attributes for station identified by * %NL80211_ATTR_MAC on the interface identified by %NL80211_ATTR_IFINDEX. * @NL80211_CMD_SET_STATION: Set station attributes for station identified by * %NL80211_ATTR_MAC on the interface identified by %NL80211_ATTR_IFINDEX. * @NL80211_CMD_NEW_STATION: Add a station with given attributes to the * the interface identified by %NL80211_ATTR_IFINDEX. * @NL80211_CMD_DEL_STATION: Remove a station identified by %NL80211_ATTR_MAC * or, if no MAC address given, all stations, on the interface identified * by %NL80211_ATTR_IFINDEX. * * @NL80211_CMD_GET_MPATH: Get mesh path attributes for mesh path to * destination %NL80211_ATTR_MAC on the interface identified by * %NL80211_ATTR_IFINDEX. * @NL80211_CMD_SET_MPATH: Set mesh path attributes for mesh path to * destination %NL80211_ATTR_MAC on the interface identified by * %NL80211_ATTR_IFINDEX. * @NL80211_CMD_NEW_MPATH: Create a new mesh path for the destination given by * %NL80211_ATTR_MAC via %NL80211_ATTR_MPATH_NEXT_HOP. * @NL80211_CMD_DEL_MPATH: Delete a mesh path to the destination given by * %NL80211_ATTR_MAC. * @NL80211_CMD_NEW_PATH: Add a mesh path with given attributes to the * the interface identified by %NL80211_ATTR_IFINDEX. * @NL80211_CMD_DEL_PATH: Remove a mesh path identified by %NL80211_ATTR_MAC * or, if no MAC address given, all mesh paths, on the interface identified * by %NL80211_ATTR_IFINDEX. * @NL80211_CMD_SET_BSS: Set BSS attributes for BSS identified by * %NL80211_ATTR_IFINDEX. * * @NL80211_CMD_GET_REG: ask the wireless core to send us its currently set * regulatory domain. * @NL80211_CMD_SET_REG: Set current regulatory domain. CRDA sends this command * after being queried by the kernel. CRDA replies by sending a regulatory * domain structure which consists of %NL80211_ATTR_REG_ALPHA set to our * current alpha2 if it found a match. It also provides * NL80211_ATTR_REG_RULE_FLAGS, and a set of regulatory rules. Each * regulatory rule is a nested set of attributes given by * %NL80211_ATTR_REG_RULE_FREQ_[START|END] and * %NL80211_ATTR_FREQ_RANGE_MAX_BW with an attached power rule given by * %NL80211_ATTR_REG_RULE_POWER_MAX_ANT_GAIN and * %NL80211_ATTR_REG_RULE_POWER_MAX_EIRP. * @NL80211_CMD_REQ_SET_REG: ask the wireless core to set the regulatory domain * to the specified ISO/IEC 3166-1 alpha2 country code. The core will * store this as a valid request and then query userspace for it. * * @NL80211_CMD_GET_MESH_CONFIG: Get mesh networking properties for the * interface identified by %NL80211_ATTR_IFINDEX * * @NL80211_CMD_SET_MESH_CONFIG: Set mesh networking properties for the * interface identified by %NL80211_ATTR_IFINDEX * * @NL80211_CMD_SET_MGMT_EXTRA_IE: Set extra IEs for management frames. The * interface is identified with %NL80211_ATTR_IFINDEX and the management * frame subtype with %NL80211_ATTR_MGMT_SUBTYPE. The extra IE data to be * added to the end of the specified management frame is specified with * %NL80211_ATTR_IE. If the command succeeds, the requested data will be * added to all specified management frames generated by * kernel/firmware/driver. * Note: This command has been removed and it is only reserved at this * point to avoid re-using existing command number. The functionality this * command was planned for has been provided with cleaner design with the * option to specify additional IEs in NL80211_CMD_TRIGGER_SCAN, * NL80211_CMD_AUTHENTICATE, NL80211_CMD_ASSOCIATE, * NL80211_CMD_DEAUTHENTICATE, and NL80211_CMD_DISASSOCIATE. * * @NL80211_CMD_GET_SCAN: get scan results * @NL80211_CMD_TRIGGER_SCAN: trigger a new scan with the given parameters * %NL80211_ATTR_TX_NO_CCK_RATE is used to decide whether to send the * probe requests at CCK rate or not. * @NL80211_CMD_NEW_SCAN_RESULTS: scan notification (as a reply to * NL80211_CMD_GET_SCAN and on the "scan" multicast group) * @NL80211_CMD_SCAN_ABORTED: scan was aborted, for unspecified reasons, * partial scan results may be available * * @NL80211_CMD_START_SCHED_SCAN: start a scheduled scan at certain * intervals, as specified by %NL80211_ATTR_SCHED_SCAN_INTERVAL. * Like with normal scans, if SSIDs (%NL80211_ATTR_SCAN_SSIDS) * are passed, they are used in the probe requests. For * broadcast, a broadcast SSID must be passed (ie. an empty * string). If no SSID is passed, no probe requests are sent and * a passive scan is performed. %NL80211_ATTR_SCAN_FREQUENCIES, * if passed, define which channels should be scanned; if not * passed, all channels allowed for the current regulatory domain * are used. Extra IEs can also be passed from the userspace by * using the %NL80211_ATTR_IE attribute. * @NL80211_CMD_STOP_SCHED_SCAN: stop a scheduled scan. Returns -ENOENT * if scheduled scan is not running. * @NL80211_CMD_SCHED_SCAN_RESULTS: indicates that there are scheduled scan * results available. * @NL80211_CMD_SCHED_SCAN_STOPPED: indicates that the scheduled scan has * stopped. The driver may issue this event at any time during a * scheduled scan. One reason for stopping the scan is if the hardware * does not support starting an association or a normal scan while running * a scheduled scan. This event is also sent when the * %NL80211_CMD_STOP_SCHED_SCAN command is received or when the interface * is brought down while a scheduled scan was running. * * @NL80211_CMD_GET_SURVEY: get survey resuls, e.g. channel occupation * or noise level * @NL80211_CMD_NEW_SURVEY_RESULTS: survey data notification (as a reply to * NL80211_CMD_GET_SURVEY and on the "scan" multicast group) * * @NL80211_CMD_SET_PMKSA: Add a PMKSA cache entry, using %NL80211_ATTR_MAC * (for the BSSID) and %NL80211_ATTR_PMKID. * @NL80211_CMD_DEL_PMKSA: Delete a PMKSA cache entry, using %NL80211_ATTR_MAC * (for the BSSID) and %NL80211_ATTR_PMKID. * @NL80211_CMD_FLUSH_PMKSA: Flush all PMKSA cache entries. * * @NL80211_CMD_REG_CHANGE: indicates to userspace the regulatory domain * has been changed and provides details of the request information * that caused the change such as who initiated the regulatory request * (%NL80211_ATTR_REG_INITIATOR), the wiphy_idx * (%NL80211_ATTR_REG_ALPHA2) on which the request was made from if * the initiator was %NL80211_REGDOM_SET_BY_COUNTRY_IE or * %NL80211_REGDOM_SET_BY_DRIVER, the type of regulatory domain * set (%NL80211_ATTR_REG_TYPE), if the type of regulatory domain is * %NL80211_REG_TYPE_COUNTRY the alpha2 to which we have moved on * to (%NL80211_ATTR_REG_ALPHA2). * @NL80211_CMD_REG_BEACON_HINT: indicates to userspace that an AP beacon * has been found while world roaming thus enabling active scan or * any mode of operation that initiates TX (beacons) on a channel * where we would not have been able to do either before. As an example * if you are world roaming (regulatory domain set to world or if your * driver is using a custom world roaming regulatory domain) and while * doing a passive scan on the 5 GHz band you find an AP there (if not * on a DFS channel) you will now be able to actively scan for that AP * or use AP mode on your card on that same channel. Note that this will * never be used for channels 1-11 on the 2 GHz band as they are always * enabled world wide. This beacon hint is only sent if your device had * either disabled active scanning or beaconing on a channel. We send to * userspace the wiphy on which we removed a restriction from * (%NL80211_ATTR_WIPHY) and the channel on which this occurred * before (%NL80211_ATTR_FREQ_BEFORE) and after (%NL80211_ATTR_FREQ_AFTER) * the beacon hint was processed. * * @NL80211_CMD_AUTHENTICATE: authentication request and notification. * This command is used both as a command (request to authenticate) and * as an event on the "mlme" multicast group indicating completion of the * authentication process. * When used as a command, %NL80211_ATTR_IFINDEX is used to identify the * interface. %NL80211_ATTR_MAC is used to specify PeerSTAAddress (and * BSSID in case of station mode). %NL80211_ATTR_SSID is used to specify * the SSID (mainly for association, but is included in authentication * request, too, to help BSS selection. %NL80211_ATTR_WIPHY_FREQ is used * to specify the frequence of the channel in MHz. %NL80211_ATTR_AUTH_TYPE * is used to specify the authentication type. %NL80211_ATTR_IE is used to * define IEs (VendorSpecificInfo, but also including RSN IE and FT IEs) * to be added to the frame. * When used as an event, this reports reception of an Authentication * frame in station and IBSS modes when the local MLME processed the * frame, i.e., it was for the local STA and was received in correct * state. This is similar to MLME-AUTHENTICATE.confirm primitive in the * MLME SAP interface (kernel providing MLME, userspace SME). The * included %NL80211_ATTR_FRAME attribute contains the management frame * (including both the header and frame body, but not FCS). This event is * also used to indicate if the authentication attempt timed out. In that * case the %NL80211_ATTR_FRAME attribute is replaced with a * %NL80211_ATTR_TIMED_OUT flag (and %NL80211_ATTR_MAC to indicate which * pending authentication timed out). * @NL80211_CMD_ASSOCIATE: association request and notification; like * NL80211_CMD_AUTHENTICATE but for Association and Reassociation * (similar to MLME-ASSOCIATE.request, MLME-REASSOCIATE.request, * MLME-ASSOCIATE.confirm or MLME-REASSOCIATE.confirm primitives). * @NL80211_CMD_DEAUTHENTICATE: deauthentication request and notification; like * NL80211_CMD_AUTHENTICATE but for Deauthentication frames (similar to * MLME-DEAUTHENTICATION.request and MLME-DEAUTHENTICATE.indication * primitives). * @NL80211_CMD_DISASSOCIATE: disassociation request and notification; like * NL80211_CMD_AUTHENTICATE but for Disassociation frames (similar to * MLME-DISASSOCIATE.request and MLME-DISASSOCIATE.indication primitives). * * @NL80211_CMD_MICHAEL_MIC_FAILURE: notification of a locally detected Michael * MIC (part of TKIP) failure; sent on the "mlme" multicast group; the * event includes %NL80211_ATTR_MAC to describe the source MAC address of * the frame with invalid MIC, %NL80211_ATTR_KEY_TYPE to show the key * type, %NL80211_ATTR_KEY_IDX to indicate the key identifier, and * %NL80211_ATTR_KEY_SEQ to indicate the TSC value of the frame; this * event matches with MLME-MICHAELMICFAILURE.indication() primitive * * @NL80211_CMD_JOIN_IBSS: Join a new IBSS -- given at least an SSID and a * FREQ attribute (for the initial frequency if no peer can be found) * and optionally a MAC (as BSSID) and FREQ_FIXED attribute if those * should be fixed rather than automatically determined. Can only be * executed on a network interface that is UP, and fixed BSSID/FREQ * may be rejected. Another optional parameter is the beacon interval, * given in the %NL80211_ATTR_BEACON_INTERVAL attribute, which if not * given defaults to 100 TU (102.4ms). * @NL80211_CMD_LEAVE_IBSS: Leave the IBSS -- no special arguments, the IBSS is * determined by the network interface. * * @NL80211_CMD_TESTMODE: testmode command, takes a wiphy (or ifindex) attribute * to identify the device, and the TESTDATA blob attribute to pass through * to the driver. * * @NL80211_CMD_CONNECT: connection request and notification; this command * requests to connect to a specified network but without separating * auth and assoc steps. For this, you need to specify the SSID in a * %NL80211_ATTR_SSID attribute, and can optionally specify the association * IEs in %NL80211_ATTR_IE, %NL80211_ATTR_AUTH_TYPE, %NL80211_ATTR_USE_MFP, * %NL80211_ATTR_MAC, %NL80211_ATTR_WIPHY_FREQ, %NL80211_ATTR_CONTROL_PORT, * %NL80211_ATTR_CONTROL_PORT_ETHERTYPE and * %NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT. * Background scan period can optionally be * specified in %NL80211_ATTR_BG_SCAN_PERIOD, * if not specified default background scan configuration * in driver is used and if period value is 0, bg scan will be disabled. * This attribute is ignored if driver does not support roam scan. * It is also sent as an event, with the BSSID and response IEs when the * connection is established or failed to be established. This can be * determined by the STATUS_CODE attribute. * @NL80211_CMD_ROAM: request that the card roam (currently not implemented), * sent as an event when the card/driver roamed by itself. * @NL80211_CMD_DISCONNECT: drop a given connection; also used to notify * userspace that a connection was dropped by the AP or due to other * reasons, for this the %NL80211_ATTR_DISCONNECTED_BY_AP and * %NL80211_ATTR_REASON_CODE attributes are used. * * @NL80211_CMD_SET_WIPHY_NETNS: Set a wiphy's netns. Note that all devices * associated with this wiphy must be down and will follow. * * @NL80211_CMD_REMAIN_ON_CHANNEL: Request to remain awake on the specified * channel for the specified amount of time. This can be used to do * off-channel operations like transmit a Public Action frame and wait for * a response while being associated to an AP on another channel. * %NL80211_ATTR_IFINDEX is used to specify which interface (and thus * radio) is used. %NL80211_ATTR_WIPHY_FREQ is used to specify the * frequency for the operation. * %NL80211_ATTR_DURATION is used to specify the duration in milliseconds * to remain on the channel. This command is also used as an event to * notify when the requested duration starts (it may take a while for the * driver to schedule this time due to other concurrent needs for the * radio). * When called, this operation returns a cookie (%NL80211_ATTR_COOKIE) * that will be included with any events pertaining to this request; * the cookie is also used to cancel the request. * @NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL: This command can be used to cancel a * pending remain-on-channel duration if the desired operation has been * completed prior to expiration of the originally requested duration. * %NL80211_ATTR_WIPHY or %NL80211_ATTR_IFINDEX is used to specify the * radio. The %NL80211_ATTR_COOKIE attribute must be given as well to * uniquely identify the request. * This command is also used as an event to notify when a requested * remain-on-channel duration has expired. * * @NL80211_CMD_SET_TX_BITRATE_MASK: Set the mask of rates to be used in TX * rate selection. %NL80211_ATTR_IFINDEX is used to specify the interface * and @NL80211_ATTR_TX_RATES the set of allowed rates. * * @NL80211_CMD_REGISTER_FRAME: Register for receiving certain mgmt frames * (via @NL80211_CMD_FRAME) for processing in userspace. This command * requires an interface index, a frame type attribute (optional for * backward compatibility reasons, if not given assumes action frames) * and a match attribute containing the first few bytes of the frame * that should match, e.g. a single byte for only a category match or * four bytes for vendor frames including the OUI. The registration * cannot be dropped, but is removed automatically when the netlink * socket is closed. Multiple registrations can be made. * @NL80211_CMD_REGISTER_ACTION: Alias for @NL80211_CMD_REGISTER_FRAME for * backward compatibility * @NL80211_CMD_FRAME: Management frame TX request and RX notification. This * command is used both as a request to transmit a management frame and * as an event indicating reception of a frame that was not processed in * kernel code, but is for us (i.e., which may need to be processed in a * user space application). %NL80211_ATTR_FRAME is used to specify the * frame contents (including header). %NL80211_ATTR_WIPHY_FREQ is used * to indicate on which channel the frame is to be transmitted or was * received. If this channel is not the current channel (remain-on-channel * or the operational channel) the device will switch to the given channel * and transmit the frame, optionally waiting for a response for the time * specified using %NL80211_ATTR_DURATION. When called, this operation * returns a cookie (%NL80211_ATTR_COOKIE) that will be included with the * TX status event pertaining to the TX request. * %NL80211_ATTR_TX_NO_CCK_RATE is used to decide whether to send the * management frames at CCK rate or not in 2GHz band. * @NL80211_CMD_FRAME_WAIT_CANCEL: When an off-channel TX was requested, this * command may be used with the corresponding cookie to cancel the wait * time if it is known that it is no longer necessary. * @NL80211_CMD_ACTION: Alias for @NL80211_CMD_FRAME for backward compatibility. * @NL80211_CMD_FRAME_TX_STATUS: Report TX status of a management frame * transmitted with %NL80211_CMD_FRAME. %NL80211_ATTR_COOKIE identifies * the TX command and %NL80211_ATTR_FRAME includes the contents of the * frame. %NL80211_ATTR_ACK flag is included if the recipient acknowledged * the frame. * @NL80211_CMD_ACTION_TX_STATUS: Alias for @NL80211_CMD_FRAME_TX_STATUS for * backward compatibility. * * @NL80211_CMD_SET_POWER_SAVE: Set powersave, using %NL80211_ATTR_PS_STATE * @NL80211_CMD_GET_POWER_SAVE: Get powersave status in %NL80211_ATTR_PS_STATE * * @NL80211_CMD_SET_CQM: Connection quality monitor configuration. This command * is used to configure connection quality monitoring notification trigger * levels. * @NL80211_CMD_NOTIFY_CQM: Connection quality monitor notification. This * command is used as an event to indicate the that a trigger level was * reached. * @NL80211_CMD_SET_CHANNEL: Set the channel (using %NL80211_ATTR_WIPHY_FREQ * and the attributes determining channel width) the given interface * (identifed by %NL80211_ATTR_IFINDEX) shall operate on. * In case multiple channels are supported by the device, the mechanism * with which it switches channels is implementation-defined. * When a monitor interface is given, it can only switch channel while * no other interfaces are operating to avoid disturbing the operation * of any other interfaces, and other interfaces will again take * precedence when they are used. * * @NL80211_CMD_SET_WDS_PEER: Set the MAC address of the peer on a WDS interface. * * @NL80211_CMD_JOIN_MESH: Join a mesh. The mesh ID must be given, and initial * mesh config parameters may be given. * @NL80211_CMD_LEAVE_MESH: Leave the mesh network -- no special arguments, the * network is determined by the network interface. * * @NL80211_CMD_UNPROT_DEAUTHENTICATE: Unprotected deauthentication frame * notification. This event is used to indicate that an unprotected * deauthentication frame was dropped when MFP is in use. * @NL80211_CMD_UNPROT_DISASSOCIATE: Unprotected disassociation frame * notification. This event is used to indicate that an unprotected * disassociation frame was dropped when MFP is in use. * * @NL80211_CMD_NEW_PEER_CANDIDATE: Notification on the reception of a * beacon or probe response from a compatible mesh peer. This is only * sent while no station information (sta_info) exists for the new peer * candidate and when @NL80211_MESH_SETUP_USERSPACE_AUTH, * @NL80211_MESH_SETUP_USERSPACE_AMPE, or * @NL80211_MESH_SETUP_USERSPACE_MPM is set. On reception of this * notification, userspace may decide to create a new station * (@NL80211_CMD_NEW_STATION). To stop this notification from * reoccurring, the userspace authentication daemon may want to create the * new station with the AUTHENTICATED flag unset and maybe change it later * depending on the authentication result. * * @NL80211_CMD_GET_WOWLAN: get Wake-on-Wireless-LAN (WoWLAN) settings. * @NL80211_CMD_SET_WOWLAN: set Wake-on-Wireless-LAN (WoWLAN) settings. * Since wireless is more complex than wired ethernet, it supports * various triggers. These triggers can be configured through this * command with the %NL80211_ATTR_WOWLAN_TRIGGERS attribute. For * more background information, see * http://wireless.kernel.org/en/users/Documentation/WoWLAN. * The @NL80211_CMD_SET_WOWLAN command can also be used as a notification * from the driver reporting the wakeup reason. In this case, the * @NL80211_ATTR_WOWLAN_TRIGGERS attribute will contain the reason * for the wakeup, if it was caused by wireless. If it is not present * in the wakeup notification, the wireless device didn't cause the * wakeup but reports that it was woken up. * * @NL80211_CMD_SET_REKEY_OFFLOAD: This command is used give the driver * the necessary information for supporting GTK rekey offload. This * feature is typically used during WoWLAN. The configuration data * is contained in %NL80211_ATTR_REKEY_DATA (which is nested and * contains the data in sub-attributes). After rekeying happened, * this command may also be sent by the driver as an MLME event to * inform userspace of the new replay counter. * * @NL80211_CMD_PMKSA_CANDIDATE: This is used as an event to inform userspace * of PMKSA caching dandidates. * * @NL80211_CMD_TDLS_OPER: Perform a high-level TDLS command (e.g. link setup). * In addition, this can be used as an event to request userspace to take * actions on TDLS links (set up a new link or tear down an existing one). * In such events, %NL80211_ATTR_TDLS_OPERATION indicates the requested * operation, %NL80211_ATTR_MAC contains the peer MAC address, and * %NL80211_ATTR_REASON_CODE the reason code to be used (only with * %NL80211_TDLS_TEARDOWN). * @NL80211_CMD_TDLS_MGMT: Send a TDLS management frame. The * %NL80211_ATTR_TDLS_ACTION attribute determines the type of frame to be * sent. Public Action codes (802.11-2012 8.1.5.1) will be sent as * 802.11 management frames, while TDLS action codes (802.11-2012 * 8.5.13.1) will be encapsulated and sent as data frames. The currently * supported Public Action code is %WLAN_PUB_ACTION_TDLS_DISCOVER_RES * and the currently supported TDLS actions codes are given in * &enum ieee80211_tdls_actioncode. * * @NL80211_CMD_UNEXPECTED_FRAME: Used by an application controlling an AP * (or GO) interface (i.e. hostapd) to ask for unexpected frames to * implement sending deauth to stations that send unexpected class 3 * frames. Also used as the event sent by the kernel when such a frame * is received. * For the event, the %NL80211_ATTR_MAC attribute carries the TA and * other attributes like the interface index are present. * If used as the command it must have an interface index and you can * only unsubscribe from the event by closing the socket. Subscription * is also for %NL80211_CMD_UNEXPECTED_4ADDR_FRAME events. * * @NL80211_CMD_UNEXPECTED_4ADDR_FRAME: Sent as an event indicating that the * associated station identified by %NL80211_ATTR_MAC sent a 4addr frame * and wasn't already in a 4-addr VLAN. The event will be sent similarly * to the %NL80211_CMD_UNEXPECTED_FRAME event, to the same listener. * * @NL80211_CMD_PROBE_CLIENT: Probe an associated station on an AP interface * by sending a null data frame to it and reporting when the frame is * acknowleged. This is used to allow timing out inactive clients. Uses * %NL80211_ATTR_IFINDEX and %NL80211_ATTR_MAC. The command returns a * direct reply with an %NL80211_ATTR_COOKIE that is later used to match * up the event with the request. The event includes the same data and * has %NL80211_ATTR_ACK set if the frame was ACKed. * * @NL80211_CMD_REGISTER_BEACONS: Register this socket to receive beacons from * other BSSes when any interfaces are in AP mode. This helps implement * OLBC handling in hostapd. Beacons are reported in %NL80211_CMD_FRAME * messages. Note that per PHY only one application may register. * * @NL80211_CMD_SET_NOACK_MAP: sets a bitmap for the individual TIDs whether * No Acknowledgement Policy should be applied. * * @NL80211_CMD_CH_SWITCH_NOTIFY: An AP or GO may decide to switch channels * independently of the userspace SME, send this event indicating * %NL80211_ATTR_IFINDEX is now on %NL80211_ATTR_WIPHY_FREQ and the * attributes determining channel width. * * @NL80211_CMD_START_P2P_DEVICE: Start the given P2P Device, identified by * its %NL80211_ATTR_WDEV identifier. It must have been created with * %NL80211_CMD_NEW_INTERFACE previously. After it has been started, the * P2P Device can be used for P2P operations, e.g. remain-on-channel and * public action frame TX. * @NL80211_CMD_STOP_P2P_DEVICE: Stop the given P2P Device, identified by * its %NL80211_ATTR_WDEV identifier. * * @NL80211_CMD_CONN_FAILED: connection request to an AP failed; used to * notify userspace that AP has rejected the connection request from a * station, due to particular reason. %NL80211_ATTR_CONN_FAILED_REASON * is used for this. * * @NL80211_CMD_SET_MCAST_RATE: Change the rate used to send multicast frames * for IBSS or MESH vif. * * @NL80211_CMD_SET_MAC_ACL: sets ACL for MAC address based access control. * This is to be used with the drivers advertising the support of MAC * address based access control. List of MAC addresses is passed in * %NL80211_ATTR_MAC_ADDRS and ACL policy is passed in * %NL80211_ATTR_ACL_POLICY. Driver will enable ACL with this list, if it * is not already done. The new list will replace any existing list. Driver * will clear its ACL when the list of MAC addresses passed is empty. This * command is used in AP/P2P GO mode. Driver has to make sure to clear its * ACL list during %NL80211_CMD_STOP_AP. * * @NL80211_CMD_RADAR_DETECT: Start a Channel availability check (CAC). Once * a radar is detected or the channel availability scan (CAC) has finished * or was aborted, or a radar was detected, usermode will be notified with * this event. This command is also used to notify userspace about radars * while operating on this channel. * %NL80211_ATTR_RADAR_EVENT is used to inform about the type of the * event. * * @NL80211_CMD_GET_PROTOCOL_FEATURES: Get global nl80211 protocol features, * i.e. features for the nl80211 protocol rather than device features. * Returns the features in the %NL80211_ATTR_PROTOCOL_FEATURES bitmap. * * @NL80211_CMD_UPDATE_FT_IES: Pass down the most up-to-date Fast Transition * Information Element to the WLAN driver * * @NL80211_CMD_FT_EVENT: Send a Fast transition event from the WLAN driver * to the supplicant. This will carry the target AP's MAC address along * with the relevant Information Elements. This event is used to report * received FT IEs (MDIE, FTIE, RSN IE, TIE, RICIE). * * @NL80211_CMD_CRIT_PROTOCOL_START: Indicates user-space will start running * a critical protocol that needs more reliability in the connection to * complete. * * @NL80211_CMD_CRIT_PROTOCOL_STOP: Indicates the connection reliability can * return back to normal. * * @NL80211_CMD_GET_COALESCE: Get currently supported coalesce rules. * @NL80211_CMD_SET_COALESCE: Configure coalesce rules or clear existing rules. * * @NL80211_CMD_CHANNEL_SWITCH: Perform a channel switch by announcing the * the new channel information (Channel Switch Announcement - CSA) * in the beacon for some time (as defined in the * %NL80211_ATTR_CH_SWITCH_COUNT parameter) and then change to the * new channel. Userspace provides the new channel information (using * %NL80211_ATTR_WIPHY_FREQ and the attributes determining channel * width). %NL80211_ATTR_CH_SWITCH_BLOCK_TX may be supplied to inform * other station that transmission must be blocked until the channel * switch is complete. * * @NL80211_CMD_VENDOR: Vendor-specified command/event. The command is specified * by the %NL80211_ATTR_VENDOR_ID attribute and a sub-command in * %NL80211_ATTR_VENDOR_SUBCMD. Parameter(s) can be transported in * %NL80211_ATTR_VENDOR_DATA. * For feature advertisement, the %NL80211_ATTR_VENDOR_DATA attribute is * used in the wiphy data as a nested attribute containing descriptions * (&struct nl80211_vendor_cmd_info) of the supported vendor commands. * This may also be sent as an event with the same attributes. * * @NL80211_CMD_SET_QOS_MAP: Set Interworking QoS mapping for IP DSCP values. * The QoS mapping information is included in %NL80211_ATTR_QOS_MAP. If * that attribute is not included, QoS mapping is disabled. Since this * QoS mapping is relevant for IP packets, it is only valid during an * association. This is cleared on disassociation and AP restart. * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ enum nl80211_commands { /* don't change the order or add anything between, this is ABI! */ NL80211_CMD_UNSPEC, NL80211_CMD_GET_WIPHY, /* can dump */ NL80211_CMD_SET_WIPHY, NL80211_CMD_NEW_WIPHY, NL80211_CMD_DEL_WIPHY, NL80211_CMD_GET_INTERFACE, /* can dump */ NL80211_CMD_SET_INTERFACE, NL80211_CMD_NEW_INTERFACE, NL80211_CMD_DEL_INTERFACE, NL80211_CMD_GET_KEY, NL80211_CMD_SET_KEY, NL80211_CMD_NEW_KEY, NL80211_CMD_DEL_KEY, NL80211_CMD_GET_BEACON, NL80211_CMD_SET_BEACON, NL80211_CMD_START_AP, NL80211_CMD_NEW_BEACON = NL80211_CMD_START_AP, NL80211_CMD_STOP_AP, NL80211_CMD_DEL_BEACON = NL80211_CMD_STOP_AP, NL80211_CMD_GET_STATION, NL80211_CMD_SET_STATION, NL80211_CMD_NEW_STATION, NL80211_CMD_DEL_STATION, NL80211_CMD_GET_MPATH, NL80211_CMD_SET_MPATH, NL80211_CMD_NEW_MPATH, NL80211_CMD_DEL_MPATH, NL80211_CMD_SET_BSS, NL80211_CMD_SET_REG, NL80211_CMD_REQ_SET_REG, NL80211_CMD_GET_MESH_CONFIG, NL80211_CMD_SET_MESH_CONFIG, NL80211_CMD_SET_MGMT_EXTRA_IE /* reserved; not used */, NL80211_CMD_GET_REG, NL80211_CMD_GET_SCAN, NL80211_CMD_TRIGGER_SCAN, NL80211_CMD_NEW_SCAN_RESULTS, NL80211_CMD_SCAN_ABORTED, NL80211_CMD_REG_CHANGE, NL80211_CMD_AUTHENTICATE, NL80211_CMD_ASSOCIATE, NL80211_CMD_DEAUTHENTICATE, NL80211_CMD_DISASSOCIATE, NL80211_CMD_MICHAEL_MIC_FAILURE, NL80211_CMD_REG_BEACON_HINT, NL80211_CMD_JOIN_IBSS, NL80211_CMD_LEAVE_IBSS, NL80211_CMD_TESTMODE, NL80211_CMD_CONNECT, NL80211_CMD_ROAM, NL80211_CMD_DISCONNECT, NL80211_CMD_SET_WIPHY_NETNS, NL80211_CMD_GET_SURVEY, NL80211_CMD_NEW_SURVEY_RESULTS, NL80211_CMD_SET_PMKSA, NL80211_CMD_DEL_PMKSA, NL80211_CMD_FLUSH_PMKSA, NL80211_CMD_REMAIN_ON_CHANNEL, NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL, NL80211_CMD_SET_TX_BITRATE_MASK, NL80211_CMD_REGISTER_FRAME, NL80211_CMD_REGISTER_ACTION = NL80211_CMD_REGISTER_FRAME, NL80211_CMD_FRAME, NL80211_CMD_ACTION = NL80211_CMD_FRAME, NL80211_CMD_FRAME_TX_STATUS, NL80211_CMD_ACTION_TX_STATUS = NL80211_CMD_FRAME_TX_STATUS, NL80211_CMD_SET_POWER_SAVE, NL80211_CMD_GET_POWER_SAVE, NL80211_CMD_SET_CQM, NL80211_CMD_NOTIFY_CQM, NL80211_CMD_SET_CHANNEL, NL80211_CMD_SET_WDS_PEER, NL80211_CMD_FRAME_WAIT_CANCEL, NL80211_CMD_JOIN_MESH, NL80211_CMD_LEAVE_MESH, NL80211_CMD_UNPROT_DEAUTHENTICATE, NL80211_CMD_UNPROT_DISASSOCIATE, NL80211_CMD_NEW_PEER_CANDIDATE, NL80211_CMD_GET_WOWLAN, NL80211_CMD_SET_WOWLAN, NL80211_CMD_START_SCHED_SCAN, NL80211_CMD_STOP_SCHED_SCAN, NL80211_CMD_SCHED_SCAN_RESULTS, NL80211_CMD_SCHED_SCAN_STOPPED, NL80211_CMD_SET_REKEY_OFFLOAD, NL80211_CMD_PMKSA_CANDIDATE, NL80211_CMD_TDLS_OPER, NL80211_CMD_TDLS_MGMT, NL80211_CMD_UNEXPECTED_FRAME, NL80211_CMD_PROBE_CLIENT, NL80211_CMD_REGISTER_BEACONS, NL80211_CMD_UNEXPECTED_4ADDR_FRAME, NL80211_CMD_SET_NOACK_MAP, NL80211_CMD_CH_SWITCH_NOTIFY, NL80211_CMD_START_P2P_DEVICE, NL80211_CMD_STOP_P2P_DEVICE, NL80211_CMD_CONN_FAILED, NL80211_CMD_SET_MCAST_RATE, NL80211_CMD_SET_MAC_ACL, NL80211_CMD_RADAR_DETECT, NL80211_CMD_GET_PROTOCOL_FEATURES, NL80211_CMD_UPDATE_FT_IES, NL80211_CMD_FT_EVENT, NL80211_CMD_CRIT_PROTOCOL_START, NL80211_CMD_CRIT_PROTOCOL_STOP, NL80211_CMD_GET_COALESCE, NL80211_CMD_SET_COALESCE, NL80211_CMD_CHANNEL_SWITCH, NL80211_CMD_VENDOR, NL80211_CMD_SET_QOS_MAP, /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ __NL80211_CMD_AFTER_LAST, NL80211_CMD_MAX = __NL80211_CMD_AFTER_LAST - 1 }; /* * Allow user space programs to use #ifdef on new commands by defining them * here */ #define NL80211_CMD_SET_BSS NL80211_CMD_SET_BSS #define NL80211_CMD_SET_MGMT_EXTRA_IE NL80211_CMD_SET_MGMT_EXTRA_IE #define NL80211_CMD_REG_CHANGE NL80211_CMD_REG_CHANGE #define NL80211_CMD_AUTHENTICATE NL80211_CMD_AUTHENTICATE #define NL80211_CMD_ASSOCIATE NL80211_CMD_ASSOCIATE #define NL80211_CMD_DEAUTHENTICATE NL80211_CMD_DEAUTHENTICATE #define NL80211_CMD_DISASSOCIATE NL80211_CMD_DISASSOCIATE #define NL80211_CMD_REG_BEACON_HINT NL80211_CMD_REG_BEACON_HINT #define NL80211_ATTR_FEATURE_FLAGS NL80211_ATTR_FEATURE_FLAGS /* source-level API compatibility */ #define NL80211_CMD_GET_MESH_PARAMS NL80211_CMD_GET_MESH_CONFIG #define NL80211_CMD_SET_MESH_PARAMS NL80211_CMD_SET_MESH_CONFIG #define NL80211_MESH_SETUP_VENDOR_PATH_SEL_IE NL80211_MESH_SETUP_IE /** * enum nl80211_attrs - nl80211 netlink attributes * * @NL80211_ATTR_UNSPEC: unspecified attribute to catch errors * * @NL80211_ATTR_WIPHY: index of wiphy to operate on, cf. * /sys/class/ieee80211//index * @NL80211_ATTR_WIPHY_NAME: wiphy name (used for renaming) * @NL80211_ATTR_WIPHY_TXQ_PARAMS: a nested array of TX queue parameters * @NL80211_ATTR_WIPHY_FREQ: frequency of the selected channel in MHz, * defines the channel together with the (deprecated) * %NL80211_ATTR_WIPHY_CHANNEL_TYPE attribute or the attributes * %NL80211_ATTR_CHANNEL_WIDTH and if needed %NL80211_ATTR_CENTER_FREQ1 * and %NL80211_ATTR_CENTER_FREQ2 * @NL80211_ATTR_CHANNEL_WIDTH: u32 attribute containing one of the values * of &enum nl80211_chan_width, describing the channel width. See the * documentation of the enum for more information. * @NL80211_ATTR_CENTER_FREQ1: Center frequency of the first part of the * channel, used for anything but 20 MHz bandwidth * @NL80211_ATTR_CENTER_FREQ2: Center frequency of the second part of the * channel, used only for 80+80 MHz bandwidth * @NL80211_ATTR_WIPHY_CHANNEL_TYPE: included with NL80211_ATTR_WIPHY_FREQ * if HT20 or HT40 are to be used (i.e., HT disabled if not included): * NL80211_CHAN_NO_HT = HT not allowed (i.e., same as not including * this attribute) * NL80211_CHAN_HT20 = HT20 only * NL80211_CHAN_HT40MINUS = secondary channel is below the primary channel * NL80211_CHAN_HT40PLUS = secondary channel is above the primary channel * This attribute is now deprecated. * @NL80211_ATTR_WIPHY_RETRY_SHORT: TX retry limit for frames whose length is * less than or equal to the RTS threshold; allowed range: 1..255; * dot11ShortRetryLimit; u8 * @NL80211_ATTR_WIPHY_RETRY_LONG: TX retry limit for frames whose length is * greater than the RTS threshold; allowed range: 1..255; * dot11ShortLongLimit; u8 * @NL80211_ATTR_WIPHY_FRAG_THRESHOLD: fragmentation threshold, i.e., maximum * length in octets for frames; allowed range: 256..8000, disable * fragmentation with (u32)-1; dot11FragmentationThreshold; u32 * @NL80211_ATTR_WIPHY_RTS_THRESHOLD: RTS threshold (TX frames with length * larger than or equal to this use RTS/CTS handshake); allowed range: * 0..65536, disable with (u32)-1; dot11RTSThreshold; u32 * @NL80211_ATTR_WIPHY_COVERAGE_CLASS: Coverage Class as defined by IEEE 802.11 * section 7.3.2.9; dot11CoverageClass; u8 * * @NL80211_ATTR_IFINDEX: network interface index of the device to operate on * @NL80211_ATTR_IFNAME: network interface name * @NL80211_ATTR_IFTYPE: type of virtual interface, see &enum nl80211_iftype * * @NL80211_ATTR_WDEV: wireless device identifier, used for pseudo-devices * that don't have a netdev (u64) * * @NL80211_ATTR_MAC: MAC address (various uses) * * @NL80211_ATTR_KEY_DATA: (temporal) key data; for TKIP this consists of * 16 bytes encryption key followed by 8 bytes each for TX and RX MIC * keys * @NL80211_ATTR_KEY_IDX: key ID (u8, 0-3) * @NL80211_ATTR_KEY_CIPHER: key cipher suite (u32, as defined by IEEE 802.11 * section 7.3.2.25.1, e.g. 0x000FAC04) * @NL80211_ATTR_KEY_SEQ: transmit key sequence number (IV/PN) for TKIP and * CCMP keys, each six bytes in little endian * @NL80211_ATTR_KEY_DEFAULT: Flag attribute indicating the key is default key * @NL80211_ATTR_KEY_DEFAULT_MGMT: Flag attribute indicating the key is the * default management key * @NL80211_ATTR_CIPHER_SUITES_PAIRWISE: For crypto settings for connect or * other commands, indicates which pairwise cipher suites are used * @NL80211_ATTR_CIPHER_SUITE_GROUP: For crypto settings for connect or * other commands, indicates which group cipher suite is used * * @NL80211_ATTR_BEACON_INTERVAL: beacon interval in TU * @NL80211_ATTR_DTIM_PERIOD: DTIM period for beaconing * @NL80211_ATTR_BEACON_HEAD: portion of the beacon before the TIM IE * @NL80211_ATTR_BEACON_TAIL: portion of the beacon after the TIM IE * * @NL80211_ATTR_STA_AID: Association ID for the station (u16) * @NL80211_ATTR_STA_FLAGS: flags, nested element with NLA_FLAG attributes of * &enum nl80211_sta_flags (deprecated, use %NL80211_ATTR_STA_FLAGS2) * @NL80211_ATTR_STA_LISTEN_INTERVAL: listen interval as defined by * IEEE 802.11 7.3.1.6 (u16). * @NL80211_ATTR_STA_SUPPORTED_RATES: supported rates, array of supported * rates as defined by IEEE 802.11 7.3.2.2 but without the length * restriction (at most %NL80211_MAX_SUPP_RATES). * @NL80211_ATTR_STA_VLAN: interface index of VLAN interface to move station * to, or the AP interface the station was originally added to to. * @NL80211_ATTR_STA_INFO: information about a station, part of station info * given for %NL80211_CMD_GET_STATION, nested attribute containing * info as possible, see &enum nl80211_sta_info. * * @NL80211_ATTR_WIPHY_BANDS: Information about an operating bands, * consisting of a nested array. * * @NL80211_ATTR_MESH_ID: mesh id (1-32 bytes). * @NL80211_ATTR_STA_PLINK_ACTION: action to perform on the mesh peer link * (see &enum nl80211_plink_action). * @NL80211_ATTR_MPATH_NEXT_HOP: MAC address of the next hop for a mesh path. * @NL80211_ATTR_MPATH_INFO: information about a mesh_path, part of mesh path * info given for %NL80211_CMD_GET_MPATH, nested attribute described at * &enum nl80211_mpath_info. * * @NL80211_ATTR_MNTR_FLAGS: flags, nested element with NLA_FLAG attributes of * &enum nl80211_mntr_flags. * * @NL80211_ATTR_REG_ALPHA2: an ISO-3166-alpha2 country code for which the * current regulatory domain should be set to or is already set to. * For example, 'CR', for Costa Rica. This attribute is used by the kernel * to query the CRDA to retrieve one regulatory domain. This attribute can * also be used by userspace to query the kernel for the currently set * regulatory domain. We chose an alpha2 as that is also used by the * IEEE-802.11 country information element to identify a country. * Users can also simply ask the wireless core to set regulatory domain * to a specific alpha2. * @NL80211_ATTR_REG_RULES: a nested array of regulatory domain regulatory * rules. * * @NL80211_ATTR_BSS_CTS_PROT: whether CTS protection is enabled (u8, 0 or 1) * @NL80211_ATTR_BSS_SHORT_PREAMBLE: whether short preamble is enabled * (u8, 0 or 1) * @NL80211_ATTR_BSS_SHORT_SLOT_TIME: whether short slot time enabled * (u8, 0 or 1) * @NL80211_ATTR_BSS_BASIC_RATES: basic rates, array of basic * rates in format defined by IEEE 802.11 7.3.2.2 but without the length * restriction (at most %NL80211_MAX_SUPP_RATES). * * @NL80211_ATTR_HT_CAPABILITY: HT Capability information element (from * association request when used with NL80211_CMD_NEW_STATION) * * @NL80211_ATTR_SUPPORTED_IFTYPES: nested attribute containing all * supported interface types, each a flag attribute with the number * of the interface mode. * * @NL80211_ATTR_MGMT_SUBTYPE: Management frame subtype for * %NL80211_CMD_SET_MGMT_EXTRA_IE. * * @NL80211_ATTR_IE: Information element(s) data (used, e.g., with * %NL80211_CMD_SET_MGMT_EXTRA_IE). * * @NL80211_ATTR_MAX_NUM_SCAN_SSIDS: number of SSIDs you can scan with * a single scan request, a wiphy attribute. * @NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS: number of SSIDs you can * scan with a single scheduled scan request, a wiphy attribute. * @NL80211_ATTR_MAX_SCAN_IE_LEN: maximum length of information elements * that can be added to a scan request * @NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN: maximum length of information * elements that can be added to a scheduled scan request * @NL80211_ATTR_MAX_MATCH_SETS: maximum number of sets that can be * used with @NL80211_ATTR_SCHED_SCAN_MATCH, a wiphy attribute. * * @NL80211_ATTR_SCAN_FREQUENCIES: nested attribute with frequencies (in MHz) * @NL80211_ATTR_SCAN_SSIDS: nested attribute with SSIDs, leave out for passive * scanning and include a zero-length SSID (wildcard) for wildcard scan * @NL80211_ATTR_BSS: scan result BSS * * @NL80211_ATTR_REG_INITIATOR: indicates who requested the regulatory domain * currently in effect. This could be any of the %NL80211_REGDOM_SET_BY_* * @NL80211_ATTR_REG_TYPE: indicates the type of the regulatory domain currently * set. This can be one of the nl80211_reg_type (%NL80211_REGDOM_TYPE_*) * * @NL80211_ATTR_SUPPORTED_COMMANDS: wiphy attribute that specifies * an array of command numbers (i.e. a mapping index to command number) * that the driver for the given wiphy supports. * * @NL80211_ATTR_FRAME: frame data (binary attribute), including frame header * and body, but not FCS; used, e.g., with NL80211_CMD_AUTHENTICATE and * NL80211_CMD_ASSOCIATE events * @NL80211_ATTR_SSID: SSID (binary attribute, 0..32 octets) * @NL80211_ATTR_AUTH_TYPE: AuthenticationType, see &enum nl80211_auth_type, * represented as a u32 * @NL80211_ATTR_REASON_CODE: ReasonCode for %NL80211_CMD_DEAUTHENTICATE and * %NL80211_CMD_DISASSOCIATE, u16 * * @NL80211_ATTR_KEY_TYPE: Key Type, see &enum nl80211_key_type, represented as * a u32 * * @NL80211_ATTR_FREQ_BEFORE: A channel which has suffered a regulatory change * due to considerations from a beacon hint. This attribute reflects * the state of the channel _before_ the beacon hint processing. This * attributes consists of a nested attribute containing * NL80211_FREQUENCY_ATTR_* * @NL80211_ATTR_FREQ_AFTER: A channel which has suffered a regulatory change * due to considerations from a beacon hint. This attribute reflects * the state of the channel _after_ the beacon hint processing. This * attributes consists of a nested attribute containing * NL80211_FREQUENCY_ATTR_* * * @NL80211_ATTR_CIPHER_SUITES: a set of u32 values indicating the supported * cipher suites * * @NL80211_ATTR_FREQ_FIXED: a flag indicating the IBSS should not try to look * for other networks on different channels * * @NL80211_ATTR_TIMED_OUT: a flag indicating than an operation timed out; this * is used, e.g., with %NL80211_CMD_AUTHENTICATE event * * @NL80211_ATTR_USE_MFP: Whether management frame protection (IEEE 802.11w) is * used for the association (&enum nl80211_mfp, represented as a u32); * this attribute can be used * with %NL80211_CMD_ASSOCIATE and %NL80211_CMD_CONNECT requests * * @NL80211_ATTR_STA_FLAGS2: Attribute containing a * &struct nl80211_sta_flag_update. * * @NL80211_ATTR_CONTROL_PORT: A flag indicating whether user space controls * IEEE 802.1X port, i.e., sets/clears %NL80211_STA_FLAG_AUTHORIZED, in * station mode. If the flag is included in %NL80211_CMD_ASSOCIATE * request, the driver will assume that the port is unauthorized until * authorized by user space. Otherwise, port is marked authorized by * default in station mode. * @NL80211_ATTR_CONTROL_PORT_ETHERTYPE: A 16-bit value indicating the * ethertype that will be used for key negotiation. It can be * specified with the associate and connect commands. If it is not * specified, the value defaults to 0x888E (PAE, 802.1X). This * attribute is also used as a flag in the wiphy information to * indicate that protocols other than PAE are supported. * @NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT: When included along with * %NL80211_ATTR_CONTROL_PORT_ETHERTYPE, indicates that the custom * ethertype frames used for key negotiation must not be encrypted. * * @NL80211_ATTR_TESTDATA: Testmode data blob, passed through to the driver. * We recommend using nested, driver-specific attributes within this. * * @NL80211_ATTR_DISCONNECTED_BY_AP: A flag indicating that the DISCONNECT * event was due to the AP disconnecting the station, and not due to * a local disconnect request. * @NL80211_ATTR_STATUS_CODE: StatusCode for the %NL80211_CMD_CONNECT * event (u16) * @NL80211_ATTR_PRIVACY: Flag attribute, used with connect(), indicating * that protected APs should be used. This is also used with NEW_BEACON to * indicate that the BSS is to use protection. * * @NL80211_ATTR_CIPHERS_PAIRWISE: Used with CONNECT, ASSOCIATE, and NEW_BEACON * to indicate which unicast key ciphers will be used with the connection * (an array of u32). * @NL80211_ATTR_CIPHER_GROUP: Used with CONNECT, ASSOCIATE, and NEW_BEACON to * indicate which group key cipher will be used with the connection (a * u32). * @NL80211_ATTR_WPA_VERSIONS: Used with CONNECT, ASSOCIATE, and NEW_BEACON to * indicate which WPA version(s) the AP we want to associate with is using * (a u32 with flags from &enum nl80211_wpa_versions). * @NL80211_ATTR_AKM_SUITES: Used with CONNECT, ASSOCIATE, and NEW_BEACON to * indicate which key management algorithm(s) to use (an array of u32). * * @NL80211_ATTR_REQ_IE: (Re)association request information elements as * sent out by the card, for ROAM and successful CONNECT events. * @NL80211_ATTR_RESP_IE: (Re)association response information elements as * sent by peer, for ROAM and successful CONNECT events. * * @NL80211_ATTR_PREV_BSSID: previous BSSID, to be used by in ASSOCIATE * commands to specify using a reassociate frame * * @NL80211_ATTR_KEY: key information in a nested attribute with * %NL80211_KEY_* sub-attributes * @NL80211_ATTR_KEYS: array of keys for static WEP keys for connect() * and join_ibss(), key information is in a nested attribute each * with %NL80211_KEY_* sub-attributes * * @NL80211_ATTR_PID: Process ID of a network namespace. * * @NL80211_ATTR_GENERATION: Used to indicate consistent snapshots for * dumps. This number increases whenever the object list being * dumped changes, and as such userspace can verify that it has * obtained a complete and consistent snapshot by verifying that * all dump messages contain the same generation number. If it * changed then the list changed and the dump should be repeated * completely from scratch. * * @NL80211_ATTR_4ADDR: Use 4-address frames on a virtual interface * * @NL80211_ATTR_SURVEY_INFO: survey information about a channel, part of * the survey response for %NL80211_CMD_GET_SURVEY, nested attribute * containing info as possible, see &enum survey_info. * * @NL80211_ATTR_PMKID: PMK material for PMKSA caching. * @NL80211_ATTR_MAX_NUM_PMKIDS: maximum number of PMKIDs a firmware can * cache, a wiphy attribute. * * @NL80211_ATTR_DURATION: Duration of an operation in milliseconds, u32. * @NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION: Device attribute that * specifies the maximum duration that can be requested with the * remain-on-channel operation, in milliseconds, u32. * * @NL80211_ATTR_COOKIE: Generic 64-bit cookie to identify objects. * * @NL80211_ATTR_TX_RATES: Nested set of attributes * (enum nl80211_tx_rate_attributes) describing TX rates per band. The * enum nl80211_band value is used as the index (nla_type() of the nested * data. If a band is not included, it will be configured to allow all * rates based on negotiated supported rates information. This attribute * is used with %NL80211_CMD_SET_TX_BITRATE_MASK. * * @NL80211_ATTR_FRAME_MATCH: A binary attribute which typically must contain * at least one byte, currently used with @NL80211_CMD_REGISTER_FRAME. * @NL80211_ATTR_FRAME_TYPE: A u16 indicating the frame type/subtype for the * @NL80211_CMD_REGISTER_FRAME command. * @NL80211_ATTR_TX_FRAME_TYPES: wiphy capability attribute, which is a * nested attribute of %NL80211_ATTR_FRAME_TYPE attributes, containing * information about which frame types can be transmitted with * %NL80211_CMD_FRAME. * @NL80211_ATTR_RX_FRAME_TYPES: wiphy capability attribute, which is a * nested attribute of %NL80211_ATTR_FRAME_TYPE attributes, containing * information about which frame types can be registered for RX. * * @NL80211_ATTR_ACK: Flag attribute indicating that the frame was * acknowledged by the recipient. * * @NL80211_ATTR_PS_STATE: powersave state, using &enum nl80211_ps_state values. * * @NL80211_ATTR_CQM: connection quality monitor configuration in a * nested attribute with %NL80211_ATTR_CQM_* sub-attributes. * * @NL80211_ATTR_LOCAL_STATE_CHANGE: Flag attribute to indicate that a command * is requesting a local authentication/association state change without * invoking actual management frame exchange. This can be used with * NL80211_CMD_AUTHENTICATE, NL80211_CMD_DEAUTHENTICATE, * NL80211_CMD_DISASSOCIATE. * * @NL80211_ATTR_AP_ISOLATE: (AP mode) Do not forward traffic between stations * connected to this BSS. * * @NL80211_ATTR_WIPHY_TX_POWER_SETTING: Transmit power setting type. See * &enum nl80211_tx_power_setting for possible values. * @NL80211_ATTR_WIPHY_TX_POWER_LEVEL: Transmit power level in signed mBm units. * This is used in association with @NL80211_ATTR_WIPHY_TX_POWER_SETTING * for non-automatic settings. * * @NL80211_ATTR_SUPPORT_IBSS_RSN: The device supports IBSS RSN, which mostly * means support for per-station GTKs. * * @NL80211_ATTR_WIPHY_ANTENNA_TX: Bitmap of allowed antennas for transmitting. * This can be used to mask out antennas which are not attached or should * not be used for transmitting. If an antenna is not selected in this * bitmap the hardware is not allowed to transmit on this antenna. * * Each bit represents one antenna, starting with antenna 1 at the first * bit. Depending on which antennas are selected in the bitmap, 802.11n * drivers can derive which chainmasks to use (if all antennas belonging to * a particular chain are disabled this chain should be disabled) and if * a chain has diversity antennas wether diversity should be used or not. * HT capabilities (STBC, TX Beamforming, Antenna selection) can be * derived from the available chains after applying the antenna mask. * Non-802.11n drivers can derive wether to use diversity or not. * Drivers may reject configurations or RX/TX mask combinations they cannot * support by returning -EINVAL. * * @NL80211_ATTR_WIPHY_ANTENNA_RX: Bitmap of allowed antennas for receiving. * This can be used to mask out antennas which are not attached or should * not be used for receiving. If an antenna is not selected in this bitmap * the hardware should not be configured to receive on this antenna. * For a more detailed description see @NL80211_ATTR_WIPHY_ANTENNA_TX. * * @NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX: Bitmap of antennas which are available * for configuration as TX antennas via the above parameters. * * @NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX: Bitmap of antennas which are available * for configuration as RX antennas via the above parameters. * * @NL80211_ATTR_MCAST_RATE: Multicast tx rate (in 100 kbps) for IBSS * * @NL80211_ATTR_OFFCHANNEL_TX_OK: For management frame TX, the frame may be * transmitted on another channel when the channel given doesn't match * the current channel. If the current channel doesn't match and this * flag isn't set, the frame will be rejected. This is also used as an * nl80211 capability flag. * * @NL80211_ATTR_BSS_HT_OPMODE: HT operation mode (u16) * * @NL80211_ATTR_KEY_DEFAULT_TYPES: A nested attribute containing flags * attributes, specifying what a key should be set as default as. * See &enum nl80211_key_default_types. * * @NL80211_ATTR_MESH_SETUP: Optional mesh setup parameters. These cannot be * changed once the mesh is active. * @NL80211_ATTR_MESH_CONFIG: Mesh configuration parameters, a nested attribute * containing attributes from &enum nl80211_meshconf_params. * @NL80211_ATTR_SUPPORT_MESH_AUTH: Currently, this means the underlying driver * allows auth frames in a mesh to be passed to userspace for processing via * the @NL80211_MESH_SETUP_USERSPACE_AUTH flag. * @NL80211_ATTR_STA_PLINK_STATE: The state of a mesh peer link as defined in * &enum nl80211_plink_state. Used when userspace is driving the peer link * management state machine. @NL80211_MESH_SETUP_USERSPACE_AMPE or * @NL80211_MESH_SETUP_USERSPACE_MPM must be enabled. * * @NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED: indicates, as part of the wiphy * capabilities, the supported WoWLAN triggers * @NL80211_ATTR_WOWLAN_TRIGGERS: used by %NL80211_CMD_SET_WOWLAN to * indicate which WoW triggers should be enabled. This is also * used by %NL80211_CMD_GET_WOWLAN to get the currently enabled WoWLAN * triggers. * * @NL80211_ATTR_SCHED_SCAN_INTERVAL: Interval between scheduled scan * cycles, in msecs. * * @NL80211_ATTR_SCHED_SCAN_MATCH: Nested attribute with one or more * sets of attributes to match during scheduled scans. Only BSSs * that match any of the sets will be reported. These are * pass-thru filter rules. * For a match to succeed, the BSS must match all attributes of a * set. Since not every hardware supports matching all types of * attributes, there is no guarantee that the reported BSSs are * fully complying with the match sets and userspace needs to be * able to ignore them by itself. * Thus, the implementation is somewhat hardware-dependent, but * this is only an optimization and the userspace application * needs to handle all the non-filtered results anyway. * If the match attributes don't make sense when combined with * the values passed in @NL80211_ATTR_SCAN_SSIDS (eg. if an SSID * is included in the probe request, but the match attributes * will never let it go through), -EINVAL may be returned. * If ommited, no filtering is done. * * @NL80211_ATTR_INTERFACE_COMBINATIONS: Nested attribute listing the supported * interface combinations. In each nested item, it contains attributes * defined in &enum nl80211_if_combination_attrs. * @NL80211_ATTR_SOFTWARE_IFTYPES: Nested attribute (just like * %NL80211_ATTR_SUPPORTED_IFTYPES) containing the interface types that * are managed in software: interfaces of these types aren't subject to * any restrictions in their number or combinations. * * @NL80211_ATTR_REKEY_DATA: nested attribute containing the information * necessary for GTK rekeying in the device, see &enum nl80211_rekey_data. * * @NL80211_ATTR_SCAN_SUPP_RATES: rates per to be advertised as supported in scan, * nested array attribute containing an entry for each band, with the entry * being a list of supported rates as defined by IEEE 802.11 7.3.2.2 but * without the length restriction (at most %NL80211_MAX_SUPP_RATES). * * @NL80211_ATTR_HIDDEN_SSID: indicates whether SSID is to be hidden from Beacon * and Probe Response (when response to wildcard Probe Request); see * &enum nl80211_hidden_ssid, represented as a u32 * * @NL80211_ATTR_IE_PROBE_RESP: Information element(s) for Probe Response frame. * This is used with %NL80211_CMD_NEW_BEACON and %NL80211_CMD_SET_BEACON to * provide extra IEs (e.g., WPS/P2P IE) into Probe Response frames when the * driver (or firmware) replies to Probe Request frames. * @NL80211_ATTR_IE_ASSOC_RESP: Information element(s) for (Re)Association * Response frames. This is used with %NL80211_CMD_NEW_BEACON and * %NL80211_CMD_SET_BEACON to provide extra IEs (e.g., WPS/P2P IE) into * (Re)Association Response frames when the driver (or firmware) replies to * (Re)Association Request frames. * * @NL80211_ATTR_STA_WME: Nested attribute containing the wme configuration * of the station, see &enum nl80211_sta_wme_attr. * @NL80211_ATTR_SUPPORT_AP_UAPSD: the device supports uapsd when working * as AP. * * @NL80211_ATTR_ROAM_SUPPORT: Indicates whether the firmware is capable of * roaming to another AP in the same ESS if the signal lever is low. * * @NL80211_ATTR_PMKSA_CANDIDATE: Nested attribute containing the PMKSA caching * candidate information, see &enum nl80211_pmksa_candidate_attr. * * @NL80211_ATTR_TX_NO_CCK_RATE: Indicates whether to use CCK rate or not * for management frames transmission. In order to avoid p2p probe/action * frames are being transmitted at CCK rate in 2GHz band, the user space * applications use this attribute. * This attribute is used with %NL80211_CMD_TRIGGER_SCAN and * %NL80211_CMD_FRAME commands. * * @NL80211_ATTR_TDLS_ACTION: Low level TDLS action code (e.g. link setup * request, link setup confirm, link teardown, etc.). Values are * described in the TDLS (802.11z) specification. * @NL80211_ATTR_TDLS_DIALOG_TOKEN: Non-zero token for uniquely identifying a * TDLS conversation between two devices. * @NL80211_ATTR_TDLS_OPERATION: High level TDLS operation; see * &enum nl80211_tdls_operation, represented as a u8. * @NL80211_ATTR_TDLS_SUPPORT: A flag indicating the device can operate * as a TDLS peer sta. * @NL80211_ATTR_TDLS_EXTERNAL_SETUP: The TDLS discovery/setup and teardown * procedures should be performed by sending TDLS packets via * %NL80211_CMD_TDLS_MGMT. Otherwise %NL80211_CMD_TDLS_OPER should be * used for asking the driver to perform a TDLS operation. * * @NL80211_ATTR_DEVICE_AP_SME: This u32 attribute may be listed for devices * that have AP support to indicate that they have the AP SME integrated * with support for the features listed in this attribute, see * &enum nl80211_ap_sme_features. * * @NL80211_ATTR_DONT_WAIT_FOR_ACK: Used with %NL80211_CMD_FRAME, this tells * the driver to not wait for an acknowledgement. Note that due to this, * it will also not give a status callback nor return a cookie. This is * mostly useful for probe responses to save airtime. * * @NL80211_ATTR_FEATURE_FLAGS: This u32 attribute contains flags from * &enum nl80211_feature_flags and is advertised in wiphy information. * @NL80211_ATTR_PROBE_RESP_OFFLOAD: Indicates that the HW responds to probe * requests while operating in AP-mode. * This attribute holds a bitmap of the supported protocols for * offloading (see &enum nl80211_probe_resp_offload_support_attr). * * @NL80211_ATTR_PROBE_RESP: Probe Response template data. Contains the entire * probe-response frame. The DA field in the 802.11 header is zero-ed out, * to be filled by the FW. * @NL80211_ATTR_DISABLE_HT: Force HT capable interfaces to disable * this feature. Currently, only supported in mac80211 drivers. * @NL80211_ATTR_HT_CAPABILITY_MASK: Specify which bits of the * ATTR_HT_CAPABILITY to which attention should be paid. * Currently, only mac80211 NICs support this feature. * The values that may be configured are: * MCS rates, MAX-AMSDU, HT-20-40 and HT_CAP_SGI_40 * AMPDU density and AMPDU factor. * All values are treated as suggestions and may be ignored * by the driver as required. The actual values may be seen in * the station debugfs ht_caps file. * * @NL80211_ATTR_DFS_REGION: region for regulatory rules which this country * abides to when initiating radiation on DFS channels. A country maps * to one DFS region. * * @NL80211_ATTR_NOACK_MAP: This u16 bitmap contains the No Ack Policy of * up to 16 TIDs. * * @NL80211_ATTR_INACTIVITY_TIMEOUT: timeout value in seconds, this can be * used by the drivers which has MLME in firmware and does not have support * to report per station tx/rx activity to free up the staion entry from * the list. This needs to be used when the driver advertises the * capability to timeout the stations. * * @NL80211_ATTR_RX_SIGNAL_DBM: signal strength in dBm (as a 32-bit int); * this attribute is (depending on the driver capabilities) added to * received frames indicated with %NL80211_CMD_FRAME. * * @NL80211_ATTR_BG_SCAN_PERIOD: Background scan period in seconds * or 0 to disable background scan. * * @NL80211_ATTR_USER_REG_HINT_TYPE: type of regulatory hint passed from * userspace. If unset it is assumed the hint comes directly from * a user. If set code could specify exactly what type of source * was used to provide the hint. For the different types of * allowed user regulatory hints see nl80211_user_reg_hint_type. * * @NL80211_ATTR_CONN_FAILED_REASON: The reason for which AP has rejected * the connection request from a station. nl80211_connect_failed_reason * enum has different reasons of connection failure. * * @NL80211_ATTR_SAE_DATA: SAE elements in Authentication frames. This starts * with the Authentication transaction sequence number field. * * @NL80211_ATTR_VHT_CAPABILITY: VHT Capability information element (from * association request when used with NL80211_CMD_NEW_STATION) * * @NL80211_ATTR_SCAN_FLAGS: scan request control flags (u32) * * @NL80211_ATTR_P2P_CTWINDOW: P2P GO Client Traffic Window (u8), used with * the START_AP and SET_BSS commands * @NL80211_ATTR_P2P_OPPPS: P2P GO opportunistic PS (u8), used with the * START_AP and SET_BSS commands. This can have the values 0 or 1; * if not given in START_AP 0 is assumed, if not given in SET_BSS * no change is made. * * @NL80211_ATTR_LOCAL_MESH_POWER_MODE: local mesh STA link-specific power mode * defined in &enum nl80211_mesh_power_mode. * * @NL80211_ATTR_ACL_POLICY: ACL policy, see &enum nl80211_acl_policy, * carried in a u32 attribute * * @NL80211_ATTR_MAC_ADDRS: Array of nested MAC addresses, used for * MAC ACL. * * @NL80211_ATTR_MAC_ACL_MAX: u32 attribute to advertise the maximum * number of MAC addresses that a device can support for MAC * ACL. * * @NL80211_ATTR_RADAR_EVENT: Type of radar event for notification to userspace, * contains a value of enum nl80211_radar_event (u32). * * @NL80211_ATTR_EXT_CAPA: 802.11 extended capabilities that the kernel driver * has and handles. The format is the same as the IE contents. See * 802.11-2012 8.4.2.29 for more information. * @NL80211_ATTR_EXT_CAPA_MASK: Extended capabilities that the kernel driver * has set in the %NL80211_ATTR_EXT_CAPA value, for multibit fields. * * @NL80211_ATTR_STA_CAPABILITY: Station capabilities (u16) are advertised to * the driver, e.g., to enable TDLS power save (PU-APSD). * * @NL80211_ATTR_STA_EXT_CAPABILITY: Station extended capabilities are * advertised to the driver, e.g., to enable TDLS off channel operations * and PU-APSD. * * @NL80211_ATTR_PROTOCOL_FEATURES: global nl80211 feature flags, see * &enum nl80211_protocol_features, the attribute is a u32. * * @NL80211_ATTR_SPLIT_WIPHY_DUMP: flag attribute, userspace supports * receiving the data for a single wiphy split across multiple * messages, given with wiphy dump message * * @NL80211_ATTR_MDID: Mobility Domain Identifier * * @NL80211_ATTR_IE_RIC: Resource Information Container Information * Element * * @NL80211_ATTR_CRIT_PROT_ID: critical protocol identifier requiring increased * reliability, see &enum nl80211_crit_proto_id (u16). * @NL80211_ATTR_MAX_CRIT_PROT_DURATION: duration in milliseconds in which * the connection should have increased reliability (u16). * * @NL80211_ATTR_PEER_AID: Association ID for the peer TDLS station (u16). * This is similar to @NL80211_ATTR_STA_AID but with a difference of being * allowed to be used with the first @NL80211_CMD_SET_STATION command to * update a TDLS peer STA entry. * * @NL80211_ATTR_COALESCE_RULE: Coalesce rule information. * * @NL80211_ATTR_CH_SWITCH_COUNT: u32 attribute specifying the number of TBTT's * until the channel switch event. * @NL80211_ATTR_CH_SWITCH_BLOCK_TX: flag attribute specifying that transmission * must be blocked on the current channel (before the channel switch * operation). * @NL80211_ATTR_CSA_IES: Nested set of attributes containing the IE information * for the time while performing a channel switch. * @NL80211_ATTR_CSA_C_OFF_BEACON: Offset of the channel switch counter * field in the beacons tail (%NL80211_ATTR_BEACON_TAIL). * @NL80211_ATTR_CSA_C_OFF_PRESP: Offset of the channel switch counter * field in the probe response (%NL80211_ATTR_PROBE_RESP). * * @NL80211_ATTR_RXMGMT_FLAGS: flags for nl80211_send_mgmt(), u32. * As specified in the &enum nl80211_rxmgmt_flags. * * @NL80211_ATTR_STA_SUPPORTED_CHANNELS: array of supported channels. * * @NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES: array of supported * supported operating classes. * * @NL80211_ATTR_HANDLE_DFS: A flag indicating whether user space * controls DFS operation in IBSS mode. If the flag is included in * %NL80211_CMD_JOIN_IBSS request, the driver will allow use of DFS * channels and reports radar events to userspace. Userspace is required * to react to radar events, e.g. initiate a channel switch or leave the * IBSS network. * * @NL80211_ATTR_SUPPORT_5_MHZ: A flag indicating that the device supports * 5 MHz channel bandwidth. * @NL80211_ATTR_SUPPORT_10_MHZ: A flag indicating that the device supports * 10 MHz channel bandwidth. * * @NL80211_ATTR_OPMODE_NOTIF: Operating mode field from Operating Mode * Notification Element based on association request when used with * %NL80211_CMD_NEW_STATION; u8 attribute. * * @NL80211_ATTR_VENDOR_ID: The vendor ID, either a 24-bit OUI or, if * %NL80211_VENDOR_ID_IS_LINUX is set, a special Linux ID (not used yet) * @NL80211_ATTR_VENDOR_SUBCMD: vendor sub-command * @NL80211_ATTR_VENDOR_DATA: data for the vendor command, if any; this * attribute is also used for vendor command feature advertisement * @NL80211_ATTR_VENDOR_EVENTS: used for event list advertising in the wiphy * info, containing a nested array of possible events * * @NL80211_ATTR_QOS_MAP: IP DSCP mapping for Interworking QoS mapping. This * data is in the format defined for the payload of the QoS Map Set element * in IEEE Std 802.11-2012, 8.4.2.97. * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use */ enum nl80211_attrs { /* don't change the order or add anything between, this is ABI! */ NL80211_ATTR_UNSPEC, NL80211_ATTR_WIPHY, NL80211_ATTR_WIPHY_NAME, NL80211_ATTR_IFINDEX, NL80211_ATTR_IFNAME, NL80211_ATTR_IFTYPE, NL80211_ATTR_MAC, NL80211_ATTR_KEY_DATA, NL80211_ATTR_KEY_IDX, NL80211_ATTR_KEY_CIPHER, NL80211_ATTR_KEY_SEQ, NL80211_ATTR_KEY_DEFAULT, NL80211_ATTR_BEACON_INTERVAL, NL80211_ATTR_DTIM_PERIOD, NL80211_ATTR_BEACON_HEAD, NL80211_ATTR_BEACON_TAIL, NL80211_ATTR_STA_AID, NL80211_ATTR_STA_FLAGS, NL80211_ATTR_STA_LISTEN_INTERVAL, NL80211_ATTR_STA_SUPPORTED_RATES, NL80211_ATTR_STA_VLAN, NL80211_ATTR_STA_INFO, NL80211_ATTR_WIPHY_BANDS, NL80211_ATTR_MNTR_FLAGS, NL80211_ATTR_MESH_ID, NL80211_ATTR_STA_PLINK_ACTION, NL80211_ATTR_MPATH_NEXT_HOP, NL80211_ATTR_MPATH_INFO, NL80211_ATTR_BSS_CTS_PROT, NL80211_ATTR_BSS_SHORT_PREAMBLE, NL80211_ATTR_BSS_SHORT_SLOT_TIME, NL80211_ATTR_HT_CAPABILITY, NL80211_ATTR_SUPPORTED_IFTYPES, NL80211_ATTR_REG_ALPHA2, NL80211_ATTR_REG_RULES, NL80211_ATTR_MESH_CONFIG, NL80211_ATTR_BSS_BASIC_RATES, NL80211_ATTR_WIPHY_TXQ_PARAMS, NL80211_ATTR_WIPHY_FREQ, NL80211_ATTR_WIPHY_CHANNEL_TYPE, NL80211_ATTR_KEY_DEFAULT_MGMT, NL80211_ATTR_MGMT_SUBTYPE, NL80211_ATTR_IE, NL80211_ATTR_MAX_NUM_SCAN_SSIDS, NL80211_ATTR_SCAN_FREQUENCIES, NL80211_ATTR_SCAN_SSIDS, NL80211_ATTR_GENERATION, /* replaces old SCAN_GENERATION */ NL80211_ATTR_BSS, NL80211_ATTR_REG_INITIATOR, NL80211_ATTR_REG_TYPE, NL80211_ATTR_SUPPORTED_COMMANDS, NL80211_ATTR_FRAME, NL80211_ATTR_SSID, NL80211_ATTR_AUTH_TYPE, NL80211_ATTR_REASON_CODE, NL80211_ATTR_KEY_TYPE, NL80211_ATTR_MAX_SCAN_IE_LEN, NL80211_ATTR_CIPHER_SUITES, NL80211_ATTR_FREQ_BEFORE, NL80211_ATTR_FREQ_AFTER, NL80211_ATTR_FREQ_FIXED, NL80211_ATTR_WIPHY_RETRY_SHORT, NL80211_ATTR_WIPHY_RETRY_LONG, NL80211_ATTR_WIPHY_FRAG_THRESHOLD, NL80211_ATTR_WIPHY_RTS_THRESHOLD, NL80211_ATTR_TIMED_OUT, NL80211_ATTR_USE_MFP, NL80211_ATTR_STA_FLAGS2, NL80211_ATTR_CONTROL_PORT, NL80211_ATTR_TESTDATA, NL80211_ATTR_PRIVACY, NL80211_ATTR_DISCONNECTED_BY_AP, NL80211_ATTR_STATUS_CODE, NL80211_ATTR_CIPHER_SUITES_PAIRWISE, NL80211_ATTR_CIPHER_SUITE_GROUP, NL80211_ATTR_WPA_VERSIONS, NL80211_ATTR_AKM_SUITES, NL80211_ATTR_REQ_IE, NL80211_ATTR_RESP_IE, NL80211_ATTR_PREV_BSSID, NL80211_ATTR_KEY, NL80211_ATTR_KEYS, NL80211_ATTR_PID, NL80211_ATTR_4ADDR, NL80211_ATTR_SURVEY_INFO, NL80211_ATTR_PMKID, NL80211_ATTR_MAX_NUM_PMKIDS, NL80211_ATTR_DURATION, NL80211_ATTR_COOKIE, NL80211_ATTR_WIPHY_COVERAGE_CLASS, NL80211_ATTR_TX_RATES, NL80211_ATTR_FRAME_MATCH, NL80211_ATTR_ACK, NL80211_ATTR_PS_STATE, NL80211_ATTR_CQM, NL80211_ATTR_LOCAL_STATE_CHANGE, NL80211_ATTR_AP_ISOLATE, NL80211_ATTR_WIPHY_TX_POWER_SETTING, NL80211_ATTR_WIPHY_TX_POWER_LEVEL, NL80211_ATTR_TX_FRAME_TYPES, NL80211_ATTR_RX_FRAME_TYPES, NL80211_ATTR_FRAME_TYPE, NL80211_ATTR_CONTROL_PORT_ETHERTYPE, NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT, NL80211_ATTR_SUPPORT_IBSS_RSN, NL80211_ATTR_WIPHY_ANTENNA_TX, NL80211_ATTR_WIPHY_ANTENNA_RX, NL80211_ATTR_MCAST_RATE, NL80211_ATTR_OFFCHANNEL_TX_OK, NL80211_ATTR_BSS_HT_OPMODE, NL80211_ATTR_KEY_DEFAULT_TYPES, NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION, NL80211_ATTR_MESH_SETUP, NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX, NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX, NL80211_ATTR_SUPPORT_MESH_AUTH, NL80211_ATTR_STA_PLINK_STATE, NL80211_ATTR_WOWLAN_TRIGGERS, NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED, NL80211_ATTR_SCHED_SCAN_INTERVAL, NL80211_ATTR_INTERFACE_COMBINATIONS, NL80211_ATTR_SOFTWARE_IFTYPES, NL80211_ATTR_REKEY_DATA, NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS, NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN, NL80211_ATTR_SCAN_SUPP_RATES, NL80211_ATTR_HIDDEN_SSID, NL80211_ATTR_IE_PROBE_RESP, NL80211_ATTR_IE_ASSOC_RESP, NL80211_ATTR_STA_WME, NL80211_ATTR_SUPPORT_AP_UAPSD, NL80211_ATTR_ROAM_SUPPORT, NL80211_ATTR_SCHED_SCAN_MATCH, NL80211_ATTR_MAX_MATCH_SETS, NL80211_ATTR_PMKSA_CANDIDATE, NL80211_ATTR_TX_NO_CCK_RATE, NL80211_ATTR_TDLS_ACTION, NL80211_ATTR_TDLS_DIALOG_TOKEN, NL80211_ATTR_TDLS_OPERATION, NL80211_ATTR_TDLS_SUPPORT, NL80211_ATTR_TDLS_EXTERNAL_SETUP, NL80211_ATTR_DEVICE_AP_SME, NL80211_ATTR_DONT_WAIT_FOR_ACK, NL80211_ATTR_FEATURE_FLAGS, NL80211_ATTR_PROBE_RESP_OFFLOAD, NL80211_ATTR_PROBE_RESP, NL80211_ATTR_DFS_REGION, NL80211_ATTR_DISABLE_HT, NL80211_ATTR_HT_CAPABILITY_MASK, NL80211_ATTR_NOACK_MAP, NL80211_ATTR_INACTIVITY_TIMEOUT, NL80211_ATTR_RX_SIGNAL_DBM, NL80211_ATTR_BG_SCAN_PERIOD, NL80211_ATTR_WDEV, NL80211_ATTR_USER_REG_HINT_TYPE, NL80211_ATTR_CONN_FAILED_REASON, NL80211_ATTR_SAE_DATA, NL80211_ATTR_VHT_CAPABILITY, NL80211_ATTR_SCAN_FLAGS, NL80211_ATTR_CHANNEL_WIDTH, NL80211_ATTR_CENTER_FREQ1, NL80211_ATTR_CENTER_FREQ2, NL80211_ATTR_P2P_CTWINDOW, NL80211_ATTR_P2P_OPPPS, NL80211_ATTR_LOCAL_MESH_POWER_MODE, NL80211_ATTR_ACL_POLICY, NL80211_ATTR_MAC_ADDRS, NL80211_ATTR_MAC_ACL_MAX, NL80211_ATTR_RADAR_EVENT, NL80211_ATTR_EXT_CAPA, NL80211_ATTR_EXT_CAPA_MASK, NL80211_ATTR_STA_CAPABILITY, NL80211_ATTR_STA_EXT_CAPABILITY, NL80211_ATTR_PROTOCOL_FEATURES, NL80211_ATTR_SPLIT_WIPHY_DUMP, NL80211_ATTR_DISABLE_VHT, NL80211_ATTR_VHT_CAPABILITY_MASK, NL80211_ATTR_MDID, NL80211_ATTR_IE_RIC, NL80211_ATTR_CRIT_PROT_ID, NL80211_ATTR_MAX_CRIT_PROT_DURATION, NL80211_ATTR_PEER_AID, NL80211_ATTR_COALESCE_RULE, NL80211_ATTR_CH_SWITCH_COUNT, NL80211_ATTR_CH_SWITCH_BLOCK_TX, NL80211_ATTR_CSA_IES, NL80211_ATTR_CSA_C_OFF_BEACON, NL80211_ATTR_CSA_C_OFF_PRESP, NL80211_ATTR_RXMGMT_FLAGS, NL80211_ATTR_STA_SUPPORTED_CHANNELS, NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES, NL80211_ATTR_HANDLE_DFS, NL80211_ATTR_SUPPORT_5_MHZ, NL80211_ATTR_SUPPORT_10_MHZ, NL80211_ATTR_OPMODE_NOTIF, NL80211_ATTR_VENDOR_ID, NL80211_ATTR_VENDOR_SUBCMD, NL80211_ATTR_VENDOR_DATA, NL80211_ATTR_VENDOR_EVENTS, NL80211_ATTR_QOS_MAP, /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, NL80211_ATTR_MAX = __NL80211_ATTR_AFTER_LAST - 1 }; /* source-level API compatibility */ #define NL80211_ATTR_SCAN_GENERATION NL80211_ATTR_GENERATION #define NL80211_ATTR_MESH_PARAMS NL80211_ATTR_MESH_CONFIG /* * Allow user space programs to use #ifdef on new attributes by defining them * here */ #define NL80211_CMD_CONNECT NL80211_CMD_CONNECT #define NL80211_ATTR_HT_CAPABILITY NL80211_ATTR_HT_CAPABILITY #define NL80211_ATTR_BSS_BASIC_RATES NL80211_ATTR_BSS_BASIC_RATES #define NL80211_ATTR_WIPHY_TXQ_PARAMS NL80211_ATTR_WIPHY_TXQ_PARAMS #define NL80211_ATTR_WIPHY_FREQ NL80211_ATTR_WIPHY_FREQ #define NL80211_ATTR_WIPHY_CHANNEL_TYPE NL80211_ATTR_WIPHY_CHANNEL_TYPE #define NL80211_ATTR_MGMT_SUBTYPE NL80211_ATTR_MGMT_SUBTYPE #define NL80211_ATTR_IE NL80211_ATTR_IE #define NL80211_ATTR_REG_INITIATOR NL80211_ATTR_REG_INITIATOR #define NL80211_ATTR_REG_TYPE NL80211_ATTR_REG_TYPE #define NL80211_ATTR_FRAME NL80211_ATTR_FRAME #define NL80211_ATTR_SSID NL80211_ATTR_SSID #define NL80211_ATTR_AUTH_TYPE NL80211_ATTR_AUTH_TYPE #define NL80211_ATTR_REASON_CODE NL80211_ATTR_REASON_CODE #define NL80211_ATTR_CIPHER_SUITES_PAIRWISE NL80211_ATTR_CIPHER_SUITES_PAIRWISE #define NL80211_ATTR_CIPHER_SUITE_GROUP NL80211_ATTR_CIPHER_SUITE_GROUP #define NL80211_ATTR_WPA_VERSIONS NL80211_ATTR_WPA_VERSIONS #define NL80211_ATTR_AKM_SUITES NL80211_ATTR_AKM_SUITES #define NL80211_ATTR_KEY NL80211_ATTR_KEY #define NL80211_ATTR_KEYS NL80211_ATTR_KEYS #define NL80211_ATTR_FEATURE_FLAGS NL80211_ATTR_FEATURE_FLAGS #define NL80211_MAX_SUPP_RATES 32 #define NL80211_MAX_SUPP_HT_RATES 77 #define NL80211_MAX_SUPP_REG_RULES 32 #define NL80211_TKIP_DATA_OFFSET_ENCR_KEY 0 #define NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY 16 #define NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY 24 #define NL80211_HT_CAPABILITY_LEN 26 #define NL80211_VHT_CAPABILITY_LEN 12 #define NL80211_MAX_NR_CIPHER_SUITES 5 #define NL80211_MAX_NR_AKM_SUITES 2 #define NL80211_MIN_REMAIN_ON_CHANNEL_TIME 10 /* default RSSI threshold for scan results if none specified. */ #define NL80211_SCAN_RSSI_THOLD_OFF -300 #define NL80211_CQM_TXE_MAX_INTVL 1800 /** * enum nl80211_iftype - (virtual) interface types * * @NL80211_IFTYPE_UNSPECIFIED: unspecified type, driver decides * @NL80211_IFTYPE_ADHOC: independent BSS member * @NL80211_IFTYPE_STATION: managed BSS member * @NL80211_IFTYPE_AP: access point * @NL80211_IFTYPE_AP_VLAN: VLAN interface for access points; VLAN interfaces * are a bit special in that they must always be tied to a pre-existing * AP type interface. * @NL80211_IFTYPE_WDS: wireless distribution interface * @NL80211_IFTYPE_MONITOR: monitor interface receiving all frames * @NL80211_IFTYPE_MESH_POINT: mesh point * @NL80211_IFTYPE_P2P_CLIENT: P2P client * @NL80211_IFTYPE_P2P_GO: P2P group owner * @NL80211_IFTYPE_P2P_DEVICE: P2P device interface type, this is not a netdev * and therefore can't be created in the normal ways, use the * %NL80211_CMD_START_P2P_DEVICE and %NL80211_CMD_STOP_P2P_DEVICE * commands to create and destroy one * @NL80211_IFTYPE_MAX: highest interface type number currently defined * @NUM_NL80211_IFTYPES: number of defined interface types * * These values are used with the %NL80211_ATTR_IFTYPE * to set the type of an interface. * */ enum nl80211_iftype { NL80211_IFTYPE_UNSPECIFIED, NL80211_IFTYPE_ADHOC, NL80211_IFTYPE_STATION, NL80211_IFTYPE_AP, NL80211_IFTYPE_AP_VLAN, NL80211_IFTYPE_WDS, NL80211_IFTYPE_MONITOR, NL80211_IFTYPE_MESH_POINT, NL80211_IFTYPE_P2P_CLIENT, NL80211_IFTYPE_P2P_GO, NL80211_IFTYPE_P2P_DEVICE, /* keep last */ NUM_NL80211_IFTYPES, NL80211_IFTYPE_MAX = NUM_NL80211_IFTYPES - 1 }; /** * enum nl80211_sta_flags - station flags * * Station flags. When a station is added to an AP interface, it is * assumed to be already associated (and hence authenticated.) * * @__NL80211_STA_FLAG_INVALID: attribute number 0 is reserved * @NL80211_STA_FLAG_AUTHORIZED: station is authorized (802.1X) * @NL80211_STA_FLAG_SHORT_PREAMBLE: station is capable of receiving frames * with short barker preamble * @NL80211_STA_FLAG_WME: station is WME/QoS capable * @NL80211_STA_FLAG_MFP: station uses management frame protection * @NL80211_STA_FLAG_AUTHENTICATED: station is authenticated * @NL80211_STA_FLAG_TDLS_PEER: station is a TDLS peer -- this flag should * only be used in managed mode (even in the flags mask). Note that the * flag can't be changed, it is only valid while adding a station, and * attempts to change it will silently be ignored (rather than rejected * as errors.) * @NL80211_STA_FLAG_ASSOCIATED: station is associated; used with drivers * that support %NL80211_FEATURE_FULL_AP_CLIENT_STATE to transition a * previously added station into associated state * @NL80211_STA_FLAG_MAX: highest station flag number currently defined * @__NL80211_STA_FLAG_AFTER_LAST: internal use */ enum nl80211_sta_flags { __NL80211_STA_FLAG_INVALID, NL80211_STA_FLAG_AUTHORIZED, NL80211_STA_FLAG_SHORT_PREAMBLE, NL80211_STA_FLAG_WME, NL80211_STA_FLAG_MFP, NL80211_STA_FLAG_AUTHENTICATED, NL80211_STA_FLAG_TDLS_PEER, NL80211_STA_FLAG_ASSOCIATED, /* keep last */ __NL80211_STA_FLAG_AFTER_LAST, NL80211_STA_FLAG_MAX = __NL80211_STA_FLAG_AFTER_LAST - 1 }; #define NL80211_STA_FLAG_MAX_OLD_API NL80211_STA_FLAG_TDLS_PEER /** * struct nl80211_sta_flag_update - station flags mask/set * @mask: mask of station flags to set * @set: which values to set them to * * Both mask and set contain bits as per &enum nl80211_sta_flags. */ struct nl80211_sta_flag_update { __u32 mask; __u32 set; } __attribute__((packed)); /** * enum nl80211_rate_info - bitrate information * * These attribute types are used with %NL80211_STA_INFO_TXRATE * when getting information about the bitrate of a station. * There are 2 attributes for bitrate, a legacy one that represents * a 16-bit value, and new one that represents a 32-bit value. * If the rate value fits into 16 bit, both attributes are reported * with the same value. If the rate is too high to fit into 16 bits * (>6.5535Gbps) only 32-bit attribute is included. * User space tools encouraged to use the 32-bit attribute and fall * back to the 16-bit one for compatibility with older kernels. * * @__NL80211_RATE_INFO_INVALID: attribute number 0 is reserved * @NL80211_RATE_INFO_BITRATE: total bitrate (u16, 100kbit/s) * @NL80211_RATE_INFO_MCS: mcs index for 802.11n (u8) * @NL80211_RATE_INFO_40_MHZ_WIDTH: 40 MHz dualchannel bitrate * @NL80211_RATE_INFO_SHORT_GI: 400ns guard interval * @NL80211_RATE_INFO_BITRATE32: total bitrate (u32, 100kbit/s) * @NL80211_RATE_INFO_MAX: highest rate_info number currently defined * @NL80211_RATE_INFO_VHT_MCS: MCS index for VHT (u8) * @NL80211_RATE_INFO_VHT_NSS: number of streams in VHT (u8) * @NL80211_RATE_INFO_80_MHZ_WIDTH: 80 MHz VHT rate * @NL80211_RATE_INFO_80P80_MHZ_WIDTH: 80+80 MHz VHT rate * @NL80211_RATE_INFO_160_MHZ_WIDTH: 160 MHz VHT rate * @__NL80211_RATE_INFO_AFTER_LAST: internal use */ enum nl80211_rate_info { __NL80211_RATE_INFO_INVALID, NL80211_RATE_INFO_BITRATE, NL80211_RATE_INFO_MCS, NL80211_RATE_INFO_40_MHZ_WIDTH, NL80211_RATE_INFO_SHORT_GI, NL80211_RATE_INFO_BITRATE32, NL80211_RATE_INFO_VHT_MCS, NL80211_RATE_INFO_VHT_NSS, NL80211_RATE_INFO_80_MHZ_WIDTH, NL80211_RATE_INFO_80P80_MHZ_WIDTH, NL80211_RATE_INFO_160_MHZ_WIDTH, /* keep last */ __NL80211_RATE_INFO_AFTER_LAST, NL80211_RATE_INFO_MAX = __NL80211_RATE_INFO_AFTER_LAST - 1 }; /** * enum nl80211_sta_bss_param - BSS information collected by STA * * These attribute types are used with %NL80211_STA_INFO_BSS_PARAM * when getting information about the bitrate of a station. * * @__NL80211_STA_BSS_PARAM_INVALID: attribute number 0 is reserved * @NL80211_STA_BSS_PARAM_CTS_PROT: whether CTS protection is enabled (flag) * @NL80211_STA_BSS_PARAM_SHORT_PREAMBLE: whether short preamble is enabled * (flag) * @NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME: whether short slot time is enabled * (flag) * @NL80211_STA_BSS_PARAM_DTIM_PERIOD: DTIM period for beaconing (u8) * @NL80211_STA_BSS_PARAM_BEACON_INTERVAL: Beacon interval (u16) * @NL80211_STA_BSS_PARAM_MAX: highest sta_bss_param number currently defined * @__NL80211_STA_BSS_PARAM_AFTER_LAST: internal use */ enum nl80211_sta_bss_param { __NL80211_STA_BSS_PARAM_INVALID, NL80211_STA_BSS_PARAM_CTS_PROT, NL80211_STA_BSS_PARAM_SHORT_PREAMBLE, NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME, NL80211_STA_BSS_PARAM_DTIM_PERIOD, NL80211_STA_BSS_PARAM_BEACON_INTERVAL, /* keep last */ __NL80211_STA_BSS_PARAM_AFTER_LAST, NL80211_STA_BSS_PARAM_MAX = __NL80211_STA_BSS_PARAM_AFTER_LAST - 1 }; /** * enum nl80211_sta_info - station information * * These attribute types are used with %NL80211_ATTR_STA_INFO * when getting information about a station. * * @__NL80211_STA_INFO_INVALID: attribute number 0 is reserved * @NL80211_STA_INFO_INACTIVE_TIME: time since last activity (u32, msecs) * @NL80211_STA_INFO_RX_BYTES: total received bytes (u32, from this station) * @NL80211_STA_INFO_TX_BYTES: total transmitted bytes (u32, to this station) * @NL80211_STA_INFO_RX_BYTES64: total received bytes (u64, from this station) * @NL80211_STA_INFO_TX_BYTES64: total transmitted bytes (u64, to this station) * @NL80211_STA_INFO_SIGNAL: signal strength of last received PPDU (u8, dBm) * @NL80211_STA_INFO_TX_BITRATE: current unicast tx rate, nested attribute * containing info as possible, see &enum nl80211_rate_info * @NL80211_STA_INFO_RX_PACKETS: total received packet (u32, from this station) * @NL80211_STA_INFO_TX_PACKETS: total transmitted packets (u32, to this * station) * @NL80211_STA_INFO_TX_RETRIES: total retries (u32, to this station) * @NL80211_STA_INFO_TX_FAILED: total failed packets (u32, to this station) * @NL80211_STA_INFO_SIGNAL_AVG: signal strength average (u8, dBm) * @NL80211_STA_INFO_LLID: the station's mesh LLID * @NL80211_STA_INFO_PLID: the station's mesh PLID * @NL80211_STA_INFO_PLINK_STATE: peer link state for the station * (see %enum nl80211_plink_state) * @NL80211_STA_INFO_RX_BITRATE: last unicast data frame rx rate, nested * attribute, like NL80211_STA_INFO_TX_BITRATE. * @NL80211_STA_INFO_BSS_PARAM: current station's view of BSS, nested attribute * containing info as possible, see &enum nl80211_sta_bss_param * @NL80211_STA_INFO_CONNECTED_TIME: time since the station is last connected * @NL80211_STA_INFO_STA_FLAGS: Contains a struct nl80211_sta_flag_update. * @NL80211_STA_INFO_BEACON_LOSS: count of times beacon loss was detected (u32) * @NL80211_STA_INFO_T_OFFSET: timing offset with respect to this STA (s64) * @NL80211_STA_INFO_LOCAL_PM: local mesh STA link-specific power mode * @NL80211_STA_INFO_PEER_PM: peer mesh STA link-specific power mode * @NL80211_STA_INFO_NONPEER_PM: neighbor mesh STA power save mode towards * non-peer STA * @NL80211_STA_INFO_CHAIN_SIGNAL: per-chain signal strength of last PPDU * Contains a nested array of signal strength attributes (u8, dBm) * @NL80211_STA_INFO_CHAIN_SIGNAL_AVG: per-chain signal strength average * Same format as NL80211_STA_INFO_CHAIN_SIGNAL. * @__NL80211_STA_INFO_AFTER_LAST: internal * @NL80211_STA_INFO_MAX: highest possible station info attribute */ enum nl80211_sta_info { __NL80211_STA_INFO_INVALID, NL80211_STA_INFO_INACTIVE_TIME, NL80211_STA_INFO_RX_BYTES, NL80211_STA_INFO_TX_BYTES, NL80211_STA_INFO_LLID, NL80211_STA_INFO_PLID, NL80211_STA_INFO_PLINK_STATE, NL80211_STA_INFO_SIGNAL, NL80211_STA_INFO_TX_BITRATE, NL80211_STA_INFO_RX_PACKETS, NL80211_STA_INFO_TX_PACKETS, NL80211_STA_INFO_TX_RETRIES, NL80211_STA_INFO_TX_FAILED, NL80211_STA_INFO_SIGNAL_AVG, NL80211_STA_INFO_RX_BITRATE, NL80211_STA_INFO_BSS_PARAM, NL80211_STA_INFO_CONNECTED_TIME, NL80211_STA_INFO_STA_FLAGS, NL80211_STA_INFO_BEACON_LOSS, NL80211_STA_INFO_T_OFFSET, NL80211_STA_INFO_LOCAL_PM, NL80211_STA_INFO_PEER_PM, NL80211_STA_INFO_NONPEER_PM, NL80211_STA_INFO_RX_BYTES64, NL80211_STA_INFO_TX_BYTES64, NL80211_STA_INFO_CHAIN_SIGNAL, NL80211_STA_INFO_CHAIN_SIGNAL_AVG, /* keep last */ __NL80211_STA_INFO_AFTER_LAST, NL80211_STA_INFO_MAX = __NL80211_STA_INFO_AFTER_LAST - 1 }; /** * enum nl80211_mpath_flags - nl80211 mesh path flags * * @NL80211_MPATH_FLAG_ACTIVE: the mesh path is active * @NL80211_MPATH_FLAG_RESOLVING: the mesh path discovery process is running * @NL80211_MPATH_FLAG_SN_VALID: the mesh path contains a valid SN * @NL80211_MPATH_FLAG_FIXED: the mesh path has been manually set * @NL80211_MPATH_FLAG_RESOLVED: the mesh path discovery process succeeded */ enum nl80211_mpath_flags { NL80211_MPATH_FLAG_ACTIVE = 1<<0, NL80211_MPATH_FLAG_RESOLVING = 1<<1, NL80211_MPATH_FLAG_SN_VALID = 1<<2, NL80211_MPATH_FLAG_FIXED = 1<<3, NL80211_MPATH_FLAG_RESOLVED = 1<<4, }; /** * enum nl80211_mpath_info - mesh path information * * These attribute types are used with %NL80211_ATTR_MPATH_INFO when getting * information about a mesh path. * * @__NL80211_MPATH_INFO_INVALID: attribute number 0 is reserved * @NL80211_MPATH_INFO_FRAME_QLEN: number of queued frames for this destination * @NL80211_MPATH_INFO_SN: destination sequence number * @NL80211_MPATH_INFO_METRIC: metric (cost) of this mesh path * @NL80211_MPATH_INFO_EXPTIME: expiration time for the path, in msec from now * @NL80211_MPATH_INFO_FLAGS: mesh path flags, enumerated in * &enum nl80211_mpath_flags; * @NL80211_MPATH_INFO_DISCOVERY_TIMEOUT: total path discovery timeout, in msec * @NL80211_MPATH_INFO_DISCOVERY_RETRIES: mesh path discovery retries * @NL80211_MPATH_INFO_MAX: highest mesh path information attribute number * currently defind * @__NL80211_MPATH_INFO_AFTER_LAST: internal use */ enum nl80211_mpath_info { __NL80211_MPATH_INFO_INVALID, NL80211_MPATH_INFO_FRAME_QLEN, NL80211_MPATH_INFO_SN, NL80211_MPATH_INFO_METRIC, NL80211_MPATH_INFO_EXPTIME, NL80211_MPATH_INFO_FLAGS, NL80211_MPATH_INFO_DISCOVERY_TIMEOUT, NL80211_MPATH_INFO_DISCOVERY_RETRIES, /* keep last */ __NL80211_MPATH_INFO_AFTER_LAST, NL80211_MPATH_INFO_MAX = __NL80211_MPATH_INFO_AFTER_LAST - 1 }; /** * enum nl80211_band_attr - band attributes * @__NL80211_BAND_ATTR_INVALID: attribute number 0 is reserved * @NL80211_BAND_ATTR_FREQS: supported frequencies in this band, * an array of nested frequency attributes * @NL80211_BAND_ATTR_RATES: supported bitrates in this band, * an array of nested bitrate attributes * @NL80211_BAND_ATTR_HT_MCS_SET: 16-byte attribute containing the MCS set as * defined in 802.11n * @NL80211_BAND_ATTR_HT_CAPA: HT capabilities, as in the HT information IE * @NL80211_BAND_ATTR_HT_AMPDU_FACTOR: A-MPDU factor, as in 11n * @NL80211_BAND_ATTR_HT_AMPDU_DENSITY: A-MPDU density, as in 11n * @NL80211_BAND_ATTR_VHT_MCS_SET: 32-byte attribute containing the MCS set as * defined in 802.11ac * @NL80211_BAND_ATTR_VHT_CAPA: VHT capabilities, as in the HT information IE * @NL80211_BAND_ATTR_MAX: highest band attribute currently defined * @__NL80211_BAND_ATTR_AFTER_LAST: internal use */ enum nl80211_band_attr { __NL80211_BAND_ATTR_INVALID, NL80211_BAND_ATTR_FREQS, NL80211_BAND_ATTR_RATES, NL80211_BAND_ATTR_HT_MCS_SET, NL80211_BAND_ATTR_HT_CAPA, NL80211_BAND_ATTR_HT_AMPDU_FACTOR, NL80211_BAND_ATTR_HT_AMPDU_DENSITY, NL80211_BAND_ATTR_VHT_MCS_SET, NL80211_BAND_ATTR_VHT_CAPA, /* keep last */ __NL80211_BAND_ATTR_AFTER_LAST, NL80211_BAND_ATTR_MAX = __NL80211_BAND_ATTR_AFTER_LAST - 1 }; #define NL80211_BAND_ATTR_HT_CAPA NL80211_BAND_ATTR_HT_CAPA /** * enum nl80211_frequency_attr - frequency attributes * @__NL80211_FREQUENCY_ATTR_INVALID: attribute number 0 is reserved * @NL80211_FREQUENCY_ATTR_FREQ: Frequency in MHz * @NL80211_FREQUENCY_ATTR_DISABLED: Channel is disabled in current * regulatory domain. * @NL80211_FREQUENCY_ATTR_NO_IR: no mechanisms that initiate radiation * are permitted on this channel, this includes sending probe * requests, or modes of operation that require beaconing. * @NL80211_FREQUENCY_ATTR_RADAR: Radar detection is mandatory * on this channel in current regulatory domain. * @NL80211_FREQUENCY_ATTR_MAX_TX_POWER: Maximum transmission power in mBm * (100 * dBm). * @NL80211_FREQUENCY_ATTR_DFS_STATE: current state for DFS * (enum nl80211_dfs_state) * @NL80211_FREQUENCY_ATTR_DFS_TIME: time in miliseconds for how long * this channel is in this DFS state. * @NL80211_FREQUENCY_ATTR_NO_HT40_MINUS: HT40- isn't possible with this * channel as the control channel * @NL80211_FREQUENCY_ATTR_NO_HT40_PLUS: HT40+ isn't possible with this * channel as the control channel * @NL80211_FREQUENCY_ATTR_NO_80MHZ: any 80 MHz channel using this channel * as the primary or any of the secondary channels isn't possible, * this includes 80+80 channels * @NL80211_FREQUENCY_ATTR_NO_160MHZ: any 160 MHz (but not 80+80) channel * using this channel as the primary or any of the secondary channels * isn't possible * @NL80211_FREQUENCY_ATTR_MAX: highest frequency attribute number * currently defined * @__NL80211_FREQUENCY_ATTR_AFTER_LAST: internal use */ enum nl80211_frequency_attr { __NL80211_FREQUENCY_ATTR_INVALID, NL80211_FREQUENCY_ATTR_FREQ, NL80211_FREQUENCY_ATTR_DISABLED, NL80211_FREQUENCY_ATTR_NO_IR, __NL80211_FREQUENCY_ATTR_NO_IBSS, NL80211_FREQUENCY_ATTR_RADAR, NL80211_FREQUENCY_ATTR_MAX_TX_POWER, NL80211_FREQUENCY_ATTR_DFS_STATE, NL80211_FREQUENCY_ATTR_DFS_TIME, NL80211_FREQUENCY_ATTR_NO_HT40_MINUS, NL80211_FREQUENCY_ATTR_NO_HT40_PLUS, NL80211_FREQUENCY_ATTR_NO_80MHZ, NL80211_FREQUENCY_ATTR_NO_160MHZ, /* keep last */ __NL80211_FREQUENCY_ATTR_AFTER_LAST, NL80211_FREQUENCY_ATTR_MAX = __NL80211_FREQUENCY_ATTR_AFTER_LAST - 1 }; #define NL80211_FREQUENCY_ATTR_MAX_TX_POWER NL80211_FREQUENCY_ATTR_MAX_TX_POWER #define NL80211_FREQUENCY_ATTR_PASSIVE_SCAN NL80211_FREQUENCY_ATTR_NO_IR #define NL80211_FREQUENCY_ATTR_NO_IBSS NL80211_FREQUENCY_ATTR_NO_IR #define NL80211_FREQUENCY_ATTR_NO_IR NL80211_FREQUENCY_ATTR_NO_IR /** * enum nl80211_bitrate_attr - bitrate attributes * @__NL80211_BITRATE_ATTR_INVALID: attribute number 0 is reserved * @NL80211_BITRATE_ATTR_RATE: Bitrate in units of 100 kbps * @NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE: Short preamble supported * in 2.4 GHz band. * @NL80211_BITRATE_ATTR_MAX: highest bitrate attribute number * currently defined * @__NL80211_BITRATE_ATTR_AFTER_LAST: internal use */ enum nl80211_bitrate_attr { __NL80211_BITRATE_ATTR_INVALID, NL80211_BITRATE_ATTR_RATE, NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE, /* keep last */ __NL80211_BITRATE_ATTR_AFTER_LAST, NL80211_BITRATE_ATTR_MAX = __NL80211_BITRATE_ATTR_AFTER_LAST - 1 }; /** * enum nl80211_initiator - Indicates the initiator of a reg domain request * @NL80211_REGDOM_SET_BY_CORE: Core queried CRDA for a dynamic world * regulatory domain. * @NL80211_REGDOM_SET_BY_USER: User asked the wireless core to set the * regulatory domain. * @NL80211_REGDOM_SET_BY_DRIVER: a wireless drivers has hinted to the * wireless core it thinks its knows the regulatory domain we should be in. * @NL80211_REGDOM_SET_BY_COUNTRY_IE: the wireless core has received an * 802.11 country information element with regulatory information it * thinks we should consider. cfg80211 only processes the country * code from the IE, and relies on the regulatory domain information * structure passed by userspace (CRDA) from our wireless-regdb. * If a channel is enabled but the country code indicates it should * be disabled we disable the channel and re-enable it upon disassociation. */ enum nl80211_reg_initiator { NL80211_REGDOM_SET_BY_CORE, NL80211_REGDOM_SET_BY_USER, NL80211_REGDOM_SET_BY_DRIVER, NL80211_REGDOM_SET_BY_COUNTRY_IE, }; /** * enum nl80211_reg_type - specifies the type of regulatory domain * @NL80211_REGDOM_TYPE_COUNTRY: the regulatory domain set is one that pertains * to a specific country. When this is set you can count on the * ISO / IEC 3166 alpha2 country code being valid. * @NL80211_REGDOM_TYPE_WORLD: the regulatory set domain is the world regulatory * domain. * @NL80211_REGDOM_TYPE_CUSTOM_WORLD: the regulatory domain set is a custom * driver specific world regulatory domain. These do not apply system-wide * and are only applicable to the individual devices which have requested * them to be applied. * @NL80211_REGDOM_TYPE_INTERSECTION: the regulatory domain set is the product * of an intersection between two regulatory domains -- the previously * set regulatory domain on the system and the last accepted regulatory * domain request to be processed. */ enum nl80211_reg_type { NL80211_REGDOM_TYPE_COUNTRY, NL80211_REGDOM_TYPE_WORLD, NL80211_REGDOM_TYPE_CUSTOM_WORLD, NL80211_REGDOM_TYPE_INTERSECTION, }; /** * enum nl80211_reg_rule_attr - regulatory rule attributes * @__NL80211_REG_RULE_ATTR_INVALID: attribute number 0 is reserved * @NL80211_ATTR_REG_RULE_FLAGS: a set of flags which specify additional * considerations for a given frequency range. These are the * &enum nl80211_reg_rule_flags. * @NL80211_ATTR_FREQ_RANGE_START: starting frequencry for the regulatory * rule in KHz. This is not a center of frequency but an actual regulatory * band edge. * @NL80211_ATTR_FREQ_RANGE_END: ending frequency for the regulatory rule * in KHz. This is not a center a frequency but an actual regulatory * band edge. * @NL80211_ATTR_FREQ_RANGE_MAX_BW: maximum allowed bandwidth for this * frequency range, in KHz. * @NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN: the maximum allowed antenna gain * for a given frequency range. The value is in mBi (100 * dBi). * If you don't have one then don't send this. * @NL80211_ATTR_POWER_RULE_MAX_EIRP: the maximum allowed EIRP for * a given frequency range. The value is in mBm (100 * dBm). * @NL80211_REG_RULE_ATTR_MAX: highest regulatory rule attribute number * currently defined * @__NL80211_REG_RULE_ATTR_AFTER_LAST: internal use */ enum nl80211_reg_rule_attr { __NL80211_REG_RULE_ATTR_INVALID, NL80211_ATTR_REG_RULE_FLAGS, NL80211_ATTR_FREQ_RANGE_START, NL80211_ATTR_FREQ_RANGE_END, NL80211_ATTR_FREQ_RANGE_MAX_BW, NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN, NL80211_ATTR_POWER_RULE_MAX_EIRP, /* keep last */ __NL80211_REG_RULE_ATTR_AFTER_LAST, NL80211_REG_RULE_ATTR_MAX = __NL80211_REG_RULE_ATTR_AFTER_LAST - 1 }; /** * enum nl80211_sched_scan_match_attr - scheduled scan match attributes * @__NL80211_SCHED_SCAN_MATCH_ATTR_INVALID: attribute number 0 is reserved * @NL80211_SCHED_SCAN_MATCH_ATTR_SSID: SSID to be used for matching, * only report BSS with matching SSID. * @NL80211_SCHED_SCAN_MATCH_ATTR_RSSI: RSSI threshold (in dBm) for reporting a * BSS in scan results. Filtering is turned off if not specified. * @NL80211_SCHED_SCAN_MATCH_ATTR_MAX: highest scheduled scan filter * attribute number currently defined * @__NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST: internal use */ enum nl80211_sched_scan_match_attr { __NL80211_SCHED_SCAN_MATCH_ATTR_INVALID, NL80211_SCHED_SCAN_MATCH_ATTR_SSID, NL80211_SCHED_SCAN_MATCH_ATTR_RSSI, /* keep last */ __NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST, NL80211_SCHED_SCAN_MATCH_ATTR_MAX = __NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST - 1 }; /* only for backward compatibility */ #define NL80211_ATTR_SCHED_SCAN_MATCH_SSID NL80211_SCHED_SCAN_MATCH_ATTR_SSID /** * enum nl80211_reg_rule_flags - regulatory rule flags * * @NL80211_RRF_NO_OFDM: OFDM modulation not allowed * @NL80211_RRF_NO_CCK: CCK modulation not allowed * @NL80211_RRF_NO_INDOOR: indoor operation not allowed * @NL80211_RRF_NO_OUTDOOR: outdoor operation not allowed * @NL80211_RRF_DFS: DFS support is required to be used * @NL80211_RRF_PTP_ONLY: this is only for Point To Point links * @NL80211_RRF_PTMP_ONLY: this is only for Point To Multi Point links * @NL80211_RRF_NO_IR: no mechanisms that initiate radiation are allowed, * this includes probe requests or modes of operation that require * beaconing. */ enum nl80211_reg_rule_flags { NL80211_RRF_NO_OFDM = 1<<0, NL80211_RRF_NO_CCK = 1<<1, NL80211_RRF_NO_INDOOR = 1<<2, NL80211_RRF_NO_OUTDOOR = 1<<3, NL80211_RRF_DFS = 1<<4, NL80211_RRF_PTP_ONLY = 1<<5, NL80211_RRF_PTMP_ONLY = 1<<6, NL80211_RRF_NO_IR = 1<<7, __NL80211_RRF_NO_IBSS = 1<<8, }; #define NL80211_RRF_PASSIVE_SCAN NL80211_RRF_NO_IR #define NL80211_RRF_NO_IBSS NL80211_RRF_NO_IR #define NL80211_RRF_NO_IR NL80211_RRF_NO_IR /* For backport compatibility with older userspace */ #define NL80211_RRF_NO_IR_ALL (NL80211_RRF_NO_IR | __NL80211_RRF_NO_IBSS) /** * enum nl80211_dfs_regions - regulatory DFS regions * * @NL80211_DFS_UNSET: Country has no DFS master region specified * @NL80211_DFS_FCC: Country follows DFS master rules from FCC * @NL80211_DFS_ETSI: Country follows DFS master rules from ETSI * @NL80211_DFS_JP: Country follows DFS master rules from JP/MKK/Telec */ enum nl80211_dfs_regions { NL80211_DFS_UNSET = 0, NL80211_DFS_FCC = 1, NL80211_DFS_ETSI = 2, NL80211_DFS_JP = 3, }; /** * enum nl80211_user_reg_hint_type - type of user regulatory hint * * @NL80211_USER_REG_HINT_USER: a user sent the hint. This is always * assumed if the attribute is not set. * @NL80211_USER_REG_HINT_CELL_BASE: the hint comes from a cellular * base station. Device drivers that have been tested to work * properly to support this type of hint can enable these hints * by setting the NL80211_FEATURE_CELL_BASE_REG_HINTS feature * capability on the struct wiphy. The wireless core will * ignore all cell base station hints until at least one device * present has been registered with the wireless core that * has listed NL80211_FEATURE_CELL_BASE_REG_HINTS as a * supported feature. */ enum nl80211_user_reg_hint_type { NL80211_USER_REG_HINT_USER = 0, NL80211_USER_REG_HINT_CELL_BASE = 1, }; /** * enum nl80211_survey_info - survey information * * These attribute types are used with %NL80211_ATTR_SURVEY_INFO * when getting information about a survey. * * @__NL80211_SURVEY_INFO_INVALID: attribute number 0 is reserved * @NL80211_SURVEY_INFO_FREQUENCY: center frequency of channel * @NL80211_SURVEY_INFO_NOISE: noise level of channel (u8, dBm) * @NL80211_SURVEY_INFO_IN_USE: channel is currently being used * @NL80211_SURVEY_INFO_CHANNEL_TIME: amount of time (in ms) that the radio * spent on this channel * @NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY: amount of the time the primary * channel was sensed busy (either due to activity or energy detect) * @NL80211_SURVEY_INFO_CHANNEL_TIME_EXT_BUSY: amount of time the extension * channel was sensed busy * @NL80211_SURVEY_INFO_CHANNEL_TIME_RX: amount of time the radio spent * receiving data * @NL80211_SURVEY_INFO_CHANNEL_TIME_TX: amount of time the radio spent * transmitting data * @NL80211_SURVEY_INFO_MAX: highest survey info attribute number * currently defined * @__NL80211_SURVEY_INFO_AFTER_LAST: internal use */ enum nl80211_survey_info { __NL80211_SURVEY_INFO_INVALID, NL80211_SURVEY_INFO_FREQUENCY, NL80211_SURVEY_INFO_NOISE, NL80211_SURVEY_INFO_IN_USE, NL80211_SURVEY_INFO_CHANNEL_TIME, NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY, NL80211_SURVEY_INFO_CHANNEL_TIME_EXT_BUSY, NL80211_SURVEY_INFO_CHANNEL_TIME_RX, NL80211_SURVEY_INFO_CHANNEL_TIME_TX, /* keep last */ __NL80211_SURVEY_INFO_AFTER_LAST, NL80211_SURVEY_INFO_MAX = __NL80211_SURVEY_INFO_AFTER_LAST - 1 }; /** * enum nl80211_mntr_flags - monitor configuration flags * * Monitor configuration flags. * * @__NL80211_MNTR_FLAG_INVALID: reserved * * @NL80211_MNTR_FLAG_FCSFAIL: pass frames with bad FCS * @NL80211_MNTR_FLAG_PLCPFAIL: pass frames with bad PLCP * @NL80211_MNTR_FLAG_CONTROL: pass control frames * @NL80211_MNTR_FLAG_OTHER_BSS: disable BSSID filtering * @NL80211_MNTR_FLAG_COOK_FRAMES: report frames after processing. * overrides all other flags. * @NL80211_MNTR_FLAG_ACTIVE: use the configured MAC address * and ACK incoming unicast packets. * * @__NL80211_MNTR_FLAG_AFTER_LAST: internal use * @NL80211_MNTR_FLAG_MAX: highest possible monitor flag */ enum nl80211_mntr_flags { __NL80211_MNTR_FLAG_INVALID, NL80211_MNTR_FLAG_FCSFAIL, NL80211_MNTR_FLAG_PLCPFAIL, NL80211_MNTR_FLAG_CONTROL, NL80211_MNTR_FLAG_OTHER_BSS, NL80211_MNTR_FLAG_COOK_FRAMES, NL80211_MNTR_FLAG_ACTIVE, /* keep last */ __NL80211_MNTR_FLAG_AFTER_LAST, NL80211_MNTR_FLAG_MAX = __NL80211_MNTR_FLAG_AFTER_LAST - 1 }; /** * enum nl80211_mesh_power_mode - mesh power save modes * * @NL80211_MESH_POWER_UNKNOWN: The mesh power mode of the mesh STA is * not known or has not been set yet. * @NL80211_MESH_POWER_ACTIVE: Active mesh power mode. The mesh STA is * in Awake state all the time. * @NL80211_MESH_POWER_LIGHT_SLEEP: Light sleep mode. The mesh STA will * alternate between Active and Doze states, but will wake up for * neighbor's beacons. * @NL80211_MESH_POWER_DEEP_SLEEP: Deep sleep mode. The mesh STA will * alternate between Active and Doze states, but may not wake up * for neighbor's beacons. * * @__NL80211_MESH_POWER_AFTER_LAST - internal use * @NL80211_MESH_POWER_MAX - highest possible power save level */ enum nl80211_mesh_power_mode { NL80211_MESH_POWER_UNKNOWN, NL80211_MESH_POWER_ACTIVE, NL80211_MESH_POWER_LIGHT_SLEEP, NL80211_MESH_POWER_DEEP_SLEEP, __NL80211_MESH_POWER_AFTER_LAST, NL80211_MESH_POWER_MAX = __NL80211_MESH_POWER_AFTER_LAST - 1 }; /** * enum nl80211_meshconf_params - mesh configuration parameters * * Mesh configuration parameters. These can be changed while the mesh is * active. * * @__NL80211_MESHCONF_INVALID: internal use * * @NL80211_MESHCONF_RETRY_TIMEOUT: specifies the initial retry timeout in * millisecond units, used by the Peer Link Open message * * @NL80211_MESHCONF_CONFIRM_TIMEOUT: specifies the initial confirm timeout, in * millisecond units, used by the peer link management to close a peer link * * @NL80211_MESHCONF_HOLDING_TIMEOUT: specifies the holding timeout, in * millisecond units * * @NL80211_MESHCONF_MAX_PEER_LINKS: maximum number of peer links allowed * on this mesh interface * * @NL80211_MESHCONF_MAX_RETRIES: specifies the maximum number of peer link * open retries that can be sent to establish a new peer link instance in a * mesh * * @NL80211_MESHCONF_TTL: specifies the value of TTL field set at a source mesh * point. * * @NL80211_MESHCONF_AUTO_OPEN_PLINKS: whether we should automatically open * peer links when we detect compatible mesh peers. Disabled if * @NL80211_MESH_SETUP_USERSPACE_MPM or @NL80211_MESH_SETUP_USERSPACE_AMPE are * set. * * @NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES: the number of action frames * containing a PREQ that an MP can send to a particular destination (path * target) * * @NL80211_MESHCONF_PATH_REFRESH_TIME: how frequently to refresh mesh paths * (in milliseconds) * * @NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT: minimum length of time to wait * until giving up on a path discovery (in milliseconds) * * @NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT: The time (in TUs) for which mesh * points receiving a PREQ shall consider the forwarding information from * the root to be valid. (TU = time unit) * * @NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL: The minimum interval of time (in * TUs) during which an MP can send only one action frame containing a PREQ * reference element * * @NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME: The interval of time (in TUs) * that it takes for an HWMP information element to propagate across the * mesh * * @NL80211_MESHCONF_HWMP_ROOTMODE: whether root mode is enabled or not * * @NL80211_MESHCONF_ELEMENT_TTL: specifies the value of TTL field set at a * source mesh point for path selection elements. * * @NL80211_MESHCONF_HWMP_RANN_INTERVAL: The interval of time (in TUs) between * root announcements are transmitted. * * @NL80211_MESHCONF_GATE_ANNOUNCEMENTS: Advertise that this mesh station has * access to a broader network beyond the MBSS. This is done via Root * Announcement frames. * * @NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL: The minimum interval of time (in * TUs) during which a mesh STA can send only one Action frame containing a * PERR element. * * @NL80211_MESHCONF_FORWARDING: set Mesh STA as forwarding or non-forwarding * or forwarding entity (default is TRUE - forwarding entity) * * @NL80211_MESHCONF_RSSI_THRESHOLD: RSSI threshold in dBm. This specifies the * threshold for average signal strength of candidate station to establish * a peer link. * * @NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR: maximum number of neighbors * to synchronize to for 11s default synchronization method * (see 11C.12.2.2) * * @NL80211_MESHCONF_HT_OPMODE: set mesh HT protection mode. * * @NL80211_MESHCONF_ATTR_MAX: highest possible mesh configuration attribute * * @NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT: The time (in TUs) for * which mesh STAs receiving a proactive PREQ shall consider the forwarding * information to the root mesh STA to be valid. * * @NL80211_MESHCONF_HWMP_ROOT_INTERVAL: The interval of time (in TUs) between * proactive PREQs are transmitted. * * @NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL: The minimum interval of time * (in TUs) during which a mesh STA can send only one Action frame * containing a PREQ element for root path confirmation. * * @NL80211_MESHCONF_POWER_MODE: Default mesh power mode for new peer links. * type &enum nl80211_mesh_power_mode (u32) * * @NL80211_MESHCONF_AWAKE_WINDOW: awake window duration (in TUs) * * @NL80211_MESHCONF_PLINK_TIMEOUT: If no tx activity is seen from a STA we've * established peering with for longer than this time (in seconds), then * remove it from the STA's list of peers. Default is 30 minutes. * * @__NL80211_MESHCONF_ATTR_AFTER_LAST: internal use */ enum nl80211_meshconf_params { __NL80211_MESHCONF_INVALID, NL80211_MESHCONF_RETRY_TIMEOUT, NL80211_MESHCONF_CONFIRM_TIMEOUT, NL80211_MESHCONF_HOLDING_TIMEOUT, NL80211_MESHCONF_MAX_PEER_LINKS, NL80211_MESHCONF_MAX_RETRIES, NL80211_MESHCONF_TTL, NL80211_MESHCONF_AUTO_OPEN_PLINKS, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES, NL80211_MESHCONF_PATH_REFRESH_TIME, NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT, NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT, NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME, NL80211_MESHCONF_HWMP_ROOTMODE, NL80211_MESHCONF_ELEMENT_TTL, NL80211_MESHCONF_HWMP_RANN_INTERVAL, NL80211_MESHCONF_GATE_ANNOUNCEMENTS, NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL, NL80211_MESHCONF_FORWARDING, NL80211_MESHCONF_RSSI_THRESHOLD, NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR, NL80211_MESHCONF_HT_OPMODE, NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT, NL80211_MESHCONF_HWMP_ROOT_INTERVAL, NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL, NL80211_MESHCONF_POWER_MODE, NL80211_MESHCONF_AWAKE_WINDOW, NL80211_MESHCONF_PLINK_TIMEOUT, /* keep last */ __NL80211_MESHCONF_ATTR_AFTER_LAST, NL80211_MESHCONF_ATTR_MAX = __NL80211_MESHCONF_ATTR_AFTER_LAST - 1 }; /** * enum nl80211_mesh_setup_params - mesh setup parameters * * Mesh setup parameters. These are used to start/join a mesh and cannot be * changed while the mesh is active. * * @__NL80211_MESH_SETUP_INVALID: Internal use * * @NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL: Enable this option to use a * vendor specific path selection algorithm or disable it to use the * default HWMP. * * @NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC: Enable this option to use a * vendor specific path metric or disable it to use the default Airtime * metric. * * @NL80211_MESH_SETUP_IE: Information elements for this mesh, for instance, a * robust security network ie, or a vendor specific information element * that vendors will use to identify the path selection methods and * metrics in use. * * @NL80211_MESH_SETUP_USERSPACE_AUTH: Enable this option if an authentication * daemon will be authenticating mesh candidates. * * @NL80211_MESH_SETUP_USERSPACE_AMPE: Enable this option if an authentication * daemon will be securing peer link frames. AMPE is a secured version of * Mesh Peering Management (MPM) and is implemented with the assistance of * a userspace daemon. When this flag is set, the kernel will send peer * management frames to a userspace daemon that will implement AMPE * functionality (security capabilities selection, key confirmation, and * key management). When the flag is unset (default), the kernel can * autonomously complete (unsecured) mesh peering without the need of a * userspace daemon. * * @NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC: Enable this option to use a * vendor specific synchronization method or disable it to use the default * neighbor offset synchronization * * @NL80211_MESH_SETUP_USERSPACE_MPM: Enable this option if userspace will * implement an MPM which handles peer allocation and state. * * @NL80211_MESH_SETUP_AUTH_PROTOCOL: Inform the kernel of the authentication * method (u8, as defined in IEEE 8.4.2.100.6, e.g. 0x1 for SAE). * Default is no authentication method required. * * @NL80211_MESH_SETUP_ATTR_MAX: highest possible mesh setup attribute number * * @__NL80211_MESH_SETUP_ATTR_AFTER_LAST: Internal use */ enum nl80211_mesh_setup_params { __NL80211_MESH_SETUP_INVALID, NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL, NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC, NL80211_MESH_SETUP_IE, NL80211_MESH_SETUP_USERSPACE_AUTH, NL80211_MESH_SETUP_USERSPACE_AMPE, NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC, NL80211_MESH_SETUP_USERSPACE_MPM, NL80211_MESH_SETUP_AUTH_PROTOCOL, /* keep last */ __NL80211_MESH_SETUP_ATTR_AFTER_LAST, NL80211_MESH_SETUP_ATTR_MAX = __NL80211_MESH_SETUP_ATTR_AFTER_LAST - 1 }; /** * enum nl80211_txq_attr - TX queue parameter attributes * @__NL80211_TXQ_ATTR_INVALID: Attribute number 0 is reserved * @NL80211_TXQ_ATTR_AC: AC identifier (NL80211_AC_*) * @NL80211_TXQ_ATTR_TXOP: Maximum burst time in units of 32 usecs, 0 meaning * disabled * @NL80211_TXQ_ATTR_CWMIN: Minimum contention window [a value of the form * 2^n-1 in the range 1..32767] * @NL80211_TXQ_ATTR_CWMAX: Maximum contention window [a value of the form * 2^n-1 in the range 1..32767] * @NL80211_TXQ_ATTR_AIFS: Arbitration interframe space [0..255] * @__NL80211_TXQ_ATTR_AFTER_LAST: Internal * @NL80211_TXQ_ATTR_MAX: Maximum TXQ attribute number */ enum nl80211_txq_attr { __NL80211_TXQ_ATTR_INVALID, NL80211_TXQ_ATTR_AC, NL80211_TXQ_ATTR_TXOP, NL80211_TXQ_ATTR_CWMIN, NL80211_TXQ_ATTR_CWMAX, NL80211_TXQ_ATTR_AIFS, /* keep last */ __NL80211_TXQ_ATTR_AFTER_LAST, NL80211_TXQ_ATTR_MAX = __NL80211_TXQ_ATTR_AFTER_LAST - 1 }; enum nl80211_ac { NL80211_AC_VO, NL80211_AC_VI, NL80211_AC_BE, NL80211_AC_BK, NL80211_NUM_ACS }; /* backward compat */ #define NL80211_TXQ_ATTR_QUEUE NL80211_TXQ_ATTR_AC #define NL80211_TXQ_Q_VO NL80211_AC_VO #define NL80211_TXQ_Q_VI NL80211_AC_VI #define NL80211_TXQ_Q_BE NL80211_AC_BE #define NL80211_TXQ_Q_BK NL80211_AC_BK /** * enum nl80211_channel_type - channel type * @NL80211_CHAN_NO_HT: 20 MHz, non-HT channel * @NL80211_CHAN_HT20: 20 MHz HT channel * @NL80211_CHAN_HT40MINUS: HT40 channel, secondary channel * below the control channel * @NL80211_CHAN_HT40PLUS: HT40 channel, secondary channel * above the control channel */ enum nl80211_channel_type { NL80211_CHAN_NO_HT, NL80211_CHAN_HT20, NL80211_CHAN_HT40MINUS, NL80211_CHAN_HT40PLUS }; /** * enum nl80211_chan_width - channel width definitions * * These values are used with the %NL80211_ATTR_CHANNEL_WIDTH * attribute. * * @NL80211_CHAN_WIDTH_20_NOHT: 20 MHz, non-HT channel * @NL80211_CHAN_WIDTH_20: 20 MHz HT channel * @NL80211_CHAN_WIDTH_40: 40 MHz channel, the %NL80211_ATTR_CENTER_FREQ1 * attribute must be provided as well * @NL80211_CHAN_WIDTH_80: 80 MHz channel, the %NL80211_ATTR_CENTER_FREQ1 * attribute must be provided as well * @NL80211_CHAN_WIDTH_80P80: 80+80 MHz channel, the %NL80211_ATTR_CENTER_FREQ1 * and %NL80211_ATTR_CENTER_FREQ2 attributes must be provided as well * @NL80211_CHAN_WIDTH_160: 160 MHz channel, the %NL80211_ATTR_CENTER_FREQ1 * attribute must be provided as well * @NL80211_CHAN_WIDTH_5: 5 MHz OFDM channel * @NL80211_CHAN_WIDTH_10: 10 MHz OFDM channel */ enum nl80211_chan_width { NL80211_CHAN_WIDTH_20_NOHT, NL80211_CHAN_WIDTH_20, NL80211_CHAN_WIDTH_40, NL80211_CHAN_WIDTH_80, NL80211_CHAN_WIDTH_80P80, NL80211_CHAN_WIDTH_160, NL80211_CHAN_WIDTH_5, NL80211_CHAN_WIDTH_10, }; /** * enum nl80211_bss_scan_width - control channel width for a BSS * * These values are used with the %NL80211_BSS_CHAN_WIDTH attribute. * * @NL80211_BSS_CHAN_WIDTH_20: control channel is 20 MHz wide or compatible * @NL80211_BSS_CHAN_WIDTH_10: control channel is 10 MHz wide * @NL80211_BSS_CHAN_WIDTH_5: control channel is 5 MHz wide */ enum nl80211_bss_scan_width { NL80211_BSS_CHAN_WIDTH_20, NL80211_BSS_CHAN_WIDTH_10, NL80211_BSS_CHAN_WIDTH_5, }; /** * enum nl80211_bss - netlink attributes for a BSS * * @__NL80211_BSS_INVALID: invalid * @NL80211_BSS_BSSID: BSSID of the BSS (6 octets) * @NL80211_BSS_FREQUENCY: frequency in MHz (u32) * @NL80211_BSS_TSF: TSF of the received probe response/beacon (u64) * @NL80211_BSS_BEACON_INTERVAL: beacon interval of the (I)BSS (u16) * @NL80211_BSS_CAPABILITY: capability field (CPU order, u16) * @NL80211_BSS_INFORMATION_ELEMENTS: binary attribute containing the * raw information elements from the probe response/beacon (bin); * if the %NL80211_BSS_BEACON_IES attribute is present, the IEs here are * from a Probe Response frame; otherwise they are from a Beacon frame. * However, if the driver does not indicate the source of the IEs, these * IEs may be from either frame subtype. * @NL80211_BSS_SIGNAL_MBM: signal strength of probe response/beacon * in mBm (100 * dBm) (s32) * @NL80211_BSS_SIGNAL_UNSPEC: signal strength of the probe response/beacon * in unspecified units, scaled to 0..100 (u8) * @NL80211_BSS_STATUS: status, if this BSS is "used" * @NL80211_BSS_SEEN_MS_AGO: age of this BSS entry in ms * @NL80211_BSS_BEACON_IES: binary attribute containing the raw information * elements from a Beacon frame (bin); not present if no Beacon frame has * yet been received * @NL80211_BSS_CHAN_WIDTH: channel width of the control channel * (u32, enum nl80211_bss_scan_width) * @__NL80211_BSS_AFTER_LAST: internal * @NL80211_BSS_MAX: highest BSS attribute */ enum nl80211_bss { __NL80211_BSS_INVALID, NL80211_BSS_BSSID, NL80211_BSS_FREQUENCY, NL80211_BSS_TSF, NL80211_BSS_BEACON_INTERVAL, NL80211_BSS_CAPABILITY, NL80211_BSS_INFORMATION_ELEMENTS, NL80211_BSS_SIGNAL_MBM, NL80211_BSS_SIGNAL_UNSPEC, NL80211_BSS_STATUS, NL80211_BSS_SEEN_MS_AGO, NL80211_BSS_BEACON_IES, NL80211_BSS_CHAN_WIDTH, /* keep last */ __NL80211_BSS_AFTER_LAST, NL80211_BSS_MAX = __NL80211_BSS_AFTER_LAST - 1 }; /** * enum nl80211_bss_status - BSS "status" * @NL80211_BSS_STATUS_AUTHENTICATED: Authenticated with this BSS. * @NL80211_BSS_STATUS_ASSOCIATED: Associated with this BSS. * @NL80211_BSS_STATUS_IBSS_JOINED: Joined to this IBSS. * * The BSS status is a BSS attribute in scan dumps, which * indicates the status the interface has wrt. this BSS. */ enum nl80211_bss_status { NL80211_BSS_STATUS_AUTHENTICATED, NL80211_BSS_STATUS_ASSOCIATED, NL80211_BSS_STATUS_IBSS_JOINED, }; /** * enum nl80211_auth_type - AuthenticationType * * @NL80211_AUTHTYPE_OPEN_SYSTEM: Open System authentication * @NL80211_AUTHTYPE_SHARED_KEY: Shared Key authentication (WEP only) * @NL80211_AUTHTYPE_FT: Fast BSS Transition (IEEE 802.11r) * @NL80211_AUTHTYPE_NETWORK_EAP: Network EAP (some Cisco APs and mainly LEAP) * @NL80211_AUTHTYPE_SAE: Simultaneous authentication of equals * @__NL80211_AUTHTYPE_NUM: internal * @NL80211_AUTHTYPE_MAX: maximum valid auth algorithm * @NL80211_AUTHTYPE_AUTOMATIC: determine automatically (if necessary by * trying multiple times); this is invalid in netlink -- leave out * the attribute for this on CONNECT commands. */ enum nl80211_auth_type { NL80211_AUTHTYPE_OPEN_SYSTEM, NL80211_AUTHTYPE_SHARED_KEY, NL80211_AUTHTYPE_FT, NL80211_AUTHTYPE_NETWORK_EAP, NL80211_AUTHTYPE_SAE, /* keep last */ __NL80211_AUTHTYPE_NUM, NL80211_AUTHTYPE_MAX = __NL80211_AUTHTYPE_NUM - 1, NL80211_AUTHTYPE_AUTOMATIC }; /** * enum nl80211_key_type - Key Type * @NL80211_KEYTYPE_GROUP: Group (broadcast/multicast) key * @NL80211_KEYTYPE_PAIRWISE: Pairwise (unicast/individual) key * @NL80211_KEYTYPE_PEERKEY: PeerKey (DLS) * @NUM_NL80211_KEYTYPES: number of defined key types */ enum nl80211_key_type { NL80211_KEYTYPE_GROUP, NL80211_KEYTYPE_PAIRWISE, NL80211_KEYTYPE_PEERKEY, NUM_NL80211_KEYTYPES }; /** * enum nl80211_mfp - Management frame protection state * @NL80211_MFP_NO: Management frame protection not used * @NL80211_MFP_REQUIRED: Management frame protection required */ enum nl80211_mfp { NL80211_MFP_NO, NL80211_MFP_REQUIRED, }; enum nl80211_wpa_versions { NL80211_WPA_VERSION_1 = 1 << 0, NL80211_WPA_VERSION_2 = 1 << 1, }; /** * enum nl80211_key_default_types - key default types * @__NL80211_KEY_DEFAULT_TYPE_INVALID: invalid * @NL80211_KEY_DEFAULT_TYPE_UNICAST: key should be used as default * unicast key * @NL80211_KEY_DEFAULT_TYPE_MULTICAST: key should be used as default * multicast key * @NUM_NL80211_KEY_DEFAULT_TYPES: number of default types */ enum nl80211_key_default_types { __NL80211_KEY_DEFAULT_TYPE_INVALID, NL80211_KEY_DEFAULT_TYPE_UNICAST, NL80211_KEY_DEFAULT_TYPE_MULTICAST, NUM_NL80211_KEY_DEFAULT_TYPES }; /** * enum nl80211_key_attributes - key attributes * @__NL80211_KEY_INVALID: invalid * @NL80211_KEY_DATA: (temporal) key data; for TKIP this consists of * 16 bytes encryption key followed by 8 bytes each for TX and RX MIC * keys * @NL80211_KEY_IDX: key ID (u8, 0-3) * @NL80211_KEY_CIPHER: key cipher suite (u32, as defined by IEEE 802.11 * section 7.3.2.25.1, e.g. 0x000FAC04) * @NL80211_KEY_SEQ: transmit key sequence number (IV/PN) for TKIP and * CCMP keys, each six bytes in little endian * @NL80211_KEY_DEFAULT: flag indicating default key * @NL80211_KEY_DEFAULT_MGMT: flag indicating default management key * @NL80211_KEY_TYPE: the key type from enum nl80211_key_type, if not * specified the default depends on whether a MAC address was * given with the command using the key or not (u32) * @NL80211_KEY_DEFAULT_TYPES: A nested attribute containing flags * attributes, specifying what a key should be set as default as. * See &enum nl80211_key_default_types. * @__NL80211_KEY_AFTER_LAST: internal * @NL80211_KEY_MAX: highest key attribute */ enum nl80211_key_attributes { __NL80211_KEY_INVALID, NL80211_KEY_DATA, NL80211_KEY_IDX, NL80211_KEY_CIPHER, NL80211_KEY_SEQ, NL80211_KEY_DEFAULT, NL80211_KEY_DEFAULT_MGMT, NL80211_KEY_TYPE, NL80211_KEY_DEFAULT_TYPES, /* keep last */ __NL80211_KEY_AFTER_LAST, NL80211_KEY_MAX = __NL80211_KEY_AFTER_LAST - 1 }; /** * enum nl80211_tx_rate_attributes - TX rate set attributes * @__NL80211_TXRATE_INVALID: invalid * @NL80211_TXRATE_LEGACY: Legacy (non-MCS) rates allowed for TX rate selection * in an array of rates as defined in IEEE 802.11 7.3.2.2 (u8 values with * 1 = 500 kbps) but without the IE length restriction (at most * %NL80211_MAX_SUPP_RATES in a single array). * @NL80211_TXRATE_HT: HT (MCS) rates allowed for TX rate selection * in an array of MCS numbers. * @NL80211_TXRATE_VHT: VHT rates allowed for TX rate selection, * see &struct nl80211_txrate_vht * @__NL80211_TXRATE_AFTER_LAST: internal * @NL80211_TXRATE_MAX: highest TX rate attribute */ enum nl80211_tx_rate_attributes { __NL80211_TXRATE_INVALID, NL80211_TXRATE_LEGACY, NL80211_TXRATE_HT, NL80211_TXRATE_VHT, /* keep last */ __NL80211_TXRATE_AFTER_LAST, NL80211_TXRATE_MAX = __NL80211_TXRATE_AFTER_LAST - 1 }; #define NL80211_TXRATE_MCS NL80211_TXRATE_HT #define NL80211_VHT_NSS_MAX 8 /** * struct nl80211_txrate_vht - VHT MCS/NSS txrate bitmap * @mcs: MCS bitmap table for each NSS (array index 0 for 1 stream, etc.) */ struct nl80211_txrate_vht { __u16 mcs[NL80211_VHT_NSS_MAX]; }; /** * enum nl80211_band - Frequency band * @NL80211_BAND_2GHZ: 2.4 GHz ISM band * @NL80211_BAND_5GHZ: around 5 GHz band (4.9 - 5.7 GHz) * @NL80211_BAND_60GHZ: around 60 GHz band (58.32 - 64.80 GHz) */ enum nl80211_band { NL80211_BAND_2GHZ, NL80211_BAND_5GHZ, NL80211_BAND_60GHZ, }; /** * enum nl80211_ps_state - powersave state * @NL80211_PS_DISABLED: powersave is disabled * @NL80211_PS_ENABLED: powersave is enabled */ enum nl80211_ps_state { NL80211_PS_DISABLED, NL80211_PS_ENABLED, }; /** * enum nl80211_attr_cqm - connection quality monitor attributes * @__NL80211_ATTR_CQM_INVALID: invalid * @NL80211_ATTR_CQM_RSSI_THOLD: RSSI threshold in dBm. This value specifies * the threshold for the RSSI level at which an event will be sent. Zero * to disable. * @NL80211_ATTR_CQM_RSSI_HYST: RSSI hysteresis in dBm. This value specifies * the minimum amount the RSSI level must change after an event before a * new event may be issued (to reduce effects of RSSI oscillation). * @NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT: RSSI threshold event * @NL80211_ATTR_CQM_PKT_LOSS_EVENT: a u32 value indicating that this many * consecutive packets were not acknowledged by the peer * @NL80211_ATTR_CQM_TXE_RATE: TX error rate in %. Minimum % of TX failures * during the given %NL80211_ATTR_CQM_TXE_INTVL before an * %NL80211_CMD_NOTIFY_CQM with reported %NL80211_ATTR_CQM_TXE_RATE and * %NL80211_ATTR_CQM_TXE_PKTS is generated. * @NL80211_ATTR_CQM_TXE_PKTS: number of attempted packets in a given * %NL80211_ATTR_CQM_TXE_INTVL before %NL80211_ATTR_CQM_TXE_RATE is * checked. * @NL80211_ATTR_CQM_TXE_INTVL: interval in seconds. Specifies the periodic * interval in which %NL80211_ATTR_CQM_TXE_PKTS and * %NL80211_ATTR_CQM_TXE_RATE must be satisfied before generating an * %NL80211_CMD_NOTIFY_CQM. Set to 0 to turn off TX error reporting. * @__NL80211_ATTR_CQM_AFTER_LAST: internal * @NL80211_ATTR_CQM_MAX: highest key attribute */ enum nl80211_attr_cqm { __NL80211_ATTR_CQM_INVALID, NL80211_ATTR_CQM_RSSI_THOLD, NL80211_ATTR_CQM_RSSI_HYST, NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT, NL80211_ATTR_CQM_PKT_LOSS_EVENT, NL80211_ATTR_CQM_TXE_RATE, NL80211_ATTR_CQM_TXE_PKTS, NL80211_ATTR_CQM_TXE_INTVL, /* keep last */ __NL80211_ATTR_CQM_AFTER_LAST, NL80211_ATTR_CQM_MAX = __NL80211_ATTR_CQM_AFTER_LAST - 1 }; /** * enum nl80211_cqm_rssi_threshold_event - RSSI threshold event * @NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW: The RSSI level is lower than the * configured threshold * @NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH: The RSSI is higher than the * configured threshold * @NL80211_CQM_RSSI_BEACON_LOSS_EVENT: The device experienced beacon loss. * (Note that deauth/disassoc will still follow if the AP is not * available. This event might get used as roaming event, etc.) */ enum nl80211_cqm_rssi_threshold_event { NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW, NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH, NL80211_CQM_RSSI_BEACON_LOSS_EVENT, }; /** * enum nl80211_tx_power_setting - TX power adjustment * @NL80211_TX_POWER_AUTOMATIC: automatically determine transmit power * @NL80211_TX_POWER_LIMITED: limit TX power by the mBm parameter * @NL80211_TX_POWER_FIXED: fix TX power to the mBm parameter */ enum nl80211_tx_power_setting { NL80211_TX_POWER_AUTOMATIC, NL80211_TX_POWER_LIMITED, NL80211_TX_POWER_FIXED, }; /** * enum nl80211_packet_pattern_attr - packet pattern attribute * @__NL80211_PKTPAT_INVALID: invalid number for nested attribute * @NL80211_PKTPAT_PATTERN: the pattern, values where the mask has * a zero bit are ignored * @NL80211_PKTPAT_MASK: pattern mask, must be long enough to have * a bit for each byte in the pattern. The lowest-order bit corresponds * to the first byte of the pattern, but the bytes of the pattern are * in a little-endian-like format, i.e. the 9th byte of the pattern * corresponds to the lowest-order bit in the second byte of the mask. * For example: The match 00:xx:00:00:xx:00:00:00:00:xx:xx:xx (where * xx indicates "don't care") would be represented by a pattern of * twelve zero bytes, and a mask of "0xed,0x01". * Note that the pattern matching is done as though frames were not * 802.11 frames but 802.3 frames, i.e. the frame is fully unpacked * first (including SNAP header unpacking) and then matched. * @NL80211_PKTPAT_OFFSET: packet offset, pattern is matched after * these fixed number of bytes of received packet * @NUM_NL80211_PKTPAT: number of attributes * @MAX_NL80211_PKTPAT: max attribute number */ enum nl80211_packet_pattern_attr { __NL80211_PKTPAT_INVALID, NL80211_PKTPAT_MASK, NL80211_PKTPAT_PATTERN, NL80211_PKTPAT_OFFSET, NUM_NL80211_PKTPAT, MAX_NL80211_PKTPAT = NUM_NL80211_PKTPAT - 1, }; /** * struct nl80211_pattern_support - packet pattern support information * @max_patterns: maximum number of patterns supported * @min_pattern_len: minimum length of each pattern * @max_pattern_len: maximum length of each pattern * @max_pkt_offset: maximum Rx packet offset * * This struct is carried in %NL80211_WOWLAN_TRIG_PKT_PATTERN when * that is part of %NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED or in * %NL80211_ATTR_COALESCE_RULE_PKT_PATTERN when that is part of * %NL80211_ATTR_COALESCE_RULE in the capability information given * by the kernel to userspace. */ struct nl80211_pattern_support { __u32 max_patterns; __u32 min_pattern_len; __u32 max_pattern_len; __u32 max_pkt_offset; } __attribute__((packed)); /* only for backward compatibility */ #define __NL80211_WOWLAN_PKTPAT_INVALID __NL80211_PKTPAT_INVALID #define NL80211_WOWLAN_PKTPAT_MASK NL80211_PKTPAT_MASK #define NL80211_WOWLAN_PKTPAT_PATTERN NL80211_PKTPAT_PATTERN #define NL80211_WOWLAN_PKTPAT_OFFSET NL80211_PKTPAT_OFFSET #define NUM_NL80211_WOWLAN_PKTPAT NUM_NL80211_PKTPAT #define MAX_NL80211_WOWLAN_PKTPAT MAX_NL80211_PKTPAT #define nl80211_wowlan_pattern_support nl80211_pattern_support /** * enum nl80211_wowlan_triggers - WoWLAN trigger definitions * @__NL80211_WOWLAN_TRIG_INVALID: invalid number for nested attributes * @NL80211_WOWLAN_TRIG_ANY: wake up on any activity, do not really put * the chip into a special state -- works best with chips that have * support for low-power operation already (flag) * @NL80211_WOWLAN_TRIG_DISCONNECT: wake up on disconnect, the way disconnect * is detected is implementation-specific (flag) * @NL80211_WOWLAN_TRIG_MAGIC_PKT: wake up on magic packet (6x 0xff, followed * by 16 repetitions of MAC addr, anywhere in payload) (flag) * @NL80211_WOWLAN_TRIG_PKT_PATTERN: wake up on the specified packet patterns * which are passed in an array of nested attributes, each nested attribute * defining a with attributes from &struct nl80211_wowlan_trig_pkt_pattern. * Each pattern defines a wakeup packet. Packet offset is associated with * each pattern which is used while matching the pattern. The matching is * done on the MSDU, i.e. as though the packet was an 802.3 packet, so the * pattern matching is done after the packet is converted to the MSDU. * * In %NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED, it is a binary attribute * carrying a &struct nl80211_pattern_support. * * When reporting wakeup. it is a u32 attribute containing the 0-based * index of the pattern that caused the wakeup, in the patterns passed * to the kernel when configuring. * @NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED: Not a real trigger, and cannot be * used when setting, used only to indicate that GTK rekeying is supported * by the device (flag) * @NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE: wake up on GTK rekey failure (if * done by the device) (flag) * @NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST: wake up on EAP Identity Request * packet (flag) * @NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE: wake up on 4-way handshake (flag) * @NL80211_WOWLAN_TRIG_RFKILL_RELEASE: wake up when rfkill is released * (on devices that have rfkill in the device) (flag) * @NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211: For wakeup reporting only, contains * the 802.11 packet that caused the wakeup, e.g. a deauth frame. The frame * may be truncated, the @NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211_LEN * attribute contains the original length. * @NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211_LEN: Original length of the 802.11 * packet, may be bigger than the @NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211 * attribute if the packet was truncated somewhere. * @NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023: For wakeup reporting only, contains the * 802.11 packet that caused the wakeup, e.g. a magic packet. The frame may * be truncated, the @NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023_LEN attribute * contains the original length. * @NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023_LEN: Original length of the 802.3 * packet, may be bigger than the @NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023 * attribute if the packet was truncated somewhere. * @NL80211_WOWLAN_TRIG_TCP_CONNECTION: TCP connection wake, see DOC section * "TCP connection wakeup" for more details. This is a nested attribute * containing the exact information for establishing and keeping alive * the TCP connection. * @NL80211_WOWLAN_TRIG_TCP_WAKEUP_MATCH: For wakeup reporting only, the * wakeup packet was received on the TCP connection * @NL80211_WOWLAN_TRIG_WAKEUP_TCP_CONNLOST: For wakeup reporting only, the * TCP connection was lost or failed to be established * @NL80211_WOWLAN_TRIG_WAKEUP_TCP_NOMORETOKENS: For wakeup reporting only, * the TCP connection ran out of tokens to use for data to send to the * service * @NUM_NL80211_WOWLAN_TRIG: number of wake on wireless triggers * @MAX_NL80211_WOWLAN_TRIG: highest wowlan trigger attribute number * * These nested attributes are used to configure the wakeup triggers and * to report the wakeup reason(s). */ enum nl80211_wowlan_triggers { __NL80211_WOWLAN_TRIG_INVALID, NL80211_WOWLAN_TRIG_ANY, NL80211_WOWLAN_TRIG_DISCONNECT, NL80211_WOWLAN_TRIG_MAGIC_PKT, NL80211_WOWLAN_TRIG_PKT_PATTERN, NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE, NL80211_WOWLAN_TRIG_RFKILL_RELEASE, NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211, NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211_LEN, NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023, NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023_LEN, NL80211_WOWLAN_TRIG_TCP_CONNECTION, NL80211_WOWLAN_TRIG_WAKEUP_TCP_MATCH, NL80211_WOWLAN_TRIG_WAKEUP_TCP_CONNLOST, NL80211_WOWLAN_TRIG_WAKEUP_TCP_NOMORETOKENS, /* keep last */ NUM_NL80211_WOWLAN_TRIG, MAX_NL80211_WOWLAN_TRIG = NUM_NL80211_WOWLAN_TRIG - 1 }; /** * DOC: TCP connection wakeup * * Some devices can establish a TCP connection in order to be woken up by a * packet coming in from outside their network segment, or behind NAT. If * configured, the device will establish a TCP connection to the given * service, and periodically send data to that service. The first data * packet is usually transmitted after SYN/ACK, also ACKing the SYN/ACK. * The data packets can optionally include a (little endian) sequence * number (in the TCP payload!) that is generated by the device, and, also * optionally, a token from a list of tokens. This serves as a keep-alive * with the service, and for NATed connections, etc. * * During this keep-alive period, the server doesn't send any data to the * client. When receiving data, it is compared against the wakeup pattern * (and mask) and if it matches, the host is woken up. Similarly, if the * connection breaks or cannot be established to start with, the host is * also woken up. * * Developer's note: ARP offload is required for this, otherwise TCP * response packets might not go through correctly. */ /** * struct nl80211_wowlan_tcp_data_seq - WoWLAN TCP data sequence * @start: starting value * @offset: offset of sequence number in packet * @len: length of the sequence value to write, 1 through 4 * * Note: don't confuse with the TCP sequence number(s), this is for the * keepalive packet payload. The actual value is written into the packet * in little endian. */ struct nl80211_wowlan_tcp_data_seq { __u32 start, offset, len; }; /** * struct nl80211_wowlan_tcp_data_token - WoWLAN TCP data token config * @offset: offset of token in packet * @len: length of each token * @token_stream: stream of data to be used for the tokens, the length must * be a multiple of @len for this to make sense */ struct nl80211_wowlan_tcp_data_token { __u32 offset, len; __u8 token_stream[]; }; /** * struct nl80211_wowlan_tcp_data_token_feature - data token features * @min_len: minimum token length * @max_len: maximum token length * @bufsize: total available token buffer size (max size of @token_stream) */ struct nl80211_wowlan_tcp_data_token_feature { __u32 min_len, max_len, bufsize; }; /** * enum nl80211_wowlan_tcp_attrs - WoWLAN TCP connection parameters * @__NL80211_WOWLAN_TCP_INVALID: invalid number for nested attributes * @NL80211_WOWLAN_TCP_SRC_IPV4: source IPv4 address (in network byte order) * @NL80211_WOWLAN_TCP_DST_IPV4: destination IPv4 address * (in network byte order) * @NL80211_WOWLAN_TCP_DST_MAC: destination MAC address, this is given because * route lookup when configured might be invalid by the time we suspend, * and doing a route lookup when suspending is no longer possible as it * might require ARP querying. * @NL80211_WOWLAN_TCP_SRC_PORT: source port (u16); optional, if not given a * socket and port will be allocated * @NL80211_WOWLAN_TCP_DST_PORT: destination port (u16) * @NL80211_WOWLAN_TCP_DATA_PAYLOAD: data packet payload, at least one byte. * For feature advertising, a u32 attribute holding the maximum length * of the data payload. * @NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ: data packet sequence configuration * (if desired), a &struct nl80211_wowlan_tcp_data_seq. For feature * advertising it is just a flag * @NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN: data packet token configuration, * see &struct nl80211_wowlan_tcp_data_token and for advertising see * &struct nl80211_wowlan_tcp_data_token_feature. * @NL80211_WOWLAN_TCP_DATA_INTERVAL: data interval in seconds, maximum * interval in feature advertising (u32) * @NL80211_WOWLAN_TCP_WAKE_PAYLOAD: wake packet payload, for advertising a * u32 attribute holding the maximum length * @NL80211_WOWLAN_TCP_WAKE_MASK: Wake packet payload mask, not used for * feature advertising. The mask works like @NL80211_PKTPAT_MASK * but on the TCP payload only. * @NUM_NL80211_WOWLAN_TCP: number of TCP attributes * @MAX_NL80211_WOWLAN_TCP: highest attribute number */ enum nl80211_wowlan_tcp_attrs { __NL80211_WOWLAN_TCP_INVALID, NL80211_WOWLAN_TCP_SRC_IPV4, NL80211_WOWLAN_TCP_DST_IPV4, NL80211_WOWLAN_TCP_DST_MAC, NL80211_WOWLAN_TCP_SRC_PORT, NL80211_WOWLAN_TCP_DST_PORT, NL80211_WOWLAN_TCP_DATA_PAYLOAD, NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ, NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN, NL80211_WOWLAN_TCP_DATA_INTERVAL, NL80211_WOWLAN_TCP_WAKE_PAYLOAD, NL80211_WOWLAN_TCP_WAKE_MASK, /* keep last */ NUM_NL80211_WOWLAN_TCP, MAX_NL80211_WOWLAN_TCP = NUM_NL80211_WOWLAN_TCP - 1 }; /** * struct nl80211_coalesce_rule_support - coalesce rule support information * @max_rules: maximum number of rules supported * @pat: packet pattern support information * @max_delay: maximum supported coalescing delay in msecs * * This struct is carried in %NL80211_ATTR_COALESCE_RULE in the * capability information given by the kernel to userspace. */ struct nl80211_coalesce_rule_support { __u32 max_rules; struct nl80211_pattern_support pat; __u32 max_delay; } __attribute__((packed)); /** * enum nl80211_attr_coalesce_rule - coalesce rule attribute * @__NL80211_COALESCE_RULE_INVALID: invalid number for nested attribute * @NL80211_ATTR_COALESCE_RULE_DELAY: delay in msecs used for packet coalescing * @NL80211_ATTR_COALESCE_RULE_CONDITION: condition for packet coalescence, * see &enum nl80211_coalesce_condition. * @NL80211_ATTR_COALESCE_RULE_PKT_PATTERN: packet offset, pattern is matched * after these fixed number of bytes of received packet * @NUM_NL80211_ATTR_COALESCE_RULE: number of attributes * @NL80211_ATTR_COALESCE_RULE_MAX: max attribute number */ enum nl80211_attr_coalesce_rule { __NL80211_COALESCE_RULE_INVALID, NL80211_ATTR_COALESCE_RULE_DELAY, NL80211_ATTR_COALESCE_RULE_CONDITION, NL80211_ATTR_COALESCE_RULE_PKT_PATTERN, /* keep last */ NUM_NL80211_ATTR_COALESCE_RULE, NL80211_ATTR_COALESCE_RULE_MAX = NUM_NL80211_ATTR_COALESCE_RULE - 1 }; /** * enum nl80211_coalesce_condition - coalesce rule conditions * @NL80211_COALESCE_CONDITION_MATCH: coalaesce Rx packets when patterns * in a rule are matched. * @NL80211_COALESCE_CONDITION_NO_MATCH: coalesce Rx packets when patterns * in a rule are not matched. */ enum nl80211_coalesce_condition { NL80211_COALESCE_CONDITION_MATCH, NL80211_COALESCE_CONDITION_NO_MATCH }; /** * enum nl80211_iface_limit_attrs - limit attributes * @NL80211_IFACE_LIMIT_UNSPEC: (reserved) * @NL80211_IFACE_LIMIT_MAX: maximum number of interfaces that * can be chosen from this set of interface types (u32) * @NL80211_IFACE_LIMIT_TYPES: nested attribute containing a * flag attribute for each interface type in this set * @NUM_NL80211_IFACE_LIMIT: number of attributes * @MAX_NL80211_IFACE_LIMIT: highest attribute number */ enum nl80211_iface_limit_attrs { NL80211_IFACE_LIMIT_UNSPEC, NL80211_IFACE_LIMIT_MAX, NL80211_IFACE_LIMIT_TYPES, /* keep last */ NUM_NL80211_IFACE_LIMIT, MAX_NL80211_IFACE_LIMIT = NUM_NL80211_IFACE_LIMIT - 1 }; /** * enum nl80211_if_combination_attrs -- interface combination attributes * * @NL80211_IFACE_COMB_UNSPEC: (reserved) * @NL80211_IFACE_COMB_LIMITS: Nested attributes containing the limits * for given interface types, see &enum nl80211_iface_limit_attrs. * @NL80211_IFACE_COMB_MAXNUM: u32 attribute giving the total number of * interfaces that can be created in this group. This number doesn't * apply to interfaces purely managed in software, which are listed * in a separate attribute %NL80211_ATTR_INTERFACES_SOFTWARE. * @NL80211_IFACE_COMB_STA_AP_BI_MATCH: flag attribute specifying that * beacon intervals within this group must be all the same even for * infrastructure and AP/GO combinations, i.e. the GO(s) must adopt * the infrastructure network's beacon interval. * @NL80211_IFACE_COMB_NUM_CHANNELS: u32 attribute specifying how many * different channels may be used within this group. * @NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS: u32 attribute containing the bitmap * of supported channel widths for radar detection. * @NUM_NL80211_IFACE_COMB: number of attributes * @MAX_NL80211_IFACE_COMB: highest attribute number * * Examples: * limits = [ #{STA} <= 1, #{AP} <= 1 ], matching BI, channels = 1, max = 2 * => allows an AP and a STA that must match BIs * * numbers = [ #{AP, P2P-GO} <= 8 ], channels = 1, max = 8 * => allows 8 of AP/GO * * numbers = [ #{STA} <= 2 ], channels = 2, max = 2 * => allows two STAs on different channels * * numbers = [ #{STA} <= 1, #{P2P-client,P2P-GO} <= 3 ], max = 4 * => allows a STA plus three P2P interfaces * * The list of these four possiblities could completely be contained * within the %NL80211_ATTR_INTERFACE_COMBINATIONS attribute to indicate * that any of these groups must match. * * "Combinations" of just a single interface will not be listed here, * a single interface of any valid interface type is assumed to always * be possible by itself. This means that implicitly, for each valid * interface type, the following group always exists: * numbers = [ #{} <= 1 ], channels = 1, max = 1 */ enum nl80211_if_combination_attrs { NL80211_IFACE_COMB_UNSPEC, NL80211_IFACE_COMB_LIMITS, NL80211_IFACE_COMB_MAXNUM, NL80211_IFACE_COMB_STA_AP_BI_MATCH, NL80211_IFACE_COMB_NUM_CHANNELS, NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS, /* keep last */ NUM_NL80211_IFACE_COMB, MAX_NL80211_IFACE_COMB = NUM_NL80211_IFACE_COMB - 1 }; /** * enum nl80211_plink_state - state of a mesh peer link finite state machine * * @NL80211_PLINK_LISTEN: initial state, considered the implicit * state of non existant mesh peer links * @NL80211_PLINK_OPN_SNT: mesh plink open frame has been sent to * this mesh peer * @NL80211_PLINK_OPN_RCVD: mesh plink open frame has been received * from this mesh peer * @NL80211_PLINK_CNF_RCVD: mesh plink confirm frame has been * received from this mesh peer * @NL80211_PLINK_ESTAB: mesh peer link is established * @NL80211_PLINK_HOLDING: mesh peer link is being closed or cancelled * @NL80211_PLINK_BLOCKED: all frames transmitted from this mesh * plink are discarded * @NUM_NL80211_PLINK_STATES: number of peer link states * @MAX_NL80211_PLINK_STATES: highest numerical value of plink states */ enum nl80211_plink_state { NL80211_PLINK_LISTEN, NL80211_PLINK_OPN_SNT, NL80211_PLINK_OPN_RCVD, NL80211_PLINK_CNF_RCVD, NL80211_PLINK_ESTAB, NL80211_PLINK_HOLDING, NL80211_PLINK_BLOCKED, /* keep last */ NUM_NL80211_PLINK_STATES, MAX_NL80211_PLINK_STATES = NUM_NL80211_PLINK_STATES - 1 }; /** * enum nl80211_plink_action - actions to perform in mesh peers * * @NL80211_PLINK_ACTION_NO_ACTION: perform no action * @NL80211_PLINK_ACTION_OPEN: start mesh peer link establishment * @NL80211_PLINK_ACTION_BLOCK: block traffic from this mesh peer * @NUM_NL80211_PLINK_ACTIONS: number of possible actions */ enum plink_actions { NL80211_PLINK_ACTION_NO_ACTION, NL80211_PLINK_ACTION_OPEN, NL80211_PLINK_ACTION_BLOCK, NUM_NL80211_PLINK_ACTIONS, }; #define NL80211_KCK_LEN 16 #define NL80211_KEK_LEN 16 #define NL80211_REPLAY_CTR_LEN 8 /** * enum nl80211_rekey_data - attributes for GTK rekey offload * @__NL80211_REKEY_DATA_INVALID: invalid number for nested attributes * @NL80211_REKEY_DATA_KEK: key encryption key (binary) * @NL80211_REKEY_DATA_KCK: key confirmation key (binary) * @NL80211_REKEY_DATA_REPLAY_CTR: replay counter (binary) * @NUM_NL80211_REKEY_DATA: number of rekey attributes (internal) * @MAX_NL80211_REKEY_DATA: highest rekey attribute (internal) */ enum nl80211_rekey_data { __NL80211_REKEY_DATA_INVALID, NL80211_REKEY_DATA_KEK, NL80211_REKEY_DATA_KCK, NL80211_REKEY_DATA_REPLAY_CTR, /* keep last */ NUM_NL80211_REKEY_DATA, MAX_NL80211_REKEY_DATA = NUM_NL80211_REKEY_DATA - 1 }; /** * enum nl80211_hidden_ssid - values for %NL80211_ATTR_HIDDEN_SSID * @NL80211_HIDDEN_SSID_NOT_IN_USE: do not hide SSID (i.e., broadcast it in * Beacon frames) * @NL80211_HIDDEN_SSID_ZERO_LEN: hide SSID by using zero-length SSID element * in Beacon frames * @NL80211_HIDDEN_SSID_ZERO_CONTENTS: hide SSID by using correct length of SSID * element in Beacon frames but zero out each byte in the SSID */ enum nl80211_hidden_ssid { NL80211_HIDDEN_SSID_NOT_IN_USE, NL80211_HIDDEN_SSID_ZERO_LEN, NL80211_HIDDEN_SSID_ZERO_CONTENTS }; /** * enum nl80211_sta_wme_attr - station WME attributes * @__NL80211_STA_WME_INVALID: invalid number for nested attribute * @NL80211_STA_WME_UAPSD_QUEUES: bitmap of uapsd queues. the format * is the same as the AC bitmap in the QoS info field. * @NL80211_STA_WME_MAX_SP: max service period. the format is the same * as the MAX_SP field in the QoS info field (but already shifted down). * @__NL80211_STA_WME_AFTER_LAST: internal * @NL80211_STA_WME_MAX: highest station WME attribute */ enum nl80211_sta_wme_attr { __NL80211_STA_WME_INVALID, NL80211_STA_WME_UAPSD_QUEUES, NL80211_STA_WME_MAX_SP, /* keep last */ __NL80211_STA_WME_AFTER_LAST, NL80211_STA_WME_MAX = __NL80211_STA_WME_AFTER_LAST - 1 }; /** * enum nl80211_pmksa_candidate_attr - attributes for PMKSA caching candidates * @__NL80211_PMKSA_CANDIDATE_INVALID: invalid number for nested attributes * @NL80211_PMKSA_CANDIDATE_INDEX: candidate index (u32; the smaller, the higher * priority) * @NL80211_PMKSA_CANDIDATE_BSSID: candidate BSSID (6 octets) * @NL80211_PMKSA_CANDIDATE_PREAUTH: RSN pre-authentication supported (flag) * @NUM_NL80211_PMKSA_CANDIDATE: number of PMKSA caching candidate attributes * (internal) * @MAX_NL80211_PMKSA_CANDIDATE: highest PMKSA caching candidate attribute * (internal) */ enum nl80211_pmksa_candidate_attr { __NL80211_PMKSA_CANDIDATE_INVALID, NL80211_PMKSA_CANDIDATE_INDEX, NL80211_PMKSA_CANDIDATE_BSSID, NL80211_PMKSA_CANDIDATE_PREAUTH, /* keep last */ NUM_NL80211_PMKSA_CANDIDATE, MAX_NL80211_PMKSA_CANDIDATE = NUM_NL80211_PMKSA_CANDIDATE - 1 }; /** * enum nl80211_tdls_operation - values for %NL80211_ATTR_TDLS_OPERATION * @NL80211_TDLS_DISCOVERY_REQ: Send a TDLS discovery request * @NL80211_TDLS_SETUP: Setup TDLS link * @NL80211_TDLS_TEARDOWN: Teardown a TDLS link which is already established * @NL80211_TDLS_ENABLE_LINK: Enable TDLS link * @NL80211_TDLS_DISABLE_LINK: Disable TDLS link */ enum nl80211_tdls_operation { NL80211_TDLS_DISCOVERY_REQ, NL80211_TDLS_SETUP, NL80211_TDLS_TEARDOWN, NL80211_TDLS_ENABLE_LINK, NL80211_TDLS_DISABLE_LINK, }; /* * enum nl80211_ap_sme_features - device-integrated AP features * Reserved for future use, no bits are defined in * NL80211_ATTR_DEVICE_AP_SME yet. enum nl80211_ap_sme_features { }; */ /** * enum nl80211_feature_flags - device/driver features * @NL80211_FEATURE_SK_TX_STATUS: This driver supports reflecting back * TX status to the socket error queue when requested with the * socket option. * @NL80211_FEATURE_HT_IBSS: This driver supports IBSS with HT datarates. * @NL80211_FEATURE_INACTIVITY_TIMER: This driver takes care of freeing up * the connected inactive stations in AP mode. * @NL80211_FEATURE_CELL_BASE_REG_HINTS: This driver has been tested * to work properly to suppport receiving regulatory hints from * cellular base stations. * @NL80211_FEATURE_P2P_DEVICE_NEEDS_CHANNEL: If this is set, an active * P2P Device (%NL80211_IFTYPE_P2P_DEVICE) requires its own channel * in the interface combinations, even when it's only used for scan * and remain-on-channel. This could be due to, for example, the * remain-on-channel implementation requiring a channel context. * @NL80211_FEATURE_SAE: This driver supports simultaneous authentication of * equals (SAE) with user space SME (NL80211_CMD_AUTHENTICATE) in station * mode * @NL80211_FEATURE_LOW_PRIORITY_SCAN: This driver supports low priority scan * @NL80211_FEATURE_SCAN_FLUSH: Scan flush is supported * @NL80211_FEATURE_AP_SCAN: Support scanning using an AP vif * @NL80211_FEATURE_VIF_TXPOWER: The driver supports per-vif TX power setting * @NL80211_FEATURE_NEED_OBSS_SCAN: The driver expects userspace to perform * OBSS scans and generate 20/40 BSS coex reports. This flag is used only * for drivers implementing the CONNECT API, for AUTH/ASSOC it is implied. * @NL80211_FEATURE_P2P_GO_CTWIN: P2P GO implementation supports CT Window * setting * @NL80211_FEATURE_P2P_GO_OPPPS: P2P GO implementation supports opportunistic * powersave * @NL80211_FEATURE_FULL_AP_CLIENT_STATE: The driver supports full state * transitions for AP clients. Without this flag (and if the driver * doesn't have the AP SME in the device) the driver supports adding * stations only when they're associated and adds them in associated * state (to later be transitioned into authorized), with this flag * they should be added before even sending the authentication reply * and then transitioned into authenticated, associated and authorized * states using station flags. * Note that even for drivers that support this, the default is to add * stations in authenticated/associated state, so to add unauthenticated * stations the authenticated/associated bits have to be set in the mask. * @NL80211_FEATURE_ADVERTISE_CHAN_LIMITS: cfg80211 advertises channel limits * (HT40, VHT 80/160 MHz) if this flag is set * @NL80211_FEATURE_USERSPACE_MPM: This driver supports a userspace Mesh * Peering Management entity which may be implemented by registering for * beacons or NL80211_CMD_NEW_PEER_CANDIDATE events. The mesh beacon is * still generated by the driver. * @NL80211_FEATURE_ACTIVE_MONITOR: This driver supports an active monitor * interface. An active monitor interface behaves like a normal monitor * interface, but gets added to the driver. It ensures that incoming * unicast packets directed at the configured interface address get ACKed. */ enum nl80211_feature_flags { NL80211_FEATURE_SK_TX_STATUS = 1 << 0, NL80211_FEATURE_HT_IBSS = 1 << 1, NL80211_FEATURE_INACTIVITY_TIMER = 1 << 2, NL80211_FEATURE_CELL_BASE_REG_HINTS = 1 << 3, NL80211_FEATURE_P2P_DEVICE_NEEDS_CHANNEL = 1 << 4, NL80211_FEATURE_SAE = 1 << 5, NL80211_FEATURE_LOW_PRIORITY_SCAN = 1 << 6, NL80211_FEATURE_SCAN_FLUSH = 1 << 7, NL80211_FEATURE_AP_SCAN = 1 << 8, NL80211_FEATURE_VIF_TXPOWER = 1 << 9, NL80211_FEATURE_NEED_OBSS_SCAN = 1 << 10, NL80211_FEATURE_P2P_GO_CTWIN = 1 << 11, NL80211_FEATURE_P2P_GO_OPPPS = 1 << 12, /* bit 13 is reserved */ NL80211_FEATURE_ADVERTISE_CHAN_LIMITS = 1 << 14, NL80211_FEATURE_FULL_AP_CLIENT_STATE = 1 << 15, NL80211_FEATURE_USERSPACE_MPM = 1 << 16, NL80211_FEATURE_ACTIVE_MONITOR = 1 << 17, }; /** * enum nl80211_probe_resp_offload_support_attr - optional supported * protocols for probe-response offloading by the driver/FW. * To be used with the %NL80211_ATTR_PROBE_RESP_OFFLOAD attribute. * Each enum value represents a bit in the bitmap of supported * protocols. Typically a subset of probe-requests belonging to a * supported protocol will be excluded from offload and uploaded * to the host. * * @NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS: Support for WPS ver. 1 * @NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2: Support for WPS ver. 2 * @NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P: Support for P2P * @NL80211_PROBE_RESP_OFFLOAD_SUPPORT_80211U: Support for 802.11u */ enum nl80211_probe_resp_offload_support_attr { NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS = 1<<0, NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 = 1<<1, NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P = 1<<2, NL80211_PROBE_RESP_OFFLOAD_SUPPORT_80211U = 1<<3, }; /** * enum nl80211_connect_failed_reason - connection request failed reasons * @NL80211_CONN_FAIL_MAX_CLIENTS: Maximum number of clients that can be * handled by the AP is reached. * @NL80211_CONN_FAIL_BLOCKED_CLIENT: Connection request is rejected due to ACL. */ enum nl80211_connect_failed_reason { NL80211_CONN_FAIL_MAX_CLIENTS, NL80211_CONN_FAIL_BLOCKED_CLIENT, }; /** * enum nl80211_scan_flags - scan request control flags * * Scan request control flags are used to control the handling * of NL80211_CMD_TRIGGER_SCAN and NL80211_CMD_START_SCHED_SCAN * requests. * * @NL80211_SCAN_FLAG_LOW_PRIORITY: scan request has low priority * @NL80211_SCAN_FLAG_FLUSH: flush cache before scanning * @NL80211_SCAN_FLAG_AP: force a scan even if the interface is configured * as AP and the beaconing has already been configured. This attribute is * dangerous because will destroy stations performance as a lot of frames * will be lost while scanning off-channel, therefore it must be used only * when really needed */ enum nl80211_scan_flags { NL80211_SCAN_FLAG_LOW_PRIORITY = 1<<0, NL80211_SCAN_FLAG_FLUSH = 1<<1, NL80211_SCAN_FLAG_AP = 1<<2, }; /** * enum nl80211_acl_policy - access control policy * * Access control policy is applied on a MAC list set by * %NL80211_CMD_START_AP and %NL80211_CMD_SET_MAC_ACL, to * be used with %NL80211_ATTR_ACL_POLICY. * * @NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED: Deny stations which are * listed in ACL, i.e. allow all the stations which are not listed * in ACL to authenticate. * @NL80211_ACL_POLICY_DENY_UNLESS_LISTED: Allow the stations which are listed * in ACL, i.e. deny all the stations which are not listed in ACL. */ enum nl80211_acl_policy { NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED, NL80211_ACL_POLICY_DENY_UNLESS_LISTED, }; /** * enum nl80211_radar_event - type of radar event for DFS operation * * Type of event to be used with NL80211_ATTR_RADAR_EVENT to inform userspace * about detected radars or success of the channel available check (CAC) * * @NL80211_RADAR_DETECTED: A radar pattern has been detected. The channel is * now unusable. * @NL80211_RADAR_CAC_FINISHED: Channel Availability Check has been finished, * the channel is now available. * @NL80211_RADAR_CAC_ABORTED: Channel Availability Check has been aborted, no * change to the channel status. * @NL80211_RADAR_NOP_FINISHED: The Non-Occupancy Period for this channel is * over, channel becomes usable. */ enum nl80211_radar_event { NL80211_RADAR_DETECTED, NL80211_RADAR_CAC_FINISHED, NL80211_RADAR_CAC_ABORTED, NL80211_RADAR_NOP_FINISHED, }; /** * enum nl80211_dfs_state - DFS states for channels * * Channel states used by the DFS code. * * @NL80211_DFS_USABLE: The channel can be used, but channel availability * check (CAC) must be performed before using it for AP or IBSS. * @NL80211_DFS_UNAVAILABLE: A radar has been detected on this channel, it * is therefore marked as not available. * @NL80211_DFS_AVAILABLE: The channel has been CAC checked and is available. */ enum nl80211_dfs_state { NL80211_DFS_USABLE, NL80211_DFS_UNAVAILABLE, NL80211_DFS_AVAILABLE, }; /** * enum enum nl80211_protocol_features - nl80211 protocol features * @NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP: nl80211 supports splitting * wiphy dumps (if requested by the application with the attribute * %NL80211_ATTR_SPLIT_WIPHY_DUMP. Also supported is filtering the * wiphy dump by %NL80211_ATTR_WIPHY, %NL80211_ATTR_IFINDEX or * %NL80211_ATTR_WDEV. */ enum nl80211_protocol_features { NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP = 1 << 0, }; /** * enum nl80211_crit_proto_id - nl80211 critical protocol identifiers * * @NL80211_CRIT_PROTO_UNSPEC: protocol unspecified. * @NL80211_CRIT_PROTO_DHCP: BOOTP or DHCPv6 protocol. * @NL80211_CRIT_PROTO_EAPOL: EAPOL protocol. * @NL80211_CRIT_PROTO_APIPA: APIPA protocol. * @NUM_NL80211_CRIT_PROTO: must be kept last. */ enum nl80211_crit_proto_id { NL80211_CRIT_PROTO_UNSPEC, NL80211_CRIT_PROTO_DHCP, NL80211_CRIT_PROTO_EAPOL, NL80211_CRIT_PROTO_APIPA, /* add other protocols before this one */ NUM_NL80211_CRIT_PROTO }; /* maximum duration for critical protocol measures */ #define NL80211_CRIT_PROTO_MAX_DURATION 5000 /* msec */ /** * enum nl80211_rxmgmt_flags - flags for received management frame. * * Used by cfg80211_rx_mgmt() * * @NL80211_RXMGMT_FLAG_ANSWERED: frame was answered by device/driver. */ enum nl80211_rxmgmt_flags { NL80211_RXMGMT_FLAG_ANSWERED = 1 << 0, }; /* * If this flag is unset, the lower 24 bits are an OUI, if set * a Linux nl80211 vendor ID is used (no such IDs are allocated * yet, so that's not valid so far) */ #define NL80211_VENDOR_ID_IS_LINUX 0x80000000 /** * struct nl80211_vendor_cmd_info - vendor command data * @vendor_id: If the %NL80211_VENDOR_ID_IS_LINUX flag is clear, then the * value is a 24-bit OUI; if it is set then a separately allocated ID * may be used, but no such IDs are allocated yet. New IDs should be * added to this file when needed. * @subcmd: sub-command ID for the command */ struct nl80211_vendor_cmd_info { __u32 vendor_id; __u32 subcmd; }; #endif /* __LINUX_NL80211_H */ wpa-2.1/src/drivers/priv_netlink.h000066400000000000000000000054201227414666700173050ustar00rootroot00000000000000/* * wpa_supplicant - Private copy of Linux netlink/rtnetlink definitions. * Copyright (c) 2003-2005, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef PRIV_NETLINK_H #define PRIV_NETLINK_H /* * This should be replaced with user space header once one is available with C * library, etc.. */ #ifndef IFF_LOWER_UP #define IFF_LOWER_UP 0x10000 /* driver signals L1 up */ #endif #ifndef IFF_DORMANT #define IFF_DORMANT 0x20000 /* driver signals dormant */ #endif #ifndef IFLA_IFNAME #define IFLA_IFNAME 3 #endif #ifndef IFLA_WIRELESS #define IFLA_WIRELESS 11 #endif #ifndef IFLA_OPERSTATE #define IFLA_OPERSTATE 16 #endif #ifndef IFLA_LINKMODE #define IFLA_LINKMODE 17 #define IF_OPER_DORMANT 5 #define IF_OPER_UP 6 #endif #define NLM_F_REQUEST 1 #define NETLINK_ROUTE 0 #define RTMGRP_LINK 1 #define RTM_BASE 0x10 #define RTM_NEWLINK (RTM_BASE + 0) #define RTM_DELLINK (RTM_BASE + 1) #define RTM_SETLINK (RTM_BASE + 3) #define NLMSG_ALIGNTO 4 #define NLMSG_ALIGN(len) (((len) + NLMSG_ALIGNTO - 1) & ~(NLMSG_ALIGNTO - 1)) #define NLMSG_HDRLEN ((int) NLMSG_ALIGN(sizeof(struct nlmsghdr))) #define NLMSG_LENGTH(len) ((len) + NLMSG_ALIGN(sizeof(struct nlmsghdr))) #define NLMSG_SPACE(len) NLMSG_ALIGN(NLMSG_LENGTH(len)) #define NLMSG_DATA(nlh) ((void*) (((char*) nlh) + NLMSG_LENGTH(0))) #define NLMSG_NEXT(nlh,len) ((len) -= NLMSG_ALIGN((nlh)->nlmsg_len), \ (struct nlmsghdr *) \ (((char *)(nlh)) + NLMSG_ALIGN((nlh)->nlmsg_len))) #define NLMSG_OK(nlh,len) ((len) >= (int) sizeof(struct nlmsghdr) && \ (nlh)->nlmsg_len >= sizeof(struct nlmsghdr) && \ (int) (nlh)->nlmsg_len <= (len)) #define NLMSG_PAYLOAD(nlh,len) ((nlh)->nlmsg_len - NLMSG_SPACE((len))) #define RTA_ALIGNTO 4 #define RTA_ALIGN(len) (((len) + RTA_ALIGNTO - 1) & ~(RTA_ALIGNTO - 1)) #define RTA_OK(rta,len) \ ((len) > 0 && (rta)->rta_len >= sizeof(struct rtattr) && \ (rta)->rta_len <= (len)) #define RTA_NEXT(rta,attrlen) \ ((attrlen) -= RTA_ALIGN((rta)->rta_len), \ (struct rtattr *) (((char *)(rta)) + RTA_ALIGN((rta)->rta_len))) #define RTA_LENGTH(len) (RTA_ALIGN(sizeof(struct rtattr)) + (len)) #define RTA_DATA(rta) ((void *) (((char *) (rta)) + RTA_LENGTH(0))) #define RTA_PAYLOAD(rta) ((int) ((rta)->rta_len) - RTA_LENGTH(0)) struct sockaddr_nl { sa_family_t nl_family; unsigned short nl_pad; u32 nl_pid; u32 nl_groups; }; struct nlmsghdr { u32 nlmsg_len; u16 nlmsg_type; u16 nlmsg_flags; u32 nlmsg_seq; u32 nlmsg_pid; }; struct ifinfomsg { unsigned char ifi_family; unsigned char __ifi_pad; unsigned short ifi_type; int ifi_index; unsigned ifi_flags; unsigned ifi_change; }; struct rtattr { unsigned short rta_len; unsigned short rta_type; }; #endif /* PRIV_NETLINK_H */ wpa-2.1/src/drivers/rfkill.c000066400000000000000000000075661227414666700160740ustar00rootroot00000000000000/* * Linux rfkill helper functions for driver wrappers * Copyright (c) 2010, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "includes.h" #include #include "utils/common.h" #include "utils/eloop.h" #include "rfkill.h" #define RFKILL_EVENT_SIZE_V1 8 struct rfkill_event { u32 idx; u8 type; u8 op; u8 soft; u8 hard; } STRUCT_PACKED; enum rfkill_operation { RFKILL_OP_ADD = 0, RFKILL_OP_DEL, RFKILL_OP_CHANGE, RFKILL_OP_CHANGE_ALL, }; enum rfkill_type { RFKILL_TYPE_ALL = 0, RFKILL_TYPE_WLAN, RFKILL_TYPE_BLUETOOTH, RFKILL_TYPE_UWB, RFKILL_TYPE_WIMAX, RFKILL_TYPE_WWAN, RFKILL_TYPE_GPS, RFKILL_TYPE_FM, NUM_RFKILL_TYPES, }; struct rfkill_data { struct rfkill_config *cfg; int fd; int blocked; }; static void rfkill_receive(int sock, void *eloop_ctx, void *sock_ctx) { struct rfkill_data *rfkill = eloop_ctx; struct rfkill_event event; ssize_t len; int new_blocked; len = read(rfkill->fd, &event, sizeof(event)); if (len < 0) { wpa_printf(MSG_ERROR, "rfkill: Event read failed: %s", strerror(errno)); return; } if (len != RFKILL_EVENT_SIZE_V1) { wpa_printf(MSG_DEBUG, "rfkill: Unexpected event size " "%d (expected %d)", (int) len, RFKILL_EVENT_SIZE_V1); return; } wpa_printf(MSG_DEBUG, "rfkill: event: idx=%u type=%d " "op=%u soft=%u hard=%u", event.idx, event.type, event.op, event.soft, event.hard); if (event.op != RFKILL_OP_CHANGE || event.type != RFKILL_TYPE_WLAN) return; if (event.hard) { wpa_printf(MSG_INFO, "rfkill: WLAN hard blocked"); new_blocked = 1; } else if (event.soft) { wpa_printf(MSG_INFO, "rfkill: WLAN soft blocked"); new_blocked = 1; } else { wpa_printf(MSG_INFO, "rfkill: WLAN unblocked"); new_blocked = 0; } if (new_blocked != rfkill->blocked) { rfkill->blocked = new_blocked; if (new_blocked) rfkill->cfg->blocked_cb(rfkill->cfg->ctx); else rfkill->cfg->unblocked_cb(rfkill->cfg->ctx); } } struct rfkill_data * rfkill_init(struct rfkill_config *cfg) { struct rfkill_data *rfkill; struct rfkill_event event; ssize_t len; rfkill = os_zalloc(sizeof(*rfkill)); if (rfkill == NULL) return NULL; rfkill->cfg = cfg; rfkill->fd = open("/dev/rfkill", O_RDONLY); if (rfkill->fd < 0) { wpa_printf(MSG_INFO, "rfkill: Cannot open RFKILL control " "device"); goto fail; } if (fcntl(rfkill->fd, F_SETFL, O_NONBLOCK) < 0) { wpa_printf(MSG_ERROR, "rfkill: Cannot set non-blocking mode: " "%s", strerror(errno)); goto fail2; } for (;;) { len = read(rfkill->fd, &event, sizeof(event)); if (len < 0) { if (errno == EAGAIN) break; /* No more entries */ wpa_printf(MSG_ERROR, "rfkill: Event read failed: %s", strerror(errno)); break; } if (len != RFKILL_EVENT_SIZE_V1) { wpa_printf(MSG_DEBUG, "rfkill: Unexpected event size " "%d (expected %d)", (int) len, RFKILL_EVENT_SIZE_V1); continue; } wpa_printf(MSG_DEBUG, "rfkill: initial event: idx=%u type=%d " "op=%u soft=%u hard=%u", event.idx, event.type, event.op, event.soft, event.hard); if (event.op != RFKILL_OP_ADD || event.type != RFKILL_TYPE_WLAN) continue; if (event.hard) { wpa_printf(MSG_INFO, "rfkill: WLAN hard blocked"); rfkill->blocked = 1; } else if (event.soft) { wpa_printf(MSG_INFO, "rfkill: WLAN soft blocked"); rfkill->blocked = 1; } } eloop_register_read_sock(rfkill->fd, rfkill_receive, rfkill, NULL); return rfkill; fail2: close(rfkill->fd); fail: os_free(rfkill); return NULL; } void rfkill_deinit(struct rfkill_data *rfkill) { if (rfkill == NULL) return; if (rfkill->fd >= 0) { eloop_unregister_read_sock(rfkill->fd); close(rfkill->fd); } os_free(rfkill->cfg); os_free(rfkill); } int rfkill_is_blocked(struct rfkill_data *rfkill) { if (rfkill == NULL) return 0; return rfkill->blocked; } wpa-2.1/src/drivers/rfkill.h000066400000000000000000000011071227414666700160620ustar00rootroot00000000000000/* * Linux rfkill helper functions for driver wrappers * Copyright (c) 2010, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef RFKILL_H #define RFKILL_H struct rfkill_data; struct rfkill_config { void *ctx; char ifname[IFNAMSIZ]; void (*blocked_cb)(void *ctx); void (*unblocked_cb)(void *ctx); }; struct rfkill_data * rfkill_init(struct rfkill_config *cfg); void rfkill_deinit(struct rfkill_data *rfkill); int rfkill_is_blocked(struct rfkill_data *rfkill); #endif /* RFKILL_H */ wpa-2.1/src/eap_common/000077500000000000000000000000001227414666700150665ustar00rootroot00000000000000wpa-2.1/src/eap_common/Makefile000066400000000000000000000001641227414666700165270ustar00rootroot00000000000000all: @echo Nothing to be made. clean: rm -f *~ *.o *.d *.gcno *.gcda *.gcov install: @echo Nothing to be made. wpa-2.1/src/eap_common/chap.c000066400000000000000000000011321227414666700161420ustar00rootroot00000000000000/* * CHAP-MD5 (RFC 1994) * Copyright (c) 2007-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 "crypto/crypto.h" #include "chap.h" int chap_md5(u8 id, const u8 *secret, size_t secret_len, const u8 *challenge, size_t challenge_len, u8 *response) { const u8 *addr[3]; size_t len[3]; addr[0] = &id; len[0] = 1; addr[1] = secret; len[1] = secret_len; addr[2] = challenge; len[2] = challenge_len; return md5_vector(3, addr, len, response); } wpa-2.1/src/eap_common/chap.h000066400000000000000000000006041227414666700161520ustar00rootroot00000000000000/* * CHAP-MD5 (RFC 1994) * Copyright (c) 2007-2009, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef CHAP_H #define CHAP_H #define CHAP_MD5_LEN 16 int chap_md5(u8 id, const u8 *secret, size_t secret_len, const u8 *challenge, size_t challenge_len, u8 *response); #endif /* CHAP_H */ wpa-2.1/src/eap_common/eap_common.c000066400000000000000000000127101227414666700173500ustar00rootroot00000000000000/* * EAP common peer/server definitions * Copyright (c) 2004-2012, 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_defs.h" #include "eap_common.h" /** * eap_hdr_len_valid - Validate EAP header length field * @msg: EAP frame (starting with EAP header) * @min_payload: Minimum payload length needed * Returns: 1 for valid header, 0 for invalid * * This is a helper function that does minimal validation of EAP messages. The * length field is verified to be large enough to include the header and not * too large to go beyond the end of the buffer. */ int eap_hdr_len_valid(const struct wpabuf *msg, size_t min_payload) { const struct eap_hdr *hdr; size_t len; if (msg == NULL) return 0; hdr = wpabuf_head(msg); if (wpabuf_len(msg) < sizeof(*hdr)) { wpa_printf(MSG_INFO, "EAP: Too short EAP frame"); return 0; } len = be_to_host16(hdr->length); if (len < sizeof(*hdr) + min_payload || len > wpabuf_len(msg)) { wpa_printf(MSG_INFO, "EAP: Invalid EAP length"); return 0; } return 1; } /** * eap_hdr_validate - Validate EAP header * @vendor: Expected EAP Vendor-Id (0 = IETF) * @eap_type: Expected EAP type number * @msg: EAP frame (starting with EAP header) * @plen: Pointer to variable to contain the returned payload length * Returns: Pointer to EAP payload (after type field), or %NULL on failure * * This is a helper function for EAP method implementations. This is usually * called in the beginning of struct eap_method::process() function to verify * that the received EAP request packet has a valid header. This function is * able to process both legacy and expanded EAP headers and in most cases, the * caller can just use the returned payload pointer (into *plen) for processing * the payload regardless of whether the packet used the expanded EAP header or * not. */ const u8 * eap_hdr_validate(int vendor, EapType eap_type, const struct wpabuf *msg, size_t *plen) { const struct eap_hdr *hdr; const u8 *pos; size_t len; if (!eap_hdr_len_valid(msg, 1)) return NULL; hdr = wpabuf_head(msg); len = be_to_host16(hdr->length); pos = (const u8 *) (hdr + 1); if (*pos == EAP_TYPE_EXPANDED) { int exp_vendor; u32 exp_type; if (len < sizeof(*hdr) + 8) { wpa_printf(MSG_INFO, "EAP: Invalid expanded EAP " "length"); return NULL; } pos++; exp_vendor = WPA_GET_BE24(pos); pos += 3; exp_type = WPA_GET_BE32(pos); pos += 4; if (exp_vendor != vendor || exp_type != (u32) eap_type) { wpa_printf(MSG_INFO, "EAP: Invalid expanded frame " "type"); return NULL; } *plen = len - sizeof(*hdr) - 8; return pos; } else { if (vendor != EAP_VENDOR_IETF || *pos != eap_type) { wpa_printf(MSG_INFO, "EAP: Invalid frame type"); return NULL; } *plen = len - sizeof(*hdr) - 1; return pos + 1; } } /** * eap_msg_alloc - Allocate a buffer for an EAP message * @vendor: Vendor-Id (0 = IETF) * @type: EAP type * @payload_len: Payload length in bytes (data after Type) * @code: Message Code (EAP_CODE_*) * @identifier: Identifier * Returns: Pointer to the allocated message buffer or %NULL on error * * This function can be used to allocate a buffer for an EAP message and fill * in the EAP header. This function is automatically using expanded EAP header * if the selected Vendor-Id is not IETF. In other words, most EAP methods do * not need to separately select which header type to use when using this * function to allocate the message buffers. The returned buffer has room for * payload_len bytes and has the EAP header and Type field already filled in. */ struct wpabuf * eap_msg_alloc(int vendor, EapType type, size_t payload_len, u8 code, u8 identifier) { struct wpabuf *buf; struct eap_hdr *hdr; size_t len; len = sizeof(struct eap_hdr) + (vendor == EAP_VENDOR_IETF ? 1 : 8) + payload_len; buf = wpabuf_alloc(len); if (buf == NULL) return NULL; hdr = wpabuf_put(buf, sizeof(*hdr)); hdr->code = code; hdr->identifier = identifier; hdr->length = host_to_be16(len); if (vendor == EAP_VENDOR_IETF) { wpabuf_put_u8(buf, type); } else { wpabuf_put_u8(buf, EAP_TYPE_EXPANDED); wpabuf_put_be24(buf, vendor); wpabuf_put_be32(buf, type); } return buf; } /** * eap_update_len - Update EAP header length * @msg: EAP message from eap_msg_alloc * * This function updates the length field in the EAP header to match with the * current length for the buffer. This allows eap_msg_alloc() to be used to * allocate a larger buffer than the exact message length (e.g., if exact * message length is not yet known). */ void eap_update_len(struct wpabuf *msg) { struct eap_hdr *hdr; hdr = wpabuf_mhead(msg); if (wpabuf_len(msg) < sizeof(*hdr)) return; hdr->length = host_to_be16(wpabuf_len(msg)); } /** * eap_get_id - Get EAP Identifier from wpabuf * @msg: Buffer starting with an EAP header * Returns: The Identifier field from the EAP header */ u8 eap_get_id(const struct wpabuf *msg) { const struct eap_hdr *eap; if (wpabuf_len(msg) < sizeof(*eap)) return 0; eap = wpabuf_head(msg); return eap->identifier; } /** * eap_get_id - Get EAP Type from wpabuf * @msg: Buffer starting with an EAP header * Returns: The EAP Type after the EAP header */ EapType eap_get_type(const struct wpabuf *msg) { if (wpabuf_len(msg) < sizeof(struct eap_hdr) + 1) return EAP_TYPE_NONE; return ((const u8 *) wpabuf_head(msg))[sizeof(struct eap_hdr)]; } wpa-2.1/src/eap_common/eap_common.h000066400000000000000000000013071227414666700173550ustar00rootroot00000000000000/* * EAP common peer/server definitions * Copyright (c) 2004-2012, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef EAP_COMMON_H #define EAP_COMMON_H #include "wpabuf.h" int eap_hdr_len_valid(const struct wpabuf *msg, size_t min_payload); const u8 * eap_hdr_validate(int vendor, EapType eap_type, const struct wpabuf *msg, size_t *plen); struct wpabuf * eap_msg_alloc(int vendor, EapType type, size_t payload_len, u8 code, u8 identifier); void eap_update_len(struct wpabuf *msg); u8 eap_get_id(const struct wpabuf *msg); EapType eap_get_type(const struct wpabuf *msg); #endif /* EAP_COMMON_H */ wpa-2.1/src/eap_common/eap_defs.h000066400000000000000000000047671227414666700170230ustar00rootroot00000000000000/* * EAP server/peer: Shared EAP definitions * Copyright (c) 2004-2007, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef EAP_DEFS_H #define EAP_DEFS_H /* RFC 3748 - Extensible Authentication Protocol (EAP) */ #ifdef _MSC_VER #pragma pack(push, 1) #endif /* _MSC_VER */ struct eap_hdr { u8 code; u8 identifier; be16 length; /* including code and identifier; network byte order */ /* followed by length-4 octets of data */ } STRUCT_PACKED; #ifdef _MSC_VER #pragma pack(pop) #endif /* _MSC_VER */ enum { EAP_CODE_REQUEST = 1, EAP_CODE_RESPONSE = 2, EAP_CODE_SUCCESS = 3, EAP_CODE_FAILURE = 4 }; /* EAP Request and Response data begins with one octet Type. Success and * Failure do not have additional data. */ /* * EAP Method Types as allocated by IANA: * http://www.iana.org/assignments/eap-numbers */ typedef enum { EAP_TYPE_NONE = 0, EAP_TYPE_IDENTITY = 1 /* RFC 3748 */, EAP_TYPE_NOTIFICATION = 2 /* RFC 3748 */, EAP_TYPE_NAK = 3 /* Response only, RFC 3748 */, EAP_TYPE_MD5 = 4, /* RFC 3748 */ EAP_TYPE_OTP = 5 /* RFC 3748 */, EAP_TYPE_GTC = 6, /* RFC 3748 */ EAP_TYPE_TLS = 13 /* RFC 2716 */, EAP_TYPE_LEAP = 17 /* Cisco proprietary */, EAP_TYPE_SIM = 18 /* RFC 4186 */, EAP_TYPE_TTLS = 21 /* RFC 5281 */, EAP_TYPE_AKA = 23 /* RFC 4187 */, EAP_TYPE_PEAP = 25 /* draft-josefsson-pppext-eap-tls-eap-06.txt */, EAP_TYPE_MSCHAPV2 = 26 /* draft-kamath-pppext-eap-mschapv2-00.txt */, EAP_TYPE_TLV = 33 /* draft-josefsson-pppext-eap-tls-eap-07.txt */, EAP_TYPE_TNC = 38 /* TNC IF-T v1.0-r3; note: tentative assignment; * type 38 has previously been allocated for * EAP-HTTP Digest, (funk.com) */, EAP_TYPE_FAST = 43 /* RFC 4851 */, EAP_TYPE_PAX = 46 /* RFC 4746 */, EAP_TYPE_PSK = 47 /* RFC 4764 */, EAP_TYPE_SAKE = 48 /* RFC 4763 */, EAP_TYPE_IKEV2 = 49 /* RFC 5106 */, EAP_TYPE_AKA_PRIME = 50 /* RFC 5448 */, EAP_TYPE_GPSK = 51 /* RFC 5433 */, EAP_TYPE_PWD = 52 /* RFC 5931 */, EAP_TYPE_EKE = 53 /* RFC 6124 */, EAP_TYPE_EXPANDED = 254 /* RFC 3748 */ } EapType; /* SMI Network Management Private Enterprise Code for vendor specific types */ enum { EAP_VENDOR_IETF = 0, EAP_VENDOR_MICROSOFT = 0x000137 /* Microsoft */, EAP_VENDOR_WFA = 0x00372A /* Wi-Fi Alliance */, EAP_VENDOR_HOSTAP = 39068 /* hostapd/wpa_supplicant project */ }; #define EAP_VENDOR_UNAUTH_TLS EAP_VENDOR_HOSTAP #define EAP_VENDOR_TYPE_UNAUTH_TLS 1 #define EAP_MSK_LEN 64 #define EAP_EMSK_LEN 64 #endif /* EAP_DEFS_H */ wpa-2.1/src/eap_common/eap_eke_common.c000066400000000000000000000427421227414666700202040ustar00rootroot00000000000000/* * EAP server/peer: EAP-EKE shared routines * Copyright (c) 2011-2013, 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/aes.h" #include "crypto/aes_wrap.h" #include "crypto/crypto.h" #include "crypto/dh_groups.h" #include "crypto/random.h" #include "crypto/sha1.h" #include "crypto/sha256.h" #include "eap_common/eap_defs.h" #include "eap_eke_common.h" static int eap_eke_dh_len(u8 group) { switch (group) { case EAP_EKE_DHGROUP_EKE_2: return 128; case EAP_EKE_DHGROUP_EKE_5: return 192; case EAP_EKE_DHGROUP_EKE_14: return 256; case EAP_EKE_DHGROUP_EKE_15: return 384; case EAP_EKE_DHGROUP_EKE_16: return 512; } return -1; } static int eap_eke_dhcomp_len(u8 dhgroup, u8 encr) { int dhlen; dhlen = eap_eke_dh_len(dhgroup); if (dhlen < 0) return -1; if (encr != EAP_EKE_ENCR_AES128_CBC) return -1; return AES_BLOCK_SIZE + dhlen; } static const struct dh_group * eap_eke_dh_group(u8 group) { switch (group) { case EAP_EKE_DHGROUP_EKE_2: return dh_groups_get(2); case EAP_EKE_DHGROUP_EKE_5: return dh_groups_get(5); case EAP_EKE_DHGROUP_EKE_14: return dh_groups_get(14); case EAP_EKE_DHGROUP_EKE_15: return dh_groups_get(15); case EAP_EKE_DHGROUP_EKE_16: return dh_groups_get(16); } return NULL; } static int eap_eke_dh_generator(u8 group) { switch (group) { case EAP_EKE_DHGROUP_EKE_2: return 5; case EAP_EKE_DHGROUP_EKE_5: return 31; case EAP_EKE_DHGROUP_EKE_14: return 11; case EAP_EKE_DHGROUP_EKE_15: return 5; case EAP_EKE_DHGROUP_EKE_16: return 5; } return -1; } static int eap_eke_pnonce_len(u8 mac) { int mac_len; if (mac == EAP_EKE_MAC_HMAC_SHA1) mac_len = SHA1_MAC_LEN; else if (mac == EAP_EKE_MAC_HMAC_SHA2_256) mac_len = SHA256_MAC_LEN; else return -1; return AES_BLOCK_SIZE + 16 + mac_len; } static int eap_eke_pnonce_ps_len(u8 mac) { int mac_len; if (mac == EAP_EKE_MAC_HMAC_SHA1) mac_len = SHA1_MAC_LEN; else if (mac == EAP_EKE_MAC_HMAC_SHA2_256) mac_len = SHA256_MAC_LEN; else return -1; return AES_BLOCK_SIZE + 2 * 16 + mac_len; } static int eap_eke_prf_len(u8 prf) { if (prf == EAP_EKE_PRF_HMAC_SHA1) return 20; if (prf == EAP_EKE_PRF_HMAC_SHA2_256) return 32; return -1; } static int eap_eke_nonce_len(u8 prf) { int prf_len; prf_len = eap_eke_prf_len(prf); if (prf_len < 0) return -1; if (prf_len > 2 * 16) return (prf_len + 1) / 2; return 16; } static int eap_eke_auth_len(u8 prf) { switch (prf) { case EAP_EKE_PRF_HMAC_SHA1: return SHA1_MAC_LEN; case EAP_EKE_PRF_HMAC_SHA2_256: return SHA256_MAC_LEN; } return -1; } int eap_eke_dh_init(u8 group, u8 *ret_priv, u8 *ret_pub) { int generator; u8 gen; const struct dh_group *dh; size_t pub_len, i; generator = eap_eke_dh_generator(group); if (generator < 0 || generator > 255) return -1; gen = generator; dh = eap_eke_dh_group(group); if (dh == NULL) return -1; /* x = random number 2 .. p-1 */ if (random_get_bytes(ret_priv, dh->prime_len)) return -1; if (os_memcmp(ret_priv, dh->prime, dh->prime_len) > 0) { /* Make sure private value is smaller than prime */ ret_priv[0] = 0; } for (i = 0; i < dh->prime_len - 1; i++) { if (ret_priv[i]) break; } if (i == dh->prime_len - 1 && (ret_priv[i] == 0 || ret_priv[i] == 1)) return -1; wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: DH private value", ret_priv, dh->prime_len); /* y = g ^ x (mod p) */ pub_len = dh->prime_len; if (crypto_mod_exp(&gen, 1, ret_priv, dh->prime_len, dh->prime, dh->prime_len, ret_pub, &pub_len) < 0) return -1; if (pub_len < dh->prime_len) { size_t pad = dh->prime_len - pub_len; os_memmove(ret_pub + pad, ret_pub, pub_len); os_memset(ret_pub, 0, pad); } wpa_hexdump(MSG_DEBUG, "EAP-EKE: DH public value", ret_pub, dh->prime_len); return 0; } static int eap_eke_prf(u8 prf, const u8 *key, size_t key_len, const u8 *data, size_t data_len, const u8 *data2, size_t data2_len, u8 *res) { const u8 *addr[2]; size_t len[2]; size_t num_elem = 1; addr[0] = data; len[0] = data_len; if (data2) { num_elem++; addr[1] = data2; len[1] = data2_len; } if (prf == EAP_EKE_PRF_HMAC_SHA1) return hmac_sha1_vector(key, key_len, num_elem, addr, len, res); if (prf == EAP_EKE_PRF_HMAC_SHA2_256) return hmac_sha256_vector(key, key_len, num_elem, addr, len, res); return -1; } static int eap_eke_prf_hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len, u8 *res, size_t len) { u8 hash[SHA1_MAC_LEN]; u8 idx; const u8 *addr[3]; size_t vlen[3]; int ret; idx = 0; addr[0] = hash; vlen[0] = SHA1_MAC_LEN; addr[1] = data; vlen[1] = data_len; addr[2] = &idx; vlen[2] = 1; while (len > 0) { idx++; if (idx == 1) ret = hmac_sha1_vector(key, key_len, 2, &addr[1], &vlen[1], hash); else ret = hmac_sha1_vector(key, key_len, 3, addr, vlen, hash); if (ret < 0) return -1; if (len > SHA1_MAC_LEN) { os_memcpy(res, hash, SHA1_MAC_LEN); res += SHA1_MAC_LEN; len -= SHA1_MAC_LEN; } else { os_memcpy(res, hash, len); len = 0; } } return 0; } static int eap_eke_prf_hmac_sha256(const u8 *key, size_t key_len, const u8 *data, size_t data_len, u8 *res, size_t len) { u8 hash[SHA256_MAC_LEN]; u8 idx; const u8 *addr[3]; size_t vlen[3]; int ret; idx = 0; addr[0] = hash; vlen[0] = SHA256_MAC_LEN; addr[1] = data; vlen[1] = data_len; addr[2] = &idx; vlen[2] = 1; while (len > 0) { idx++; if (idx == 1) ret = hmac_sha256_vector(key, key_len, 2, &addr[1], &vlen[1], hash); else ret = hmac_sha256_vector(key, key_len, 3, addr, vlen, hash); if (ret < 0) return -1; if (len > SHA256_MAC_LEN) { os_memcpy(res, hash, SHA256_MAC_LEN); res += SHA256_MAC_LEN; len -= SHA256_MAC_LEN; } else { os_memcpy(res, hash, len); len = 0; } } return 0; } static int eap_eke_prfplus(u8 prf, const u8 *key, size_t key_len, const u8 *data, size_t data_len, u8 *res, size_t len) { if (prf == EAP_EKE_PRF_HMAC_SHA1) return eap_eke_prf_hmac_sha1(key, key_len, data, data_len, res, len); if (prf == EAP_EKE_PRF_HMAC_SHA2_256) return eap_eke_prf_hmac_sha256(key, key_len, data, data_len, res, len); return -1; } int eap_eke_derive_key(struct eap_eke_session *sess, const u8 *password, size_t password_len, const u8 *id_s, size_t id_s_len, const u8 *id_p, size_t id_p_len, u8 *key) { u8 zeros[EAP_EKE_MAX_HASH_LEN]; u8 temp[EAP_EKE_MAX_HASH_LEN]; size_t key_len = 16; /* Only AES-128-CBC is used here */ u8 *id; /* temp = prf(0+, password) */ os_memset(zeros, 0, sess->prf_len); if (eap_eke_prf(sess->prf, zeros, sess->prf_len, password, password_len, NULL, 0, temp) < 0) return -1; wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: temp = prf(0+, password)", temp, sess->prf_len); /* key = prf+(temp, ID_S | ID_P) */ id = os_malloc(id_s_len + id_p_len); if (id == NULL) return -1; os_memcpy(id, id_s, id_s_len); os_memcpy(id + id_s_len, id_p, id_p_len); wpa_hexdump_ascii(MSG_DEBUG, "EAP-EKE: ID_S | ID_P", id, id_s_len + id_p_len); if (eap_eke_prfplus(sess->prf, temp, sess->prf_len, id, id_s_len + id_p_len, key, key_len) < 0) { os_free(id); return -1; } os_free(id); wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: key = prf+(temp, ID_S | ID_P)", key, key_len); return 0; } int eap_eke_dhcomp(struct eap_eke_session *sess, const u8 *key, const u8 *dhpub, u8 *ret_dhcomp) { u8 pub[EAP_EKE_MAX_DH_LEN]; int dh_len; u8 iv[AES_BLOCK_SIZE]; dh_len = eap_eke_dh_len(sess->dhgroup); if (dh_len < 0) return -1; /* * DHComponent = Encr(key, y) * * All defined DH groups use primes that have length devisible by 16, so * no need to do extra padding for y (= pub). */ if (sess->encr != EAP_EKE_ENCR_AES128_CBC) return -1; if (random_get_bytes(iv, AES_BLOCK_SIZE)) return -1; wpa_hexdump(MSG_DEBUG, "EAP-EKE: IV for Encr(key, y)", iv, AES_BLOCK_SIZE); os_memcpy(pub, dhpub, dh_len); if (aes_128_cbc_encrypt(key, iv, pub, dh_len) < 0) return -1; os_memcpy(ret_dhcomp, iv, AES_BLOCK_SIZE); os_memcpy(ret_dhcomp + AES_BLOCK_SIZE, pub, dh_len); wpa_hexdump(MSG_DEBUG, "EAP-EKE: DHComponent = Encr(key, y)", ret_dhcomp, AES_BLOCK_SIZE + dh_len); return 0; } int eap_eke_shared_secret(struct eap_eke_session *sess, const u8 *key, const u8 *dhpriv, const u8 *peer_dhcomp) { u8 zeros[EAP_EKE_MAX_HASH_LEN]; u8 peer_pub[EAP_EKE_MAX_DH_LEN]; u8 modexp[EAP_EKE_MAX_DH_LEN]; size_t len; const struct dh_group *dh; if (sess->encr != EAP_EKE_ENCR_AES128_CBC) return -1; dh = eap_eke_dh_group(sess->dhgroup); if (dh == NULL) return -1; /* Decrypt peer DHComponent */ os_memcpy(peer_pub, peer_dhcomp + AES_BLOCK_SIZE, dh->prime_len); if (aes_128_cbc_decrypt(key, peer_dhcomp, peer_pub, dh->prime_len) < 0) { wpa_printf(MSG_INFO, "EAP-EKE: Failed to decrypt DHComponent"); return -1; } wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Decrypted peer DH pubkey", peer_pub, dh->prime_len); /* SharedSecret = prf(0+, g ^ (x_s * x_p) (mod p)) */ len = dh->prime_len; if (crypto_mod_exp(peer_pub, dh->prime_len, dhpriv, dh->prime_len, dh->prime, dh->prime_len, modexp, &len) < 0) return -1; if (len < dh->prime_len) { size_t pad = dh->prime_len - len; os_memmove(modexp + pad, modexp, len); os_memset(modexp, 0, pad); } os_memset(zeros, 0, sess->auth_len); if (eap_eke_prf(sess->prf, zeros, sess->auth_len, modexp, dh->prime_len, NULL, 0, sess->shared_secret) < 0) return -1; wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: SharedSecret", sess->shared_secret, sess->auth_len); return 0; } int eap_eke_derive_ke_ki(struct eap_eke_session *sess, const u8 *id_s, size_t id_s_len, const u8 *id_p, size_t id_p_len) { u8 buf[EAP_EKE_MAX_KE_LEN + EAP_EKE_MAX_KI_LEN]; size_t ke_len, ki_len; u8 *data; size_t data_len; const char *label = "EAP-EKE Keys"; size_t label_len; /* * Ke | Ki = prf+(SharedSecret, "EAP-EKE Keys" | ID_S | ID_P) * Ke = encryption key * Ki = integrity protection key * Length of each key depends on the selected algorithms. */ if (sess->encr == EAP_EKE_ENCR_AES128_CBC) ke_len = 16; else return -1; if (sess->mac == EAP_EKE_PRF_HMAC_SHA1) ki_len = 20; else if (sess->mac == EAP_EKE_PRF_HMAC_SHA2_256) ki_len = 32; else return -1; label_len = os_strlen(label); data_len = label_len + id_s_len + id_p_len; data = os_malloc(data_len); if (data == NULL) return -1; os_memcpy(data, label, label_len); os_memcpy(data + label_len, id_s, id_s_len); os_memcpy(data + label_len + id_s_len, id_p, id_p_len); if (eap_eke_prfplus(sess->prf, sess->shared_secret, sess->prf_len, data, data_len, buf, ke_len + ki_len) < 0) { os_free(data); return -1; } os_memcpy(sess->ke, buf, ke_len); wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Ke", sess->ke, ke_len); os_memcpy(sess->ki, buf + ke_len, ki_len); wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Ki", sess->ki, ki_len); os_free(data); return 0; } int eap_eke_derive_ka(struct eap_eke_session *sess, const u8 *id_s, size_t id_s_len, const u8 *id_p, size_t id_p_len, const u8 *nonce_p, const u8 *nonce_s) { u8 *data, *pos; size_t data_len; const char *label = "EAP-EKE Ka"; size_t label_len; /* * Ka = prf+(SharedSecret, "EAP-EKE Ka" | ID_S | ID_P | Nonce_P | * Nonce_S) * Ka = authentication key * Length of the key depends on the selected algorithms. */ label_len = os_strlen(label); data_len = label_len + id_s_len + id_p_len + 2 * sess->nonce_len; data = os_malloc(data_len); if (data == NULL) return -1; pos = data; os_memcpy(pos, label, label_len); pos += label_len; os_memcpy(pos, id_s, id_s_len); pos += id_s_len; os_memcpy(pos, id_p, id_p_len); pos += id_p_len; os_memcpy(pos, nonce_p, sess->nonce_len); pos += sess->nonce_len; os_memcpy(pos, nonce_s, sess->nonce_len); if (eap_eke_prfplus(sess->prf, sess->shared_secret, sess->prf_len, data, data_len, sess->ka, sess->prf_len) < 0) { os_free(data); return -1; } os_free(data); wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Ka", sess->ka, sess->prf_len); return 0; } int eap_eke_derive_msk(struct eap_eke_session *sess, const u8 *id_s, size_t id_s_len, const u8 *id_p, size_t id_p_len, const u8 *nonce_p, const u8 *nonce_s, u8 *msk, u8 *emsk) { u8 *data, *pos; size_t data_len; const char *label = "EAP-EKE Exported Keys"; size_t label_len; u8 buf[EAP_MSK_LEN + EAP_EMSK_LEN]; /* * MSK | EMSK = prf+(SharedSecret, "EAP-EKE Exported Keys" | ID_S | * ID_P | Nonce_P | Nonce_S) */ label_len = os_strlen(label); data_len = label_len + id_s_len + id_p_len + 2 * sess->nonce_len; data = os_malloc(data_len); if (data == NULL) return -1; pos = data; os_memcpy(pos, label, label_len); pos += label_len; os_memcpy(pos, id_s, id_s_len); pos += id_s_len; os_memcpy(pos, id_p, id_p_len); pos += id_p_len; os_memcpy(pos, nonce_p, sess->nonce_len); pos += sess->nonce_len; os_memcpy(pos, nonce_s, sess->nonce_len); if (eap_eke_prfplus(sess->prf, sess->shared_secret, sess->prf_len, data, data_len, buf, EAP_MSK_LEN + EAP_EMSK_LEN) < 0) { os_free(data); return -1; } os_free(data); os_memcpy(msk, buf, EAP_MSK_LEN); os_memcpy(emsk, buf + EAP_MSK_LEN, EAP_EMSK_LEN); os_memset(buf, 0, sizeof(buf)); wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: MSK", msk, EAP_MSK_LEN); wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: EMSK", msk, EAP_EMSK_LEN); return 0; } static int eap_eke_mac(u8 mac, const u8 *key, const u8 *data, size_t data_len, u8 *res) { if (mac == EAP_EKE_MAC_HMAC_SHA1) return hmac_sha1(key, SHA1_MAC_LEN, data, data_len, res); if (mac == EAP_EKE_MAC_HMAC_SHA2_256) return hmac_sha256(key, SHA256_MAC_LEN, data, data_len, res); return -1; } int eap_eke_prot(struct eap_eke_session *sess, const u8 *data, size_t data_len, u8 *prot, size_t *prot_len) { size_t block_size, icv_len, pad; u8 *pos, *iv, *e; if (sess->encr == EAP_EKE_ENCR_AES128_CBC) block_size = AES_BLOCK_SIZE; else return -1; if (sess->mac == EAP_EKE_PRF_HMAC_SHA1) icv_len = SHA1_MAC_LEN; else if (sess->mac == EAP_EKE_PRF_HMAC_SHA2_256) icv_len = SHA256_MAC_LEN; else return -1; pad = data_len % block_size; if (pad) pad = block_size - pad; if (*prot_len < block_size + data_len + pad + icv_len) { wpa_printf(MSG_INFO, "EAP-EKE: Not enough room for Prot() data"); } pos = prot; if (random_get_bytes(pos, block_size)) return -1; iv = pos; wpa_hexdump(MSG_DEBUG, "EAP-EKE: IV for Prot()", iv, block_size); pos += block_size; e = pos; os_memcpy(pos, data, data_len); pos += data_len; if (pad) { if (random_get_bytes(pos, pad)) return -1; pos += pad; } if (aes_128_cbc_encrypt(sess->ke, iv, e, data_len + pad) < 0) return -1; if (eap_eke_mac(sess->mac, sess->ki, e, data_len + pad, pos) < 0) return -1; pos += icv_len; *prot_len = pos - prot; return 0; } int eap_eke_decrypt_prot(struct eap_eke_session *sess, const u8 *prot, size_t prot_len, u8 *data, size_t *data_len) { size_t block_size, icv_len; u8 icv[EAP_EKE_MAX_HASH_LEN]; if (sess->encr == EAP_EKE_ENCR_AES128_CBC) block_size = AES_BLOCK_SIZE; else return -1; if (sess->mac == EAP_EKE_PRF_HMAC_SHA1) icv_len = SHA1_MAC_LEN; else if (sess->mac == EAP_EKE_PRF_HMAC_SHA2_256) icv_len = SHA256_MAC_LEN; else return -1; if (prot_len < 2 * block_size + icv_len) return -1; if ((prot_len - icv_len) % block_size) return -1; if (eap_eke_mac(sess->mac, sess->ki, prot + block_size, prot_len - block_size - icv_len, icv) < 0) return -1; if (os_memcmp(icv, prot + prot_len - icv_len, icv_len) != 0) { wpa_printf(MSG_INFO, "EAP-EKE: ICV mismatch in Prot() data"); return -1; } if (*data_len < prot_len - block_size - icv_len) { wpa_printf(MSG_INFO, "EAP-EKE: Not enough room for decrypted Prot() data"); return -1; } *data_len = prot_len - block_size - icv_len; os_memcpy(data, prot + block_size, *data_len); if (aes_128_cbc_decrypt(sess->ke, prot, data, *data_len) < 0) { wpa_printf(MSG_INFO, "EAP-EKE: Failed to decrypt Prot() data"); return -1; } wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Decrypted Prot() data", data, *data_len); return 0; } int eap_eke_auth(struct eap_eke_session *sess, const char *label, const struct wpabuf *msgs, u8 *auth) { wpa_printf(MSG_DEBUG, "EAP-EKE: Auth(%s)", label); wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Ka for Auth", sess->ka, sess->auth_len); wpa_hexdump_buf(MSG_MSGDUMP, "EAP-EKE: Messages for Auth", msgs); return eap_eke_prf(sess->prf, sess->ka, sess->auth_len, (const u8 *) label, os_strlen(label), wpabuf_head(msgs), wpabuf_len(msgs), auth); } int eap_eke_session_init(struct eap_eke_session *sess, u8 dhgroup, u8 encr, u8 prf, u8 mac) { sess->dhgroup = dhgroup; sess->encr = encr; sess->prf = prf; sess->mac = mac; sess->prf_len = eap_eke_prf_len(prf); if (sess->prf_len < 0) return -1; sess->nonce_len = eap_eke_nonce_len(prf); if (sess->nonce_len < 0) return -1; sess->auth_len = eap_eke_auth_len(prf); if (sess->auth_len < 0) return -1; sess->dhcomp_len = eap_eke_dhcomp_len(sess->dhgroup, sess->encr); if (sess->dhcomp_len < 0) return -1; sess->pnonce_len = eap_eke_pnonce_len(sess->mac); if (sess->pnonce_len < 0) return -1; sess->pnonce_ps_len = eap_eke_pnonce_ps_len(sess->mac); if (sess->pnonce_ps_len < 0) return -1; return 0; } void eap_eke_session_clean(struct eap_eke_session *sess) { os_memset(sess->shared_secret, 0, EAP_EKE_MAX_HASH_LEN); os_memset(sess->ke, 0, EAP_EKE_MAX_KE_LEN); os_memset(sess->ki, 0, EAP_EKE_MAX_KI_LEN); os_memset(sess->ka, 0, EAP_EKE_MAX_KA_LEN); } wpa-2.1/src/eap_common/eap_eke_common.h000066400000000000000000000067371227414666700202150ustar00rootroot00000000000000/* * EAP server/peer: EAP-EKE shared routines * Copyright (c) 2011-2013, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef EAP_EKE_COMMON_H #define EAP_EKE_COMMON_H /* EKE Exchange */ #define EAP_EKE_ID 1 #define EAP_EKE_COMMIT 2 #define EAP_EKE_CONFIRM 3 #define EAP_EKE_FAILURE 4 /* Diffie-Hellman Group Registry */ #define EAP_EKE_DHGROUP_EKE_2 1 #define EAP_EKE_DHGROUP_EKE_5 2 #define EAP_EKE_DHGROUP_EKE_14 3 /* mandatory to implement */ #define EAP_EKE_DHGROUP_EKE_15 4 #define EAP_EKE_DHGROUP_EKE_16 5 /* Encryption Algorithm Registry */ #define EAP_EKE_ENCR_AES128_CBC 1 /* mandatory to implement */ /* Pseudo Random Function Registry */ #define EAP_EKE_PRF_HMAC_SHA1 1 /* mandatory to implement */ #define EAP_EKE_PRF_HMAC_SHA2_256 2 /* Keyed Message Digest (MAC) Registry */ #define EAP_EKE_MAC_HMAC_SHA1 1 /* mandatory to implement */ #define EAP_EKE_MAC_HMAC_SHA2_256 2 /* Identity Type Registry */ #define EAP_EKE_ID_OPAQUE 1 #define EAP_EKE_ID_NAI 2 #define EAP_EKE_ID_IPv4 3 #define EAP_EKE_ID_IPv6 4 #define EAP_EKE_ID_FQDN 5 #define EAP_EKE_ID_DN 6 /* Failure-Code */ #define EAP_EKE_FAIL_NO_ERROR 1 #define EAP_EKE_FAIL_PROTO_ERROR 2 #define EAP_EKE_FAIL_PASSWD_NOT_FOUND 3 #define EAP_EKE_FAIL_AUTHENTICATION_FAIL 4 #define EAP_EKE_FAIL_AUTHORIZATION_FAIL 5 #define EAP_EKE_FAIL_NO_PROPOSAL_CHOSEN 6 #define EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR 0xffffffff #define EAP_EKE_MAX_DH_LEN 512 #define EAP_EKE_MAX_HASH_LEN 32 #define EAP_EKE_MAX_KEY_LEN 16 #define EAP_EKE_MAX_KE_LEN 16 #define EAP_EKE_MAX_KI_LEN 32 #define EAP_EKE_MAX_KA_LEN 32 #define EAP_EKE_MAX_NONCE_LEN 16 struct eap_eke_session { /* Selected proposal */ u8 dhgroup; u8 encr; u8 prf; u8 mac; u8 shared_secret[EAP_EKE_MAX_HASH_LEN]; u8 ke[EAP_EKE_MAX_KE_LEN]; u8 ki[EAP_EKE_MAX_KI_LEN]; u8 ka[EAP_EKE_MAX_KA_LEN]; int prf_len; int nonce_len; int auth_len; int dhcomp_len; int pnonce_len; int pnonce_ps_len; }; int eap_eke_session_init(struct eap_eke_session *sess, u8 dhgroup, u8 encr, u8 prf, u8 mac); void eap_eke_session_clean(struct eap_eke_session *sess); int eap_eke_dh_init(u8 group, u8 *ret_priv, u8 *ret_pub); int eap_eke_derive_key(struct eap_eke_session *sess, const u8 *password, size_t password_len, const u8 *id_s, size_t id_s_len, const u8 *id_p, size_t id_p_len, u8 *key); int eap_eke_dhcomp(struct eap_eke_session *sess, const u8 *key, const u8 *dhpub, u8 *ret_dhcomp); int eap_eke_shared_secret(struct eap_eke_session *sess, const u8 *key, const u8 *dhpriv, const u8 *peer_dhcomp); int eap_eke_derive_ke_ki(struct eap_eke_session *sess, const u8 *id_s, size_t id_s_len, const u8 *id_p, size_t id_p_len); int eap_eke_derive_ka(struct eap_eke_session *sess, const u8 *id_s, size_t id_s_len, const u8 *id_p, size_t id_p_len, const u8 *nonce_p, const u8 *nonce_s); int eap_eke_derive_msk(struct eap_eke_session *sess, const u8 *id_s, size_t id_s_len, const u8 *id_p, size_t id_p_len, const u8 *nonce_p, const u8 *nonce_s, u8 *msk, u8 *emsk); int eap_eke_prot(struct eap_eke_session *sess, const u8 *data, size_t data_len, u8 *prot, size_t *prot_len); int eap_eke_decrypt_prot(struct eap_eke_session *sess, const u8 *prot, size_t prot_len, u8 *data, size_t *data_len); int eap_eke_auth(struct eap_eke_session *sess, const char *label, const struct wpabuf *msgs, u8 *auth); #endif /* EAP_EKE_COMMON_H */ wpa-2.1/src/eap_common/eap_fast_common.c000066400000000000000000000200571227414666700203700ustar00rootroot00000000000000/* * EAP-FAST common helper functions (RFC 4851) * Copyright (c) 2008, 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/sha1.h" #include "crypto/tls.h" #include "eap_defs.h" #include "eap_tlv_common.h" #include "eap_fast_common.h" void eap_fast_put_tlv_hdr(struct wpabuf *buf, u16 type, u16 len) { struct pac_tlv_hdr hdr; hdr.type = host_to_be16(type); hdr.len = host_to_be16(len); wpabuf_put_data(buf, &hdr, sizeof(hdr)); } void eap_fast_put_tlv(struct wpabuf *buf, u16 type, const void *data, u16 len) { eap_fast_put_tlv_hdr(buf, type, len); wpabuf_put_data(buf, data, len); } void eap_fast_put_tlv_buf(struct wpabuf *buf, u16 type, const struct wpabuf *data) { eap_fast_put_tlv_hdr(buf, type, wpabuf_len(data)); wpabuf_put_buf(buf, data); } struct wpabuf * eap_fast_tlv_eap_payload(struct wpabuf *buf) { struct wpabuf *e; if (buf == NULL) return NULL; /* Encapsulate EAP packet in EAP-Payload TLV */ wpa_printf(MSG_DEBUG, "EAP-FAST: Add EAP-Payload TLV"); e = wpabuf_alloc(sizeof(struct pac_tlv_hdr) + wpabuf_len(buf)); if (e == NULL) { wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to allocate memory " "for TLV encapsulation"); wpabuf_free(buf); return NULL; } eap_fast_put_tlv_buf(e, EAP_TLV_TYPE_MANDATORY | EAP_TLV_EAP_PAYLOAD_TLV, buf); wpabuf_free(buf); return e; } void eap_fast_derive_master_secret(const u8 *pac_key, const u8 *server_random, const u8 *client_random, u8 *master_secret) { #define TLS_RANDOM_LEN 32 #define TLS_MASTER_SECRET_LEN 48 u8 seed[2 * TLS_RANDOM_LEN]; wpa_hexdump(MSG_DEBUG, "EAP-FAST: client_random", client_random, TLS_RANDOM_LEN); wpa_hexdump(MSG_DEBUG, "EAP-FAST: server_random", server_random, TLS_RANDOM_LEN); /* * RFC 4851, Section 5.1: * master_secret = T-PRF(PAC-Key, "PAC to master secret label hash", * server_random + client_random, 48) */ os_memcpy(seed, server_random, TLS_RANDOM_LEN); os_memcpy(seed + TLS_RANDOM_LEN, client_random, TLS_RANDOM_LEN); sha1_t_prf(pac_key, EAP_FAST_PAC_KEY_LEN, "PAC to master secret label hash", seed, sizeof(seed), master_secret, TLS_MASTER_SECRET_LEN); wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: master_secret", master_secret, TLS_MASTER_SECRET_LEN); } u8 * eap_fast_derive_key(void *ssl_ctx, struct tls_connection *conn, const char *label, size_t len) { struct tls_keys keys; u8 *rnd = NULL, *out; int block_size; block_size = tls_connection_get_keyblock_size(ssl_ctx, conn); if (block_size < 0) return NULL; out = os_malloc(block_size + len); if (out == NULL) return NULL; if (tls_connection_prf(ssl_ctx, conn, label, 1, out, block_size + len) == 0) { os_memmove(out, out + block_size, len); return out; } if (tls_connection_get_keys(ssl_ctx, conn, &keys)) goto fail; rnd = os_malloc(keys.client_random_len + keys.server_random_len); if (rnd == NULL) goto fail; os_memcpy(rnd, keys.server_random, keys.server_random_len); os_memcpy(rnd + keys.server_random_len, keys.client_random, keys.client_random_len); wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: master_secret for key " "expansion", keys.master_key, keys.master_key_len); if (tls_prf_sha1_md5(keys.master_key, keys.master_key_len, label, rnd, keys.client_random_len + keys.server_random_len, out, block_size + len)) goto fail; os_free(rnd); os_memmove(out, out + block_size, len); return out; fail: os_free(rnd); os_free(out); return NULL; } void eap_fast_derive_eap_msk(const u8 *simck, u8 *msk) { /* * RFC 4851, Section 5.4: EAP Master Session Key Generation * MSK = T-PRF(S-IMCK[j], "Session Key Generating Function", 64) */ sha1_t_prf(simck, EAP_FAST_SIMCK_LEN, "Session Key Generating Function", (u8 *) "", 0, msk, EAP_FAST_KEY_LEN); wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: Derived key (MSK)", msk, EAP_FAST_KEY_LEN); } void eap_fast_derive_eap_emsk(const u8 *simck, u8 *emsk) { /* * RFC 4851, Section 5.4: EAP Master Session Key Genreration * EMSK = T-PRF(S-IMCK[j], * "Extended Session Key Generating Function", 64) */ sha1_t_prf(simck, EAP_FAST_SIMCK_LEN, "Extended Session Key Generating Function", (u8 *) "", 0, emsk, EAP_EMSK_LEN); wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: Derived key (EMSK)", emsk, EAP_EMSK_LEN); } int eap_fast_parse_tlv(struct eap_fast_tlv_parse *tlv, int tlv_type, u8 *pos, int len) { switch (tlv_type) { case EAP_TLV_EAP_PAYLOAD_TLV: wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: EAP-Payload TLV", pos, len); if (tlv->eap_payload_tlv) { wpa_printf(MSG_DEBUG, "EAP-FAST: More than one " "EAP-Payload TLV in the message"); tlv->iresult = EAP_TLV_RESULT_FAILURE; return -2; } tlv->eap_payload_tlv = pos; tlv->eap_payload_tlv_len = len; break; case EAP_TLV_RESULT_TLV: wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Result TLV", pos, len); if (tlv->result) { wpa_printf(MSG_DEBUG, "EAP-FAST: More than one " "Result TLV in the message"); tlv->result = EAP_TLV_RESULT_FAILURE; return -2; } if (len < 2) { wpa_printf(MSG_DEBUG, "EAP-FAST: Too short " "Result TLV"); tlv->result = EAP_TLV_RESULT_FAILURE; break; } tlv->result = WPA_GET_BE16(pos); if (tlv->result != EAP_TLV_RESULT_SUCCESS && tlv->result != EAP_TLV_RESULT_FAILURE) { wpa_printf(MSG_DEBUG, "EAP-FAST: Unknown Result %d", tlv->result); tlv->result = EAP_TLV_RESULT_FAILURE; } wpa_printf(MSG_DEBUG, "EAP-FAST: Result: %s", tlv->result == EAP_TLV_RESULT_SUCCESS ? "Success" : "Failure"); break; case EAP_TLV_INTERMEDIATE_RESULT_TLV: wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Intermediate Result TLV", pos, len); if (len < 2) { wpa_printf(MSG_DEBUG, "EAP-FAST: Too short " "Intermediate-Result TLV"); tlv->iresult = EAP_TLV_RESULT_FAILURE; break; } if (tlv->iresult) { wpa_printf(MSG_DEBUG, "EAP-FAST: More than one " "Intermediate-Result TLV in the message"); tlv->iresult = EAP_TLV_RESULT_FAILURE; return -2; } tlv->iresult = WPA_GET_BE16(pos); if (tlv->iresult != EAP_TLV_RESULT_SUCCESS && tlv->iresult != EAP_TLV_RESULT_FAILURE) { wpa_printf(MSG_DEBUG, "EAP-FAST: Unknown Intermediate " "Result %d", tlv->iresult); tlv->iresult = EAP_TLV_RESULT_FAILURE; } wpa_printf(MSG_DEBUG, "EAP-FAST: Intermediate Result: %s", tlv->iresult == EAP_TLV_RESULT_SUCCESS ? "Success" : "Failure"); break; case EAP_TLV_CRYPTO_BINDING_TLV: wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Crypto-Binding TLV", pos, len); if (tlv->crypto_binding) { wpa_printf(MSG_DEBUG, "EAP-FAST: More than one " "Crypto-Binding TLV in the message"); tlv->iresult = EAP_TLV_RESULT_FAILURE; return -2; } tlv->crypto_binding_len = sizeof(struct eap_tlv_hdr) + len; if (tlv->crypto_binding_len < sizeof(*tlv->crypto_binding)) { wpa_printf(MSG_DEBUG, "EAP-FAST: Too short " "Crypto-Binding TLV"); tlv->iresult = EAP_TLV_RESULT_FAILURE; return -2; } tlv->crypto_binding = (struct eap_tlv_crypto_binding_tlv *) (pos - sizeof(struct eap_tlv_hdr)); break; case EAP_TLV_REQUEST_ACTION_TLV: wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Request-Action TLV", pos, len); if (tlv->request_action) { wpa_printf(MSG_DEBUG, "EAP-FAST: More than one " "Request-Action TLV in the message"); tlv->iresult = EAP_TLV_RESULT_FAILURE; return -2; } if (len < 2) { wpa_printf(MSG_DEBUG, "EAP-FAST: Too short " "Request-Action TLV"); tlv->iresult = EAP_TLV_RESULT_FAILURE; break; } tlv->request_action = WPA_GET_BE16(pos); wpa_printf(MSG_DEBUG, "EAP-FAST: Request-Action: %d", tlv->request_action); break; case EAP_TLV_PAC_TLV: wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: PAC TLV", pos, len); if (tlv->pac) { wpa_printf(MSG_DEBUG, "EAP-FAST: More than one " "PAC TLV in the message"); tlv->iresult = EAP_TLV_RESULT_FAILURE; return -2; } tlv->pac = pos; tlv->pac_len = len; break; default: /* Unknown TLV */ return -1; } return 0; } wpa-2.1/src/eap_common/eap_fast_common.h000066400000000000000000000056121227414666700203750ustar00rootroot00000000000000/* * EAP-FAST definitions (RFC 4851) * Copyright (c) 2004-2008, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef EAP_FAST_H #define EAP_FAST_H #define EAP_FAST_VERSION 1 #define EAP_FAST_KEY_LEN 64 #define EAP_FAST_SIMCK_LEN 40 #define EAP_FAST_SKS_LEN 40 #define EAP_FAST_CMK_LEN 20 #define TLS_EXT_PAC_OPAQUE 35 /* * RFC 5422: Section 4.2.1 - Formats for PAC TLV Attributes / Type Field * Note: bit 0x8000 (Mandatory) and bit 0x4000 (Reserved) are also defined * in the general PAC TLV format (Section 4.2). */ #define PAC_TYPE_PAC_KEY 1 #define PAC_TYPE_PAC_OPAQUE 2 #define PAC_TYPE_CRED_LIFETIME 3 #define PAC_TYPE_A_ID 4 #define PAC_TYPE_I_ID 5 /* * 6 was previous assigned for SERVER_PROTECTED_DATA, but * draft-cam-winget-eap-fast-provisioning-02.txt changed this to Reserved. */ #define PAC_TYPE_A_ID_INFO 7 #define PAC_TYPE_PAC_ACKNOWLEDGEMENT 8 #define PAC_TYPE_PAC_INFO 9 #define PAC_TYPE_PAC_TYPE 10 #ifdef _MSC_VER #pragma pack(push, 1) #endif /* _MSC_VER */ struct pac_tlv_hdr { be16 type; be16 len; } STRUCT_PACKED; #ifdef _MSC_VER #pragma pack(pop) #endif /* _MSC_VER */ #define EAP_FAST_PAC_KEY_LEN 32 /* RFC 5422: 4.2.6 PAC-Type TLV */ #define PAC_TYPE_TUNNEL_PAC 1 /* Application Specific Short Lived PACs (only in volatile storage) */ /* User Authorization PAC */ #define PAC_TYPE_USER_AUTHORIZATION 3 /* Application Specific Long Lived PACs */ /* Machine Authentication PAC */ #define PAC_TYPE_MACHINE_AUTHENTICATION 2 /* * RFC 5422: * Section 3.3 - Key Derivations Used in the EAP-FAST Provisioning Exchange */ struct eap_fast_key_block_provisioning { /* Extra key material after TLS key_block */ u8 session_key_seed[EAP_FAST_SKS_LEN]; u8 server_challenge[16]; /* MSCHAPv2 ServerChallenge */ u8 client_challenge[16]; /* MSCHAPv2 ClientChallenge */ }; struct wpabuf; struct tls_connection; struct eap_fast_tlv_parse { u8 *eap_payload_tlv; size_t eap_payload_tlv_len; struct eap_tlv_crypto_binding_tlv *crypto_binding; size_t crypto_binding_len; int iresult; int result; int request_action; u8 *pac; size_t pac_len; }; void eap_fast_put_tlv_hdr(struct wpabuf *buf, u16 type, u16 len); void eap_fast_put_tlv(struct wpabuf *buf, u16 type, const void *data, u16 len); void eap_fast_put_tlv_buf(struct wpabuf *buf, u16 type, const struct wpabuf *data); struct wpabuf * eap_fast_tlv_eap_payload(struct wpabuf *buf); void eap_fast_derive_master_secret(const u8 *pac_key, const u8 *server_random, const u8 *client_random, u8 *master_secret); u8 * eap_fast_derive_key(void *ssl_ctx, struct tls_connection *conn, const char *label, size_t len); void eap_fast_derive_eap_msk(const u8 *simck, u8 *msk); void eap_fast_derive_eap_emsk(const u8 *simck, u8 *emsk); int eap_fast_parse_tlv(struct eap_fast_tlv_parse *tlv, int tlv_type, u8 *pos, int len); #endif /* EAP_FAST_H */ wpa-2.1/src/eap_common/eap_gpsk_common.c000066400000000000000000000344121227414666700203770ustar00rootroot00000000000000/* * EAP server/peer: EAP-GPSK shared routines * Copyright (c) 2006-2007, 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/aes_wrap.h" #include "crypto/sha256.h" #include "eap_defs.h" #include "eap_gpsk_common.h" /** * eap_gpsk_supported_ciphersuite - Check whether ciphersuite is supported * @vendor: CSuite/Vendor * @specifier: CSuite/Specifier * Returns: 1 if ciphersuite is support, or 0 if not */ int eap_gpsk_supported_ciphersuite(int vendor, int specifier) { if (vendor == EAP_GPSK_VENDOR_IETF && specifier == EAP_GPSK_CIPHER_AES) return 1; #ifdef EAP_GPSK_SHA256 if (vendor == EAP_GPSK_VENDOR_IETF && specifier == EAP_GPSK_CIPHER_SHA256) return 1; #endif /* EAP_GPSK_SHA256 */ return 0; } static int eap_gpsk_gkdf_cmac(const u8 *psk /* Y */, const u8 *data /* Z */, size_t data_len, u8 *buf, size_t len /* X */) { u8 *opos; size_t i, n, hashlen, left, clen; u8 ibuf[2], hash[16]; const u8 *addr[2]; size_t vlen[2]; hashlen = sizeof(hash); /* M_i = MAC_Y (i || Z); (MAC = AES-CMAC-128) */ addr[0] = ibuf; vlen[0] = sizeof(ibuf); addr[1] = data; vlen[1] = data_len; opos = buf; left = len; n = (len + hashlen - 1) / hashlen; for (i = 1; i <= n; i++) { WPA_PUT_BE16(ibuf, i); if (omac1_aes_128_vector(psk, 2, addr, vlen, hash)) return -1; clen = left > hashlen ? hashlen : left; os_memcpy(opos, hash, clen); opos += clen; left -= clen; } return 0; } #ifdef EAP_GPSK_SHA256 static int eap_gpsk_gkdf_sha256(const u8 *psk /* Y */, const u8 *data /* Z */, size_t data_len, u8 *buf, size_t len /* X */) { u8 *opos; size_t i, n, hashlen, left, clen; u8 ibuf[2], hash[SHA256_MAC_LEN]; const u8 *addr[2]; size_t vlen[2]; hashlen = SHA256_MAC_LEN; /* M_i = MAC_Y (i || Z); (MAC = HMAC-SHA256) */ addr[0] = ibuf; vlen[0] = sizeof(ibuf); addr[1] = data; vlen[1] = data_len; opos = buf; left = len; n = (len + hashlen - 1) / hashlen; for (i = 1; i <= n; i++) { WPA_PUT_BE16(ibuf, i); hmac_sha256_vector(psk, 32, 2, addr, vlen, hash); clen = left > hashlen ? hashlen : left; os_memcpy(opos, hash, clen); opos += clen; left -= clen; } return 0; } #endif /* EAP_GPSK_SHA256 */ static int eap_gpsk_derive_keys_helper(u32 csuite_specifier, u8 *kdf_out, size_t kdf_out_len, const u8 *psk, size_t psk_len, const u8 *seed, size_t seed_len, u8 *msk, u8 *emsk, u8 *sk, size_t sk_len, u8 *pk, size_t pk_len) { u8 mk[32], *pos, *data; size_t data_len, mk_len; int (*gkdf)(const u8 *_psk, const u8 *_data, size_t _data_len, u8 *buf, size_t len); gkdf = NULL; switch (csuite_specifier) { case EAP_GPSK_CIPHER_AES: gkdf = eap_gpsk_gkdf_cmac; mk_len = 16; break; #ifdef EAP_GPSK_SHA256 case EAP_GPSK_CIPHER_SHA256: gkdf = eap_gpsk_gkdf_sha256; mk_len = SHA256_MAC_LEN; break; #endif /* EAP_GPSK_SHA256 */ default: return -1; } if (psk_len < mk_len) return -1; data_len = 2 + psk_len + 6 + seed_len; data = os_malloc(data_len); if (data == NULL) return -1; pos = data; WPA_PUT_BE16(pos, psk_len); pos += 2; os_memcpy(pos, psk, psk_len); pos += psk_len; WPA_PUT_BE32(pos, EAP_GPSK_VENDOR_IETF); /* CSuite/Vendor = IETF */ pos += 4; WPA_PUT_BE16(pos, csuite_specifier); /* CSuite/Specifier */ pos += 2; os_memcpy(pos, seed, seed_len); /* inputString */ wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: Data to MK derivation", data, data_len); if (gkdf(psk, data, data_len, mk, mk_len) < 0) { os_free(data); return -1; } os_free(data); wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: MK", mk, mk_len); if (gkdf(mk, seed, seed_len, kdf_out, kdf_out_len) < 0) return -1; pos = kdf_out; wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: MSK", pos, EAP_MSK_LEN); os_memcpy(msk, pos, EAP_MSK_LEN); pos += EAP_MSK_LEN; wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: EMSK", pos, EAP_EMSK_LEN); os_memcpy(emsk, pos, EAP_EMSK_LEN); pos += EAP_EMSK_LEN; wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: SK", pos, sk_len); os_memcpy(sk, pos, sk_len); pos += sk_len; if (pk) { wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: PK", pos, pk_len); os_memcpy(pk, pos, pk_len); } return 0; } static int eap_gpsk_derive_keys_aes(const u8 *psk, size_t psk_len, const u8 *seed, size_t seed_len, u8 *msk, u8 *emsk, u8 *sk, size_t *sk_len, u8 *pk, size_t *pk_len) { #define EAP_GPSK_SK_LEN_AES 16 #define EAP_GPSK_PK_LEN_AES 16 u8 kdf_out[EAP_MSK_LEN + EAP_EMSK_LEN + EAP_GPSK_SK_LEN_AES + EAP_GPSK_PK_LEN_AES]; /* * inputString = RAND_Peer || ID_Peer || RAND_Server || ID_Server * (= seed) * KS = 16, PL = psk_len, CSuite_Sel = 0x00000000 0x0001 * MK = GKDF-16 (PSK[0..15], PL || PSK || CSuite_Sel || inputString) * MSK = GKDF-160 (MK, inputString)[0..63] * EMSK = GKDF-160 (MK, inputString)[64..127] * SK = GKDF-160 (MK, inputString)[128..143] * PK = GKDF-160 (MK, inputString)[144..159] * zero = 0x00 || 0x00 || ... || 0x00 (16 times) * Method-ID = GKDF-16 (zero, "Method ID" || EAP_Method_Type || * CSuite_Sel || inputString) */ *sk_len = EAP_GPSK_SK_LEN_AES; *pk_len = EAP_GPSK_PK_LEN_AES; return eap_gpsk_derive_keys_helper(EAP_GPSK_CIPHER_AES, kdf_out, sizeof(kdf_out), psk, psk_len, seed, seed_len, msk, emsk, sk, *sk_len, pk, *pk_len); } #ifdef EAP_GPSK_SHA256 static int eap_gpsk_derive_keys_sha256(const u8 *psk, size_t psk_len, const u8 *seed, size_t seed_len, u8 *msk, u8 *emsk, u8 *sk, size_t *sk_len) { #define EAP_GPSK_SK_LEN_SHA256 SHA256_MAC_LEN #define EAP_GPSK_PK_LEN_SHA256 SHA256_MAC_LEN u8 kdf_out[EAP_MSK_LEN + EAP_EMSK_LEN + EAP_GPSK_SK_LEN_SHA256 + EAP_GPSK_PK_LEN_SHA256]; /* * inputString = RAND_Peer || ID_Peer || RAND_Server || ID_Server * (= seed) * KS = 32, PL = psk_len, CSuite_Sel = 0x00000000 0x0002 * MK = GKDF-32 (PSK[0..31], PL || PSK || CSuite_Sel || inputString) * MSK = GKDF-160 (MK, inputString)[0..63] * EMSK = GKDF-160 (MK, inputString)[64..127] * SK = GKDF-160 (MK, inputString)[128..159] * zero = 0x00 || 0x00 || ... || 0x00 (32 times) * Method-ID = GKDF-16 (zero, "Method ID" || EAP_Method_Type || * CSuite_Sel || inputString) */ *sk_len = EAP_GPSK_SK_LEN_SHA256; return eap_gpsk_derive_keys_helper(EAP_GPSK_CIPHER_SHA256, kdf_out, sizeof(kdf_out), psk, psk_len, seed, seed_len, msk, emsk, sk, *sk_len, NULL, 0); } #endif /* EAP_GPSK_SHA256 */ /** * eap_gpsk_derive_keys - Derive EAP-GPSK keys * @psk: Pre-shared key * @psk_len: Length of psk in bytes * @vendor: CSuite/Vendor * @specifier: CSuite/Specifier * @rand_peer: 32-byte RAND_Peer * @rand_server: 32-byte RAND_Server * @id_peer: ID_Peer * @id_peer_len: Length of ID_Peer * @id_server: ID_Server * @id_server_len: Length of ID_Server * @msk: Buffer for 64-byte MSK * @emsk: Buffer for 64-byte EMSK * @sk: Buffer for SK (at least EAP_GPSK_MAX_SK_LEN bytes) * @sk_len: Buffer for returning length of SK * @pk: Buffer for PK (at least EAP_GPSK_MAX_PK_LEN bytes) * @pk_len: Buffer for returning length of PK * Returns: 0 on success, -1 on failure */ int eap_gpsk_derive_keys(const u8 *psk, size_t psk_len, int vendor, int specifier, const u8 *rand_peer, const u8 *rand_server, const u8 *id_peer, size_t id_peer_len, const u8 *id_server, size_t id_server_len, u8 *msk, u8 *emsk, u8 *sk, size_t *sk_len, u8 *pk, size_t *pk_len) { u8 *seed, *pos; size_t seed_len; int ret; wpa_printf(MSG_DEBUG, "EAP-GPSK: Deriving keys (%d:%d)", vendor, specifier); if (vendor != EAP_GPSK_VENDOR_IETF) return -1; wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: PSK", psk, psk_len); /* Seed = RAND_Peer || ID_Peer || RAND_Server || ID_Server */ seed_len = 2 * EAP_GPSK_RAND_LEN + id_server_len + id_peer_len; seed = os_malloc(seed_len); if (seed == NULL) { wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to allocate memory " "for key derivation"); return -1; } pos = seed; os_memcpy(pos, rand_peer, EAP_GPSK_RAND_LEN); pos += EAP_GPSK_RAND_LEN; os_memcpy(pos, id_peer, id_peer_len); pos += id_peer_len; os_memcpy(pos, rand_server, EAP_GPSK_RAND_LEN); pos += EAP_GPSK_RAND_LEN; os_memcpy(pos, id_server, id_server_len); pos += id_server_len; wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Seed", seed, seed_len); switch (specifier) { case EAP_GPSK_CIPHER_AES: ret = eap_gpsk_derive_keys_aes(psk, psk_len, seed, seed_len, msk, emsk, sk, sk_len, pk, pk_len); break; #ifdef EAP_GPSK_SHA256 case EAP_GPSK_CIPHER_SHA256: ret = eap_gpsk_derive_keys_sha256(psk, psk_len, seed, seed_len, msk, emsk, sk, sk_len); break; #endif /* EAP_GPSK_SHA256 */ default: wpa_printf(MSG_DEBUG, "EAP-GPSK: Unknown cipher %d:%d used in " "key derivation", vendor, specifier); ret = -1; break; } os_free(seed); return ret; } static int eap_gpsk_derive_mid_helper(u32 csuite_specifier, u8 *kdf_out, size_t kdf_out_len, const u8 *psk, const u8 *seed, size_t seed_len, u8 method_type) { u8 *pos, *data; size_t data_len; int (*gkdf)(const u8 *_psk, const u8 *_data, size_t _data_len, u8 *buf, size_t len); gkdf = NULL; switch (csuite_specifier) { case EAP_GPSK_CIPHER_AES: gkdf = eap_gpsk_gkdf_cmac; break; #ifdef EAP_GPSK_SHA256 case EAP_GPSK_CIPHER_SHA256: gkdf = eap_gpsk_gkdf_sha256; break; #endif /* EAP_GPSK_SHA256 */ default: wpa_printf(MSG_DEBUG, "EAP-GPSK: Unknown cipher %d used in " "Session-Id derivation", csuite_specifier); return -1; } #define SID_LABEL "Method ID" /* "Method ID" || EAP_Method_Type || CSuite_Sel || inputString */ data_len = strlen(SID_LABEL) + 1 + 6 + seed_len; data = os_malloc(data_len); if (data == NULL) return -1; pos = data; os_memcpy(pos, SID_LABEL, strlen(SID_LABEL)); pos += strlen(SID_LABEL); #undef SID_LABEL os_memcpy(pos, &method_type, 1); pos += 1; WPA_PUT_BE32(pos, EAP_GPSK_VENDOR_IETF); /* CSuite/Vendor = IETF */ pos += 4; WPA_PUT_BE16(pos, csuite_specifier); /* CSuite/Specifier */ pos += 2; os_memcpy(pos, seed, seed_len); /* inputString */ wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Data to Method ID derivation", data, data_len); if (gkdf(psk, data, data_len, kdf_out, kdf_out_len) < 0) { os_free(data); return -1; } os_free(data); wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Method ID", kdf_out, kdf_out_len); return 0; } /** * eap_gpsk_session_id - Derive EAP-GPSK Session ID * @psk: Pre-shared key * @psk_len: Length of psk in bytes * @vendor: CSuite/Vendor * @specifier: CSuite/Specifier * @rand_peer: 32-byte RAND_Peer * @rand_server: 32-byte RAND_Server * @id_peer: ID_Peer * @id_peer_len: Length of ID_Peer * @id_server: ID_Server * @id_server_len: Length of ID_Server * @method_type: EAP Authentication Method Type * @sid: Buffer for 17-byte Session ID * @sid_len: Buffer for returning length of Session ID * Returns: 0 on success, -1 on failure */ int eap_gpsk_derive_session_id(const u8 *psk, size_t psk_len, int vendor, int specifier, const u8 *rand_peer, const u8 *rand_server, const u8 *id_peer, size_t id_peer_len, const u8 *id_server, size_t id_server_len, u8 method_type, u8 *sid, size_t *sid_len) { u8 *seed, *pos; u8 kdf_out[16]; size_t seed_len; int ret; wpa_printf(MSG_DEBUG, "EAP-GPSK: Deriving Session ID(%d:%d)", vendor, specifier); if (vendor != EAP_GPSK_VENDOR_IETF) return -1; wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: PSK", psk, psk_len); /* * inputString = RAND_Peer || ID_Peer || RAND_Server || ID_Server * (= seed) * KS = 16, CSuite_Sel = 0x00000000 0x0001 * Method-ID = GKDF-16 (zero, "Method ID" || EAP_Method_Type || * CSuite_Sel || inputString) */ seed_len = 2 * EAP_GPSK_RAND_LEN + id_server_len + id_peer_len; seed = os_malloc(seed_len); if (seed == NULL) { wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to allocate memory " "for Session-Id derivation"); return -1; } pos = seed; os_memcpy(pos, rand_peer, EAP_GPSK_RAND_LEN); pos += EAP_GPSK_RAND_LEN; os_memcpy(pos, id_peer, id_peer_len); pos += id_peer_len; os_memcpy(pos, rand_server, EAP_GPSK_RAND_LEN); pos += EAP_GPSK_RAND_LEN; os_memcpy(pos, id_server, id_server_len); pos += id_server_len; wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Seed", seed, seed_len); ret = eap_gpsk_derive_mid_helper(specifier, kdf_out, sizeof(kdf_out), psk, seed, seed_len, method_type); sid[0] = method_type; os_memcpy(sid + 1, kdf_out, sizeof(kdf_out)); *sid_len = 1 + sizeof(kdf_out); os_free(seed); return ret; } /** * eap_gpsk_mic_len - Get the length of the MIC * @vendor: CSuite/Vendor * @specifier: CSuite/Specifier * Returns: MIC length in bytes */ size_t eap_gpsk_mic_len(int vendor, int specifier) { if (vendor != EAP_GPSK_VENDOR_IETF) return 0; switch (specifier) { case EAP_GPSK_CIPHER_AES: return 16; #ifdef EAP_GPSK_SHA256 case EAP_GPSK_CIPHER_SHA256: return 32; #endif /* EAP_GPSK_SHA256 */ default: return 0; } } static int eap_gpsk_compute_mic_aes(const u8 *sk, size_t sk_len, const u8 *data, size_t len, u8 *mic) { if (sk_len != 16) { wpa_printf(MSG_DEBUG, "EAP-GPSK: Invalid SK length %lu for " "AES-CMAC MIC", (unsigned long) sk_len); return -1; } return omac1_aes_128(sk, data, len, mic); } /** * eap_gpsk_compute_mic - Compute EAP-GPSK MIC for an EAP packet * @sk: Session key SK from eap_gpsk_derive_keys() * @sk_len: SK length in bytes from eap_gpsk_derive_keys() * @vendor: CSuite/Vendor * @specifier: CSuite/Specifier * @data: Input data to MIC * @len: Input data length in bytes * @mic: Buffer for the computed MIC, eap_gpsk_mic_len(cipher) bytes * Returns: 0 on success, -1 on failure */ int eap_gpsk_compute_mic(const u8 *sk, size_t sk_len, int vendor, int specifier, const u8 *data, size_t len, u8 *mic) { int ret; if (vendor != EAP_GPSK_VENDOR_IETF) return -1; switch (specifier) { case EAP_GPSK_CIPHER_AES: ret = eap_gpsk_compute_mic_aes(sk, sk_len, data, len, mic); break; #ifdef EAP_GPSK_SHA256 case EAP_GPSK_CIPHER_SHA256: hmac_sha256(sk, sk_len, data, len, mic); ret = 0; break; #endif /* EAP_GPSK_SHA256 */ default: wpa_printf(MSG_DEBUG, "EAP-GPSK: Unknown cipher %d:%d used in " "MIC computation", vendor, specifier); ret = -1; break; } return ret; } wpa-2.1/src/eap_common/eap_gpsk_common.h000066400000000000000000000040041227414666700203760ustar00rootroot00000000000000/* * EAP server/peer: EAP-GPSK shared routines * Copyright (c) 2006-2007, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef EAP_GPSK_COMMON_H #define EAP_GPSK_COMMON_H #define EAP_GPSK_OPCODE_GPSK_1 1 #define EAP_GPSK_OPCODE_GPSK_2 2 #define EAP_GPSK_OPCODE_GPSK_3 3 #define EAP_GPSK_OPCODE_GPSK_4 4 #define EAP_GPSK_OPCODE_FAIL 5 #define EAP_GPSK_OPCODE_PROTECTED_FAIL 6 /* Failure-Code in GPSK-Fail and GPSK-Protected-Fail */ #define EAP_GPSK_FAIL_PSK_NOT_FOUND 0x00000001 #define EAP_GPSK_FAIL_AUTHENTICATION_FAILURE 0x00000002 #define EAP_GPSK_FAIL_AUTHORIZATION_FAILURE 0x00000003 #define EAP_GPSK_RAND_LEN 32 #define EAP_GPSK_MAX_SK_LEN 32 #define EAP_GPSK_MAX_PK_LEN 32 #define EAP_GPSK_MAX_MIC_LEN 32 #define EAP_GPSK_VENDOR_IETF 0x00000000 #define EAP_GPSK_CIPHER_RESERVED 0x000000 #define EAP_GPSK_CIPHER_AES 0x000001 #define EAP_GPSK_CIPHER_SHA256 0x000002 #ifdef _MSC_VER #pragma pack(push, 1) #endif /* _MSC_VER */ struct eap_gpsk_csuite { u8 vendor[4]; u8 specifier[2]; } STRUCT_PACKED; #ifdef _MSC_VER #pragma pack(pop) #endif /* _MSC_VER */ int eap_gpsk_supported_ciphersuite(int vendor, int specifier); int eap_gpsk_derive_keys(const u8 *psk, size_t psk_len, int vendor, int specifier, const u8 *rand_client, const u8 *rand_server, const u8 *id_client, size_t id_client_len, const u8 *id_server, size_t id_server_len, u8 *msk, u8 *emsk, u8 *sk, size_t *sk_len, u8 *pk, size_t *pk_len); int eap_gpsk_derive_session_id(const u8 *psk, size_t psk_len, int vendor, int specifier, const u8 *rand_peer, const u8 *rand_server, const u8 *id_peer, size_t id_peer_len, const u8 *id_server, size_t id_server_len, u8 method_type, u8 *sid, size_t *sid_len); size_t eap_gpsk_mic_len(int vendor, int specifier); int eap_gpsk_compute_mic(const u8 *sk, size_t sk_len, int vendor, int specifier, const u8 *data, size_t len, u8 *mic); #endif /* EAP_GPSK_COMMON_H */ wpa-2.1/src/eap_common/eap_ikev2_common.c000066400000000000000000000060661227414666700204570ustar00rootroot00000000000000/* * EAP-IKEv2 common routines * Copyright (c) 2007, 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_defs.h" #include "eap_common.h" #include "ikev2_common.h" #include "eap_ikev2_common.h" int eap_ikev2_derive_keymat(int prf, struct ikev2_keys *keys, const u8 *i_nonce, size_t i_nonce_len, const u8 *r_nonce, size_t r_nonce_len, u8 *keymat) { u8 *nonces; size_t nlen; /* KEYMAT = prf+(SK_d, Ni | Nr) */ if (keys->SK_d == NULL || i_nonce == NULL || r_nonce == NULL) return -1; nlen = i_nonce_len + r_nonce_len; nonces = os_malloc(nlen); if (nonces == NULL) return -1; os_memcpy(nonces, i_nonce, i_nonce_len); os_memcpy(nonces + i_nonce_len, r_nonce, r_nonce_len); if (ikev2_prf_plus(prf, keys->SK_d, keys->SK_d_len, nonces, nlen, keymat, EAP_MSK_LEN + EAP_EMSK_LEN)) { os_free(nonces); return -1; } os_free(nonces); wpa_hexdump_key(MSG_DEBUG, "EAP-IKEV2: KEYMAT", keymat, EAP_MSK_LEN + EAP_EMSK_LEN); return 0; } struct wpabuf * eap_ikev2_build_frag_ack(u8 id, u8 code) { struct wpabuf *msg; #ifdef CCNS_PL msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_IKEV2, 1, code, id); if (msg == NULL) { wpa_printf(MSG_ERROR, "EAP-IKEV2: Failed to allocate memory " "for fragment ack"); return NULL; } wpabuf_put_u8(msg, 0); /* Flags */ #else /* CCNS_PL */ msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_IKEV2, 0, code, id); if (msg == NULL) { wpa_printf(MSG_ERROR, "EAP-IKEV2: Failed to allocate memory " "for fragment ack"); return NULL; } #endif /* CCNS_PL */ wpa_printf(MSG_DEBUG, "EAP-IKEV2: Send fragment ack"); return msg; } int eap_ikev2_validate_icv(int integ_alg, struct ikev2_keys *keys, int initiator, const struct wpabuf *msg, const u8 *pos, const u8 *end) { const struct ikev2_integ_alg *integ; size_t icv_len; u8 icv[IKEV2_MAX_HASH_LEN]; const u8 *SK_a = initiator ? keys->SK_ai : keys->SK_ar; integ = ikev2_get_integ(integ_alg); if (integ == NULL) { wpa_printf(MSG_DEBUG, "EAP-IKEV2: Unknown INTEG " "transform / cannot validate ICV"); return -1; } icv_len = integ->hash_len; if (end - pos < (int) icv_len) { wpa_printf(MSG_DEBUG, "EAP-IKEV2: Not enough room in the " "message for Integrity Checksum Data"); return -1; } if (SK_a == NULL) { wpa_printf(MSG_DEBUG, "EAP-IKEV2: No SK_a for ICV validation"); return -1; } if (ikev2_integ_hash(integ_alg, SK_a, keys->SK_integ_len, wpabuf_head(msg), wpabuf_len(msg) - icv_len, icv) < 0) { wpa_printf(MSG_INFO, "EAP-IKEV2: Could not calculate ICV"); return -1; } if (os_memcmp(icv, end - icv_len, icv_len) != 0) { wpa_printf(MSG_INFO, "EAP-IKEV2: Invalid ICV"); wpa_hexdump(MSG_DEBUG, "EAP-IKEV2: Calculated ICV", icv, icv_len); wpa_hexdump(MSG_DEBUG, "EAP-IKEV2: Received ICV", end - icv_len, icv_len); return -1; } wpa_printf(MSG_DEBUG, "EAP-IKEV2: Valid Integrity Checksum Data in " "the received message"); return icv_len; } wpa-2.1/src/eap_common/eap_ikev2_common.h000066400000000000000000000020111227414666700204460ustar00rootroot00000000000000/* * EAP-IKEv2 definitions * Copyright (c) 2007, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef EAP_IKEV2_COMMON_H #define EAP_IKEV2_COMMON_H #ifdef CCNS_PL /* incorrect bit order */ #define IKEV2_FLAGS_LENGTH_INCLUDED 0x01 #define IKEV2_FLAGS_MORE_FRAGMENTS 0x02 #define IKEV2_FLAGS_ICV_INCLUDED 0x04 #else /* CCNS_PL */ #define IKEV2_FLAGS_LENGTH_INCLUDED 0x80 #define IKEV2_FLAGS_MORE_FRAGMENTS 0x40 #define IKEV2_FLAGS_ICV_INCLUDED 0x20 #endif /* CCNS_PL */ #define IKEV2_FRAGMENT_SIZE 1400 struct ikev2_keys; int eap_ikev2_derive_keymat(int prf, struct ikev2_keys *keys, const u8 *i_nonce, size_t i_nonce_len, const u8 *r_nonce, size_t r_nonce_len, u8 *keymat); struct wpabuf * eap_ikev2_build_frag_ack(u8 id, u8 code); int eap_ikev2_validate_icv(int integ_alg, struct ikev2_keys *keys, int initiator, const struct wpabuf *msg, const u8 *pos, const u8 *end); #endif /* EAP_IKEV2_COMMON_H */ wpa-2.1/src/eap_common/eap_pax_common.c000066400000000000000000000102121227414666700202130ustar00rootroot00000000000000/* * EAP server/peer: EAP-PAX shared routines * 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/sha1.h" #include "eap_pax_common.h" /** * eap_pax_kdf - PAX Key Derivation Function * @mac_id: MAC ID (EAP_PAX_MAC_*) / currently, only HMAC_SHA1_128 is supported * @key: Secret key (X) * @key_len: Length of the secret key in bytes * @identifier: Public identifier for the key (Y) * @entropy: Exchanged entropy to seed the KDF (Z) * @entropy_len: Length of the entropy in bytes * @output_len: Output len in bytes (W) * @output: Buffer for the derived key * Returns: 0 on success, -1 failed * * RFC 4746, Section 2.6: PAX-KDF-W(X, Y, Z) */ int eap_pax_kdf(u8 mac_id, const u8 *key, size_t key_len, const char *identifier, const u8 *entropy, size_t entropy_len, size_t output_len, u8 *output) { u8 mac[SHA1_MAC_LEN]; u8 counter, *pos; const u8 *addr[3]; size_t len[3]; size_t num_blocks, left; num_blocks = (output_len + EAP_PAX_MAC_LEN - 1) / EAP_PAX_MAC_LEN; if (identifier == NULL || num_blocks >= 255) return -1; /* TODO: add support for EAP_PAX_HMAC_SHA256_128 */ if (mac_id != EAP_PAX_MAC_HMAC_SHA1_128) return -1; addr[0] = (const u8 *) identifier; len[0] = os_strlen(identifier); addr[1] = entropy; len[1] = entropy_len; addr[2] = &counter; len[2] = 1; pos = output; left = output_len; for (counter = 1; counter <= (u8) num_blocks; counter++) { size_t clen = left > EAP_PAX_MAC_LEN ? EAP_PAX_MAC_LEN : left; hmac_sha1_vector(key, key_len, 3, addr, len, mac); os_memcpy(pos, mac, clen); pos += clen; left -= clen; } return 0; } /** * eap_pax_mac - EAP-PAX MAC * @mac_id: MAC ID (EAP_PAX_MAC_*) / currently, only HMAC_SHA1_128 is supported * @key: Secret key * @key_len: Length of the secret key in bytes * @data1: Optional data, first block; %NULL if not used * @data1_len: Length of data1 in bytes * @data2: Optional data, second block; %NULL if not used * @data2_len: Length of data2 in bytes * @data3: Optional data, third block; %NULL if not used * @data3_len: Length of data3 in bytes * @mac: Buffer for the MAC value (EAP_PAX_MAC_LEN = 16 bytes) * Returns: 0 on success, -1 on failure * * Wrapper function to calculate EAP-PAX MAC. */ int eap_pax_mac(u8 mac_id, const u8 *key, size_t key_len, const u8 *data1, size_t data1_len, const u8 *data2, size_t data2_len, const u8 *data3, size_t data3_len, u8 *mac) { u8 hash[SHA1_MAC_LEN]; const u8 *addr[3]; size_t len[3]; size_t count; /* TODO: add support for EAP_PAX_HMAC_SHA256_128 */ if (mac_id != EAP_PAX_MAC_HMAC_SHA1_128) return -1; addr[0] = data1; len[0] = data1_len; addr[1] = data2; len[1] = data2_len; addr[2] = data3; len[2] = data3_len; count = (data1 ? 1 : 0) + (data2 ? 1 : 0) + (data3 ? 1 : 0); hmac_sha1_vector(key, key_len, count, addr, len, hash); os_memcpy(mac, hash, EAP_PAX_MAC_LEN); return 0; } /** * eap_pax_initial_key_derivation - EAP-PAX initial key derivation * @mac_id: MAC ID (EAP_PAX_MAC_*) / currently, only HMAC_SHA1_128 is supported * @ak: Authentication Key * @e: Entropy * @mk: Buffer for the derived Master Key * @ck: Buffer for the derived Confirmation Key * @ick: Buffer for the derived Integrity Check Key * Returns: 0 on success, -1 on failure */ int eap_pax_initial_key_derivation(u8 mac_id, const u8 *ak, const u8 *e, u8 *mk, u8 *ck, u8 *ick) { wpa_printf(MSG_DEBUG, "EAP-PAX: initial key derivation"); if (eap_pax_kdf(mac_id, ak, EAP_PAX_AK_LEN, "Master Key", e, 2 * EAP_PAX_RAND_LEN, EAP_PAX_MK_LEN, mk) || eap_pax_kdf(mac_id, mk, EAP_PAX_MK_LEN, "Confirmation Key", e, 2 * EAP_PAX_RAND_LEN, EAP_PAX_CK_LEN, ck) || eap_pax_kdf(mac_id, mk, EAP_PAX_MK_LEN, "Integrity Check Key", e, 2 * EAP_PAX_RAND_LEN, EAP_PAX_ICK_LEN, ick)) return -1; wpa_hexdump_key(MSG_MSGDUMP, "EAP-PAX: AK", ak, EAP_PAX_AK_LEN); wpa_hexdump_key(MSG_MSGDUMP, "EAP-PAX: MK", mk, EAP_PAX_MK_LEN); wpa_hexdump_key(MSG_MSGDUMP, "EAP-PAX: CK", ck, EAP_PAX_CK_LEN); wpa_hexdump_key(MSG_MSGDUMP, "EAP-PAX: ICK", ick, EAP_PAX_ICK_LEN); return 0; } wpa-2.1/src/eap_common/eap_pax_common.h000066400000000000000000000042451227414666700202310ustar00rootroot00000000000000/* * EAP server/peer: EAP-PAX shared routines * Copyright (c) 2005-2007, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef EAP_PAX_COMMON_H #define EAP_PAX_COMMON_H #ifdef _MSC_VER #pragma pack(push, 1) #endif /* _MSC_VER */ struct eap_pax_hdr { u8 op_code; u8 flags; u8 mac_id; u8 dh_group_id; u8 public_key_id; /* Followed by variable length payload and ICV */ } STRUCT_PACKED; #ifdef _MSC_VER #pragma pack(pop) #endif /* _MSC_VER */ /* op_code: */ enum { EAP_PAX_OP_STD_1 = 0x01, EAP_PAX_OP_STD_2 = 0x02, EAP_PAX_OP_STD_3 = 0x03, EAP_PAX_OP_SEC_1 = 0x11, EAP_PAX_OP_SEC_2 = 0x12, EAP_PAX_OP_SEC_3 = 0x13, EAP_PAX_OP_SEC_4 = 0x14, EAP_PAX_OP_SEC_5 = 0x15, EAP_PAX_OP_ACK = 0x21 }; /* flags: */ #define EAP_PAX_FLAGS_MF 0x01 #define EAP_PAX_FLAGS_CE 0x02 #define EAP_PAX_FLAGS_AI 0x04 /* mac_id: */ #define EAP_PAX_MAC_HMAC_SHA1_128 0x01 #define EAP_PAX_HMAC_SHA256_128 0x02 /* dh_group_id: */ #define EAP_PAX_DH_GROUP_NONE 0x00 #define EAP_PAX_DH_GROUP_2048_MODP 0x01 #define EAP_PAX_DH_GROUP_3072_MODP 0x02 #define EAP_PAX_DH_GROUP_NIST_ECC_P_256 0x03 /* public_key_id: */ #define EAP_PAX_PUBLIC_KEY_NONE 0x00 #define EAP_PAX_PUBLIC_KEY_RSAES_OAEP 0x01 #define EAP_PAX_PUBLIC_KEY_RSA_PKCS1_V1_5 0x02 #define EAP_PAX_PUBLIC_KEY_EL_GAMAL_NIST_ECC 0x03 /* ADE type: */ #define EAP_PAX_ADE_VENDOR_SPECIFIC 0x01 #define EAP_PAX_ADE_CLIENT_CHANNEL_BINDING 0x02 #define EAP_PAX_ADE_SERVER_CHANNEL_BINDING 0x03 #define EAP_PAX_RAND_LEN 32 #define EAP_PAX_MAC_LEN 16 #define EAP_PAX_ICV_LEN 16 #define EAP_PAX_AK_LEN 16 #define EAP_PAX_MK_LEN 16 #define EAP_PAX_CK_LEN 16 #define EAP_PAX_ICK_LEN 16 int eap_pax_kdf(u8 mac_id, const u8 *key, size_t key_len, const char *identifier, const u8 *entropy, size_t entropy_len, size_t output_len, u8 *output); int eap_pax_mac(u8 mac_id, const u8 *key, size_t key_len, const u8 *data1, size_t data1_len, const u8 *data2, size_t data2_len, const u8 *data3, size_t data3_len, u8 *mac); int eap_pax_initial_key_derivation(u8 mac_id, const u8 *ak, const u8 *e, u8 *mk, u8 *ck, u8 *ick); #endif /* EAP_PAX_COMMON_H */ wpa-2.1/src/eap_common/eap_peap_common.c000066400000000000000000000033701227414666700203570ustar00rootroot00000000000000/* * EAP-PEAP common routines * Copyright (c) 2008-2011, 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/sha1.h" #include "eap_peap_common.h" int peap_prfplus(int version, const u8 *key, size_t key_len, const char *label, const u8 *seed, size_t seed_len, u8 *buf, size_t buf_len) { unsigned char counter = 0; size_t pos, plen; u8 hash[SHA1_MAC_LEN]; size_t label_len = os_strlen(label); u8 extra[2]; const unsigned char *addr[5]; size_t len[5]; addr[0] = hash; len[0] = 0; addr[1] = (unsigned char *) label; len[1] = label_len; addr[2] = seed; len[2] = seed_len; if (version == 0) { /* * PRF+(K, S, LEN) = T1 | T2 | ... | Tn * T1 = HMAC-SHA1(K, S | 0x01 | 0x00 | 0x00) * T2 = HMAC-SHA1(K, T1 | S | 0x02 | 0x00 | 0x00) * ... * Tn = HMAC-SHA1(K, Tn-1 | S | n | 0x00 | 0x00) */ extra[0] = 0; extra[1] = 0; addr[3] = &counter; len[3] = 1; addr[4] = extra; len[4] = 2; } else { /* * PRF (K,S,LEN) = T1 | T2 | T3 | T4 | ... where: * T1 = HMAC-SHA1(K, S | LEN | 0x01) * T2 = HMAC-SHA1 (K, T1 | S | LEN | 0x02) * T3 = HMAC-SHA1 (K, T2 | S | LEN | 0x03) * T4 = HMAC-SHA1 (K, T3 | S | LEN | 0x04) * ... */ extra[0] = buf_len & 0xff; addr[3] = extra; len[3] = 1; addr[4] = &counter; len[4] = 1; } pos = 0; while (pos < buf_len) { counter++; plen = buf_len - pos; if (hmac_sha1_vector(key, key_len, 5, addr, len, hash) < 0) return -1; if (plen >= SHA1_MAC_LEN) { os_memcpy(&buf[pos], hash, SHA1_MAC_LEN); pos += SHA1_MAC_LEN; } else { os_memcpy(&buf[pos], hash, plen); break; } len[0] = SHA1_MAC_LEN; } return 0; } wpa-2.1/src/eap_common/eap_peap_common.h000066400000000000000000000006511227414666700203630ustar00rootroot00000000000000/* * EAP-PEAP common routines * Copyright (c) 2008-2011, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef EAP_PEAP_COMMON_H #define EAP_PEAP_COMMON_H int peap_prfplus(int version, const u8 *key, size_t key_len, const char *label, const u8 *seed, size_t seed_len, u8 *buf, size_t buf_len); #endif /* EAP_PEAP_COMMON_H */ wpa-2.1/src/eap_common/eap_psk_common.c000066400000000000000000000030221227414666700202210ustar00rootroot00000000000000/* * EAP server/peer: EAP-PSK shared routines * Copyright (c) 2004-2006, 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/aes_wrap.h" #include "eap_defs.h" #include "eap_psk_common.h" #define aes_block_size 16 int eap_psk_key_setup(const u8 *psk, u8 *ak, u8 *kdk) { os_memset(ak, 0, aes_block_size); if (aes_128_encrypt_block(psk, ak, ak)) return -1; os_memcpy(kdk, ak, aes_block_size); ak[aes_block_size - 1] ^= 0x01; kdk[aes_block_size - 1] ^= 0x02; if (aes_128_encrypt_block(psk, ak, ak) || aes_128_encrypt_block(psk, kdk, kdk)) return -1; return 0; } int eap_psk_derive_keys(const u8 *kdk, const u8 *rand_p, u8 *tek, u8 *msk, u8 *emsk) { u8 hash[aes_block_size]; u8 counter = 1; int i; if (aes_128_encrypt_block(kdk, rand_p, hash)) return -1; hash[aes_block_size - 1] ^= counter; if (aes_128_encrypt_block(kdk, hash, tek)) return -1; hash[aes_block_size - 1] ^= counter; counter++; for (i = 0; i < EAP_MSK_LEN / aes_block_size; i++) { hash[aes_block_size - 1] ^= counter; if (aes_128_encrypt_block(kdk, hash, &msk[i * aes_block_size])) return -1; hash[aes_block_size - 1] ^= counter; counter++; } for (i = 0; i < EAP_EMSK_LEN / aes_block_size; i++) { hash[aes_block_size - 1] ^= counter; if (aes_128_encrypt_block(kdk, hash, &emsk[i * aes_block_size])) return -1; hash[aes_block_size - 1] ^= counter; counter++; } return 0; } wpa-2.1/src/eap_common/eap_psk_common.h000066400000000000000000000033461227414666700202370ustar00rootroot00000000000000/* * EAP server/peer: EAP-PSK shared routines * Copyright (c) 2004-2007, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef EAP_PSK_COMMON_H #define EAP_PSK_COMMON_H #define EAP_PSK_RAND_LEN 16 #define EAP_PSK_MAC_LEN 16 #define EAP_PSK_TEK_LEN 16 #define EAP_PSK_PSK_LEN 16 #define EAP_PSK_AK_LEN 16 #define EAP_PSK_KDK_LEN 16 #define EAP_PSK_R_FLAG_CONT 1 #define EAP_PSK_R_FLAG_DONE_SUCCESS 2 #define EAP_PSK_R_FLAG_DONE_FAILURE 3 #define EAP_PSK_E_FLAG 0x20 #define EAP_PSK_FLAGS_GET_T(flags) (((flags) & 0xc0) >> 6) #define EAP_PSK_FLAGS_SET_T(t) ((u8) (t) << 6) #ifdef _MSC_VER #pragma pack(push, 1) #endif /* _MSC_VER */ /* EAP-PSK First Message (AS -> Supplicant) */ struct eap_psk_hdr_1 { u8 flags; u8 rand_s[EAP_PSK_RAND_LEN]; /* Followed by variable length ID_S */ } STRUCT_PACKED; /* EAP-PSK Second Message (Supplicant -> AS) */ struct eap_psk_hdr_2 { u8 flags; u8 rand_s[EAP_PSK_RAND_LEN]; u8 rand_p[EAP_PSK_RAND_LEN]; u8 mac_p[EAP_PSK_MAC_LEN]; /* Followed by variable length ID_P */ } STRUCT_PACKED; /* EAP-PSK Third Message (AS -> Supplicant) */ struct eap_psk_hdr_3 { u8 flags; u8 rand_s[EAP_PSK_RAND_LEN]; u8 mac_s[EAP_PSK_MAC_LEN]; /* Followed by variable length PCHANNEL */ } STRUCT_PACKED; /* EAP-PSK Fourth Message (Supplicant -> AS) */ struct eap_psk_hdr_4 { u8 flags; u8 rand_s[EAP_PSK_RAND_LEN]; /* Followed by variable length PCHANNEL */ } STRUCT_PACKED; #ifdef _MSC_VER #pragma pack(pop) #endif /* _MSC_VER */ int __must_check eap_psk_key_setup(const u8 *psk, u8 *ak, u8 *kdk); int __must_check eap_psk_derive_keys(const u8 *kdk, const u8 *rand_p, u8 *tek, u8 *msk, u8 *emsk); #endif /* EAP_PSK_COMMON_H */ wpa-2.1/src/eap_common/eap_pwd_common.c000066400000000000000000000223311227414666700202220ustar00rootroot00000000000000/* * EAP server/peer: EAP-pwd shared routines * Copyright (c) 2010, Dan Harkins * * 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/sha256.h" #include "crypto/crypto.h" #include "eap_defs.h" #include "eap_pwd_common.h" /* The random function H(x) = HMAC-SHA256(0^32, x) */ struct crypto_hash * eap_pwd_h_init(void) { u8 allzero[SHA256_MAC_LEN]; os_memset(allzero, 0, SHA256_MAC_LEN); return crypto_hash_init(CRYPTO_HASH_ALG_HMAC_SHA256, allzero, SHA256_MAC_LEN); } void eap_pwd_h_update(struct crypto_hash *hash, const u8 *data, size_t len) { crypto_hash_update(hash, data, len); } void eap_pwd_h_final(struct crypto_hash *hash, u8 *digest) { size_t len = SHA256_MAC_LEN; crypto_hash_finish(hash, digest, &len); } /* a counter-based KDF based on NIST SP800-108 */ static int eap_pwd_kdf(const u8 *key, size_t keylen, const u8 *label, size_t labellen, u8 *result, size_t resultbitlen) { struct crypto_hash *hash; u8 digest[SHA256_MAC_LEN]; u16 i, ctr, L; size_t resultbytelen, len = 0, mdlen; resultbytelen = (resultbitlen + 7) / 8; ctr = 0; L = htons(resultbitlen); while (len < resultbytelen) { ctr++; i = htons(ctr); hash = crypto_hash_init(CRYPTO_HASH_ALG_HMAC_SHA256, key, keylen); if (hash == NULL) return -1; if (ctr > 1) crypto_hash_update(hash, digest, SHA256_MAC_LEN); crypto_hash_update(hash, (u8 *) &i, sizeof(u16)); crypto_hash_update(hash, label, labellen); crypto_hash_update(hash, (u8 *) &L, sizeof(u16)); mdlen = SHA256_MAC_LEN; if (crypto_hash_finish(hash, digest, &mdlen) < 0) return -1; if ((len + mdlen) > resultbytelen) os_memcpy(result + len, digest, resultbytelen - len); else os_memcpy(result + len, digest, mdlen); len += mdlen; } /* since we're expanding to a bit length, mask off the excess */ if (resultbitlen % 8) { u8 mask = 0xff; mask <<= (8 - (resultbitlen % 8)); result[resultbytelen - 1] &= mask; } return 0; } /* * compute a "random" secret point on an elliptic curve based * on the password and identities. */ int compute_password_element(EAP_PWD_group *grp, u16 num, u8 *password, int password_len, u8 *id_server, int id_server_len, u8 *id_peer, int id_peer_len, u8 *token) { BIGNUM *x_candidate = NULL, *rnd = NULL, *cofactor = NULL; struct crypto_hash *hash; unsigned char pwe_digest[SHA256_MAC_LEN], *prfbuf = NULL, ctr; int nid, is_odd, ret = 0; size_t primebytelen, primebitlen; switch (num) { /* from IANA registry for IKE D-H groups */ case 19: nid = NID_X9_62_prime256v1; break; case 20: nid = NID_secp384r1; break; case 21: nid = NID_secp521r1; break; case 25: nid = NID_X9_62_prime192v1; break; case 26: nid = NID_secp224r1; break; default: wpa_printf(MSG_INFO, "EAP-pwd: unsupported group %d", num); return -1; } grp->pwe = NULL; grp->order = NULL; grp->prime = NULL; if ((grp->group = EC_GROUP_new_by_curve_name(nid)) == NULL) { wpa_printf(MSG_INFO, "EAP-pwd: unable to create EC_GROUP"); goto fail; } if (((rnd = BN_new()) == NULL) || ((cofactor = BN_new()) == NULL) || ((grp->pwe = EC_POINT_new(grp->group)) == NULL) || ((grp->order = BN_new()) == NULL) || ((grp->prime = BN_new()) == NULL) || ((x_candidate = BN_new()) == NULL)) { wpa_printf(MSG_INFO, "EAP-pwd: unable to create bignums"); goto fail; } if (!EC_GROUP_get_curve_GFp(grp->group, grp->prime, NULL, NULL, NULL)) { wpa_printf(MSG_INFO, "EAP-pwd: unable to get prime for GFp " "curve"); goto fail; } if (!EC_GROUP_get_order(grp->group, grp->order, NULL)) { wpa_printf(MSG_INFO, "EAP-pwd: unable to get order for curve"); goto fail; } if (!EC_GROUP_get_cofactor(grp->group, cofactor, NULL)) { wpa_printf(MSG_INFO, "EAP-pwd: unable to get cofactor for " "curve"); goto fail; } primebitlen = BN_num_bits(grp->prime); primebytelen = BN_num_bytes(grp->prime); if ((prfbuf = os_malloc(primebytelen)) == NULL) { wpa_printf(MSG_INFO, "EAP-pwd: unable to malloc space for prf " "buffer"); goto fail; } os_memset(prfbuf, 0, primebytelen); ctr = 0; while (1) { if (ctr > 30) { wpa_printf(MSG_INFO, "EAP-pwd: unable to find random " "point on curve for group %d, something's " "fishy", num); goto fail; } ctr++; /* * compute counter-mode password value and stretch to prime * pwd-seed = H(token | peer-id | server-id | password | * counter) */ hash = eap_pwd_h_init(); if (hash == NULL) goto fail; eap_pwd_h_update(hash, token, sizeof(u32)); eap_pwd_h_update(hash, id_peer, id_peer_len); eap_pwd_h_update(hash, id_server, id_server_len); eap_pwd_h_update(hash, password, password_len); eap_pwd_h_update(hash, &ctr, sizeof(ctr)); eap_pwd_h_final(hash, pwe_digest); BN_bin2bn(pwe_digest, SHA256_MAC_LEN, rnd); if (eap_pwd_kdf(pwe_digest, SHA256_MAC_LEN, (u8 *) "EAP-pwd Hunting And Pecking", os_strlen("EAP-pwd Hunting And Pecking"), prfbuf, primebitlen) < 0) goto fail; BN_bin2bn(prfbuf, primebytelen, x_candidate); /* * eap_pwd_kdf() returns a string of bits 0..primebitlen but * BN_bin2bn will treat that string of bits as a big endian * number. If the primebitlen is not an even multiple of 8 * then excessive bits-- those _after_ primebitlen-- so now * we have to shift right the amount we masked off. */ if (primebitlen % 8) BN_rshift(x_candidate, x_candidate, (8 - (primebitlen % 8))); if (BN_ucmp(x_candidate, grp->prime) >= 0) continue; wpa_hexdump(MSG_DEBUG, "EAP-pwd: x_candidate", prfbuf, primebytelen); /* * need to unambiguously identify the solution, if there is * one... */ if (BN_is_odd(rnd)) is_odd = 1; else is_odd = 0; /* * solve the quadratic equation, if it's not solvable then we * don't have a point */ if (!EC_POINT_set_compressed_coordinates_GFp(grp->group, grp->pwe, x_candidate, is_odd, NULL)) continue; /* * If there's a solution to the equation then the point must be * on the curve so why check again explicitly? OpenSSL code * says this is required by X9.62. We're not X9.62 but it can't * hurt just to be sure. */ if (!EC_POINT_is_on_curve(grp->group, grp->pwe, NULL)) { wpa_printf(MSG_INFO, "EAP-pwd: point is not on curve"); continue; } if (BN_cmp(cofactor, BN_value_one())) { /* make sure the point is not in a small sub-group */ if (!EC_POINT_mul(grp->group, grp->pwe, NULL, grp->pwe, cofactor, NULL)) { wpa_printf(MSG_INFO, "EAP-pwd: cannot " "multiply generator by order"); continue; } if (EC_POINT_is_at_infinity(grp->group, grp->pwe)) { wpa_printf(MSG_INFO, "EAP-pwd: point is at " "infinity"); continue; } } /* if we got here then we have a new generator. */ break; } wpa_printf(MSG_DEBUG, "EAP-pwd: found a PWE in %d tries", ctr); grp->group_num = num; if (0) { fail: EC_GROUP_free(grp->group); grp->group = NULL; EC_POINT_free(grp->pwe); grp->pwe = NULL; BN_free(grp->order); grp->order = NULL; BN_free(grp->prime); grp->prime = NULL; ret = 1; } /* cleanliness and order.... */ BN_free(cofactor); BN_free(x_candidate); BN_free(rnd); os_free(prfbuf); return ret; } int compute_keys(EAP_PWD_group *grp, BN_CTX *bnctx, BIGNUM *k, BIGNUM *peer_scalar, BIGNUM *server_scalar, u8 *confirm_peer, u8 *confirm_server, u32 *ciphersuite, u8 *msk, u8 *emsk) { struct crypto_hash *hash; u8 mk[SHA256_MAC_LEN], *cruft; u8 session_id[SHA256_MAC_LEN + 1]; u8 msk_emsk[EAP_MSK_LEN + EAP_EMSK_LEN]; int offset; if ((cruft = os_malloc(BN_num_bytes(grp->prime))) == NULL) return -1; /* * first compute the session-id = TypeCode | H(ciphersuite | scal_p | * scal_s) */ session_id[0] = EAP_TYPE_PWD; hash = eap_pwd_h_init(); if (hash == NULL) { os_free(cruft); return -1; } eap_pwd_h_update(hash, (u8 *) ciphersuite, sizeof(u32)); offset = BN_num_bytes(grp->order) - BN_num_bytes(peer_scalar); os_memset(cruft, 0, BN_num_bytes(grp->prime)); BN_bn2bin(peer_scalar, cruft + offset); eap_pwd_h_update(hash, cruft, BN_num_bytes(grp->order)); offset = BN_num_bytes(grp->order) - BN_num_bytes(server_scalar); os_memset(cruft, 0, BN_num_bytes(grp->prime)); BN_bn2bin(server_scalar, cruft + offset); eap_pwd_h_update(hash, cruft, BN_num_bytes(grp->order)); eap_pwd_h_final(hash, &session_id[1]); /* then compute MK = H(k | confirm-peer | confirm-server) */ hash = eap_pwd_h_init(); if (hash == NULL) { os_free(cruft); return -1; } offset = BN_num_bytes(grp->prime) - BN_num_bytes(k); os_memset(cruft, 0, BN_num_bytes(grp->prime)); BN_bn2bin(k, cruft + offset); eap_pwd_h_update(hash, cruft, BN_num_bytes(grp->prime)); os_free(cruft); eap_pwd_h_update(hash, confirm_peer, SHA256_MAC_LEN); eap_pwd_h_update(hash, confirm_server, SHA256_MAC_LEN); eap_pwd_h_final(hash, mk); /* stretch the mk with the session-id to get MSK | EMSK */ if (eap_pwd_kdf(mk, SHA256_MAC_LEN, session_id, SHA256_MAC_LEN + 1, msk_emsk, (EAP_MSK_LEN + EAP_EMSK_LEN) * 8) < 0) { return -1; } os_memcpy(msk, msk_emsk, EAP_MSK_LEN); os_memcpy(emsk, msk_emsk + EAP_MSK_LEN, EAP_EMSK_LEN); return 1; } wpa-2.1/src/eap_common/eap_pwd_common.h000066400000000000000000000036701227414666700202340ustar00rootroot00000000000000/* * EAP server/peer: EAP-pwd shared definitions * Copyright (c) 2009, Dan Harkins * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef EAP_PWD_COMMON_H #define EAP_PWD_COMMON_H #include #include #include /* * definition of a finite cyclic group * TODO: support one based on a prime field */ typedef struct group_definition_ { u16 group_num; EC_GROUP *group; EC_POINT *pwe; BIGNUM *order; BIGNUM *prime; } EAP_PWD_group; /* * EAP-pwd header, included on all payloads * L(1 bit) | M(1 bit) | exch(6 bits) | total_length(if L is set) */ #define EAP_PWD_HDR_SIZE 1 #define EAP_PWD_OPCODE_ID_EXCH 1 #define EAP_PWD_OPCODE_COMMIT_EXCH 2 #define EAP_PWD_OPCODE_CONFIRM_EXCH 3 #define EAP_PWD_GET_LENGTH_BIT(x) ((x) & 0x80) #define EAP_PWD_SET_LENGTH_BIT(x) ((x) |= 0x80) #define EAP_PWD_GET_MORE_BIT(x) ((x) & 0x40) #define EAP_PWD_SET_MORE_BIT(x) ((x) |= 0x40) #define EAP_PWD_GET_EXCHANGE(x) ((x) & 0x3f) #define EAP_PWD_SET_EXCHANGE(x,y) ((x) |= (y)) /* EAP-pwd-ID payload */ struct eap_pwd_id { be16 group_num; u8 random_function; #define EAP_PWD_DEFAULT_RAND_FUNC 1 u8 prf; #define EAP_PWD_DEFAULT_PRF 1 u8 token[4]; u8 prep; #define EAP_PWD_PREP_NONE 0 #define EAP_PWD_PREP_MS 1 u8 identity[0]; /* length inferred from payload */ } STRUCT_PACKED; /* common routines */ int compute_password_element(EAP_PWD_group *, u16, u8 *, int, u8 *, int, u8 *, int, u8 *); int compute_keys(EAP_PWD_group *, BN_CTX *, BIGNUM *, BIGNUM *, BIGNUM *, u8 *, u8 *, u32 *, u8 *, u8 *); struct crypto_hash * eap_pwd_h_init(void); void eap_pwd_h_update(struct crypto_hash *hash, const u8 *data, size_t len); void eap_pwd_h_final(struct crypto_hash *hash, u8 *digest); #endif /* EAP_PWD_COMMON_H */ wpa-2.1/src/eap_common/eap_sake_common.c000066400000000000000000000246621227414666700203640ustar00rootroot00000000000000/* * EAP server/peer: EAP-SAKE shared routines * Copyright (c) 2006-2007, 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 "wpabuf.h" #include "crypto/sha1.h" #include "eap_defs.h" #include "eap_sake_common.h" static int eap_sake_parse_add_attr(struct eap_sake_parse_attr *attr, const u8 *pos) { size_t i; switch (pos[0]) { case EAP_SAKE_AT_RAND_S: wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_RAND_S"); if (pos[1] != 2 + EAP_SAKE_RAND_LEN) { wpa_printf(MSG_DEBUG, "EAP-SAKE: AT_RAND_S with " "invalid length %d", pos[1]); return -1; } attr->rand_s = pos + 2; break; case EAP_SAKE_AT_RAND_P: wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_RAND_P"); if (pos[1] != 2 + EAP_SAKE_RAND_LEN) { wpa_printf(MSG_DEBUG, "EAP-SAKE: AT_RAND_P with " "invalid length %d", pos[1]); return -1; } attr->rand_p = pos + 2; break; case EAP_SAKE_AT_MIC_S: wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_MIC_S"); if (pos[1] != 2 + EAP_SAKE_MIC_LEN) { wpa_printf(MSG_DEBUG, "EAP-SAKE: AT_MIC_S with " "invalid length %d", pos[1]); return -1; } attr->mic_s = pos + 2; break; case EAP_SAKE_AT_MIC_P: wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_MIC_P"); if (pos[1] != 2 + EAP_SAKE_MIC_LEN) { wpa_printf(MSG_DEBUG, "EAP-SAKE: AT_MIC_P with " "invalid length %d", pos[1]); return -1; } attr->mic_p = pos + 2; break; case EAP_SAKE_AT_SERVERID: wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_SERVERID"); attr->serverid = pos + 2; attr->serverid_len = pos[1] - 2; break; case EAP_SAKE_AT_PEERID: wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_PEERID"); attr->peerid = pos + 2; attr->peerid_len = pos[1] - 2; break; case EAP_SAKE_AT_SPI_S: wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_SPI_S"); attr->spi_s = pos + 2; attr->spi_s_len = pos[1] - 2; break; case EAP_SAKE_AT_SPI_P: wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_SPI_P"); attr->spi_p = pos + 2; attr->spi_p_len = pos[1] - 2; break; case EAP_SAKE_AT_ANY_ID_REQ: wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_ANY_ID_REQ"); if (pos[1] != 4) { wpa_printf(MSG_DEBUG, "EAP-SAKE: Invalid AT_ANY_ID_REQ" " length %d", pos[1]); return -1; } attr->any_id_req = pos + 2; break; case EAP_SAKE_AT_PERM_ID_REQ: wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_PERM_ID_REQ"); if (pos[1] != 4) { wpa_printf(MSG_DEBUG, "EAP-SAKE: Invalid " "AT_PERM_ID_REQ length %d", pos[1]); return -1; } attr->perm_id_req = pos + 2; break; case EAP_SAKE_AT_ENCR_DATA: wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_ENCR_DATA"); attr->encr_data = pos + 2; attr->encr_data_len = pos[1] - 2; break; case EAP_SAKE_AT_IV: wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_IV"); attr->iv = pos + 2; attr->iv_len = pos[1] - 2; break; case EAP_SAKE_AT_PADDING: wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_PADDING"); for (i = 2; i < pos[1]; i++) { if (pos[i]) { wpa_printf(MSG_DEBUG, "EAP-SAKE: AT_PADDING " "with non-zero pad byte"); return -1; } } break; case EAP_SAKE_AT_NEXT_TMPID: wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_NEXT_TMPID"); attr->next_tmpid = pos + 2; attr->next_tmpid_len = pos[1] - 2; break; case EAP_SAKE_AT_MSK_LIFE: wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_IV"); if (pos[1] != 6) { wpa_printf(MSG_DEBUG, "EAP-SAKE: Invalid " "AT_MSK_LIFE length %d", pos[1]); return -1; } attr->msk_life = pos + 2; break; default: if (pos[0] < 128) { wpa_printf(MSG_DEBUG, "EAP-SAKE: Unknown non-skippable" " attribute %d", pos[0]); return -1; } wpa_printf(MSG_DEBUG, "EAP-SAKE: Ignoring unknown skippable " "attribute %d", pos[0]); break; } if (attr->iv && !attr->encr_data) { wpa_printf(MSG_DEBUG, "EAP-SAKE: AT_IV included without " "AT_ENCR_DATA"); return -1; } return 0; } /** * eap_sake_parse_attributes - Parse EAP-SAKE attributes * @buf: Packet payload (starting with the first attribute) * @len: Payload length * @attr: Structure to be filled with found attributes * Returns: 0 on success or -1 on failure */ int eap_sake_parse_attributes(const u8 *buf, size_t len, struct eap_sake_parse_attr *attr) { const u8 *pos = buf, *end = buf + len; os_memset(attr, 0, sizeof(*attr)); while (pos < end) { if (end - pos < 2) { wpa_printf(MSG_DEBUG, "EAP-SAKE: Too short attribute"); return -1; } if (pos[1] < 2) { wpa_printf(MSG_DEBUG, "EAP-SAKE: Invalid attribute " "length (%d)", pos[1]); return -1; } if (pos + pos[1] > end) { wpa_printf(MSG_DEBUG, "EAP-SAKE: Attribute underflow"); return -1; } if (eap_sake_parse_add_attr(attr, pos)) return -1; pos += pos[1]; } return 0; } /** * eap_sake_kdf - EAP-SAKE Key Derivation Function (KDF) * @key: Key for KDF * @key_len: Length of the key in bytes * @label: A unique label for each purpose of the KDF * @data: Extra data (start) to bind into the key * @data_len: Length of the data * @data2: Extra data (end) to bind into the key * @data2_len: Length of the data2 * @buf: Buffer for the generated pseudo-random key * @buf_len: Number of bytes of key to generate * * This function is used to derive new, cryptographically separate keys from a * given key (e.g., SMS). This is identical to the PRF used in IEEE 802.11i. */ static void eap_sake_kdf(const u8 *key, size_t key_len, const char *label, const u8 *data, size_t data_len, const u8 *data2, size_t data2_len, u8 *buf, size_t buf_len) { u8 counter = 0; size_t pos, plen; u8 hash[SHA1_MAC_LEN]; size_t label_len = os_strlen(label) + 1; const unsigned char *addr[4]; size_t len[4]; addr[0] = (u8 *) label; /* Label | Y */ len[0] = label_len; addr[1] = data; /* Msg[start] */ len[1] = data_len; addr[2] = data2; /* Msg[end] */ len[2] = data2_len; addr[3] = &counter; /* Length */ len[3] = 1; pos = 0; while (pos < buf_len) { plen = buf_len - pos; if (plen >= SHA1_MAC_LEN) { hmac_sha1_vector(key, key_len, 4, addr, len, &buf[pos]); pos += SHA1_MAC_LEN; } else { hmac_sha1_vector(key, key_len, 4, addr, len, hash); os_memcpy(&buf[pos], hash, plen); break; } counter++; } } /** * eap_sake_derive_keys - Derive EAP-SAKE keys * @root_secret_a: 16-byte Root-Secret-A * @root_secret_b: 16-byte Root-Secret-B * @rand_s: 16-byte RAND_S * @rand_p: 16-byte RAND_P * @tek: Buffer for Temporary EAK Keys (TEK-Auth[16] | TEK-Cipher[16]) * @msk: Buffer for 64-byte MSK * @emsk: Buffer for 64-byte EMSK * * This function derives EAP-SAKE keys as defined in RFC 4763, section 3.2.6. */ void eap_sake_derive_keys(const u8 *root_secret_a, const u8 *root_secret_b, const u8 *rand_s, const u8 *rand_p, u8 *tek, u8 *msk, u8 *emsk) { u8 sms_a[EAP_SAKE_SMS_LEN]; u8 sms_b[EAP_SAKE_SMS_LEN]; u8 key_buf[EAP_MSK_LEN + EAP_EMSK_LEN]; wpa_printf(MSG_DEBUG, "EAP-SAKE: Deriving keys"); wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: Root-Secret-A", root_secret_a, EAP_SAKE_ROOT_SECRET_LEN); eap_sake_kdf(root_secret_a, EAP_SAKE_ROOT_SECRET_LEN, "SAKE Master Secret A", rand_p, EAP_SAKE_RAND_LEN, rand_s, EAP_SAKE_RAND_LEN, sms_a, EAP_SAKE_SMS_LEN); wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: SMS-A", sms_a, EAP_SAKE_SMS_LEN); eap_sake_kdf(sms_a, EAP_SAKE_SMS_LEN, "Transient EAP Key", rand_s, EAP_SAKE_RAND_LEN, rand_p, EAP_SAKE_RAND_LEN, tek, EAP_SAKE_TEK_LEN); wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: TEK-Auth", tek, EAP_SAKE_TEK_AUTH_LEN); wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: TEK-Cipher", tek + EAP_SAKE_TEK_AUTH_LEN, EAP_SAKE_TEK_CIPHER_LEN); wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: Root-Secret-B", root_secret_b, EAP_SAKE_ROOT_SECRET_LEN); eap_sake_kdf(root_secret_b, EAP_SAKE_ROOT_SECRET_LEN, "SAKE Master Secret B", rand_p, EAP_SAKE_RAND_LEN, rand_s, EAP_SAKE_RAND_LEN, sms_b, EAP_SAKE_SMS_LEN); wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: SMS-B", sms_b, EAP_SAKE_SMS_LEN); eap_sake_kdf(sms_b, EAP_SAKE_SMS_LEN, "Master Session Key", rand_s, EAP_SAKE_RAND_LEN, rand_p, EAP_SAKE_RAND_LEN, key_buf, sizeof(key_buf)); os_memcpy(msk, key_buf, EAP_MSK_LEN); os_memcpy(emsk, key_buf + EAP_MSK_LEN, EAP_EMSK_LEN); wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: MSK", msk, EAP_MSK_LEN); wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: EMSK", emsk, EAP_EMSK_LEN); } /** * eap_sake_compute_mic - Compute EAP-SAKE MIC for an EAP packet * @tek_auth: 16-byte TEK-Auth * @rand_s: 16-byte RAND_S * @rand_p: 16-byte RAND_P * @serverid: SERVERID * @serverid_len: SERVERID length * @peerid: PEERID * @peerid_len: PEERID length * @peer: MIC calculation for 0 = Server, 1 = Peer message * @eap: EAP packet * @eap_len: EAP packet length * @mic_pos: MIC position in the EAP packet (must be [eap .. eap + eap_len]) * @mic: Buffer for the computed 16-byte MIC */ int eap_sake_compute_mic(const u8 *tek_auth, const u8 *rand_s, const u8 *rand_p, const u8 *serverid, size_t serverid_len, const u8 *peerid, size_t peerid_len, int peer, const u8 *eap, size_t eap_len, const u8 *mic_pos, u8 *mic) { u8 _rand[2 * EAP_SAKE_RAND_LEN]; u8 *tmp, *pos; size_t tmplen; tmplen = serverid_len + 1 + peerid_len + 1 + eap_len; tmp = os_malloc(tmplen); if (tmp == NULL) return -1; pos = tmp; if (peer) { if (peerid) { os_memcpy(pos, peerid, peerid_len); pos += peerid_len; } *pos++ = 0x00; if (serverid) { os_memcpy(pos, serverid, serverid_len); pos += serverid_len; } *pos++ = 0x00; os_memcpy(_rand, rand_s, EAP_SAKE_RAND_LEN); os_memcpy(_rand + EAP_SAKE_RAND_LEN, rand_p, EAP_SAKE_RAND_LEN); } else { if (serverid) { os_memcpy(pos, serverid, serverid_len); pos += serverid_len; } *pos++ = 0x00; if (peerid) { os_memcpy(pos, peerid, peerid_len); pos += peerid_len; } *pos++ = 0x00; os_memcpy(_rand, rand_p, EAP_SAKE_RAND_LEN); os_memcpy(_rand + EAP_SAKE_RAND_LEN, rand_s, EAP_SAKE_RAND_LEN); } os_memcpy(pos, eap, eap_len); os_memset(pos + (mic_pos - eap), 0, EAP_SAKE_MIC_LEN); eap_sake_kdf(tek_auth, EAP_SAKE_TEK_AUTH_LEN, peer ? "Peer MIC" : "Server MIC", _rand, 2 * EAP_SAKE_RAND_LEN, tmp, tmplen, mic, EAP_SAKE_MIC_LEN); os_free(tmp); return 0; } void eap_sake_add_attr(struct wpabuf *buf, u8 type, const u8 *data, size_t len) { wpabuf_put_u8(buf, type); wpabuf_put_u8(buf, 2 + len); /* Length; including attr header */ if (data) wpabuf_put_data(buf, data, len); else os_memset(wpabuf_put(buf, len), 0, len); } wpa-2.1/src/eap_common/eap_sake_common.h000066400000000000000000000046431227414666700203660ustar00rootroot00000000000000/* * EAP server/peer: EAP-SAKE shared routines * Copyright (c) 2006-2007, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef EAP_SAKE_COMMON_H #define EAP_SAKE_COMMON_H #define EAP_SAKE_VERSION 2 #define EAP_SAKE_SUBTYPE_CHALLENGE 1 #define EAP_SAKE_SUBTYPE_CONFIRM 2 #define EAP_SAKE_SUBTYPE_AUTH_REJECT 3 #define EAP_SAKE_SUBTYPE_IDENTITY 4 #define EAP_SAKE_AT_RAND_S 1 #define EAP_SAKE_AT_RAND_P 2 #define EAP_SAKE_AT_MIC_S 3 #define EAP_SAKE_AT_MIC_P 4 #define EAP_SAKE_AT_SERVERID 5 #define EAP_SAKE_AT_PEERID 6 #define EAP_SAKE_AT_SPI_S 7 #define EAP_SAKE_AT_SPI_P 8 #define EAP_SAKE_AT_ANY_ID_REQ 9 #define EAP_SAKE_AT_PERM_ID_REQ 10 #define EAP_SAKE_AT_ENCR_DATA 128 #define EAP_SAKE_AT_IV 129 #define EAP_SAKE_AT_PADDING 130 #define EAP_SAKE_AT_NEXT_TMPID 131 #define EAP_SAKE_AT_MSK_LIFE 132 #define EAP_SAKE_RAND_LEN 16 #define EAP_SAKE_MIC_LEN 16 #define EAP_SAKE_ROOT_SECRET_LEN 16 #define EAP_SAKE_SMS_LEN 16 #define EAP_SAKE_TEK_AUTH_LEN 16 #define EAP_SAKE_TEK_CIPHER_LEN 16 #define EAP_SAKE_TEK_LEN (EAP_SAKE_TEK_AUTH_LEN + EAP_SAKE_TEK_CIPHER_LEN) #ifdef _MSC_VER #pragma pack(push, 1) #endif /* _MSC_VER */ struct eap_sake_hdr { u8 version; /* EAP_SAKE_VERSION */ u8 session_id; u8 subtype; } STRUCT_PACKED; #ifdef _MSC_VER #pragma pack(pop) #endif /* _MSC_VER */ struct eap_sake_parse_attr { const u8 *rand_s; const u8 *rand_p; const u8 *mic_s; const u8 *mic_p; const u8 *serverid; size_t serverid_len; const u8 *peerid; size_t peerid_len; const u8 *spi_s; size_t spi_s_len; const u8 *spi_p; size_t spi_p_len; const u8 *any_id_req; const u8 *perm_id_req; const u8 *encr_data; size_t encr_data_len; const u8 *iv; size_t iv_len; const u8 *next_tmpid; size_t next_tmpid_len; const u8 *msk_life; }; int eap_sake_parse_attributes(const u8 *buf, size_t len, struct eap_sake_parse_attr *attr); void eap_sake_derive_keys(const u8 *root_secret_a, const u8 *root_secret_b, const u8 *rand_s, const u8 *rand_p, u8 *tek, u8 *msk, u8 *emsk); int eap_sake_compute_mic(const u8 *tek_auth, const u8 *rand_s, const u8 *rand_p, const u8 *serverid, size_t serverid_len, const u8 *peerid, size_t peerid_len, int peer, const u8 *eap, size_t eap_len, const u8 *mic_pos, u8 *mic); void eap_sake_add_attr(struct wpabuf *buf, u8 type, const u8 *data, size_t len); #endif /* EAP_SAKE_COMMON_H */ wpa-2.1/src/eap_common/eap_sim_common.c000066400000000000000000000770651227414666700202360ustar00rootroot00000000000000/* * EAP peer/server: EAP-SIM/AKA/AKA' shared routines * Copyright (c) 2004-2008, 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 "wpabuf.h" #include "crypto/aes_wrap.h" #include "crypto/crypto.h" #include "crypto/sha1.h" #include "crypto/sha256.h" #include "crypto/random.h" #include "eap_common/eap_defs.h" #include "eap_common/eap_sim_common.h" static int eap_sim_prf(const u8 *key, u8 *x, size_t xlen) { return fips186_2_prf(key, EAP_SIM_MK_LEN, x, xlen); } void eap_sim_derive_mk(const u8 *identity, size_t identity_len, const u8 *nonce_mt, u16 selected_version, const u8 *ver_list, size_t ver_list_len, int num_chal, const u8 *kc, u8 *mk) { u8 sel_ver[2]; const unsigned char *addr[5]; size_t len[5]; addr[0] = identity; len[0] = identity_len; addr[1] = kc; len[1] = num_chal * EAP_SIM_KC_LEN; addr[2] = nonce_mt; len[2] = EAP_SIM_NONCE_MT_LEN; addr[3] = ver_list; len[3] = ver_list_len; addr[4] = sel_ver; len[4] = 2; WPA_PUT_BE16(sel_ver, selected_version); /* MK = SHA1(Identity|n*Kc|NONCE_MT|Version List|Selected Version) */ sha1_vector(5, addr, len, mk); wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: MK", mk, EAP_SIM_MK_LEN); } void eap_aka_derive_mk(const u8 *identity, size_t identity_len, const u8 *ik, const u8 *ck, u8 *mk) { const u8 *addr[3]; size_t len[3]; addr[0] = identity; len[0] = identity_len; addr[1] = ik; len[1] = EAP_AKA_IK_LEN; addr[2] = ck; len[2] = EAP_AKA_CK_LEN; /* MK = SHA1(Identity|IK|CK) */ sha1_vector(3, addr, len, mk); wpa_hexdump_key(MSG_DEBUG, "EAP-AKA: IK", ik, EAP_AKA_IK_LEN); wpa_hexdump_key(MSG_DEBUG, "EAP-AKA: CK", ck, EAP_AKA_CK_LEN); wpa_hexdump_key(MSG_DEBUG, "EAP-AKA: MK", mk, EAP_SIM_MK_LEN); } int eap_sim_derive_keys(const u8 *mk, u8 *k_encr, u8 *k_aut, u8 *msk, u8 *emsk) { u8 buf[EAP_SIM_K_ENCR_LEN + EAP_SIM_K_AUT_LEN + EAP_SIM_KEYING_DATA_LEN + EAP_EMSK_LEN], *pos; if (eap_sim_prf(mk, buf, sizeof(buf)) < 0) { wpa_printf(MSG_ERROR, "EAP-SIM: Failed to derive keys"); return -1; } pos = buf; os_memcpy(k_encr, pos, EAP_SIM_K_ENCR_LEN); pos += EAP_SIM_K_ENCR_LEN; os_memcpy(k_aut, pos, EAP_SIM_K_AUT_LEN); pos += EAP_SIM_K_AUT_LEN; os_memcpy(msk, pos, EAP_SIM_KEYING_DATA_LEN); pos += EAP_SIM_KEYING_DATA_LEN; os_memcpy(emsk, pos, EAP_EMSK_LEN); wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: K_encr", k_encr, EAP_SIM_K_ENCR_LEN); wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: K_aut", k_aut, EAP_SIM_K_AUT_LEN); wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: keying material (MSK)", msk, EAP_SIM_KEYING_DATA_LEN); wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: EMSK", emsk, EAP_EMSK_LEN); os_memset(buf, 0, sizeof(buf)); return 0; } int eap_sim_derive_keys_reauth(u16 _counter, const u8 *identity, size_t identity_len, const u8 *nonce_s, const u8 *mk, u8 *msk, u8 *emsk) { u8 xkey[SHA1_MAC_LEN]; u8 buf[EAP_SIM_KEYING_DATA_LEN + EAP_EMSK_LEN + 32]; u8 counter[2]; const u8 *addr[4]; size_t len[4]; while (identity_len > 0 && identity[identity_len - 1] == 0) { wpa_printf(MSG_DEBUG, "EAP-SIM: Workaround - drop null " "character from the end of identity"); identity_len--; } addr[0] = identity; len[0] = identity_len; addr[1] = counter; len[1] = 2; addr[2] = nonce_s; len[2] = EAP_SIM_NONCE_S_LEN; addr[3] = mk; len[3] = EAP_SIM_MK_LEN; WPA_PUT_BE16(counter, _counter); wpa_printf(MSG_DEBUG, "EAP-SIM: Deriving keying data from reauth"); wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Identity", identity, identity_len); wpa_hexdump(MSG_DEBUG, "EAP-SIM: counter", counter, 2); wpa_hexdump(MSG_DEBUG, "EAP-SIM: NONCE_S", nonce_s, EAP_SIM_NONCE_S_LEN); wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: MK", mk, EAP_SIM_MK_LEN); /* XKEY' = SHA1(Identity|counter|NONCE_S|MK) */ sha1_vector(4, addr, len, xkey); wpa_hexdump(MSG_DEBUG, "EAP-SIM: XKEY'", xkey, SHA1_MAC_LEN); if (eap_sim_prf(xkey, buf, sizeof(buf)) < 0) { wpa_printf(MSG_ERROR, "EAP-SIM: Failed to derive keys"); return -1; } if (msk) { os_memcpy(msk, buf, EAP_SIM_KEYING_DATA_LEN); wpa_hexdump(MSG_DEBUG, "EAP-SIM: keying material (MSK)", msk, EAP_SIM_KEYING_DATA_LEN); } if (emsk) { os_memcpy(emsk, buf + EAP_SIM_KEYING_DATA_LEN, EAP_EMSK_LEN); wpa_hexdump(MSG_DEBUG, "EAP-SIM: EMSK", emsk, EAP_EMSK_LEN); } os_memset(buf, 0, sizeof(buf)); return 0; } int eap_sim_verify_mac(const u8 *k_aut, const struct wpabuf *req, const u8 *mac, const u8 *extra, size_t extra_len) { unsigned char hmac[SHA1_MAC_LEN]; const u8 *addr[2]; size_t len[2]; u8 *tmp; if (mac == NULL || wpabuf_len(req) < EAP_SIM_MAC_LEN || mac < wpabuf_head_u8(req) || mac > wpabuf_head_u8(req) + wpabuf_len(req) - EAP_SIM_MAC_LEN) return -1; tmp = os_malloc(wpabuf_len(req)); if (tmp == NULL) return -1; addr[0] = tmp; len[0] = wpabuf_len(req); addr[1] = extra; len[1] = extra_len; /* HMAC-SHA1-128 */ os_memcpy(tmp, wpabuf_head(req), wpabuf_len(req)); os_memset(tmp + (mac - wpabuf_head_u8(req)), 0, EAP_SIM_MAC_LEN); wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Verify MAC - msg", tmp, wpabuf_len(req)); wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Verify MAC - extra data", extra, extra_len); wpa_hexdump_key(MSG_MSGDUMP, "EAP-SIM: Verify MAC - K_aut", k_aut, EAP_SIM_K_AUT_LEN); hmac_sha1_vector(k_aut, EAP_SIM_K_AUT_LEN, 2, addr, len, hmac); wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Verify MAC: MAC", hmac, EAP_SIM_MAC_LEN); os_free(tmp); return (os_memcmp(hmac, mac, EAP_SIM_MAC_LEN) == 0) ? 0 : 1; } void eap_sim_add_mac(const u8 *k_aut, const u8 *msg, size_t msg_len, u8 *mac, const u8 *extra, size_t extra_len) { unsigned char hmac[SHA1_MAC_LEN]; const u8 *addr[2]; size_t len[2]; addr[0] = msg; len[0] = msg_len; addr[1] = extra; len[1] = extra_len; /* HMAC-SHA1-128 */ os_memset(mac, 0, EAP_SIM_MAC_LEN); wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Add MAC - msg", msg, msg_len); wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Add MAC - extra data", extra, extra_len); wpa_hexdump_key(MSG_MSGDUMP, "EAP-SIM: Add MAC - K_aut", k_aut, EAP_SIM_K_AUT_LEN); hmac_sha1_vector(k_aut, EAP_SIM_K_AUT_LEN, 2, addr, len, hmac); os_memcpy(mac, hmac, EAP_SIM_MAC_LEN); wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Add MAC: MAC", mac, EAP_SIM_MAC_LEN); } #if defined(EAP_AKA_PRIME) || defined(EAP_SERVER_AKA_PRIME) static void prf_prime(const u8 *k, const char *seed1, const u8 *seed2, size_t seed2_len, const u8 *seed3, size_t seed3_len, u8 *res, size_t res_len) { const u8 *addr[5]; size_t len[5]; u8 hash[SHA256_MAC_LEN]; u8 iter; /* * PRF'(K,S) = T1 | T2 | T3 | T4 | ... * T1 = HMAC-SHA-256 (K, S | 0x01) * T2 = HMAC-SHA-256 (K, T1 | S | 0x02) * T3 = HMAC-SHA-256 (K, T2 | S | 0x03) * T4 = HMAC-SHA-256 (K, T3 | S | 0x04) * ... */ addr[0] = hash; len[0] = 0; addr[1] = (const u8 *) seed1; len[1] = os_strlen(seed1); addr[2] = seed2; len[2] = seed2_len; addr[3] = seed3; len[3] = seed3_len; addr[4] = &iter; len[4] = 1; iter = 0; while (res_len) { size_t hlen; iter++; hmac_sha256_vector(k, 32, 5, addr, len, hash); len[0] = SHA256_MAC_LEN; hlen = res_len > SHA256_MAC_LEN ? SHA256_MAC_LEN : res_len; os_memcpy(res, hash, hlen); res += hlen; res_len -= hlen; } } void eap_aka_prime_derive_keys(const u8 *identity, size_t identity_len, const u8 *ik, const u8 *ck, u8 *k_encr, u8 *k_aut, u8 *k_re, u8 *msk, u8 *emsk) { u8 key[EAP_AKA_IK_LEN + EAP_AKA_CK_LEN]; u8 keys[EAP_SIM_K_ENCR_LEN + EAP_AKA_PRIME_K_AUT_LEN + EAP_AKA_PRIME_K_RE_LEN + EAP_MSK_LEN + EAP_EMSK_LEN]; u8 *pos; /* * MK = PRF'(IK'|CK',"EAP-AKA'"|Identity) * K_encr = MK[0..127] * K_aut = MK[128..383] * K_re = MK[384..639] * MSK = MK[640..1151] * EMSK = MK[1152..1663] */ os_memcpy(key, ik, EAP_AKA_IK_LEN); os_memcpy(key + EAP_AKA_IK_LEN, ck, EAP_AKA_CK_LEN); prf_prime(key, "EAP-AKA'", identity, identity_len, NULL, 0, keys, sizeof(keys)); pos = keys; os_memcpy(k_encr, pos, EAP_SIM_K_ENCR_LEN); wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': K_encr", k_encr, EAP_SIM_K_ENCR_LEN); pos += EAP_SIM_K_ENCR_LEN; os_memcpy(k_aut, pos, EAP_AKA_PRIME_K_AUT_LEN); wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': K_aut", k_aut, EAP_AKA_PRIME_K_AUT_LEN); pos += EAP_AKA_PRIME_K_AUT_LEN; os_memcpy(k_re, pos, EAP_AKA_PRIME_K_RE_LEN); wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': K_re", k_re, EAP_AKA_PRIME_K_RE_LEN); pos += EAP_AKA_PRIME_K_RE_LEN; os_memcpy(msk, pos, EAP_MSK_LEN); wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': MSK", msk, EAP_MSK_LEN); pos += EAP_MSK_LEN; os_memcpy(emsk, pos, EAP_EMSK_LEN); wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': EMSK", emsk, EAP_EMSK_LEN); } int eap_aka_prime_derive_keys_reauth(const u8 *k_re, u16 counter, const u8 *identity, size_t identity_len, const u8 *nonce_s, u8 *msk, u8 *emsk) { u8 seed3[2 + EAP_SIM_NONCE_S_LEN]; u8 keys[EAP_MSK_LEN + EAP_EMSK_LEN]; u8 *pos; /* * MK = PRF'(K_re,"EAP-AKA' re-auth"|Identity|counter|NONCE_S) * MSK = MK[0..511] * EMSK = MK[512..1023] */ WPA_PUT_BE16(seed3, counter); os_memcpy(seed3 + 2, nonce_s, EAP_SIM_NONCE_S_LEN); prf_prime(k_re, "EAP-AKA' re-auth", identity, identity_len, seed3, sizeof(seed3), keys, sizeof(keys)); pos = keys; os_memcpy(msk, pos, EAP_MSK_LEN); wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': MSK", msk, EAP_MSK_LEN); pos += EAP_MSK_LEN; os_memcpy(emsk, pos, EAP_EMSK_LEN); wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': EMSK", emsk, EAP_EMSK_LEN); os_memset(keys, 0, sizeof(keys)); return 0; } int eap_sim_verify_mac_sha256(const u8 *k_aut, const struct wpabuf *req, const u8 *mac, const u8 *extra, size_t extra_len) { unsigned char hmac[SHA256_MAC_LEN]; const u8 *addr[2]; size_t len[2]; u8 *tmp; if (mac == NULL || wpabuf_len(req) < EAP_SIM_MAC_LEN || mac < wpabuf_head_u8(req) || mac > wpabuf_head_u8(req) + wpabuf_len(req) - EAP_SIM_MAC_LEN) return -1; tmp = os_malloc(wpabuf_len(req)); if (tmp == NULL) return -1; addr[0] = tmp; len[0] = wpabuf_len(req); addr[1] = extra; len[1] = extra_len; /* HMAC-SHA-256-128 */ os_memcpy(tmp, wpabuf_head(req), wpabuf_len(req)); os_memset(tmp + (mac - wpabuf_head_u8(req)), 0, EAP_SIM_MAC_LEN); wpa_hexdump(MSG_MSGDUMP, "EAP-AKA': Verify MAC - msg", tmp, wpabuf_len(req)); wpa_hexdump(MSG_MSGDUMP, "EAP-AKA': Verify MAC - extra data", extra, extra_len); wpa_hexdump_key(MSG_MSGDUMP, "EAP-AKA': Verify MAC - K_aut", k_aut, EAP_AKA_PRIME_K_AUT_LEN); hmac_sha256_vector(k_aut, EAP_AKA_PRIME_K_AUT_LEN, 2, addr, len, hmac); wpa_hexdump(MSG_MSGDUMP, "EAP-AKA': Verify MAC: MAC", hmac, EAP_SIM_MAC_LEN); os_free(tmp); return (os_memcmp(hmac, mac, EAP_SIM_MAC_LEN) == 0) ? 0 : 1; } void eap_sim_add_mac_sha256(const u8 *k_aut, const u8 *msg, size_t msg_len, u8 *mac, const u8 *extra, size_t extra_len) { unsigned char hmac[SHA256_MAC_LEN]; const u8 *addr[2]; size_t len[2]; addr[0] = msg; len[0] = msg_len; addr[1] = extra; len[1] = extra_len; /* HMAC-SHA-256-128 */ os_memset(mac, 0, EAP_SIM_MAC_LEN); wpa_hexdump(MSG_MSGDUMP, "EAP-AKA': Add MAC - msg", msg, msg_len); wpa_hexdump(MSG_MSGDUMP, "EAP-AKA': Add MAC - extra data", extra, extra_len); wpa_hexdump_key(MSG_MSGDUMP, "EAP-AKA': Add MAC - K_aut", k_aut, EAP_AKA_PRIME_K_AUT_LEN); hmac_sha256_vector(k_aut, EAP_AKA_PRIME_K_AUT_LEN, 2, addr, len, hmac); os_memcpy(mac, hmac, EAP_SIM_MAC_LEN); wpa_hexdump(MSG_MSGDUMP, "EAP-AKA': Add MAC: MAC", mac, EAP_SIM_MAC_LEN); } void eap_aka_prime_derive_ck_ik_prime(u8 *ck, u8 *ik, const u8 *sqn_ak, const u8 *network_name, size_t network_name_len) { u8 key[EAP_AKA_CK_LEN + EAP_AKA_IK_LEN]; u8 hash[SHA256_MAC_LEN]; const u8 *addr[5]; size_t len[5]; u8 fc; u8 l0[2], l1[2]; /* 3GPP TS 33.402 V8.0.0 * (CK', IK') = F(CK, IK, ) */ /* TODO: CK', IK' generation should really be moved into the actual * AKA procedure with network name passed in there and option to use * AMF separation bit = 1 (3GPP TS 33.401). */ /* Change Request 33.402 CR 0033 to version 8.1.1 from * 3GPP TSG-SA WG3 Meeting #53 in September 2008: * * CK' || IK' = HMAC-SHA-256(Key, S) * S = FC || P0 || L0 || P1 || L1 || ... || Pn || Ln * Key = CK || IK * FC = 0x20 * P0 = access network identity (3GPP TS 24.302) * L0 = length of acceess network identity (2 octets, big endian) * P1 = SQN xor AK (if AK is not used, AK is treaded as 000..0 * L1 = 0x00 0x06 */ fc = 0x20; wpa_printf(MSG_DEBUG, "EAP-AKA': Derive (CK',IK') from (CK,IK)"); wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': CK", ck, EAP_AKA_CK_LEN); wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': IK", ik, EAP_AKA_IK_LEN); wpa_printf(MSG_DEBUG, "EAP-AKA': FC = 0x%x", fc); wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA': P0 = Access network identity", network_name, network_name_len); wpa_hexdump(MSG_DEBUG, "EAP-AKA': P1 = SQN xor AK", sqn_ak, 6); os_memcpy(key, ck, EAP_AKA_CK_LEN); os_memcpy(key + EAP_AKA_CK_LEN, ik, EAP_AKA_IK_LEN); wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': Key = CK || IK", key, sizeof(key)); addr[0] = &fc; len[0] = 1; addr[1] = network_name; len[1] = network_name_len; WPA_PUT_BE16(l0, network_name_len); addr[2] = l0; len[2] = 2; addr[3] = sqn_ak; len[3] = 6; WPA_PUT_BE16(l1, 6); addr[4] = l1; len[4] = 2; hmac_sha256_vector(key, sizeof(key), 5, addr, len, hash); wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': KDF output (CK' || IK')", hash, sizeof(hash)); os_memcpy(ck, hash, EAP_AKA_CK_LEN); os_memcpy(ik, hash + EAP_AKA_CK_LEN, EAP_AKA_IK_LEN); wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': CK'", ck, EAP_AKA_CK_LEN); wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': IK'", ik, EAP_AKA_IK_LEN); } #endif /* EAP_AKA_PRIME || EAP_SERVER_AKA_PRIME */ int eap_sim_parse_attr(const u8 *start, const u8 *end, struct eap_sim_attrs *attr, int aka, int encr) { const u8 *pos = start, *apos; size_t alen, plen, i, list_len; os_memset(attr, 0, sizeof(*attr)); attr->id_req = NO_ID_REQ; attr->notification = -1; attr->counter = -1; attr->selected_version = -1; attr->client_error_code = -1; while (pos < end) { if (pos + 2 > end) { wpa_printf(MSG_INFO, "EAP-SIM: Attribute overflow(1)"); return -1; } wpa_printf(MSG_MSGDUMP, "EAP-SIM: Attribute: Type=%d Len=%d", pos[0], pos[1] * 4); if (pos + pos[1] * 4 > end) { wpa_printf(MSG_INFO, "EAP-SIM: Attribute overflow " "(pos=%p len=%d end=%p)", pos, pos[1] * 4, end); return -1; } if (pos[1] == 0) { wpa_printf(MSG_INFO, "EAP-SIM: Attribute underflow"); return -1; } apos = pos + 2; alen = pos[1] * 4 - 2; wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Attribute data", apos, alen); switch (pos[0]) { case EAP_SIM_AT_RAND: wpa_printf(MSG_DEBUG, "EAP-SIM: AT_RAND"); apos += 2; alen -= 2; if ((!aka && (alen % GSM_RAND_LEN)) || (aka && alen != EAP_AKA_RAND_LEN)) { wpa_printf(MSG_INFO, "EAP-SIM: Invalid AT_RAND" " (len %lu)", (unsigned long) alen); return -1; } attr->rand = apos; attr->num_chal = alen / GSM_RAND_LEN; break; case EAP_SIM_AT_AUTN: wpa_printf(MSG_DEBUG, "EAP-AKA: AT_AUTN"); if (!aka) { wpa_printf(MSG_DEBUG, "EAP-SIM: " "Unexpected AT_AUTN"); return -1; } apos += 2; alen -= 2; if (alen != EAP_AKA_AUTN_LEN) { wpa_printf(MSG_INFO, "EAP-AKA: Invalid AT_AUTN" " (len %lu)", (unsigned long) alen); return -1; } attr->autn = apos; break; case EAP_SIM_AT_PADDING: if (!encr) { wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted " "AT_PADDING"); return -1; } wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) AT_PADDING"); for (i = 2; i < alen; i++) { if (apos[i] != 0) { wpa_printf(MSG_INFO, "EAP-SIM: (encr) " "AT_PADDING used a non-zero" " padding byte"); wpa_hexdump(MSG_DEBUG, "EAP-SIM: " "(encr) padding bytes", apos + 2, alen - 2); return -1; } } break; case EAP_SIM_AT_NONCE_MT: wpa_printf(MSG_DEBUG, "EAP-SIM: AT_NONCE_MT"); if (alen != 2 + EAP_SIM_NONCE_MT_LEN) { wpa_printf(MSG_INFO, "EAP-SIM: Invalid " "AT_NONCE_MT length"); return -1; } attr->nonce_mt = apos + 2; break; case EAP_SIM_AT_PERMANENT_ID_REQ: wpa_printf(MSG_DEBUG, "EAP-SIM: AT_PERMANENT_ID_REQ"); attr->id_req = PERMANENT_ID; break; case EAP_SIM_AT_MAC: wpa_printf(MSG_DEBUG, "EAP-SIM: AT_MAC"); if (alen != 2 + EAP_SIM_MAC_LEN) { wpa_printf(MSG_INFO, "EAP-SIM: Invalid AT_MAC " "length"); return -1; } attr->mac = apos + 2; break; case EAP_SIM_AT_NOTIFICATION: if (alen != 2) { wpa_printf(MSG_INFO, "EAP-SIM: Invalid " "AT_NOTIFICATION length %lu", (unsigned long) alen); return -1; } attr->notification = apos[0] * 256 + apos[1]; wpa_printf(MSG_DEBUG, "EAP-SIM: AT_NOTIFICATION %d", attr->notification); break; case EAP_SIM_AT_ANY_ID_REQ: wpa_printf(MSG_DEBUG, "EAP-SIM: AT_ANY_ID_REQ"); attr->id_req = ANY_ID; break; case EAP_SIM_AT_IDENTITY: wpa_printf(MSG_DEBUG, "EAP-SIM: AT_IDENTITY"); plen = WPA_GET_BE16(apos); apos += 2; alen -= 2; if (plen > alen) { wpa_printf(MSG_INFO, "EAP-SIM: Invalid " "AT_IDENTITY (Actual Length %lu, " "remaining length %lu)", (unsigned long) plen, (unsigned long) alen); return -1; } attr->identity = apos; attr->identity_len = plen; break; case EAP_SIM_AT_VERSION_LIST: if (aka) { wpa_printf(MSG_DEBUG, "EAP-AKA: " "Unexpected AT_VERSION_LIST"); return -1; } list_len = apos[0] * 256 + apos[1]; wpa_printf(MSG_DEBUG, "EAP-SIM: AT_VERSION_LIST"); if (list_len < 2 || list_len > alen - 2) { wpa_printf(MSG_WARNING, "EAP-SIM: Invalid " "AT_VERSION_LIST (list_len=%lu " "attr_len=%lu)", (unsigned long) list_len, (unsigned long) alen); return -1; } attr->version_list = apos + 2; attr->version_list_len = list_len; break; case EAP_SIM_AT_SELECTED_VERSION: wpa_printf(MSG_DEBUG, "EAP-SIM: AT_SELECTED_VERSION"); if (alen != 2) { wpa_printf(MSG_INFO, "EAP-SIM: Invalid " "AT_SELECTED_VERSION length %lu", (unsigned long) alen); return -1; } attr->selected_version = apos[0] * 256 + apos[1]; wpa_printf(MSG_DEBUG, "EAP-SIM: AT_SELECTED_VERSION " "%d", attr->selected_version); break; case EAP_SIM_AT_FULLAUTH_ID_REQ: wpa_printf(MSG_DEBUG, "EAP-SIM: AT_FULLAUTH_ID_REQ"); attr->id_req = FULLAUTH_ID; break; case EAP_SIM_AT_COUNTER: if (!encr) { wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted " "AT_COUNTER"); return -1; } if (alen != 2) { wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid " "AT_COUNTER (alen=%lu)", (unsigned long) alen); return -1; } attr->counter = apos[0] * 256 + apos[1]; wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) AT_COUNTER %d", attr->counter); break; case EAP_SIM_AT_COUNTER_TOO_SMALL: if (!encr) { wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted " "AT_COUNTER_TOO_SMALL"); return -1; } if (alen != 2) { wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid " "AT_COUNTER_TOO_SMALL (alen=%lu)", (unsigned long) alen); return -1; } wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) " "AT_COUNTER_TOO_SMALL"); attr->counter_too_small = 1; break; case EAP_SIM_AT_NONCE_S: if (!encr) { wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted " "AT_NONCE_S"); return -1; } wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) " "AT_NONCE_S"); if (alen != 2 + EAP_SIM_NONCE_S_LEN) { wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid " "AT_NONCE_S (alen=%lu)", (unsigned long) alen); return -1; } attr->nonce_s = apos + 2; break; case EAP_SIM_AT_CLIENT_ERROR_CODE: if (alen != 2) { wpa_printf(MSG_INFO, "EAP-SIM: Invalid " "AT_CLIENT_ERROR_CODE length %lu", (unsigned long) alen); return -1; } attr->client_error_code = apos[0] * 256 + apos[1]; wpa_printf(MSG_DEBUG, "EAP-SIM: AT_CLIENT_ERROR_CODE " "%d", attr->client_error_code); break; case EAP_SIM_AT_IV: wpa_printf(MSG_DEBUG, "EAP-SIM: AT_IV"); if (alen != 2 + EAP_SIM_MAC_LEN) { wpa_printf(MSG_INFO, "EAP-SIM: Invalid AT_IV " "length %lu", (unsigned long) alen); return -1; } attr->iv = apos + 2; break; case EAP_SIM_AT_ENCR_DATA: wpa_printf(MSG_DEBUG, "EAP-SIM: AT_ENCR_DATA"); attr->encr_data = apos + 2; attr->encr_data_len = alen - 2; if (attr->encr_data_len % 16) { wpa_printf(MSG_INFO, "EAP-SIM: Invalid " "AT_ENCR_DATA length %lu", (unsigned long) attr->encr_data_len); return -1; } break; case EAP_SIM_AT_NEXT_PSEUDONYM: if (!encr) { wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted " "AT_NEXT_PSEUDONYM"); return -1; } wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) " "AT_NEXT_PSEUDONYM"); plen = apos[0] * 256 + apos[1]; if (plen > alen - 2) { wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid" " AT_NEXT_PSEUDONYM (actual" " len %lu, attr len %lu)", (unsigned long) plen, (unsigned long) alen); return -1; } attr->next_pseudonym = pos + 4; attr->next_pseudonym_len = plen; break; case EAP_SIM_AT_NEXT_REAUTH_ID: if (!encr) { wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted " "AT_NEXT_REAUTH_ID"); return -1; } wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) " "AT_NEXT_REAUTH_ID"); plen = apos[0] * 256 + apos[1]; if (plen > alen - 2) { wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid" " AT_NEXT_REAUTH_ID (actual" " len %lu, attr len %lu)", (unsigned long) plen, (unsigned long) alen); return -1; } attr->next_reauth_id = pos + 4; attr->next_reauth_id_len = plen; break; case EAP_SIM_AT_RES: wpa_printf(MSG_DEBUG, "EAP-SIM: AT_RES"); attr->res_len_bits = WPA_GET_BE16(apos); apos += 2; alen -= 2; if (!aka || alen < EAP_AKA_MIN_RES_LEN || alen > EAP_AKA_MAX_RES_LEN) { wpa_printf(MSG_INFO, "EAP-SIM: Invalid AT_RES " "(len %lu)", (unsigned long) alen); return -1; } attr->res = apos; attr->res_len = alen; break; case EAP_SIM_AT_AUTS: wpa_printf(MSG_DEBUG, "EAP-AKA: AT_AUTS"); if (!aka) { wpa_printf(MSG_DEBUG, "EAP-SIM: " "Unexpected AT_AUTS"); return -1; } if (alen != EAP_AKA_AUTS_LEN) { wpa_printf(MSG_INFO, "EAP-AKA: Invalid AT_AUTS" " (len %lu)", (unsigned long) alen); return -1; } attr->auts = apos; break; case EAP_SIM_AT_CHECKCODE: wpa_printf(MSG_DEBUG, "EAP-AKA: AT_CHECKCODE"); if (!aka) { wpa_printf(MSG_DEBUG, "EAP-SIM: " "Unexpected AT_CHECKCODE"); return -1; } apos += 2; alen -= 2; if (alen != 0 && alen != EAP_AKA_CHECKCODE_LEN && alen != EAP_AKA_PRIME_CHECKCODE_LEN) { wpa_printf(MSG_INFO, "EAP-AKA: Invalid " "AT_CHECKCODE (len %lu)", (unsigned long) alen); return -1; } attr->checkcode = apos; attr->checkcode_len = alen; break; case EAP_SIM_AT_RESULT_IND: if (encr) { wpa_printf(MSG_ERROR, "EAP-SIM: Encrypted " "AT_RESULT_IND"); return -1; } if (alen != 2) { wpa_printf(MSG_INFO, "EAP-SIM: Invalid " "AT_RESULT_IND (alen=%lu)", (unsigned long) alen); return -1; } wpa_printf(MSG_DEBUG, "EAP-SIM: AT_RESULT_IND"); attr->result_ind = 1; break; #if defined(EAP_AKA_PRIME) || defined(EAP_SERVER_AKA_PRIME) case EAP_SIM_AT_KDF_INPUT: if (aka != 2) { wpa_printf(MSG_INFO, "EAP-AKA: Unexpected " "AT_KDF_INPUT"); return -1; } wpa_printf(MSG_DEBUG, "EAP-AKA: AT_KDF_INPUT"); plen = WPA_GET_BE16(apos); apos += 2; alen -= 2; if (plen > alen) { wpa_printf(MSG_INFO, "EAP-AKA': Invalid " "AT_KDF_INPUT (Actual Length %lu, " "remaining length %lu)", (unsigned long) plen, (unsigned long) alen); return -1; } attr->kdf_input = apos; attr->kdf_input_len = plen; break; case EAP_SIM_AT_KDF: if (aka != 2) { wpa_printf(MSG_INFO, "EAP-AKA: Unexpected " "AT_KDF"); return -1; } wpa_printf(MSG_DEBUG, "EAP-AKA: AT_KDF"); if (alen != 2) { wpa_printf(MSG_INFO, "EAP-AKA': Invalid " "AT_KDF (len %lu)", (unsigned long) alen); return -1; } if (attr->kdf_count == EAP_AKA_PRIME_KDF_MAX) { wpa_printf(MSG_DEBUG, "EAP-AKA': Too many " "AT_KDF attributes - ignore this"); continue; } attr->kdf[attr->kdf_count] = WPA_GET_BE16(apos); attr->kdf_count++; break; case EAP_SIM_AT_BIDDING: wpa_printf(MSG_DEBUG, "EAP-AKA: AT_BIDDING"); if (alen != 2) { wpa_printf(MSG_INFO, "EAP-AKA: Invalid " "AT_BIDDING (len %lu)", (unsigned long) alen); return -1; } attr->bidding = apos; break; #endif /* EAP_AKA_PRIME || EAP_SERVER_AKA_PRIME */ default: if (pos[0] < 128) { wpa_printf(MSG_INFO, "EAP-SIM: Unrecognized " "non-skippable attribute %d", pos[0]); return -1; } wpa_printf(MSG_DEBUG, "EAP-SIM: Unrecognized skippable" " attribute %d ignored", pos[0]); break; } pos += pos[1] * 4; } wpa_printf(MSG_DEBUG, "EAP-SIM: Attributes parsed successfully " "(aka=%d encr=%d)", aka, encr); return 0; } u8 * eap_sim_parse_encr(const u8 *k_encr, const u8 *encr_data, size_t encr_data_len, const u8 *iv, struct eap_sim_attrs *attr, int aka) { u8 *decrypted; if (!iv) { wpa_printf(MSG_INFO, "EAP-SIM: Encrypted data, but no IV"); return NULL; } decrypted = os_malloc(encr_data_len); if (decrypted == NULL) return NULL; os_memcpy(decrypted, encr_data, encr_data_len); if (aes_128_cbc_decrypt(k_encr, iv, decrypted, encr_data_len)) { os_free(decrypted); return NULL; } wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Decrypted AT_ENCR_DATA", decrypted, encr_data_len); if (eap_sim_parse_attr(decrypted, decrypted + encr_data_len, attr, aka, 1)) { wpa_printf(MSG_INFO, "EAP-SIM: (encr) Failed to parse " "decrypted AT_ENCR_DATA"); os_free(decrypted); return NULL; } return decrypted; } #define EAP_SIM_INIT_LEN 128 struct eap_sim_msg { struct wpabuf *buf; size_t mac, iv, encr; /* index from buf */ int type; }; struct eap_sim_msg * eap_sim_msg_init(int code, int id, int type, int subtype) { struct eap_sim_msg *msg; struct eap_hdr *eap; u8 *pos; msg = os_zalloc(sizeof(*msg)); if (msg == NULL) return NULL; msg->type = type; msg->buf = wpabuf_alloc(EAP_SIM_INIT_LEN); if (msg->buf == NULL) { os_free(msg); return NULL; } eap = wpabuf_put(msg->buf, sizeof(*eap)); eap->code = code; eap->identifier = id; pos = wpabuf_put(msg->buf, 4); *pos++ = type; *pos++ = subtype; *pos++ = 0; /* Reserved */ *pos++ = 0; /* Reserved */ return msg; } struct wpabuf * eap_sim_msg_finish(struct eap_sim_msg *msg, const u8 *k_aut, const u8 *extra, size_t extra_len) { struct eap_hdr *eap; struct wpabuf *buf; if (msg == NULL) return NULL; eap = wpabuf_mhead(msg->buf); eap->length = host_to_be16(wpabuf_len(msg->buf)); #if defined(EAP_AKA_PRIME) || defined(EAP_SERVER_AKA_PRIME) if (k_aut && msg->mac && msg->type == EAP_TYPE_AKA_PRIME) { eap_sim_add_mac_sha256(k_aut, (u8 *) wpabuf_head(msg->buf), wpabuf_len(msg->buf), (u8 *) wpabuf_mhead(msg->buf) + msg->mac, extra, extra_len); } else #endif /* EAP_AKA_PRIME || EAP_SERVER_AKA_PRIME */ if (k_aut && msg->mac) { eap_sim_add_mac(k_aut, (u8 *) wpabuf_head(msg->buf), wpabuf_len(msg->buf), (u8 *) wpabuf_mhead(msg->buf) + msg->mac, extra, extra_len); } buf = msg->buf; os_free(msg); return buf; } void eap_sim_msg_free(struct eap_sim_msg *msg) { if (msg) { wpabuf_free(msg->buf); os_free(msg); } } u8 * eap_sim_msg_add_full(struct eap_sim_msg *msg, u8 attr, const u8 *data, size_t len) { int attr_len = 2 + len; int pad_len; u8 *start; if (msg == NULL) return NULL; pad_len = (4 - attr_len % 4) % 4; attr_len += pad_len; if (wpabuf_resize(&msg->buf, attr_len)) return NULL; start = wpabuf_put(msg->buf, 0); wpabuf_put_u8(msg->buf, attr); wpabuf_put_u8(msg->buf, attr_len / 4); wpabuf_put_data(msg->buf, data, len); if (pad_len) os_memset(wpabuf_put(msg->buf, pad_len), 0, pad_len); return start; } u8 * eap_sim_msg_add(struct eap_sim_msg *msg, u8 attr, u16 value, const u8 *data, size_t len) { int attr_len = 4 + len; int pad_len; u8 *start; if (msg == NULL) return NULL; pad_len = (4 - attr_len % 4) % 4; attr_len += pad_len; if (wpabuf_resize(&msg->buf, attr_len)) return NULL; start = wpabuf_put(msg->buf, 0); wpabuf_put_u8(msg->buf, attr); wpabuf_put_u8(msg->buf, attr_len / 4); wpabuf_put_be16(msg->buf, value); if (data) wpabuf_put_data(msg->buf, data, len); else wpabuf_put(msg->buf, len); if (pad_len) os_memset(wpabuf_put(msg->buf, pad_len), 0, pad_len); return start; } u8 * eap_sim_msg_add_mac(struct eap_sim_msg *msg, u8 attr) { u8 *pos = eap_sim_msg_add(msg, attr, 0, NULL, EAP_SIM_MAC_LEN); if (pos) msg->mac = (pos - wpabuf_head_u8(msg->buf)) + 4; return pos; } int eap_sim_msg_add_encr_start(struct eap_sim_msg *msg, u8 attr_iv, u8 attr_encr) { u8 *pos = eap_sim_msg_add(msg, attr_iv, 0, NULL, EAP_SIM_IV_LEN); if (pos == NULL) return -1; msg->iv = (pos - wpabuf_head_u8(msg->buf)) + 4; if (random_get_bytes(wpabuf_mhead_u8(msg->buf) + msg->iv, EAP_SIM_IV_LEN)) { msg->iv = 0; return -1; } pos = eap_sim_msg_add(msg, attr_encr, 0, NULL, 0); if (pos == NULL) { msg->iv = 0; return -1; } msg->encr = pos - wpabuf_head_u8(msg->buf); return 0; } int eap_sim_msg_add_encr_end(struct eap_sim_msg *msg, u8 *k_encr, int attr_pad) { size_t encr_len; if (msg == NULL || k_encr == NULL || msg->iv == 0 || msg->encr == 0) return -1; encr_len = wpabuf_len(msg->buf) - msg->encr - 4; if (encr_len % 16) { u8 *pos; int pad_len = 16 - (encr_len % 16); if (pad_len < 4) { wpa_printf(MSG_WARNING, "EAP-SIM: " "eap_sim_msg_add_encr_end - invalid pad_len" " %d", pad_len); return -1; } wpa_printf(MSG_DEBUG, " *AT_PADDING"); pos = eap_sim_msg_add(msg, attr_pad, 0, NULL, pad_len - 4); if (pos == NULL) return -1; os_memset(pos + 4, 0, pad_len - 4); encr_len += pad_len; } wpa_printf(MSG_DEBUG, " (AT_ENCR_DATA data len %lu)", (unsigned long) encr_len); wpabuf_mhead_u8(msg->buf)[msg->encr + 1] = encr_len / 4 + 1; return aes_128_cbc_encrypt(k_encr, wpabuf_head_u8(msg->buf) + msg->iv, wpabuf_mhead_u8(msg->buf) + msg->encr + 4, encr_len); } void eap_sim_report_notification(void *msg_ctx, int notification, int aka) { #ifndef CONFIG_NO_STDOUT_DEBUG const char *type = aka ? "AKA" : "SIM"; #endif /* CONFIG_NO_STDOUT_DEBUG */ switch (notification) { case EAP_SIM_GENERAL_FAILURE_AFTER_AUTH: wpa_printf(MSG_WARNING, "EAP-%s: General failure " "notification (after authentication)", type); break; case EAP_SIM_TEMPORARILY_DENIED: wpa_printf(MSG_WARNING, "EAP-%s: Failure notification: " "User has been temporarily denied access to the " "requested service", type); break; case EAP_SIM_NOT_SUBSCRIBED: wpa_printf(MSG_WARNING, "EAP-%s: Failure notification: " "User has not subscribed to the requested service", type); break; case EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH: wpa_printf(MSG_WARNING, "EAP-%s: General failure " "notification (before authentication)", type); break; case EAP_SIM_SUCCESS: wpa_printf(MSG_INFO, "EAP-%s: Successful authentication " "notification", type); break; default: if (notification >= 32768) { wpa_printf(MSG_INFO, "EAP-%s: Unrecognized " "non-failure notification %d", type, notification); } else { wpa_printf(MSG_WARNING, "EAP-%s: Unrecognized " "failure notification %d", type, notification); } } } wpa-2.1/src/eap_common/eap_sim_common.h000066400000000000000000000170551227414666700202340ustar00rootroot00000000000000/* * EAP peer/server: EAP-SIM/AKA/AKA' shared routines * Copyright (c) 2004-2008, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef EAP_SIM_COMMON_H #define EAP_SIM_COMMON_H #define EAP_SIM_NONCE_S_LEN 16 #define EAP_SIM_NONCE_MT_LEN 16 #define EAP_SIM_MAC_LEN 16 #define EAP_SIM_MK_LEN 20 #define EAP_SIM_K_AUT_LEN 16 #define EAP_SIM_K_ENCR_LEN 16 #define EAP_SIM_KEYING_DATA_LEN 64 #define EAP_SIM_IV_LEN 16 #define EAP_SIM_KC_LEN 8 #define EAP_SIM_SRES_LEN 4 #define GSM_RAND_LEN 16 #define EAP_SIM_VERSION 1 /* EAP-SIM Subtypes */ #define EAP_SIM_SUBTYPE_START 10 #define EAP_SIM_SUBTYPE_CHALLENGE 11 #define EAP_SIM_SUBTYPE_NOTIFICATION 12 #define EAP_SIM_SUBTYPE_REAUTHENTICATION 13 #define EAP_SIM_SUBTYPE_CLIENT_ERROR 14 /* AT_CLIENT_ERROR_CODE error codes */ #define EAP_SIM_UNABLE_TO_PROCESS_PACKET 0 #define EAP_SIM_UNSUPPORTED_VERSION 1 #define EAP_SIM_INSUFFICIENT_NUM_OF_CHAL 2 #define EAP_SIM_RAND_NOT_FRESH 3 #define EAP_SIM_MAX_FAST_REAUTHS 1000 #define EAP_SIM_MAX_CHAL 3 /* EAP-AKA Subtypes */ #define EAP_AKA_SUBTYPE_CHALLENGE 1 #define EAP_AKA_SUBTYPE_AUTHENTICATION_REJECT 2 #define EAP_AKA_SUBTYPE_SYNCHRONIZATION_FAILURE 4 #define EAP_AKA_SUBTYPE_IDENTITY 5 #define EAP_AKA_SUBTYPE_NOTIFICATION 12 #define EAP_AKA_SUBTYPE_REAUTHENTICATION 13 #define EAP_AKA_SUBTYPE_CLIENT_ERROR 14 /* AT_CLIENT_ERROR_CODE error codes */ #define EAP_AKA_UNABLE_TO_PROCESS_PACKET 0 #define EAP_AKA_RAND_LEN 16 #define EAP_AKA_AUTN_LEN 16 #define EAP_AKA_AUTS_LEN 14 #define EAP_AKA_RES_MAX_LEN 16 #define EAP_AKA_IK_LEN 16 #define EAP_AKA_CK_LEN 16 #define EAP_AKA_MAX_FAST_REAUTHS 1000 #define EAP_AKA_MIN_RES_LEN 4 #define EAP_AKA_MAX_RES_LEN 16 #define EAP_AKA_CHECKCODE_LEN 20 #define EAP_AKA_PRIME_K_AUT_LEN 32 #define EAP_AKA_PRIME_CHECKCODE_LEN 32 #define EAP_AKA_PRIME_K_RE_LEN 32 struct wpabuf; void eap_sim_derive_mk(const u8 *identity, size_t identity_len, const u8 *nonce_mt, u16 selected_version, const u8 *ver_list, size_t ver_list_len, int num_chal, const u8 *kc, u8 *mk); void eap_aka_derive_mk(const u8 *identity, size_t identity_len, const u8 *ik, const u8 *ck, u8 *mk); int eap_sim_derive_keys(const u8 *mk, u8 *k_encr, u8 *k_aut, u8 *msk, u8 *emsk); int eap_sim_derive_keys_reauth(u16 _counter, const u8 *identity, size_t identity_len, const u8 *nonce_s, const u8 *mk, u8 *msk, u8 *emsk); int eap_sim_verify_mac(const u8 *k_aut, const struct wpabuf *req, const u8 *mac, const u8 *extra, size_t extra_len); void eap_sim_add_mac(const u8 *k_aut, const u8 *msg, size_t msg_len, u8 *mac, const u8 *extra, size_t extra_len); #if defined(EAP_AKA_PRIME) || defined(EAP_SERVER_AKA_PRIME) void eap_aka_prime_derive_keys(const u8 *identity, size_t identity_len, const u8 *ik, const u8 *ck, u8 *k_encr, u8 *k_aut, u8 *k_re, u8 *msk, u8 *emsk); int eap_aka_prime_derive_keys_reauth(const u8 *k_re, u16 counter, const u8 *identity, size_t identity_len, const u8 *nonce_s, u8 *msk, u8 *emsk); int eap_sim_verify_mac_sha256(const u8 *k_aut, const struct wpabuf *req, const u8 *mac, const u8 *extra, size_t extra_len); void eap_sim_add_mac_sha256(const u8 *k_aut, const u8 *msg, size_t msg_len, u8 *mac, const u8 *extra, size_t extra_len); void eap_aka_prime_derive_ck_ik_prime(u8 *ck, u8 *ik, const u8 *sqn_ak, const u8 *network_name, size_t network_name_len); #else /* EAP_AKA_PRIME || EAP_SERVER_AKA_PRIME */ static inline void eap_aka_prime_derive_keys(const u8 *identity, size_t identity_len, const u8 *ik, const u8 *ck, u8 *k_encr, u8 *k_aut, u8 *k_re, u8 *msk, u8 *emsk) { } static inline int eap_aka_prime_derive_keys_reauth(const u8 *k_re, u16 counter, const u8 *identity, size_t identity_len, const u8 *nonce_s, u8 *msk, u8 *emsk) { return -1; } static inline int eap_sim_verify_mac_sha256(const u8 *k_aut, const struct wpabuf *req, const u8 *mac, const u8 *extra, size_t extra_len) { return -1; } #endif /* EAP_AKA_PRIME || EAP_SERVER_AKA_PRIME */ /* EAP-SIM/AKA Attributes (0..127 non-skippable) */ #define EAP_SIM_AT_RAND 1 #define EAP_SIM_AT_AUTN 2 /* only AKA */ #define EAP_SIM_AT_RES 3 /* only AKA, only peer->server */ #define EAP_SIM_AT_AUTS 4 /* only AKA, only peer->server */ #define EAP_SIM_AT_PADDING 6 /* only encrypted */ #define EAP_SIM_AT_NONCE_MT 7 /* only SIM, only send */ #define EAP_SIM_AT_PERMANENT_ID_REQ 10 #define EAP_SIM_AT_MAC 11 #define EAP_SIM_AT_NOTIFICATION 12 #define EAP_SIM_AT_ANY_ID_REQ 13 #define EAP_SIM_AT_IDENTITY 14 /* only send */ #define EAP_SIM_AT_VERSION_LIST 15 /* only SIM */ #define EAP_SIM_AT_SELECTED_VERSION 16 /* only SIM */ #define EAP_SIM_AT_FULLAUTH_ID_REQ 17 #define EAP_SIM_AT_COUNTER 19 /* only encrypted */ #define EAP_SIM_AT_COUNTER_TOO_SMALL 20 /* only encrypted */ #define EAP_SIM_AT_NONCE_S 21 /* only encrypted */ #define EAP_SIM_AT_CLIENT_ERROR_CODE 22 /* only send */ #define EAP_SIM_AT_KDF_INPUT 23 /* only AKA' */ #define EAP_SIM_AT_KDF 24 /* only AKA' */ #define EAP_SIM_AT_IV 129 #define EAP_SIM_AT_ENCR_DATA 130 #define EAP_SIM_AT_NEXT_PSEUDONYM 132 /* only encrypted */ #define EAP_SIM_AT_NEXT_REAUTH_ID 133 /* only encrypted */ #define EAP_SIM_AT_CHECKCODE 134 /* only AKA */ #define EAP_SIM_AT_RESULT_IND 135 #define EAP_SIM_AT_BIDDING 136 /* AT_NOTIFICATION notification code values */ #define EAP_SIM_GENERAL_FAILURE_AFTER_AUTH 0 #define EAP_SIM_TEMPORARILY_DENIED 1026 #define EAP_SIM_NOT_SUBSCRIBED 1031 #define EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH 16384 #define EAP_SIM_SUCCESS 32768 /* EAP-AKA' AT_KDF Key Derivation Function values */ #define EAP_AKA_PRIME_KDF 1 /* AT_BIDDING flags */ #define EAP_AKA_BIDDING_FLAG_D 0x8000 enum eap_sim_id_req { NO_ID_REQ, ANY_ID, FULLAUTH_ID, PERMANENT_ID }; struct eap_sim_attrs { const u8 *rand, *autn, *mac, *iv, *encr_data, *version_list, *nonce_s; const u8 *next_pseudonym, *next_reauth_id; const u8 *nonce_mt, *identity, *res, *auts; const u8 *checkcode; const u8 *kdf_input; const u8 *bidding; size_t num_chal, version_list_len, encr_data_len; size_t next_pseudonym_len, next_reauth_id_len, identity_len, res_len; size_t res_len_bits; size_t checkcode_len; size_t kdf_input_len; enum eap_sim_id_req id_req; int notification, counter, selected_version, client_error_code; int counter_too_small; int result_ind; #define EAP_AKA_PRIME_KDF_MAX 10 u16 kdf[EAP_AKA_PRIME_KDF_MAX]; size_t kdf_count; }; int eap_sim_parse_attr(const u8 *start, const u8 *end, struct eap_sim_attrs *attr, int aka, int encr); u8 * eap_sim_parse_encr(const u8 *k_encr, const u8 *encr_data, size_t encr_data_len, const u8 *iv, struct eap_sim_attrs *attr, int aka); struct eap_sim_msg; struct eap_sim_msg * eap_sim_msg_init(int code, int id, int type, int subtype); struct wpabuf * eap_sim_msg_finish(struct eap_sim_msg *msg, const u8 *k_aut, const u8 *extra, size_t extra_len); void eap_sim_msg_free(struct eap_sim_msg *msg); u8 * eap_sim_msg_add_full(struct eap_sim_msg *msg, u8 attr, const u8 *data, size_t len); u8 * eap_sim_msg_add(struct eap_sim_msg *msg, u8 attr, u16 value, const u8 *data, size_t len); u8 * eap_sim_msg_add_mac(struct eap_sim_msg *msg, u8 attr); int eap_sim_msg_add_encr_start(struct eap_sim_msg *msg, u8 attr_iv, u8 attr_encr); int eap_sim_msg_add_encr_end(struct eap_sim_msg *msg, u8 *k_encr, int attr_pad); void eap_sim_report_notification(void *msg_ctx, int notification, int aka); #endif /* EAP_SIM_COMMON_H */ wpa-2.1/src/eap_common/eap_tlv_common.h000066400000000000000000000051121227414666700202400ustar00rootroot00000000000000/* * EAP-TLV definitions (draft-josefsson-pppext-eap-tls-eap-10.txt) * Copyright (c) 2004-2008, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef EAP_TLV_COMMON_H #define EAP_TLV_COMMON_H /* EAP-TLV TLVs (draft-josefsson-ppext-eap-tls-eap-10.txt) */ #define EAP_TLV_RESULT_TLV 3 /* Acknowledged Result */ #define EAP_TLV_NAK_TLV 4 #define EAP_TLV_ERROR_CODE_TLV 5 #define EAP_TLV_CONNECTION_BINDING_TLV 6 #define EAP_TLV_VENDOR_SPECIFIC_TLV 7 #define EAP_TLV_URI_TLV 8 #define EAP_TLV_EAP_PAYLOAD_TLV 9 #define EAP_TLV_INTERMEDIATE_RESULT_TLV 10 #define EAP_TLV_PAC_TLV 11 /* RFC 5422, Section 4.2 */ #define EAP_TLV_CRYPTO_BINDING_TLV 12 #define EAP_TLV_CALLING_STATION_ID_TLV 13 #define EAP_TLV_CALLED_STATION_ID_TLV 14 #define EAP_TLV_NAS_PORT_TYPE_TLV 15 #define EAP_TLV_SERVER_IDENTIFIER_TLV 16 #define EAP_TLV_IDENTITY_TYPE_TLV 17 #define EAP_TLV_SERVER_TRUSTED_ROOT_TLV 18 #define EAP_TLV_REQUEST_ACTION_TLV 19 #define EAP_TLV_PKCS7_TLV 20 #define EAP_TLV_RESULT_SUCCESS 1 #define EAP_TLV_RESULT_FAILURE 2 #define EAP_TLV_TYPE_MANDATORY 0x8000 #define EAP_TLV_TYPE_MASK 0x3fff #ifdef _MSC_VER #pragma pack(push, 1) #endif /* _MSC_VER */ struct eap_tlv_hdr { be16 tlv_type; be16 length; } STRUCT_PACKED; struct eap_tlv_nak_tlv { be16 tlv_type; be16 length; be32 vendor_id; be16 nak_type; } STRUCT_PACKED; struct eap_tlv_result_tlv { be16 tlv_type; be16 length; be16 status; } STRUCT_PACKED; /* RFC 4851, Section 4.2.7 - Intermediate-Result TLV */ struct eap_tlv_intermediate_result_tlv { be16 tlv_type; be16 length; be16 status; /* Followed by optional TLVs */ } STRUCT_PACKED; /* RFC 4851, Section 4.2.8 - Crypto-Binding TLV */ struct eap_tlv_crypto_binding_tlv { be16 tlv_type; be16 length; u8 reserved; u8 version; u8 received_version; u8 subtype; u8 nonce[32]; u8 compound_mac[20]; } STRUCT_PACKED; struct eap_tlv_pac_ack_tlv { be16 tlv_type; be16 length; be16 pac_type; be16 pac_len; be16 result; } STRUCT_PACKED; /* RFC 4851, Section 4.2.9 - Request-Action TLV */ struct eap_tlv_request_action_tlv { be16 tlv_type; be16 length; be16 action; } STRUCT_PACKED; /* RFC 5422, Section 4.2.6 - PAC-Type TLV */ struct eap_tlv_pac_type_tlv { be16 tlv_type; /* PAC_TYPE_PAC_TYPE */ be16 length; be16 pac_type; } STRUCT_PACKED; #ifdef _MSC_VER #pragma pack(pop) #endif /* _MSC_VER */ #define EAP_TLV_CRYPTO_BINDING_SUBTYPE_REQUEST 0 #define EAP_TLV_CRYPTO_BINDING_SUBTYPE_RESPONSE 1 #define EAP_TLV_ACTION_PROCESS_TLV 1 #define EAP_TLV_ACTION_NEGOTIATE_EAP 2 #endif /* EAP_TLV_COMMON_H */ wpa-2.1/src/eap_common/eap_ttls.h000066400000000000000000000031271227414666700170550ustar00rootroot00000000000000/* * EAP server/peer: EAP-TTLS (RFC 5281) * Copyright (c) 2004-2007, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef EAP_TTLS_H #define EAP_TTLS_H struct ttls_avp { be32 avp_code; be32 avp_length; /* 8-bit flags, 24-bit length; * length includes AVP header */ /* optional 32-bit Vendor-ID */ /* Data */ }; struct ttls_avp_vendor { be32 avp_code; be32 avp_length; /* 8-bit flags, 24-bit length; * length includes AVP header */ be32 vendor_id; /* Data */ }; #define AVP_FLAGS_VENDOR 0x80 #define AVP_FLAGS_MANDATORY 0x40 #define AVP_PAD(start, pos) \ do { \ int __pad; \ __pad = (4 - (((pos) - (start)) & 3)) & 3; \ os_memset((pos), 0, __pad); \ pos += __pad; \ } while (0) /* RFC 2865 */ #define RADIUS_ATTR_USER_NAME 1 #define RADIUS_ATTR_USER_PASSWORD 2 #define RADIUS_ATTR_CHAP_PASSWORD 3 #define RADIUS_ATTR_REPLY_MESSAGE 18 #define RADIUS_ATTR_CHAP_CHALLENGE 60 #define RADIUS_ATTR_EAP_MESSAGE 79 /* RFC 2548 */ #define RADIUS_VENDOR_ID_MICROSOFT 311 #define RADIUS_ATTR_MS_CHAP_RESPONSE 1 #define RADIUS_ATTR_MS_CHAP_ERROR 2 #define RADIUS_ATTR_MS_CHAP_NT_ENC_PW 6 #define RADIUS_ATTR_MS_CHAP_CHALLENGE 11 #define RADIUS_ATTR_MS_CHAP2_RESPONSE 25 #define RADIUS_ATTR_MS_CHAP2_SUCCESS 26 #define RADIUS_ATTR_MS_CHAP2_CPW 27 #define EAP_TTLS_MSCHAPV2_CHALLENGE_LEN 16 #define EAP_TTLS_MSCHAPV2_RESPONSE_LEN 50 #define EAP_TTLS_MSCHAP_CHALLENGE_LEN 8 #define EAP_TTLS_MSCHAP_RESPONSE_LEN 50 #define EAP_TTLS_CHAP_CHALLENGE_LEN 16 #define EAP_TTLS_CHAP_PASSWORD_LEN 16 #endif /* EAP_TTLS_H */ wpa-2.1/src/eap_common/eap_wsc_common.c000066400000000000000000000014201227414666700202200ustar00rootroot00000000000000/* * EAP-WSC common routines for Wi-Fi Protected Setup * Copyright (c) 2007, 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_defs.h" #include "eap_common.h" #include "wps/wps.h" #include "eap_wsc_common.h" struct wpabuf * eap_wsc_build_frag_ack(u8 id, u8 code) { struct wpabuf *msg; msg = eap_msg_alloc(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, 2, code, id); if (msg == NULL) { wpa_printf(MSG_ERROR, "EAP-WSC: Failed to allocate memory for " "FRAG_ACK"); return NULL; } wpa_printf(MSG_DEBUG, "EAP-WSC: Send WSC/FRAG_ACK"); wpabuf_put_u8(msg, WSC_FRAG_ACK); /* Op-Code */ wpabuf_put_u8(msg, 0); /* Flags */ return msg; } wpa-2.1/src/eap_common/eap_wsc_common.h000066400000000000000000000012041227414666700202250ustar00rootroot00000000000000/* * EAP-WSC definitions for Wi-Fi Protected Setup * Copyright (c) 2007, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef EAP_WSC_COMMON_H #define EAP_WSC_COMMON_H #define EAP_VENDOR_TYPE_WSC 1 #define WSC_FLAGS_MF 0x01 #define WSC_FLAGS_LF 0x02 #define WSC_ID_REGISTRAR "WFA-SimpleConfig-Registrar-1-0" #define WSC_ID_REGISTRAR_LEN 30 #define WSC_ID_ENROLLEE "WFA-SimpleConfig-Enrollee-1-0" #define WSC_ID_ENROLLEE_LEN 29 #define WSC_FRAGMENT_SIZE 1400 struct wpabuf * eap_wsc_build_frag_ack(u8 id, u8 code); #endif /* EAP_WSC_COMMON_H */ wpa-2.1/src/eap_common/ikev2_common.c000066400000000000000000000450451227414666700176320ustar00rootroot00000000000000/* * IKEv2 common routines for initiator and responder * Copyright (c) 2007, 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/crypto.h" #include "crypto/md5.h" #include "crypto/sha1.h" #include "crypto/random.h" #include "ikev2_common.h" static struct ikev2_integ_alg ikev2_integ_algs[] = { { AUTH_HMAC_SHA1_96, 20, 12 }, { AUTH_HMAC_MD5_96, 16, 12 } }; #define NUM_INTEG_ALGS ARRAY_SIZE(ikev2_integ_algs) static struct ikev2_prf_alg ikev2_prf_algs[] = { { PRF_HMAC_SHA1, 20, 20 }, { PRF_HMAC_MD5, 16, 16 } }; #define NUM_PRF_ALGS ARRAY_SIZE(ikev2_prf_algs) static struct ikev2_encr_alg ikev2_encr_algs[] = { { ENCR_AES_CBC, 16, 16 }, /* only 128-bit keys supported for now */ { ENCR_3DES, 24, 8 } }; #define NUM_ENCR_ALGS ARRAY_SIZE(ikev2_encr_algs) const struct ikev2_integ_alg * ikev2_get_integ(int id) { size_t i; for (i = 0; i < NUM_INTEG_ALGS; i++) { if (ikev2_integ_algs[i].id == id) return &ikev2_integ_algs[i]; } return NULL; } int ikev2_integ_hash(int alg, const u8 *key, size_t key_len, const u8 *data, size_t data_len, u8 *hash) { u8 tmphash[IKEV2_MAX_HASH_LEN]; switch (alg) { case AUTH_HMAC_SHA1_96: if (key_len != 20) return -1; hmac_sha1(key, key_len, data, data_len, tmphash); os_memcpy(hash, tmphash, 12); break; case AUTH_HMAC_MD5_96: if (key_len != 16) return -1; hmac_md5(key, key_len, data, data_len, tmphash); os_memcpy(hash, tmphash, 12); break; default: return -1; } return 0; } const struct ikev2_prf_alg * ikev2_get_prf(int id) { size_t i; for (i = 0; i < NUM_PRF_ALGS; i++) { if (ikev2_prf_algs[i].id == id) return &ikev2_prf_algs[i]; } return NULL; } int ikev2_prf_hash(int alg, const u8 *key, size_t key_len, size_t num_elem, const u8 *addr[], const size_t *len, u8 *hash) { switch (alg) { case PRF_HMAC_SHA1: hmac_sha1_vector(key, key_len, num_elem, addr, len, hash); break; case PRF_HMAC_MD5: hmac_md5_vector(key, key_len, num_elem, addr, len, hash); break; default: return -1; } return 0; } int ikev2_prf_plus(int alg, const u8 *key, size_t key_len, const u8 *data, size_t data_len, u8 *out, size_t out_len) { u8 hash[IKEV2_MAX_HASH_LEN]; size_t hash_len; u8 iter, *pos, *end; const u8 *addr[3]; size_t len[3]; const struct ikev2_prf_alg *prf; int res; prf = ikev2_get_prf(alg); if (prf == NULL) return -1; hash_len = prf->hash_len; addr[0] = hash; len[0] = hash_len; addr[1] = data; len[1] = data_len; addr[2] = &iter; len[2] = 1; pos = out; end = out + out_len; iter = 1; while (pos < end) { size_t clen; if (iter == 1) res = ikev2_prf_hash(alg, key, key_len, 2, &addr[1], &len[1], hash); else res = ikev2_prf_hash(alg, key, key_len, 3, addr, len, hash); if (res < 0) return -1; clen = hash_len; if ((int) clen > end - pos) clen = end - pos; os_memcpy(pos, hash, clen); pos += clen; iter++; } return 0; } const struct ikev2_encr_alg * ikev2_get_encr(int id) { size_t i; for (i = 0; i < NUM_ENCR_ALGS; i++) { if (ikev2_encr_algs[i].id == id) return &ikev2_encr_algs[i]; } return NULL; } #ifdef CCNS_PL /* from des.c */ struct des3_key_s { u32 ek[3][32]; u32 dk[3][32]; }; void des3_key_setup(const u8 *key, struct des3_key_s *dkey); void des3_encrypt(const u8 *plain, const struct des3_key_s *key, u8 *crypt); void des3_decrypt(const u8 *crypt, const struct des3_key_s *key, u8 *plain); #endif /* CCNS_PL */ int ikev2_encr_encrypt(int alg, const u8 *key, size_t key_len, const u8 *iv, const u8 *plain, u8 *crypt, size_t len) { struct crypto_cipher *cipher; int encr_alg; #ifdef CCNS_PL if (alg == ENCR_3DES) { struct des3_key_s des3key; size_t i, blocks; u8 *pos; /* ECB mode is used incorrectly for 3DES!? */ if (key_len != 24) { wpa_printf(MSG_INFO, "IKEV2: Invalid encr key length"); return -1; } des3_key_setup(key, &des3key); blocks = len / 8; pos = crypt; for (i = 0; i < blocks; i++) { des3_encrypt(pos, &des3key, pos); pos += 8; } } else { #endif /* CCNS_PL */ switch (alg) { case ENCR_3DES: encr_alg = CRYPTO_CIPHER_ALG_3DES; break; case ENCR_AES_CBC: encr_alg = CRYPTO_CIPHER_ALG_AES; break; default: wpa_printf(MSG_DEBUG, "IKEV2: Unsupported encr alg %d", alg); return -1; } cipher = crypto_cipher_init(encr_alg, iv, key, key_len); if (cipher == NULL) { wpa_printf(MSG_INFO, "IKEV2: Failed to initialize cipher"); return -1; } if (crypto_cipher_encrypt(cipher, plain, crypt, len) < 0) { wpa_printf(MSG_INFO, "IKEV2: Encryption failed"); crypto_cipher_deinit(cipher); return -1; } crypto_cipher_deinit(cipher); #ifdef CCNS_PL } #endif /* CCNS_PL */ return 0; } int ikev2_encr_decrypt(int alg, const u8 *key, size_t key_len, const u8 *iv, const u8 *crypt, u8 *plain, size_t len) { struct crypto_cipher *cipher; int encr_alg; #ifdef CCNS_PL if (alg == ENCR_3DES) { struct des3_key_s des3key; size_t i, blocks; /* ECB mode is used incorrectly for 3DES!? */ if (key_len != 24) { wpa_printf(MSG_INFO, "IKEV2: Invalid encr key length"); return -1; } des3_key_setup(key, &des3key); if (len % 8) { wpa_printf(MSG_INFO, "IKEV2: Invalid encrypted " "length"); return -1; } blocks = len / 8; for (i = 0; i < blocks; i++) { des3_decrypt(crypt, &des3key, plain); plain += 8; crypt += 8; } } else { #endif /* CCNS_PL */ switch (alg) { case ENCR_3DES: encr_alg = CRYPTO_CIPHER_ALG_3DES; break; case ENCR_AES_CBC: encr_alg = CRYPTO_CIPHER_ALG_AES; break; default: wpa_printf(MSG_DEBUG, "IKEV2: Unsupported encr alg %d", alg); return -1; } cipher = crypto_cipher_init(encr_alg, iv, key, key_len); if (cipher == NULL) { wpa_printf(MSG_INFO, "IKEV2: Failed to initialize cipher"); return -1; } if (crypto_cipher_decrypt(cipher, crypt, plain, len) < 0) { wpa_printf(MSG_INFO, "IKEV2: Decryption failed"); crypto_cipher_deinit(cipher); return -1; } crypto_cipher_deinit(cipher); #ifdef CCNS_PL } #endif /* CCNS_PL */ return 0; } int ikev2_parse_payloads(struct ikev2_payloads *payloads, u8 next_payload, const u8 *pos, const u8 *end) { const struct ikev2_payload_hdr *phdr; os_memset(payloads, 0, sizeof(*payloads)); while (next_payload != IKEV2_PAYLOAD_NO_NEXT_PAYLOAD) { int plen, pdatalen; const u8 *pdata; wpa_printf(MSG_DEBUG, "IKEV2: Processing payload %u", next_payload); if (end - pos < (int) sizeof(*phdr)) { wpa_printf(MSG_INFO, "IKEV2: Too short message for " "payload header (left=%ld)", (long) (end - pos)); } phdr = (const struct ikev2_payload_hdr *) pos; plen = WPA_GET_BE16(phdr->payload_length); if (plen < (int) sizeof(*phdr) || pos + plen > end) { wpa_printf(MSG_INFO, "IKEV2: Invalid payload header " "length %d", plen); return -1; } wpa_printf(MSG_DEBUG, "IKEV2: Next Payload: %u Flags: 0x%x" " Payload Length: %d", phdr->next_payload, phdr->flags, plen); pdata = (const u8 *) (phdr + 1); pdatalen = plen - sizeof(*phdr); switch (next_payload) { case IKEV2_PAYLOAD_SA: wpa_printf(MSG_DEBUG, "IKEV2: Payload: Security " "Association"); payloads->sa = pdata; payloads->sa_len = pdatalen; break; case IKEV2_PAYLOAD_KEY_EXCHANGE: wpa_printf(MSG_DEBUG, "IKEV2: Payload: Key " "Exchange"); payloads->ke = pdata; payloads->ke_len = pdatalen; break; case IKEV2_PAYLOAD_IDi: wpa_printf(MSG_DEBUG, "IKEV2: Payload: IDi"); payloads->idi = pdata; payloads->idi_len = pdatalen; break; case IKEV2_PAYLOAD_IDr: wpa_printf(MSG_DEBUG, "IKEV2: Payload: IDr"); payloads->idr = pdata; payloads->idr_len = pdatalen; break; case IKEV2_PAYLOAD_CERTIFICATE: wpa_printf(MSG_DEBUG, "IKEV2: Payload: Certificate"); payloads->cert = pdata; payloads->cert_len = pdatalen; break; case IKEV2_PAYLOAD_AUTHENTICATION: wpa_printf(MSG_DEBUG, "IKEV2: Payload: " "Authentication"); payloads->auth = pdata; payloads->auth_len = pdatalen; break; case IKEV2_PAYLOAD_NONCE: wpa_printf(MSG_DEBUG, "IKEV2: Payload: Nonce"); payloads->nonce = pdata; payloads->nonce_len = pdatalen; break; case IKEV2_PAYLOAD_ENCRYPTED: wpa_printf(MSG_DEBUG, "IKEV2: Payload: Encrypted"); payloads->encrypted = pdata; payloads->encrypted_len = pdatalen; break; case IKEV2_PAYLOAD_NOTIFICATION: wpa_printf(MSG_DEBUG, "IKEV2: Payload: " "Notification"); payloads->notification = pdata; payloads->notification_len = pdatalen; break; default: if (phdr->flags & IKEV2_PAYLOAD_FLAGS_CRITICAL) { wpa_printf(MSG_INFO, "IKEV2: Unsupported " "critical payload %u - reject the " "entire message", next_payload); return -1; } else { wpa_printf(MSG_DEBUG, "IKEV2: Skipped " "unsupported payload %u", next_payload); } } if (next_payload == IKEV2_PAYLOAD_ENCRYPTED && pos + plen == end) { /* * Next Payload in the case of Encrypted Payload is * actually the payload type for the first embedded * payload. */ payloads->encr_next_payload = phdr->next_payload; next_payload = IKEV2_PAYLOAD_NO_NEXT_PAYLOAD; } else next_payload = phdr->next_payload; pos += plen; } if (pos != end) { wpa_printf(MSG_INFO, "IKEV2: Unexpected extra data after " "payloads"); return -1; } return 0; } int ikev2_derive_auth_data(int prf_alg, const struct wpabuf *sign_msg, const u8 *ID, size_t ID_len, u8 ID_type, struct ikev2_keys *keys, int initiator, const u8 *shared_secret, size_t shared_secret_len, const u8 *nonce, size_t nonce_len, const u8 *key_pad, size_t key_pad_len, u8 *auth_data) { size_t sign_len, buf_len; u8 *sign_data, *pos, *buf, hash[IKEV2_MAX_HASH_LEN]; const struct ikev2_prf_alg *prf; const u8 *SK_p = initiator ? keys->SK_pi : keys->SK_pr; prf = ikev2_get_prf(prf_alg); if (sign_msg == NULL || ID == NULL || SK_p == NULL || shared_secret == NULL || nonce == NULL || prf == NULL) return -1; /* prf(SK_pi/r,IDi/r') */ buf_len = 4 + ID_len; buf = os_zalloc(buf_len); if (buf == NULL) return -1; buf[0] = ID_type; os_memcpy(buf + 4, ID, ID_len); if (ikev2_prf_hash(prf->id, SK_p, keys->SK_prf_len, 1, (const u8 **) &buf, &buf_len, hash) < 0) { os_free(buf); return -1; } os_free(buf); /* sign_data = msg | Nr/i | prf(SK_pi/r,IDi/r') */ sign_len = wpabuf_len(sign_msg) + nonce_len + prf->hash_len; sign_data = os_malloc(sign_len); if (sign_data == NULL) return -1; pos = sign_data; os_memcpy(pos, wpabuf_head(sign_msg), wpabuf_len(sign_msg)); pos += wpabuf_len(sign_msg); os_memcpy(pos, nonce, nonce_len); pos += nonce_len; os_memcpy(pos, hash, prf->hash_len); /* AUTH = prf(prf(Shared Secret, key pad, sign_data) */ if (ikev2_prf_hash(prf->id, shared_secret, shared_secret_len, 1, &key_pad, &key_pad_len, hash) < 0 || ikev2_prf_hash(prf->id, hash, prf->hash_len, 1, (const u8 **) &sign_data, &sign_len, auth_data) < 0) { os_free(sign_data); return -1; } os_free(sign_data); return 0; } u8 * ikev2_decrypt_payload(int encr_id, int integ_id, struct ikev2_keys *keys, int initiator, const struct ikev2_hdr *hdr, const u8 *encrypted, size_t encrypted_len, size_t *res_len) { size_t iv_len; const u8 *pos, *end, *iv, *integ; u8 hash[IKEV2_MAX_HASH_LEN], *decrypted; size_t decrypted_len, pad_len; const struct ikev2_integ_alg *integ_alg; const struct ikev2_encr_alg *encr_alg; const u8 *SK_e = initiator ? keys->SK_ei : keys->SK_er; const u8 *SK_a = initiator ? keys->SK_ai : keys->SK_ar; if (encrypted == NULL) { wpa_printf(MSG_INFO, "IKEV2: No Encrypted payload in SA_AUTH"); return NULL; } encr_alg = ikev2_get_encr(encr_id); if (encr_alg == NULL) { wpa_printf(MSG_INFO, "IKEV2: Unsupported encryption type"); return NULL; } iv_len = encr_alg->block_size; integ_alg = ikev2_get_integ(integ_id); if (integ_alg == NULL) { wpa_printf(MSG_INFO, "IKEV2: Unsupported intergrity type"); return NULL; } if (encrypted_len < iv_len + 1 + integ_alg->hash_len) { wpa_printf(MSG_INFO, "IKEV2: No room for IV or Integrity " "Checksum"); return NULL; } iv = encrypted; pos = iv + iv_len; end = encrypted + encrypted_len; integ = end - integ_alg->hash_len; if (SK_a == NULL) { wpa_printf(MSG_INFO, "IKEV2: No SK_a available"); return NULL; } if (ikev2_integ_hash(integ_id, SK_a, keys->SK_integ_len, (const u8 *) hdr, integ - (const u8 *) hdr, hash) < 0) { wpa_printf(MSG_INFO, "IKEV2: Failed to calculate integrity " "hash"); return NULL; } if (os_memcmp(integ, hash, integ_alg->hash_len) != 0) { wpa_printf(MSG_INFO, "IKEV2: Incorrect Integrity Checksum " "Data"); return NULL; } if (SK_e == NULL) { wpa_printf(MSG_INFO, "IKEV2: No SK_e available"); return NULL; } decrypted_len = integ - pos; decrypted = os_malloc(decrypted_len); if (decrypted == NULL) return NULL; if (ikev2_encr_decrypt(encr_alg->id, SK_e, keys->SK_encr_len, iv, pos, decrypted, decrypted_len) < 0) { os_free(decrypted); return NULL; } pad_len = decrypted[decrypted_len - 1]; if (decrypted_len < pad_len + 1) { wpa_printf(MSG_INFO, "IKEV2: Invalid padding in encrypted " "payload"); os_free(decrypted); return NULL; } decrypted_len -= pad_len + 1; *res_len = decrypted_len; return decrypted; } void ikev2_update_hdr(struct wpabuf *msg) { struct ikev2_hdr *hdr; /* Update lenth field in HDR */ hdr = wpabuf_mhead(msg); WPA_PUT_BE32(hdr->length, wpabuf_len(msg)); } int ikev2_build_encrypted(int encr_id, int integ_id, struct ikev2_keys *keys, int initiator, struct wpabuf *msg, struct wpabuf *plain, u8 next_payload) { struct ikev2_payload_hdr *phdr; size_t plen; size_t iv_len, pad_len; u8 *icv, *iv; const struct ikev2_integ_alg *integ_alg; const struct ikev2_encr_alg *encr_alg; const u8 *SK_e = initiator ? keys->SK_ei : keys->SK_er; const u8 *SK_a = initiator ? keys->SK_ai : keys->SK_ar; wpa_printf(MSG_DEBUG, "IKEV2: Adding Encrypted payload"); /* Encr - RFC 4306, Sect. 3.14 */ encr_alg = ikev2_get_encr(encr_id); if (encr_alg == NULL) { wpa_printf(MSG_INFO, "IKEV2: Unsupported encryption type"); return -1; } iv_len = encr_alg->block_size; integ_alg = ikev2_get_integ(integ_id); if (integ_alg == NULL) { wpa_printf(MSG_INFO, "IKEV2: Unsupported intergrity type"); return -1; } if (SK_e == NULL) { wpa_printf(MSG_INFO, "IKEV2: No SK_e available"); return -1; } if (SK_a == NULL) { wpa_printf(MSG_INFO, "IKEV2: No SK_a available"); return -1; } phdr = wpabuf_put(msg, sizeof(*phdr)); phdr->next_payload = next_payload; phdr->flags = 0; iv = wpabuf_put(msg, iv_len); if (random_get_bytes(iv, iv_len)) { wpa_printf(MSG_INFO, "IKEV2: Could not generate IV"); return -1; } pad_len = iv_len - (wpabuf_len(plain) + 1) % iv_len; if (pad_len == iv_len) pad_len = 0; wpabuf_put(plain, pad_len); wpabuf_put_u8(plain, pad_len); if (ikev2_encr_encrypt(encr_alg->id, SK_e, keys->SK_encr_len, iv, wpabuf_head(plain), wpabuf_mhead(plain), wpabuf_len(plain)) < 0) return -1; wpabuf_put_buf(msg, plain); /* Need to update all headers (Length fields) prior to hash func */ icv = wpabuf_put(msg, integ_alg->hash_len); plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr; WPA_PUT_BE16(phdr->payload_length, plen); ikev2_update_hdr(msg); return ikev2_integ_hash(integ_id, SK_a, keys->SK_integ_len, wpabuf_head(msg), wpabuf_len(msg) - integ_alg->hash_len, icv); return 0; } int ikev2_keys_set(struct ikev2_keys *keys) { return keys->SK_d && keys->SK_ai && keys->SK_ar && keys->SK_ei && keys->SK_er && keys->SK_pi && keys->SK_pr; } void ikev2_free_keys(struct ikev2_keys *keys) { os_free(keys->SK_d); os_free(keys->SK_ai); os_free(keys->SK_ar); os_free(keys->SK_ei); os_free(keys->SK_er); os_free(keys->SK_pi); os_free(keys->SK_pr); keys->SK_d = keys->SK_ai = keys->SK_ar = keys->SK_ei = keys->SK_er = keys->SK_pi = keys->SK_pr = NULL; } int ikev2_derive_sk_keys(const struct ikev2_prf_alg *prf, const struct ikev2_integ_alg *integ, const struct ikev2_encr_alg *encr, const u8 *skeyseed, const u8 *data, size_t data_len, struct ikev2_keys *keys) { u8 *keybuf, *pos; size_t keybuf_len; /* * {SK_d | SK_ai | SK_ar | SK_ei | SK_er | SK_pi | SK_pr } = * prf+(SKEYSEED, Ni | Nr | SPIi | SPIr ) */ ikev2_free_keys(keys); keys->SK_d_len = prf->key_len; keys->SK_integ_len = integ->key_len; keys->SK_encr_len = encr->key_len; keys->SK_prf_len = prf->key_len; #ifdef CCNS_PL /* Uses encryption key length for SK_d; should be PRF length */ keys->SK_d_len = keys->SK_encr_len; #endif /* CCNS_PL */ keybuf_len = keys->SK_d_len + 2 * keys->SK_integ_len + 2 * keys->SK_encr_len + 2 * keys->SK_prf_len; keybuf = os_malloc(keybuf_len); if (keybuf == NULL) return -1; if (ikev2_prf_plus(prf->id, skeyseed, prf->hash_len, data, data_len, keybuf, keybuf_len)) { os_free(keybuf); return -1; } pos = keybuf; keys->SK_d = os_malloc(keys->SK_d_len); if (keys->SK_d) { os_memcpy(keys->SK_d, pos, keys->SK_d_len); wpa_hexdump_key(MSG_DEBUG, "IKEV2: SK_d", keys->SK_d, keys->SK_d_len); } pos += keys->SK_d_len; keys->SK_ai = os_malloc(keys->SK_integ_len); if (keys->SK_ai) { os_memcpy(keys->SK_ai, pos, keys->SK_integ_len); wpa_hexdump_key(MSG_DEBUG, "IKEV2: SK_ai", keys->SK_ai, keys->SK_integ_len); } pos += keys->SK_integ_len; keys->SK_ar = os_malloc(keys->SK_integ_len); if (keys->SK_ar) { os_memcpy(keys->SK_ar, pos, keys->SK_integ_len); wpa_hexdump_key(MSG_DEBUG, "IKEV2: SK_ar", keys->SK_ar, keys->SK_integ_len); } pos += keys->SK_integ_len; keys->SK_ei = os_malloc(keys->SK_encr_len); if (keys->SK_ei) { os_memcpy(keys->SK_ei, pos, keys->SK_encr_len); wpa_hexdump_key(MSG_DEBUG, "IKEV2: SK_ei", keys->SK_ei, keys->SK_encr_len); } pos += keys->SK_encr_len; keys->SK_er = os_malloc(keys->SK_encr_len); if (keys->SK_er) { os_memcpy(keys->SK_er, pos, keys->SK_encr_len); wpa_hexdump_key(MSG_DEBUG, "IKEV2: SK_er", keys->SK_er, keys->SK_encr_len); } pos += keys->SK_encr_len; keys->SK_pi = os_malloc(keys->SK_prf_len); if (keys->SK_pi) { os_memcpy(keys->SK_pi, pos, keys->SK_prf_len); wpa_hexdump_key(MSG_DEBUG, "IKEV2: SK_pi", keys->SK_pi, keys->SK_prf_len); } pos += keys->SK_prf_len; keys->SK_pr = os_malloc(keys->SK_prf_len); if (keys->SK_pr) { os_memcpy(keys->SK_pr, pos, keys->SK_prf_len); wpa_hexdump_key(MSG_DEBUG, "IKEV2: SK_pr", keys->SK_pr, keys->SK_prf_len); } os_free(keybuf); if (!ikev2_keys_set(keys)) { ikev2_free_keys(keys); return -1; } return 0; } wpa-2.1/src/eap_common/ikev2_common.h000066400000000000000000000203461227414666700176340ustar00rootroot00000000000000/* * IKEv2 definitions * Copyright (c) 2007, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef IKEV2_COMMON_H #define IKEV2_COMMON_H /* * Nonce length must be at least 16 octets. It must also be at least half the * key size of the negotiated PRF. */ #define IKEV2_NONCE_MIN_LEN 16 #define IKEV2_NONCE_MAX_LEN 256 /* IKE Header - RFC 4306, Sect. 3.1 */ #ifdef _MSC_VER #pragma pack(push, 1) #endif /* _MSC_VER */ #define IKEV2_SPI_LEN 8 struct ikev2_hdr { u8 i_spi[IKEV2_SPI_LEN]; /* IKE_SA Initiator's SPI */ u8 r_spi[IKEV2_SPI_LEN]; /* IKE_SA Responder's SPI */ u8 next_payload; u8 version; /* MjVer | MnVer */ u8 exchange_type; u8 flags; u8 message_id[4]; u8 length[4]; /* total length of HDR + payloads */ } STRUCT_PACKED; struct ikev2_payload_hdr { u8 next_payload; u8 flags; u8 payload_length[2]; /* this payload, including the payload header */ } STRUCT_PACKED; struct ikev2_proposal { u8 type; /* 0 (last) or 2 (more) */ u8 reserved; u8 proposal_length[2]; /* including all transform and attributes */ u8 proposal_num; u8 protocol_id; /* IKEV2_PROTOCOL_* */ u8 spi_size; u8 num_transforms; /* SPI of spi_size octets */ /* Transforms */ } STRUCT_PACKED; struct ikev2_transform { u8 type; /* 0 (last) or 3 (more) */ u8 reserved; u8 transform_length[2]; /* including Header and Attributes */ u8 transform_type; u8 reserved2; u8 transform_id[2]; /* Transform Attributes */ } STRUCT_PACKED; #ifdef _MSC_VER #pragma pack(pop) #endif /* _MSC_VER */ /* Current IKEv2 version from RFC 4306 */ #define IKEV2_MjVer 2 #define IKEV2_MnVer 0 #ifdef CCNS_PL #define IKEV2_VERSION ((IKEV2_MjVer) | ((IKEV2_MnVer) << 4)) #else /* CCNS_PL */ #define IKEV2_VERSION (((IKEV2_MjVer) << 4) | (IKEV2_MnVer)) #endif /* CCNS_PL */ /* IKEv2 Exchange Types */ enum { /* 0-33 RESERVED */ IKE_SA_INIT = 34, IKE_SA_AUTH = 35, CREATE_CHILD_SA = 36, INFORMATION = 37 /* 38-239 RESERVED TO IANA */ /* 240-255 Reserved for private use */ }; /* IKEv2 Flags */ #define IKEV2_HDR_INITIATOR 0x08 #define IKEV2_HDR_VERSION 0x10 #define IKEV2_HDR_RESPONSE 0x20 /* Payload Header Flags */ #define IKEV2_PAYLOAD_FLAGS_CRITICAL 0x01 /* EAP-IKEv2 Payload Types (in Next Payload Type field) * http://www.iana.org/assignments/eap-ikev2-payloads */ enum { IKEV2_PAYLOAD_NO_NEXT_PAYLOAD = 0, IKEV2_PAYLOAD_SA = 33, IKEV2_PAYLOAD_KEY_EXCHANGE = 34, IKEV2_PAYLOAD_IDi = 35, IKEV2_PAYLOAD_IDr = 36, IKEV2_PAYLOAD_CERTIFICATE = 37, IKEV2_PAYLOAD_CERT_REQ = 38, IKEV2_PAYLOAD_AUTHENTICATION = 39, IKEV2_PAYLOAD_NONCE = 40, IKEV2_PAYLOAD_NOTIFICATION = 41, IKEV2_PAYLOAD_VENDOD_ID = 43, IKEV2_PAYLOAD_ENCRYPTED = 46, IKEV2_PAYLOAD_NEXT_FAST_ID = 121 }; /* IKEv2 Proposal - Protocol ID */ enum { IKEV2_PROTOCOL_RESERVED = 0, IKEV2_PROTOCOL_IKE = 1, /* IKE is the only one allowed for EAP-IKEv2 */ IKEV2_PROTOCOL_AH = 2, IKEV2_PROTOCOL_ESP = 3 }; /* IKEv2 Transform Types */ enum { IKEV2_TRANSFORM_ENCR = 1, IKEV2_TRANSFORM_PRF = 2, IKEV2_TRANSFORM_INTEG = 3, IKEV2_TRANSFORM_DH = 4, IKEV2_TRANSFORM_ESN = 5 }; /* IKEv2 Transform Type 1 (Encryption Algorithm) */ enum { ENCR_DES_IV64 = 1, ENCR_DES = 2, ENCR_3DES = 3, ENCR_RC5 = 4, ENCR_IDEA = 5, ENCR_CAST = 6, ENCR_BLOWFISH = 7, ENCR_3IDEA = 8, ENCR_DES_IV32 = 9, ENCR_NULL = 11, ENCR_AES_CBC = 12, ENCR_AES_CTR = 13 }; /* IKEv2 Transform Type 2 (Pseudo-random Function) */ enum { PRF_HMAC_MD5 = 1, PRF_HMAC_SHA1 = 2, PRF_HMAC_TIGER = 3, PRF_AES128_XCBC = 4 }; /* IKEv2 Transform Type 3 (Integrity Algorithm) */ enum { AUTH_HMAC_MD5_96 = 1, AUTH_HMAC_SHA1_96 = 2, AUTH_DES_MAC = 3, AUTH_KPDK_MD5 = 4, AUTH_AES_XCBC_96 = 5 }; /* IKEv2 Transform Type 4 (Diffie-Hellman Group) */ enum { DH_GROUP1_768BIT_MODP = 1, /* RFC 4306 */ DH_GROUP2_1024BIT_MODP = 2, /* RFC 4306 */ DH_GROUP5_1536BIT_MODP = 5, /* RFC 3526 */ DH_GROUP5_2048BIT_MODP = 14, /* RFC 3526 */ DH_GROUP5_3072BIT_MODP = 15, /* RFC 3526 */ DH_GROUP5_4096BIT_MODP = 16, /* RFC 3526 */ DH_GROUP5_6144BIT_MODP = 17, /* RFC 3526 */ DH_GROUP5_8192BIT_MODP = 18 /* RFC 3526 */ }; /* Identification Data Types (RFC 4306, Sect. 3.5) */ enum { ID_IPV4_ADDR = 1, ID_FQDN = 2, ID_RFC822_ADDR = 3, ID_IPV6_ADDR = 5, ID_DER_ASN1_DN = 9, ID_DER_ASN1_GN= 10, ID_KEY_ID = 11 }; /* Certificate Encoding (RFC 4306, Sect. 3.6) */ enum { CERT_ENCODING_PKCS7_X509 = 1, CERT_ENCODING_PGP_CERT = 2, CERT_ENCODING_DNS_SIGNED_KEY = 3, /* X.509 Certificate - Signature: DER encoded X.509 certificate whose * public key is used to validate the sender's AUTH payload */ CERT_ENCODING_X509_CERT_SIGN = 4, CERT_ENCODING_KERBEROS_TOKEN = 6, /* DER encoded X.509 certificate revocation list */ CERT_ENCODING_CRL = 7, CERT_ENCODING_ARL = 8, CERT_ENCODING_SPKI_CERT = 9, CERT_ENCODING_X509_CERT_ATTR = 10, /* PKCS #1 encoded RSA key */ CERT_ENCODING_RAW_RSA_KEY = 11, CERT_ENCODING_HASH_AND_URL_X509_CERT = 12, CERT_ENCODING_HASH_AND_URL_X509_BUNDLE = 13 }; /* Authentication Method (RFC 4306, Sect. 3.8) */ enum { AUTH_RSA_SIGN = 1, AUTH_SHARED_KEY_MIC = 2, AUTH_DSS_SIGN = 3 }; /* Notify Message Types (RFC 4306, Sect. 3.10.1) */ enum { UNSUPPORTED_CRITICAL_PAYLOAD = 1, INVALID_IKE_SPI = 4, INVALID_MAJOR_VERSION = 5, INVALID_SYNTAX = 7, INVALID_MESSAGE_ID = 9, INVALID_SPI = 11, NO_PROPOSAL_CHOSEN = 14, INVALID_KE_PAYLOAD = 17, AUTHENTICATION_FAILED = 24, SINGLE_PAIR_REQUIRED = 34, NO_ADDITIONAL_SAS = 35, INTERNAL_ADDRESS_FAILURE = 36, FAILED_CP_REQUIRED = 37, TS_UNACCEPTABLE = 38, INVALID_SELECTORS = 39 }; struct ikev2_keys { u8 *SK_d, *SK_ai, *SK_ar, *SK_ei, *SK_er, *SK_pi, *SK_pr; size_t SK_d_len, SK_integ_len, SK_encr_len, SK_prf_len; }; int ikev2_keys_set(struct ikev2_keys *keys); void ikev2_free_keys(struct ikev2_keys *keys); /* Maximum hash length for supported hash algorithms */ #define IKEV2_MAX_HASH_LEN 20 struct ikev2_integ_alg { int id; size_t key_len; size_t hash_len; }; struct ikev2_prf_alg { int id; size_t key_len; size_t hash_len; }; struct ikev2_encr_alg { int id; size_t key_len; size_t block_size; }; const struct ikev2_integ_alg * ikev2_get_integ(int id); int ikev2_integ_hash(int alg, const u8 *key, size_t key_len, const u8 *data, size_t data_len, u8 *hash); const struct ikev2_prf_alg * ikev2_get_prf(int id); int ikev2_prf_hash(int alg, const u8 *key, size_t key_len, size_t num_elem, const u8 *addr[], const size_t *len, u8 *hash); int ikev2_prf_plus(int alg, const u8 *key, size_t key_len, const u8 *data, size_t data_len, u8 *out, size_t out_len); const struct ikev2_encr_alg * ikev2_get_encr(int id); int ikev2_encr_encrypt(int alg, const u8 *key, size_t key_len, const u8 *iv, const u8 *plain, u8 *crypt, size_t len); int ikev2_encr_decrypt(int alg, const u8 *key, size_t key_len, const u8 *iv, const u8 *crypt, u8 *plain, size_t len); int ikev2_derive_auth_data(int prf_alg, const struct wpabuf *sign_msg, const u8 *ID, size_t ID_len, u8 ID_type, struct ikev2_keys *keys, int initiator, const u8 *shared_secret, size_t shared_secret_len, const u8 *nonce, size_t nonce_len, const u8 *key_pad, size_t key_pad_len, u8 *auth_data); struct ikev2_payloads { const u8 *sa; size_t sa_len; const u8 *ke; size_t ke_len; const u8 *idi; size_t idi_len; const u8 *idr; size_t idr_len; const u8 *cert; size_t cert_len; const u8 *auth; size_t auth_len; const u8 *nonce; size_t nonce_len; const u8 *encrypted; size_t encrypted_len; u8 encr_next_payload; const u8 *notification; size_t notification_len; }; int ikev2_parse_payloads(struct ikev2_payloads *payloads, u8 next_payload, const u8 *pos, const u8 *end); u8 * ikev2_decrypt_payload(int encr_id, int integ_id, struct ikev2_keys *keys, int initiator, const struct ikev2_hdr *hdr, const u8 *encrypted, size_t encrypted_len, size_t *res_len); void ikev2_update_hdr(struct wpabuf *msg); int ikev2_build_encrypted(int encr_id, int integ_id, struct ikev2_keys *keys, int initiator, struct wpabuf *msg, struct wpabuf *plain, u8 next_payload); int ikev2_derive_sk_keys(const struct ikev2_prf_alg *prf, const struct ikev2_integ_alg *integ, const struct ikev2_encr_alg *encr, const u8 *skeyseed, const u8 *data, size_t data_len, struct ikev2_keys *keys); #endif /* IKEV2_COMMON_H */ wpa-2.1/src/eap_peer/000077500000000000000000000000001227414666700145315ustar00rootroot00000000000000wpa-2.1/src/eap_peer/Makefile000066400000000000000000000003541227414666700161730ustar00rootroot00000000000000all: @echo Nothing to be made. clean: rm -f *~ *.o *.so *.d *.gcno *.gcda *.gcov install: if ls *.so >/dev/null 2>&1; then \ install -d $(DESTDIR)$(LIBDIR)/wpa_supplicant && \ cp *.so $(DESTDIR)$(LIBDIR)/wpa_supplicant \ ; fi wpa-2.1/src/eap_peer/eap.c000066400000000000000000002045441227414666700154530ustar00rootroot00000000000000/* * EAP peer state machines (RFC 4137) * Copyright (c) 2004-2014, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. * * This file implements the Peer State Machine as defined in RFC 4137. The used * states and state transitions match mostly with the RFC. However, there are * couple of additional transitions for working around small issues noticed * during testing. These exceptions are explained in comments within the * functions in this file. The method functions, m.func(), are similar to the * ones used in RFC 4137, but some small changes have used here to optimize * operations and to add functionality needed for fast re-authentication * (session resumption). */ #include "includes.h" #include "common.h" #include "pcsc_funcs.h" #include "state_machine.h" #include "ext_password.h" #include "crypto/crypto.h" #include "crypto/tls.h" #include "common/wpa_ctrl.h" #include "eap_common/eap_wsc_common.h" #include "eap_i.h" #include "eap_config.h" #define STATE_MACHINE_DATA struct eap_sm #define STATE_MACHINE_DEBUG_PREFIX "EAP" #define EAP_MAX_AUTH_ROUNDS 50 #define EAP_CLIENT_TIMEOUT_DEFAULT 60 static Boolean eap_sm_allowMethod(struct eap_sm *sm, int vendor, EapType method); static struct wpabuf * eap_sm_buildNak(struct eap_sm *sm, int id); static void eap_sm_processIdentity(struct eap_sm *sm, const struct wpabuf *req); static void eap_sm_processNotify(struct eap_sm *sm, const struct wpabuf *req); static struct wpabuf * eap_sm_buildNotify(int id); static void eap_sm_parseEapReq(struct eap_sm *sm, const struct wpabuf *req); #if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG) static const char * eap_sm_method_state_txt(EapMethodState state); static const char * eap_sm_decision_txt(EapDecision decision); #endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ static Boolean eapol_get_bool(struct eap_sm *sm, enum eapol_bool_var var) { return sm->eapol_cb->get_bool(sm->eapol_ctx, var); } static void eapol_set_bool(struct eap_sm *sm, enum eapol_bool_var var, Boolean value) { sm->eapol_cb->set_bool(sm->eapol_ctx, var, value); } static unsigned int eapol_get_int(struct eap_sm *sm, enum eapol_int_var var) { return sm->eapol_cb->get_int(sm->eapol_ctx, var); } static void eapol_set_int(struct eap_sm *sm, enum eapol_int_var var, unsigned int value) { sm->eapol_cb->set_int(sm->eapol_ctx, var, value); } static struct wpabuf * eapol_get_eapReqData(struct eap_sm *sm) { return sm->eapol_cb->get_eapReqData(sm->eapol_ctx); } static void eap_notify_status(struct eap_sm *sm, const char *status, const char *parameter) { wpa_printf(MSG_DEBUG, "EAP: Status notification: %s (param=%s)", status, parameter); if (sm->eapol_cb->notify_status) sm->eapol_cb->notify_status(sm->eapol_ctx, status, parameter); } static void eap_deinit_prev_method(struct eap_sm *sm, const char *txt) { ext_password_free(sm->ext_pw_buf); sm->ext_pw_buf = NULL; if (sm->m == NULL || sm->eap_method_priv == NULL) return; wpa_printf(MSG_DEBUG, "EAP: deinitialize previously used EAP method " "(%d, %s) at %s", sm->selectedMethod, sm->m->name, txt); sm->m->deinit(sm, sm->eap_method_priv); sm->eap_method_priv = NULL; sm->m = NULL; } /** * eap_allowed_method - Check whether EAP method is allowed * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * @vendor: Vendor-Id for expanded types or 0 = IETF for legacy types * @method: EAP type * Returns: 1 = allowed EAP method, 0 = not allowed */ int eap_allowed_method(struct eap_sm *sm, int vendor, u32 method) { struct eap_peer_config *config = eap_get_config(sm); int i; struct eap_method_type *m; if (config == NULL || config->eap_methods == NULL) return 1; m = config->eap_methods; for (i = 0; m[i].vendor != EAP_VENDOR_IETF || m[i].method != EAP_TYPE_NONE; i++) { if (m[i].vendor == vendor && m[i].method == method) return 1; } return 0; } /* * This state initializes state machine variables when the machine is * activated (portEnabled = TRUE). This is also used when re-starting * authentication (eapRestart == TRUE). */ SM_STATE(EAP, INITIALIZE) { SM_ENTRY(EAP, INITIALIZE); if (sm->fast_reauth && sm->m && sm->m->has_reauth_data && sm->m->has_reauth_data(sm, sm->eap_method_priv) && !sm->prev_failure) { wpa_printf(MSG_DEBUG, "EAP: maintaining EAP method data for " "fast reauthentication"); sm->m->deinit_for_reauth(sm, sm->eap_method_priv); } else { eap_deinit_prev_method(sm, "INITIALIZE"); } sm->selectedMethod = EAP_TYPE_NONE; sm->methodState = METHOD_NONE; sm->allowNotifications = TRUE; sm->decision = DECISION_FAIL; sm->ClientTimeout = EAP_CLIENT_TIMEOUT_DEFAULT; eapol_set_int(sm, EAPOL_idleWhile, sm->ClientTimeout); eapol_set_bool(sm, EAPOL_eapSuccess, FALSE); eapol_set_bool(sm, EAPOL_eapFail, FALSE); os_free(sm->eapKeyData); sm->eapKeyData = NULL; os_free(sm->eapSessionId); sm->eapSessionId = NULL; sm->eapKeyAvailable = FALSE; eapol_set_bool(sm, EAPOL_eapRestart, FALSE); sm->lastId = -1; /* new session - make sure this does not match with * the first EAP-Packet */ /* * RFC 4137 does not reset eapResp and eapNoResp here. However, this * seemed to be able to trigger cases where both were set and if EAPOL * state machine uses eapNoResp first, it may end up not sending a real * reply correctly. This occurred when the workaround in FAIL state set * eapNoResp = TRUE.. Maybe that workaround needs to be fixed to do * something else(?) */ eapol_set_bool(sm, EAPOL_eapResp, FALSE); eapol_set_bool(sm, EAPOL_eapNoResp, FALSE); sm->num_rounds = 0; sm->prev_failure = 0; sm->expected_failure = 0; } /* * This state is reached whenever service from the lower layer is interrupted * or unavailable (portEnabled == FALSE). Immediate transition to INITIALIZE * occurs when the port becomes enabled. */ SM_STATE(EAP, DISABLED) { SM_ENTRY(EAP, DISABLED); sm->num_rounds = 0; /* * RFC 4137 does not describe clearing of idleWhile here, but doing so * allows the timer tick to be stopped more quickly when EAP is not in * use. */ eapol_set_int(sm, EAPOL_idleWhile, 0); } /* * The state machine spends most of its time here, waiting for something to * happen. This state is entered unconditionally from INITIALIZE, DISCARD, and * SEND_RESPONSE states. */ SM_STATE(EAP, IDLE) { SM_ENTRY(EAP, IDLE); } /* * This state is entered when an EAP packet is received (eapReq == TRUE) to * parse the packet header. */ SM_STATE(EAP, RECEIVED) { const struct wpabuf *eapReqData; SM_ENTRY(EAP, RECEIVED); eapReqData = eapol_get_eapReqData(sm); /* parse rxReq, rxSuccess, rxFailure, reqId, reqMethod */ eap_sm_parseEapReq(sm, eapReqData); sm->num_rounds++; } /* * This state is entered when a request for a new type comes in. Either the * correct method is started, or a Nak response is built. */ SM_STATE(EAP, GET_METHOD) { int reinit; EapType method; const struct eap_method *eap_method; SM_ENTRY(EAP, GET_METHOD); if (sm->reqMethod == EAP_TYPE_EXPANDED) method = sm->reqVendorMethod; else method = sm->reqMethod; eap_method = eap_peer_get_eap_method(sm->reqVendor, method); if (!eap_sm_allowMethod(sm, sm->reqVendor, method)) { wpa_printf(MSG_DEBUG, "EAP: vendor %u method %u not allowed", sm->reqVendor, method); wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_PROPOSED_METHOD "vendor=%u method=%u -> NAK", sm->reqVendor, method); eap_notify_status(sm, "refuse proposed method", eap_method ? eap_method->name : "unknown"); goto nak; } wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_PROPOSED_METHOD "vendor=%u method=%u", sm->reqVendor, method); eap_notify_status(sm, "accept proposed method", eap_method ? eap_method->name : "unknown"); /* * RFC 4137 does not define specific operation for fast * re-authentication (session resumption). The design here is to allow * the previously used method data to be maintained for * re-authentication if the method support session resumption. * Otherwise, the previously used method data is freed and a new method * is allocated here. */ if (sm->fast_reauth && sm->m && sm->m->vendor == sm->reqVendor && sm->m->method == method && sm->m->has_reauth_data && sm->m->has_reauth_data(sm, sm->eap_method_priv)) { wpa_printf(MSG_DEBUG, "EAP: Using previous method data" " for fast re-authentication"); reinit = 1; } else { eap_deinit_prev_method(sm, "GET_METHOD"); reinit = 0; } sm->selectedMethod = sm->reqMethod; if (sm->m == NULL) sm->m = eap_method; if (!sm->m) { wpa_printf(MSG_DEBUG, "EAP: Could not find selected method: " "vendor %d method %d", sm->reqVendor, method); goto nak; } sm->ClientTimeout = EAP_CLIENT_TIMEOUT_DEFAULT; wpa_printf(MSG_DEBUG, "EAP: Initialize selected EAP method: " "vendor %u method %u (%s)", sm->reqVendor, method, sm->m->name); if (reinit) sm->eap_method_priv = sm->m->init_for_reauth( sm, sm->eap_method_priv); else sm->eap_method_priv = sm->m->init(sm); if (sm->eap_method_priv == NULL) { struct eap_peer_config *config = eap_get_config(sm); wpa_msg(sm->msg_ctx, MSG_INFO, "EAP: Failed to initialize EAP method: vendor %u " "method %u (%s)", sm->reqVendor, method, sm->m->name); sm->m = NULL; sm->methodState = METHOD_NONE; sm->selectedMethod = EAP_TYPE_NONE; if (sm->reqMethod == EAP_TYPE_TLS && config && (config->pending_req_pin || config->pending_req_passphrase)) { /* * Return without generating Nak in order to allow * entering of PIN code or passphrase to retry the * current EAP packet. */ wpa_printf(MSG_DEBUG, "EAP: Pending PIN/passphrase " "request - skip Nak"); return; } goto nak; } sm->methodState = METHOD_INIT; wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_METHOD "EAP vendor %u method %u (%s) selected", sm->reqVendor, method, sm->m->name); return; nak: wpabuf_free(sm->eapRespData); sm->eapRespData = NULL; sm->eapRespData = eap_sm_buildNak(sm, sm->reqId); } /* * The method processing happens here. The request from the authenticator is * processed, and an appropriate response packet is built. */ SM_STATE(EAP, METHOD) { struct wpabuf *eapReqData; struct eap_method_ret ret; int min_len = 1; SM_ENTRY(EAP, METHOD); if (sm->m == NULL) { wpa_printf(MSG_WARNING, "EAP::METHOD - method not selected"); return; } eapReqData = eapol_get_eapReqData(sm); if (sm->m->vendor == EAP_VENDOR_IETF && sm->m->method == EAP_TYPE_LEAP) min_len = 0; /* LEAP uses EAP-Success without payload */ if (!eap_hdr_len_valid(eapReqData, min_len)) return; /* * Get ignore, methodState, decision, allowNotifications, and * eapRespData. RFC 4137 uses three separate method procedure (check, * process, and buildResp) in this state. These have been combined into * a single function call to m->process() in order to optimize EAP * method implementation interface a bit. These procedures are only * used from within this METHOD state, so there is no need to keep * these as separate C functions. * * The RFC 4137 procedures return values as follows: * ignore = m.check(eapReqData) * (methodState, decision, allowNotifications) = m.process(eapReqData) * eapRespData = m.buildResp(reqId) */ os_memset(&ret, 0, sizeof(ret)); ret.ignore = sm->ignore; ret.methodState = sm->methodState; ret.decision = sm->decision; ret.allowNotifications = sm->allowNotifications; wpabuf_free(sm->eapRespData); sm->eapRespData = NULL; sm->eapRespData = sm->m->process(sm, sm->eap_method_priv, &ret, eapReqData); wpa_printf(MSG_DEBUG, "EAP: method process -> ignore=%s " "methodState=%s decision=%s eapRespData=%p", ret.ignore ? "TRUE" : "FALSE", eap_sm_method_state_txt(ret.methodState), eap_sm_decision_txt(ret.decision), sm->eapRespData); sm->ignore = ret.ignore; if (sm->ignore) return; sm->methodState = ret.methodState; sm->decision = ret.decision; sm->allowNotifications = ret.allowNotifications; if (sm->m->isKeyAvailable && sm->m->getKey && sm->m->isKeyAvailable(sm, sm->eap_method_priv)) { os_free(sm->eapKeyData); sm->eapKeyData = sm->m->getKey(sm, sm->eap_method_priv, &sm->eapKeyDataLen); os_free(sm->eapSessionId); sm->eapSessionId = NULL; if (sm->m->getSessionId) { sm->eapSessionId = sm->m->getSessionId( sm, sm->eap_method_priv, &sm->eapSessionIdLen); wpa_hexdump(MSG_DEBUG, "EAP: Session-Id", sm->eapSessionId, sm->eapSessionIdLen); } } } /* * This state signals the lower layer that a response packet is ready to be * sent. */ SM_STATE(EAP, SEND_RESPONSE) { SM_ENTRY(EAP, SEND_RESPONSE); wpabuf_free(sm->lastRespData); if (sm->eapRespData) { if (sm->workaround) os_memcpy(sm->last_md5, sm->req_md5, 16); sm->lastId = sm->reqId; sm->lastRespData = wpabuf_dup(sm->eapRespData); eapol_set_bool(sm, EAPOL_eapResp, TRUE); } else { wpa_printf(MSG_DEBUG, "EAP: No eapRespData available"); sm->lastRespData = NULL; } eapol_set_bool(sm, EAPOL_eapReq, FALSE); eapol_set_int(sm, EAPOL_idleWhile, sm->ClientTimeout); } /* * This state signals the lower layer that the request was discarded, and no * response packet will be sent at this time. */ SM_STATE(EAP, DISCARD) { SM_ENTRY(EAP, DISCARD); eapol_set_bool(sm, EAPOL_eapReq, FALSE); eapol_set_bool(sm, EAPOL_eapNoResp, TRUE); } /* * Handles requests for Identity method and builds a response. */ SM_STATE(EAP, IDENTITY) { const struct wpabuf *eapReqData; SM_ENTRY(EAP, IDENTITY); eapReqData = eapol_get_eapReqData(sm); if (!eap_hdr_len_valid(eapReqData, 1)) return; eap_sm_processIdentity(sm, eapReqData); wpabuf_free(sm->eapRespData); sm->eapRespData = NULL; sm->eapRespData = eap_sm_buildIdentity(sm, sm->reqId, 0); } /* * Handles requests for Notification method and builds a response. */ SM_STATE(EAP, NOTIFICATION) { const struct wpabuf *eapReqData; SM_ENTRY(EAP, NOTIFICATION); eapReqData = eapol_get_eapReqData(sm); if (!eap_hdr_len_valid(eapReqData, 1)) return; eap_sm_processNotify(sm, eapReqData); wpabuf_free(sm->eapRespData); sm->eapRespData = NULL; sm->eapRespData = eap_sm_buildNotify(sm->reqId); } /* * This state retransmits the previous response packet. */ SM_STATE(EAP, RETRANSMIT) { SM_ENTRY(EAP, RETRANSMIT); wpabuf_free(sm->eapRespData); if (sm->lastRespData) sm->eapRespData = wpabuf_dup(sm->lastRespData); else sm->eapRespData = NULL; } /* * This state is entered in case of a successful completion of authentication * and state machine waits here until port is disabled or EAP authentication is * restarted. */ SM_STATE(EAP, SUCCESS) { SM_ENTRY(EAP, SUCCESS); if (sm->eapKeyData != NULL) sm->eapKeyAvailable = TRUE; eapol_set_bool(sm, EAPOL_eapSuccess, TRUE); /* * RFC 4137 does not clear eapReq here, but this seems to be required * to avoid processing the same request twice when state machine is * initialized. */ eapol_set_bool(sm, EAPOL_eapReq, FALSE); /* * RFC 4137 does not set eapNoResp here, but this seems to be required * to get EAPOL Supplicant backend state machine into SUCCESS state. In * addition, either eapResp or eapNoResp is required to be set after * processing the received EAP frame. */ eapol_set_bool(sm, EAPOL_eapNoResp, TRUE); wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_SUCCESS "EAP authentication completed successfully"); } /* * This state is entered in case of a failure and state machine waits here * until port is disabled or EAP authentication is restarted. */ SM_STATE(EAP, FAILURE) { SM_ENTRY(EAP, FAILURE); eapol_set_bool(sm, EAPOL_eapFail, TRUE); /* * RFC 4137 does not clear eapReq here, but this seems to be required * to avoid processing the same request twice when state machine is * initialized. */ eapol_set_bool(sm, EAPOL_eapReq, FALSE); /* * RFC 4137 does not set eapNoResp here. However, either eapResp or * eapNoResp is required to be set after processing the received EAP * frame. */ eapol_set_bool(sm, EAPOL_eapNoResp, TRUE); wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_FAILURE "EAP authentication failed"); sm->prev_failure = 1; } static int eap_success_workaround(struct eap_sm *sm, int reqId, int lastId) { /* * At least Microsoft IAS and Meetinghouse Aegis seem to be sending * EAP-Success/Failure with lastId + 1 even though RFC 3748 and * RFC 4137 require that reqId == lastId. In addition, it looks like * Ringmaster v2.1.2.0 would be using lastId + 2 in EAP-Success. * * Accept this kind of Id if EAP workarounds are enabled. These are * unauthenticated plaintext messages, so this should have minimal * security implications (bit easier to fake EAP-Success/Failure). */ if (sm->workaround && (reqId == ((lastId + 1) & 0xff) || reqId == ((lastId + 2) & 0xff))) { wpa_printf(MSG_DEBUG, "EAP: Workaround for unexpected " "identifier field in EAP Success: " "reqId=%d lastId=%d (these are supposed to be " "same)", reqId, lastId); return 1; } wpa_printf(MSG_DEBUG, "EAP: EAP-Success Id mismatch - reqId=%d " "lastId=%d", reqId, lastId); return 0; } /* * RFC 4137 - Appendix A.1: EAP Peer State Machine - State transitions */ static void eap_peer_sm_step_idle(struct eap_sm *sm) { /* * The first three transitions are from RFC 4137. The last two are * local additions to handle special cases with LEAP and PEAP server * not sending EAP-Success in some cases. */ if (eapol_get_bool(sm, EAPOL_eapReq)) SM_ENTER(EAP, RECEIVED); else if ((eapol_get_bool(sm, EAPOL_altAccept) && sm->decision != DECISION_FAIL) || (eapol_get_int(sm, EAPOL_idleWhile) == 0 && sm->decision == DECISION_UNCOND_SUCC)) SM_ENTER(EAP, SUCCESS); else if (eapol_get_bool(sm, EAPOL_altReject) || (eapol_get_int(sm, EAPOL_idleWhile) == 0 && sm->decision != DECISION_UNCOND_SUCC) || (eapol_get_bool(sm, EAPOL_altAccept) && sm->methodState != METHOD_CONT && sm->decision == DECISION_FAIL)) SM_ENTER(EAP, FAILURE); else if (sm->selectedMethod == EAP_TYPE_LEAP && sm->leap_done && sm->decision != DECISION_FAIL && sm->methodState == METHOD_DONE) SM_ENTER(EAP, SUCCESS); else if (sm->selectedMethod == EAP_TYPE_PEAP && sm->peap_done && sm->decision != DECISION_FAIL && sm->methodState == METHOD_DONE) SM_ENTER(EAP, SUCCESS); } static int eap_peer_req_is_duplicate(struct eap_sm *sm) { int duplicate; duplicate = (sm->reqId == sm->lastId) && sm->rxReq; if (sm->workaround && duplicate && os_memcmp(sm->req_md5, sm->last_md5, 16) != 0) { /* * RFC 4137 uses (reqId == lastId) as the only verification for * duplicate EAP requests. However, this misses cases where the * AS is incorrectly using the same id again; and * unfortunately, such implementations exist. Use MD5 hash as * an extra verification for the packets being duplicate to * workaround these issues. */ wpa_printf(MSG_DEBUG, "EAP: AS used the same Id again, but " "EAP packets were not identical"); wpa_printf(MSG_DEBUG, "EAP: workaround - assume this is not a " "duplicate packet"); duplicate = 0; } return duplicate; } static void eap_peer_sm_step_received(struct eap_sm *sm) { int duplicate = eap_peer_req_is_duplicate(sm); /* * Two special cases below for LEAP are local additions to work around * odd LEAP behavior (EAP-Success in the middle of authentication and * then swapped roles). Other transitions are based on RFC 4137. */ if (sm->rxSuccess && sm->decision != DECISION_FAIL && (sm->reqId == sm->lastId || eap_success_workaround(sm, sm->reqId, sm->lastId))) SM_ENTER(EAP, SUCCESS); else if (sm->methodState != METHOD_CONT && ((sm->rxFailure && sm->decision != DECISION_UNCOND_SUCC) || (sm->rxSuccess && sm->decision == DECISION_FAIL && (sm->selectedMethod != EAP_TYPE_LEAP || sm->methodState != METHOD_MAY_CONT))) && (sm->reqId == sm->lastId || eap_success_workaround(sm, sm->reqId, sm->lastId))) SM_ENTER(EAP, FAILURE); else if (sm->rxReq && duplicate) SM_ENTER(EAP, RETRANSMIT); else if (sm->rxReq && !duplicate && sm->reqMethod == EAP_TYPE_NOTIFICATION && sm->allowNotifications) SM_ENTER(EAP, NOTIFICATION); else if (sm->rxReq && !duplicate && sm->selectedMethod == EAP_TYPE_NONE && sm->reqMethod == EAP_TYPE_IDENTITY) SM_ENTER(EAP, IDENTITY); else if (sm->rxReq && !duplicate && sm->selectedMethod == EAP_TYPE_NONE && sm->reqMethod != EAP_TYPE_IDENTITY && sm->reqMethod != EAP_TYPE_NOTIFICATION) SM_ENTER(EAP, GET_METHOD); else if (sm->rxReq && !duplicate && sm->reqMethod == sm->selectedMethod && sm->methodState != METHOD_DONE) SM_ENTER(EAP, METHOD); else if (sm->selectedMethod == EAP_TYPE_LEAP && (sm->rxSuccess || sm->rxResp)) SM_ENTER(EAP, METHOD); else SM_ENTER(EAP, DISCARD); } static void eap_peer_sm_step_local(struct eap_sm *sm) { switch (sm->EAP_state) { case EAP_INITIALIZE: SM_ENTER(EAP, IDLE); break; case EAP_DISABLED: if (eapol_get_bool(sm, EAPOL_portEnabled) && !sm->force_disabled) SM_ENTER(EAP, INITIALIZE); break; case EAP_IDLE: eap_peer_sm_step_idle(sm); break; case EAP_RECEIVED: eap_peer_sm_step_received(sm); break; case EAP_GET_METHOD: if (sm->selectedMethod == sm->reqMethod) SM_ENTER(EAP, METHOD); else SM_ENTER(EAP, SEND_RESPONSE); break; case EAP_METHOD: /* * Note: RFC 4137 uses methodState == DONE && decision == FAIL * as the condition. eapRespData == NULL here is used to allow * final EAP method response to be sent without having to change * all methods to either use methodState MAY_CONT or leaving * decision to something else than FAIL in cases where the only * expected response is EAP-Failure. */ if (sm->ignore) SM_ENTER(EAP, DISCARD); else if (sm->methodState == METHOD_DONE && sm->decision == DECISION_FAIL && !sm->eapRespData) SM_ENTER(EAP, FAILURE); else SM_ENTER(EAP, SEND_RESPONSE); break; case EAP_SEND_RESPONSE: SM_ENTER(EAP, IDLE); break; case EAP_DISCARD: SM_ENTER(EAP, IDLE); break; case EAP_IDENTITY: SM_ENTER(EAP, SEND_RESPONSE); break; case EAP_NOTIFICATION: SM_ENTER(EAP, SEND_RESPONSE); break; case EAP_RETRANSMIT: SM_ENTER(EAP, SEND_RESPONSE); break; case EAP_SUCCESS: break; case EAP_FAILURE: break; } } SM_STEP(EAP) { /* Global transitions */ if (eapol_get_bool(sm, EAPOL_eapRestart) && eapol_get_bool(sm, EAPOL_portEnabled)) SM_ENTER_GLOBAL(EAP, INITIALIZE); else if (!eapol_get_bool(sm, EAPOL_portEnabled) || sm->force_disabled) SM_ENTER_GLOBAL(EAP, DISABLED); else if (sm->num_rounds > EAP_MAX_AUTH_ROUNDS) { /* RFC 4137 does not place any limit on number of EAP messages * in an authentication session. However, some error cases have * ended up in a state were EAP messages were sent between the * peer and server in a loop (e.g., TLS ACK frame in both * direction). Since this is quite undesired outcome, limit the * total number of EAP round-trips and abort authentication if * this limit is exceeded. */ if (sm->num_rounds == EAP_MAX_AUTH_ROUNDS + 1) { wpa_msg(sm->msg_ctx, MSG_INFO, "EAP: more than %d " "authentication rounds - abort", EAP_MAX_AUTH_ROUNDS); sm->num_rounds++; SM_ENTER_GLOBAL(EAP, FAILURE); } } else { /* Local transitions */ eap_peer_sm_step_local(sm); } } static Boolean eap_sm_allowMethod(struct eap_sm *sm, int vendor, EapType method) { if (!eap_allowed_method(sm, vendor, method)) { wpa_printf(MSG_DEBUG, "EAP: configuration does not allow: " "vendor %u method %u", vendor, method); return FALSE; } if (eap_peer_get_eap_method(vendor, method)) return TRUE; wpa_printf(MSG_DEBUG, "EAP: not included in build: " "vendor %u method %u", vendor, method); return FALSE; } static struct wpabuf * eap_sm_build_expanded_nak( struct eap_sm *sm, int id, const struct eap_method *methods, size_t count) { struct wpabuf *resp; int found = 0; const struct eap_method *m; wpa_printf(MSG_DEBUG, "EAP: Building expanded EAP-Nak"); /* RFC 3748 - 5.3.2: Expanded Nak */ resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_EXPANDED, 8 + 8 * (count + 1), EAP_CODE_RESPONSE, id); if (resp == NULL) return NULL; wpabuf_put_be24(resp, EAP_VENDOR_IETF); wpabuf_put_be32(resp, EAP_TYPE_NAK); for (m = methods; m; m = m->next) { if (sm->reqVendor == m->vendor && sm->reqVendorMethod == m->method) continue; /* do not allow the current method again */ if (eap_allowed_method(sm, m->vendor, m->method)) { wpa_printf(MSG_DEBUG, "EAP: allowed type: " "vendor=%u method=%u", m->vendor, m->method); wpabuf_put_u8(resp, EAP_TYPE_EXPANDED); wpabuf_put_be24(resp, m->vendor); wpabuf_put_be32(resp, m->method); found++; } } if (!found) { wpa_printf(MSG_DEBUG, "EAP: no more allowed methods"); wpabuf_put_u8(resp, EAP_TYPE_EXPANDED); wpabuf_put_be24(resp, EAP_VENDOR_IETF); wpabuf_put_be32(resp, EAP_TYPE_NONE); } eap_update_len(resp); return resp; } static struct wpabuf * eap_sm_buildNak(struct eap_sm *sm, int id) { struct wpabuf *resp; u8 *start; int found = 0, expanded_found = 0; size_t count; const struct eap_method *methods, *m; wpa_printf(MSG_DEBUG, "EAP: Building EAP-Nak (requested type %u " "vendor=%u method=%u not allowed)", sm->reqMethod, sm->reqVendor, sm->reqVendorMethod); methods = eap_peer_get_methods(&count); if (methods == NULL) return NULL; if (sm->reqMethod == EAP_TYPE_EXPANDED) return eap_sm_build_expanded_nak(sm, id, methods, count); /* RFC 3748 - 5.3.1: Legacy Nak */ resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_NAK, sizeof(struct eap_hdr) + 1 + count + 1, EAP_CODE_RESPONSE, id); if (resp == NULL) return NULL; start = wpabuf_put(resp, 0); for (m = methods; m; m = m->next) { if (m->vendor == EAP_VENDOR_IETF && m->method == sm->reqMethod) continue; /* do not allow the current method again */ if (eap_allowed_method(sm, m->vendor, m->method)) { if (m->vendor != EAP_VENDOR_IETF) { if (expanded_found) continue; expanded_found = 1; wpabuf_put_u8(resp, EAP_TYPE_EXPANDED); } else wpabuf_put_u8(resp, m->method); found++; } } if (!found) wpabuf_put_u8(resp, EAP_TYPE_NONE); wpa_hexdump(MSG_DEBUG, "EAP: allowed methods", start, found); eap_update_len(resp); return resp; } static void eap_sm_processIdentity(struct eap_sm *sm, const struct wpabuf *req) { const u8 *pos; size_t msg_len; wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_STARTED "EAP authentication started"); eap_notify_status(sm, "started", ""); pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_IDENTITY, req, &msg_len); if (pos == NULL) return; /* * RFC 3748 - 5.1: Identity * Data field may contain a displayable message in UTF-8. If this * includes NUL-character, only the data before that should be * displayed. Some EAP implementasitons may piggy-back additional * options after the NUL. */ /* TODO: could save displayable message so that it can be shown to the * user in case of interaction is required */ wpa_hexdump_ascii(MSG_DEBUG, "EAP: EAP-Request Identity data", pos, msg_len); } #ifdef PCSC_FUNCS /* * Rules for figuring out MNC length based on IMSI for SIM cards that do not * include MNC length field. */ static int mnc_len_from_imsi(const char *imsi) { char mcc_str[4]; unsigned int mcc; os_memcpy(mcc_str, imsi, 3); mcc_str[3] = '\0'; mcc = atoi(mcc_str); if (mcc == 228) return 2; /* Networks in Switzerland use 2-digit MNC */ if (mcc == 244) return 2; /* Networks in Finland use 2-digit MNC */ return -1; } static int eap_sm_append_3gpp_realm(struct eap_sm *sm, char *imsi, size_t max_len, size_t *imsi_len) { int mnc_len; char *pos, mnc[4]; if (*imsi_len + 36 > max_len) { wpa_printf(MSG_WARNING, "No room for realm in IMSI buffer"); return -1; } /* MNC (2 or 3 digits) */ mnc_len = scard_get_mnc_len(sm->scard_ctx); if (mnc_len < 0) mnc_len = mnc_len_from_imsi(imsi); if (mnc_len < 0) { wpa_printf(MSG_INFO, "Failed to get MNC length from (U)SIM " "assuming 3"); mnc_len = 3; } if (mnc_len == 2) { mnc[0] = '0'; mnc[1] = imsi[3]; mnc[2] = imsi[4]; } else if (mnc_len == 3) { mnc[0] = imsi[3]; mnc[1] = imsi[4]; mnc[2] = imsi[5]; } mnc[3] = '\0'; pos = imsi + *imsi_len; pos += os_snprintf(pos, imsi + max_len - pos, "@wlan.mnc%s.mcc%c%c%c.3gppnetwork.org", mnc, imsi[0], imsi[1], imsi[2]); *imsi_len = pos - imsi; return 0; } static int eap_sm_imsi_identity(struct eap_sm *sm, struct eap_peer_config *conf) { enum { EAP_SM_SIM, EAP_SM_AKA, EAP_SM_AKA_PRIME } method = EAP_SM_SIM; char imsi[100]; size_t imsi_len; struct eap_method_type *m = conf->eap_methods; int i; imsi_len = sizeof(imsi); if (scard_get_imsi(sm->scard_ctx, imsi, &imsi_len)) { wpa_printf(MSG_WARNING, "Failed to get IMSI from SIM"); return -1; } wpa_hexdump_ascii(MSG_DEBUG, "IMSI", (u8 *) imsi, imsi_len); if (imsi_len < 7) { wpa_printf(MSG_WARNING, "Too short IMSI for SIM identity"); return -1; } if (eap_sm_append_3gpp_realm(sm, imsi, sizeof(imsi), &imsi_len) < 0) { wpa_printf(MSG_WARNING, "Could not add realm to SIM identity"); return -1; } wpa_hexdump_ascii(MSG_DEBUG, "IMSI + realm", (u8 *) imsi, imsi_len); for (i = 0; m && (m[i].vendor != EAP_VENDOR_IETF || m[i].method != EAP_TYPE_NONE); i++) { if (m[i].vendor == EAP_VENDOR_IETF && m[i].method == EAP_TYPE_AKA_PRIME) { method = EAP_SM_AKA_PRIME; break; } if (m[i].vendor == EAP_VENDOR_IETF && m[i].method == EAP_TYPE_AKA) { method = EAP_SM_AKA; break; } } os_free(conf->identity); conf->identity = os_malloc(1 + imsi_len); if (conf->identity == NULL) { wpa_printf(MSG_WARNING, "Failed to allocate buffer for " "IMSI-based identity"); return -1; } switch (method) { case EAP_SM_SIM: conf->identity[0] = '1'; break; case EAP_SM_AKA: conf->identity[0] = '0'; break; case EAP_SM_AKA_PRIME: conf->identity[0] = '6'; break; } os_memcpy(conf->identity + 1, imsi, imsi_len); conf->identity_len = 1 + imsi_len; return 0; } #endif /* PCSC_FUNCS */ static int eap_sm_set_scard_pin(struct eap_sm *sm, struct eap_peer_config *conf) { #ifdef PCSC_FUNCS if (scard_set_pin(sm->scard_ctx, conf->pin)) { /* * Make sure the same PIN is not tried again in order to avoid * blocking SIM. */ os_free(conf->pin); conf->pin = NULL; wpa_printf(MSG_WARNING, "PIN validation failed"); eap_sm_request_pin(sm); return -1; } return 0; #else /* PCSC_FUNCS */ return -1; #endif /* PCSC_FUNCS */ } static int eap_sm_get_scard_identity(struct eap_sm *sm, struct eap_peer_config *conf) { #ifdef PCSC_FUNCS if (eap_sm_set_scard_pin(sm, conf)) return -1; return eap_sm_imsi_identity(sm, conf); #else /* PCSC_FUNCS */ return -1; #endif /* PCSC_FUNCS */ } /** * eap_sm_buildIdentity - Build EAP-Identity/Response for the current network * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * @id: EAP identifier for the packet * @encrypted: Whether the packet is for encrypted tunnel (EAP phase 2) * Returns: Pointer to the allocated EAP-Identity/Response packet or %NULL on * failure * * This function allocates and builds an EAP-Identity/Response packet for the * current network. The caller is responsible for freeing the returned data. */ struct wpabuf * eap_sm_buildIdentity(struct eap_sm *sm, int id, int encrypted) { struct eap_peer_config *config = eap_get_config(sm); struct wpabuf *resp; const u8 *identity; size_t identity_len; if (config == NULL) { wpa_printf(MSG_WARNING, "EAP: buildIdentity: configuration " "was not available"); return NULL; } if (sm->m && sm->m->get_identity && (identity = sm->m->get_identity(sm, sm->eap_method_priv, &identity_len)) != NULL) { wpa_hexdump_ascii(MSG_DEBUG, "EAP: using method re-auth " "identity", identity, identity_len); } else if (!encrypted && config->anonymous_identity) { identity = config->anonymous_identity; identity_len = config->anonymous_identity_len; wpa_hexdump_ascii(MSG_DEBUG, "EAP: using anonymous identity", identity, identity_len); } else { identity = config->identity; identity_len = config->identity_len; wpa_hexdump_ascii(MSG_DEBUG, "EAP: using real identity", identity, identity_len); } if (identity == NULL) { wpa_printf(MSG_WARNING, "EAP: buildIdentity: identity " "configuration was not available"); if (config->pcsc) { if (eap_sm_get_scard_identity(sm, config) < 0) return NULL; identity = config->identity; identity_len = config->identity_len; wpa_hexdump_ascii(MSG_DEBUG, "permanent identity from " "IMSI", identity, identity_len); } else { eap_sm_request_identity(sm); return NULL; } } else if (config->pcsc) { if (eap_sm_set_scard_pin(sm, config) < 0) return NULL; } resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_IDENTITY, identity_len, EAP_CODE_RESPONSE, id); if (resp == NULL) return NULL; wpabuf_put_data(resp, identity, identity_len); return resp; } static void eap_sm_processNotify(struct eap_sm *sm, const struct wpabuf *req) { const u8 *pos; char *msg; size_t i, msg_len; pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_NOTIFICATION, req, &msg_len); if (pos == NULL) return; wpa_hexdump_ascii(MSG_DEBUG, "EAP: EAP-Request Notification data", pos, msg_len); msg = os_malloc(msg_len + 1); if (msg == NULL) return; for (i = 0; i < msg_len; i++) msg[i] = isprint(pos[i]) ? (char) pos[i] : '_'; msg[msg_len] = '\0'; wpa_msg(sm->msg_ctx, MSG_INFO, "%s%s", WPA_EVENT_EAP_NOTIFICATION, msg); os_free(msg); } static struct wpabuf * eap_sm_buildNotify(int id) { struct wpabuf *resp; wpa_printf(MSG_DEBUG, "EAP: Generating EAP-Response Notification"); resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_NOTIFICATION, 0, EAP_CODE_RESPONSE, id); if (resp == NULL) return NULL; return resp; } static void eap_sm_parseEapReq(struct eap_sm *sm, const struct wpabuf *req) { const struct eap_hdr *hdr; size_t plen; const u8 *pos; sm->rxReq = sm->rxResp = sm->rxSuccess = sm->rxFailure = FALSE; sm->reqId = 0; sm->reqMethod = EAP_TYPE_NONE; sm->reqVendor = EAP_VENDOR_IETF; sm->reqVendorMethod = EAP_TYPE_NONE; if (req == NULL || wpabuf_len(req) < sizeof(*hdr)) return; hdr = wpabuf_head(req); plen = be_to_host16(hdr->length); if (plen > wpabuf_len(req)) { wpa_printf(MSG_DEBUG, "EAP: Ignored truncated EAP-Packet " "(len=%lu plen=%lu)", (unsigned long) wpabuf_len(req), (unsigned long) plen); return; } sm->reqId = hdr->identifier; if (sm->workaround) { const u8 *addr[1]; addr[0] = wpabuf_head(req); md5_vector(1, addr, &plen, sm->req_md5); } switch (hdr->code) { case EAP_CODE_REQUEST: if (plen < sizeof(*hdr) + 1) { wpa_printf(MSG_DEBUG, "EAP: Too short EAP-Request - " "no Type field"); return; } sm->rxReq = TRUE; pos = (const u8 *) (hdr + 1); sm->reqMethod = *pos++; if (sm->reqMethod == EAP_TYPE_EXPANDED) { if (plen < sizeof(*hdr) + 8) { wpa_printf(MSG_DEBUG, "EAP: Ignored truncated " "expanded EAP-Packet (plen=%lu)", (unsigned long) plen); return; } sm->reqVendor = WPA_GET_BE24(pos); pos += 3; sm->reqVendorMethod = WPA_GET_BE32(pos); } wpa_printf(MSG_DEBUG, "EAP: Received EAP-Request id=%d " "method=%u vendor=%u vendorMethod=%u", sm->reqId, sm->reqMethod, sm->reqVendor, sm->reqVendorMethod); break; case EAP_CODE_RESPONSE: if (sm->selectedMethod == EAP_TYPE_LEAP) { /* * LEAP differs from RFC 4137 by using reversed roles * for mutual authentication and because of this, we * need to accept EAP-Response frames if LEAP is used. */ if (plen < sizeof(*hdr) + 1) { wpa_printf(MSG_DEBUG, "EAP: Too short " "EAP-Response - no Type field"); return; } sm->rxResp = TRUE; pos = (const u8 *) (hdr + 1); sm->reqMethod = *pos; wpa_printf(MSG_DEBUG, "EAP: Received EAP-Response for " "LEAP method=%d id=%d", sm->reqMethod, sm->reqId); break; } wpa_printf(MSG_DEBUG, "EAP: Ignored EAP-Response"); break; case EAP_CODE_SUCCESS: wpa_printf(MSG_DEBUG, "EAP: Received EAP-Success"); eap_notify_status(sm, "completion", "success"); sm->rxSuccess = TRUE; break; case EAP_CODE_FAILURE: wpa_printf(MSG_DEBUG, "EAP: Received EAP-Failure"); eap_notify_status(sm, "completion", "failure"); sm->rxFailure = TRUE; break; default: wpa_printf(MSG_DEBUG, "EAP: Ignored EAP-Packet with unknown " "code %d", hdr->code); break; } } static void eap_peer_sm_tls_event(void *ctx, enum tls_event ev, union tls_event_data *data) { struct eap_sm *sm = ctx; char *hash_hex = NULL; switch (ev) { case TLS_CERT_CHAIN_SUCCESS: eap_notify_status(sm, "remote certificate verification", "success"); break; case TLS_CERT_CHAIN_FAILURE: wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_TLS_CERT_ERROR "reason=%d depth=%d subject='%s' err='%s'", data->cert_fail.reason, data->cert_fail.depth, data->cert_fail.subject, data->cert_fail.reason_txt); eap_notify_status(sm, "remote certificate verification", data->cert_fail.reason_txt); break; case TLS_PEER_CERTIFICATE: if (!sm->eapol_cb->notify_cert) break; if (data->peer_cert.hash) { size_t len = data->peer_cert.hash_len * 2 + 1; hash_hex = os_malloc(len); if (hash_hex) { wpa_snprintf_hex(hash_hex, len, data->peer_cert.hash, data->peer_cert.hash_len); } } sm->eapol_cb->notify_cert(sm->eapol_ctx, data->peer_cert.depth, data->peer_cert.subject, hash_hex, data->peer_cert.cert); break; case TLS_ALERT: if (data->alert.is_local) eap_notify_status(sm, "local TLS alert", data->alert.description); else eap_notify_status(sm, "remote TLS alert", data->alert.description); break; } os_free(hash_hex); } /** * eap_peer_sm_init - Allocate and initialize EAP peer state machine * @eapol_ctx: Context data to be used with eapol_cb calls * @eapol_cb: Pointer to EAPOL callback functions * @msg_ctx: Context data for wpa_msg() calls * @conf: EAP configuration * Returns: Pointer to the allocated EAP state machine or %NULL on failure * * This function allocates and initializes an EAP state machine. In addition, * this initializes TLS library for the new EAP state machine. eapol_cb pointer * will be in use until eap_peer_sm_deinit() is used to deinitialize this EAP * state machine. Consequently, the caller must make sure that this data * structure remains alive while the EAP state machine is active. */ struct eap_sm * eap_peer_sm_init(void *eapol_ctx, struct eapol_callbacks *eapol_cb, void *msg_ctx, struct eap_config *conf) { struct eap_sm *sm; struct tls_config tlsconf; sm = os_zalloc(sizeof(*sm)); if (sm == NULL) return NULL; sm->eapol_ctx = eapol_ctx; sm->eapol_cb = eapol_cb; sm->msg_ctx = msg_ctx; sm->ClientTimeout = EAP_CLIENT_TIMEOUT_DEFAULT; sm->wps = conf->wps; os_memset(&tlsconf, 0, sizeof(tlsconf)); tlsconf.opensc_engine_path = conf->opensc_engine_path; tlsconf.pkcs11_engine_path = conf->pkcs11_engine_path; tlsconf.pkcs11_module_path = conf->pkcs11_module_path; #ifdef CONFIG_FIPS tlsconf.fips_mode = 1; #endif /* CONFIG_FIPS */ tlsconf.event_cb = eap_peer_sm_tls_event; tlsconf.cb_ctx = sm; tlsconf.cert_in_cb = conf->cert_in_cb; sm->ssl_ctx = tls_init(&tlsconf); if (sm->ssl_ctx == NULL) { wpa_printf(MSG_WARNING, "SSL: Failed to initialize TLS " "context."); os_free(sm); return NULL; } sm->ssl_ctx2 = tls_init(&tlsconf); if (sm->ssl_ctx2 == NULL) { wpa_printf(MSG_INFO, "SSL: Failed to initialize TLS " "context (2)."); /* Run without separate TLS context within TLS tunnel */ } return sm; } /** * eap_peer_sm_deinit - Deinitialize and free an EAP peer state machine * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * * This function deinitializes EAP state machine and frees all allocated * resources. */ void eap_peer_sm_deinit(struct eap_sm *sm) { if (sm == NULL) return; eap_deinit_prev_method(sm, "EAP deinit"); eap_sm_abort(sm); if (sm->ssl_ctx2) tls_deinit(sm->ssl_ctx2); tls_deinit(sm->ssl_ctx); os_free(sm); } /** * eap_peer_sm_step - Step EAP peer state machine * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * Returns: 1 if EAP state was changed or 0 if not * * This function advances EAP state machine to a new state to match with the * current variables. This should be called whenever variables used by the EAP * state machine have changed. */ int eap_peer_sm_step(struct eap_sm *sm) { int res = 0; do { sm->changed = FALSE; SM_STEP_RUN(EAP); if (sm->changed) res = 1; } while (sm->changed); return res; } /** * eap_sm_abort - Abort EAP authentication * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * * Release system resources that have been allocated for the authentication * session without fully deinitializing the EAP state machine. */ void eap_sm_abort(struct eap_sm *sm) { wpabuf_free(sm->lastRespData); sm->lastRespData = NULL; wpabuf_free(sm->eapRespData); sm->eapRespData = NULL; os_free(sm->eapKeyData); sm->eapKeyData = NULL; os_free(sm->eapSessionId); sm->eapSessionId = NULL; /* This is not clearly specified in the EAP statemachines draft, but * it seems necessary to make sure that some of the EAPOL variables get * cleared for the next authentication. */ eapol_set_bool(sm, EAPOL_eapSuccess, FALSE); } #ifdef CONFIG_CTRL_IFACE static const char * eap_sm_state_txt(int state) { switch (state) { case EAP_INITIALIZE: return "INITIALIZE"; case EAP_DISABLED: return "DISABLED"; case EAP_IDLE: return "IDLE"; case EAP_RECEIVED: return "RECEIVED"; case EAP_GET_METHOD: return "GET_METHOD"; case EAP_METHOD: return "METHOD"; case EAP_SEND_RESPONSE: return "SEND_RESPONSE"; case EAP_DISCARD: return "DISCARD"; case EAP_IDENTITY: return "IDENTITY"; case EAP_NOTIFICATION: return "NOTIFICATION"; case EAP_RETRANSMIT: return "RETRANSMIT"; case EAP_SUCCESS: return "SUCCESS"; case EAP_FAILURE: return "FAILURE"; default: return "UNKNOWN"; } } #endif /* CONFIG_CTRL_IFACE */ #if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG) static const char * eap_sm_method_state_txt(EapMethodState state) { switch (state) { case METHOD_NONE: return "NONE"; case METHOD_INIT: return "INIT"; case METHOD_CONT: return "CONT"; case METHOD_MAY_CONT: return "MAY_CONT"; case METHOD_DONE: return "DONE"; default: return "UNKNOWN"; } } static const char * eap_sm_decision_txt(EapDecision decision) { switch (decision) { case DECISION_FAIL: return "FAIL"; case DECISION_COND_SUCC: return "COND_SUCC"; case DECISION_UNCOND_SUCC: return "UNCOND_SUCC"; default: return "UNKNOWN"; } } #endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ #ifdef CONFIG_CTRL_IFACE /** * eap_sm_get_status - Get EAP state machine status * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * @buf: Buffer for status information * @buflen: Maximum buffer length * @verbose: Whether to include verbose status information * Returns: Number of bytes written to buf. * * Query EAP state machine for status information. This function fills in a * text area with current status information from the EAPOL state machine. If * the buffer (buf) is not large enough, status information will be truncated * to fit the buffer. */ int eap_sm_get_status(struct eap_sm *sm, char *buf, size_t buflen, int verbose) { int len, ret; if (sm == NULL) return 0; len = os_snprintf(buf, buflen, "EAP state=%s\n", eap_sm_state_txt(sm->EAP_state)); if (len < 0 || (size_t) len >= buflen) return 0; if (sm->selectedMethod != EAP_TYPE_NONE) { const char *name; if (sm->m) { name = sm->m->name; } else { const struct eap_method *m = eap_peer_get_eap_method(EAP_VENDOR_IETF, sm->selectedMethod); if (m) name = m->name; else name = "?"; } ret = os_snprintf(buf + len, buflen - len, "selectedMethod=%d (EAP-%s)\n", sm->selectedMethod, name); if (ret < 0 || (size_t) ret >= buflen - len) return len; len += ret; if (sm->m && sm->m->get_status) { len += sm->m->get_status(sm, sm->eap_method_priv, buf + len, buflen - len, verbose); } } if (verbose) { ret = os_snprintf(buf + len, buflen - len, "reqMethod=%d\n" "methodState=%s\n" "decision=%s\n" "ClientTimeout=%d\n", sm->reqMethod, eap_sm_method_state_txt(sm->methodState), eap_sm_decision_txt(sm->decision), sm->ClientTimeout); if (ret < 0 || (size_t) ret >= buflen - len) return len; len += ret; } return len; } #endif /* CONFIG_CTRL_IFACE */ #if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG) static void eap_sm_request(struct eap_sm *sm, enum wpa_ctrl_req_type field, const char *msg, size_t msglen) { struct eap_peer_config *config; const char *txt = NULL; char *tmp; if (sm == NULL) return; config = eap_get_config(sm); if (config == NULL) return; switch (field) { case WPA_CTRL_REQ_EAP_IDENTITY: config->pending_req_identity++; break; case WPA_CTRL_REQ_EAP_PASSWORD: config->pending_req_password++; break; case WPA_CTRL_REQ_EAP_NEW_PASSWORD: config->pending_req_new_password++; break; case WPA_CTRL_REQ_EAP_PIN: config->pending_req_pin++; break; case WPA_CTRL_REQ_EAP_OTP: if (msg) { tmp = os_malloc(msglen + 3); if (tmp == NULL) return; tmp[0] = '['; os_memcpy(tmp + 1, msg, msglen); tmp[msglen + 1] = ']'; tmp[msglen + 2] = '\0'; txt = tmp; os_free(config->pending_req_otp); config->pending_req_otp = tmp; config->pending_req_otp_len = msglen + 3; } else { if (config->pending_req_otp == NULL) return; txt = config->pending_req_otp; } break; case WPA_CTRL_REQ_EAP_PASSPHRASE: config->pending_req_passphrase++; break; case WPA_CTRL_REQ_SIM: txt = msg; break; default: return; } if (sm->eapol_cb->eap_param_needed) sm->eapol_cb->eap_param_needed(sm->eapol_ctx, field, txt); } #else /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ #define eap_sm_request(sm, type, msg, msglen) do { } while (0) #endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ const char * eap_sm_get_method_name(struct eap_sm *sm) { if (sm->m == NULL) return "UNKNOWN"; return sm->m->name; } /** * eap_sm_request_identity - Request identity from user (ctrl_iface) * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * * EAP methods can call this function to request identity information for the * current network. This is normally called when the identity is not included * in the network configuration. The request will be sent to monitor programs * through the control interface. */ void eap_sm_request_identity(struct eap_sm *sm) { eap_sm_request(sm, WPA_CTRL_REQ_EAP_IDENTITY, NULL, 0); } /** * eap_sm_request_password - Request password from user (ctrl_iface) * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * * EAP methods can call this function to request password information for the * current network. This is normally called when the password is not included * in the network configuration. The request will be sent to monitor programs * through the control interface. */ void eap_sm_request_password(struct eap_sm *sm) { eap_sm_request(sm, WPA_CTRL_REQ_EAP_PASSWORD, NULL, 0); } /** * eap_sm_request_new_password - Request new password from user (ctrl_iface) * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * * EAP methods can call this function to request new password information for * the current network. This is normally called when the EAP method indicates * that the current password has expired and password change is required. The * request will be sent to monitor programs through the control interface. */ void eap_sm_request_new_password(struct eap_sm *sm) { eap_sm_request(sm, WPA_CTRL_REQ_EAP_NEW_PASSWORD, NULL, 0); } /** * eap_sm_request_pin - Request SIM or smart card PIN from user (ctrl_iface) * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * * EAP methods can call this function to request SIM or smart card PIN * information for the current network. This is normally called when the PIN is * not included in the network configuration. The request will be sent to * monitor programs through the control interface. */ void eap_sm_request_pin(struct eap_sm *sm) { eap_sm_request(sm, WPA_CTRL_REQ_EAP_PIN, NULL, 0); } /** * eap_sm_request_otp - Request one time password from user (ctrl_iface) * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * @msg: Message to be displayed to the user when asking for OTP * @msg_len: Length of the user displayable message * * EAP methods can call this function to request open time password (OTP) for * the current network. The request will be sent to monitor programs through * the control interface. */ void eap_sm_request_otp(struct eap_sm *sm, const char *msg, size_t msg_len) { eap_sm_request(sm, WPA_CTRL_REQ_EAP_OTP, msg, msg_len); } /** * eap_sm_request_passphrase - Request passphrase from user (ctrl_iface) * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * * EAP methods can call this function to request passphrase for a private key * for the current network. This is normally called when the passphrase is not * included in the network configuration. The request will be sent to monitor * programs through the control interface. */ void eap_sm_request_passphrase(struct eap_sm *sm) { eap_sm_request(sm, WPA_CTRL_REQ_EAP_PASSPHRASE, NULL, 0); } /** * eap_sm_request_sim - Request external SIM processing * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * @req: EAP method specific request */ void eap_sm_request_sim(struct eap_sm *sm, const char *req) { eap_sm_request(sm, WPA_CTRL_REQ_SIM, req, os_strlen(req)); } /** * eap_sm_notify_ctrl_attached - Notification of attached monitor * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * * Notify EAP state machines that a monitor was attached to the control * interface to trigger re-sending of pending requests for user input. */ void eap_sm_notify_ctrl_attached(struct eap_sm *sm) { struct eap_peer_config *config = eap_get_config(sm); if (config == NULL) return; /* Re-send any pending requests for user data since a new control * interface was added. This handles cases where the EAP authentication * starts immediately after system startup when the user interface is * not yet running. */ if (config->pending_req_identity) eap_sm_request_identity(sm); if (config->pending_req_password) eap_sm_request_password(sm); if (config->pending_req_new_password) eap_sm_request_new_password(sm); if (config->pending_req_otp) eap_sm_request_otp(sm, NULL, 0); if (config->pending_req_pin) eap_sm_request_pin(sm); if (config->pending_req_passphrase) eap_sm_request_passphrase(sm); } static int eap_allowed_phase2_type(int vendor, int type) { if (vendor != EAP_VENDOR_IETF) return 0; return type != EAP_TYPE_PEAP && type != EAP_TYPE_TTLS && type != EAP_TYPE_FAST; } /** * eap_get_phase2_type - Get EAP type for the given EAP phase 2 method name * @name: EAP method name, e.g., MD5 * @vendor: Buffer for returning EAP Vendor-Id * Returns: EAP method type or %EAP_TYPE_NONE if not found * * This function maps EAP type names into EAP type numbers that are allowed for * Phase 2, i.e., for tunneled authentication. Phase 2 is used, e.g., with * EAP-PEAP, EAP-TTLS, and EAP-FAST. */ u32 eap_get_phase2_type(const char *name, int *vendor) { int v; u8 type = eap_peer_get_type(name, &v); if (eap_allowed_phase2_type(v, type)) { *vendor = v; return type; } *vendor = EAP_VENDOR_IETF; return EAP_TYPE_NONE; } /** * eap_get_phase2_types - Get list of allowed EAP phase 2 types * @config: Pointer to a network configuration * @count: Pointer to a variable to be filled with number of returned EAP types * Returns: Pointer to allocated type list or %NULL on failure * * This function generates an array of allowed EAP phase 2 (tunneled) types for * the given network configuration. */ struct eap_method_type * eap_get_phase2_types(struct eap_peer_config *config, size_t *count) { struct eap_method_type *buf; u32 method; int vendor; size_t mcount; const struct eap_method *methods, *m; methods = eap_peer_get_methods(&mcount); if (methods == NULL) return NULL; *count = 0; buf = os_malloc(mcount * sizeof(struct eap_method_type)); if (buf == NULL) return NULL; for (m = methods; m; m = m->next) { vendor = m->vendor; method = m->method; if (eap_allowed_phase2_type(vendor, method)) { if (vendor == EAP_VENDOR_IETF && method == EAP_TYPE_TLS && config && config->private_key2 == NULL) continue; buf[*count].vendor = vendor; buf[*count].method = method; (*count)++; } } return buf; } /** * eap_set_fast_reauth - Update fast_reauth setting * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * @enabled: 1 = Fast reauthentication is enabled, 0 = Disabled */ void eap_set_fast_reauth(struct eap_sm *sm, int enabled) { sm->fast_reauth = enabled; } /** * eap_set_workaround - Update EAP workarounds setting * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * @workaround: 1 = Enable EAP workarounds, 0 = Disable EAP workarounds */ void eap_set_workaround(struct eap_sm *sm, unsigned int workaround) { sm->workaround = workaround; } /** * eap_get_config - Get current network configuration * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * Returns: Pointer to the current network configuration or %NULL if not found * * EAP peer methods should avoid using this function if they can use other * access functions, like eap_get_config_identity() and * eap_get_config_password(), that do not require direct access to * struct eap_peer_config. */ struct eap_peer_config * eap_get_config(struct eap_sm *sm) { return sm->eapol_cb->get_config(sm->eapol_ctx); } /** * eap_get_config_identity - Get identity from the network configuration * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * @len: Buffer for the length of the identity * Returns: Pointer to the identity or %NULL if not found */ const u8 * eap_get_config_identity(struct eap_sm *sm, size_t *len) { struct eap_peer_config *config = eap_get_config(sm); if (config == NULL) return NULL; *len = config->identity_len; return config->identity; } static int eap_get_ext_password(struct eap_sm *sm, struct eap_peer_config *config) { char *name; if (config->password == NULL) return -1; name = os_zalloc(config->password_len + 1); if (name == NULL) return -1; os_memcpy(name, config->password, config->password_len); ext_password_free(sm->ext_pw_buf); sm->ext_pw_buf = ext_password_get(sm->ext_pw, name); os_free(name); return sm->ext_pw_buf == NULL ? -1 : 0; } /** * eap_get_config_password - Get password from the network configuration * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * @len: Buffer for the length of the password * Returns: Pointer to the password or %NULL if not found */ const u8 * eap_get_config_password(struct eap_sm *sm, size_t *len) { struct eap_peer_config *config = eap_get_config(sm); if (config == NULL) return NULL; if (config->flags & EAP_CONFIG_FLAGS_EXT_PASSWORD) { if (eap_get_ext_password(sm, config) < 0) return NULL; *len = wpabuf_len(sm->ext_pw_buf); return wpabuf_head(sm->ext_pw_buf); } *len = config->password_len; return config->password; } /** * eap_get_config_password2 - Get password from the network configuration * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * @len: Buffer for the length of the password * @hash: Buffer for returning whether the password is stored as a * NtPasswordHash instead of plaintext password; can be %NULL if this * information is not needed * Returns: Pointer to the password or %NULL if not found */ const u8 * eap_get_config_password2(struct eap_sm *sm, size_t *len, int *hash) { struct eap_peer_config *config = eap_get_config(sm); if (config == NULL) return NULL; if (config->flags & EAP_CONFIG_FLAGS_EXT_PASSWORD) { if (eap_get_ext_password(sm, config) < 0) return NULL; if (hash) *hash = 0; *len = wpabuf_len(sm->ext_pw_buf); return wpabuf_head(sm->ext_pw_buf); } *len = config->password_len; if (hash) *hash = !!(config->flags & EAP_CONFIG_FLAGS_PASSWORD_NTHASH); return config->password; } /** * eap_get_config_new_password - Get new password from network configuration * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * @len: Buffer for the length of the new password * Returns: Pointer to the new password or %NULL if not found */ const u8 * eap_get_config_new_password(struct eap_sm *sm, size_t *len) { struct eap_peer_config *config = eap_get_config(sm); if (config == NULL) return NULL; *len = config->new_password_len; return config->new_password; } /** * eap_get_config_otp - Get one-time password from the network configuration * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * @len: Buffer for the length of the one-time password * Returns: Pointer to the one-time password or %NULL if not found */ const u8 * eap_get_config_otp(struct eap_sm *sm, size_t *len) { struct eap_peer_config *config = eap_get_config(sm); if (config == NULL) return NULL; *len = config->otp_len; return config->otp; } /** * eap_clear_config_otp - Clear used one-time password * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * * This function clears a used one-time password (OTP) from the current network * configuration. This should be called when the OTP has been used and is not * needed anymore. */ void eap_clear_config_otp(struct eap_sm *sm) { struct eap_peer_config *config = eap_get_config(sm); if (config == NULL) return; os_memset(config->otp, 0, config->otp_len); os_free(config->otp); config->otp = NULL; config->otp_len = 0; } /** * eap_get_config_phase1 - Get phase1 data from the network configuration * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * Returns: Pointer to the phase1 data or %NULL if not found */ const char * eap_get_config_phase1(struct eap_sm *sm) { struct eap_peer_config *config = eap_get_config(sm); if (config == NULL) return NULL; return config->phase1; } /** * eap_get_config_phase2 - Get phase2 data from the network configuration * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * Returns: Pointer to the phase1 data or %NULL if not found */ const char * eap_get_config_phase2(struct eap_sm *sm) { struct eap_peer_config *config = eap_get_config(sm); if (config == NULL) return NULL; return config->phase2; } int eap_get_config_fragment_size(struct eap_sm *sm) { struct eap_peer_config *config = eap_get_config(sm); if (config == NULL) return -1; return config->fragment_size; } /** * eap_key_available - Get key availability (eapKeyAvailable variable) * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * Returns: 1 if EAP keying material is available, 0 if not */ int eap_key_available(struct eap_sm *sm) { return sm ? sm->eapKeyAvailable : 0; } /** * eap_notify_success - Notify EAP state machine about external success trigger * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * * This function is called when external event, e.g., successful completion of * WPA-PSK key handshake, is indicating that EAP state machine should move to * success state. This is mainly used with security modes that do not use EAP * state machine (e.g., WPA-PSK). */ void eap_notify_success(struct eap_sm *sm) { if (sm) { sm->decision = DECISION_COND_SUCC; sm->EAP_state = EAP_SUCCESS; } } /** * eap_notify_lower_layer_success - Notification of lower layer success * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * * Notify EAP state machines that a lower layer has detected a successful * authentication. This is used to recover from dropped EAP-Success messages. */ void eap_notify_lower_layer_success(struct eap_sm *sm) { if (sm == NULL) return; if (eapol_get_bool(sm, EAPOL_eapSuccess) || sm->decision == DECISION_FAIL || (sm->methodState != METHOD_MAY_CONT && sm->methodState != METHOD_DONE)) return; if (sm->eapKeyData != NULL) sm->eapKeyAvailable = TRUE; eapol_set_bool(sm, EAPOL_eapSuccess, TRUE); wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_SUCCESS "EAP authentication completed successfully (based on lower " "layer success)"); } /** * eap_get_eapSessionId - Get Session-Id from EAP state machine * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * @len: Pointer to variable that will be set to number of bytes in the session * Returns: Pointer to the EAP Session-Id or %NULL on failure * * Fetch EAP Session-Id from the EAP state machine. The Session-Id is available * only after a successful authentication. EAP state machine continues to manage * the Session-Id and the caller must not change or free the returned data. */ const u8 * eap_get_eapSessionId(struct eap_sm *sm, size_t *len) { if (sm == NULL || sm->eapSessionId == NULL) { *len = 0; return NULL; } *len = sm->eapSessionIdLen; return sm->eapSessionId; } /** * eap_get_eapKeyData - Get master session key (MSK) from EAP state machine * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * @len: Pointer to variable that will be set to number of bytes in the key * Returns: Pointer to the EAP keying data or %NULL on failure * * Fetch EAP keying material (MSK, eapKeyData) from the EAP state machine. The * key is available only after a successful authentication. EAP state machine * continues to manage the key data and the caller must not change or free the * returned data. */ const u8 * eap_get_eapKeyData(struct eap_sm *sm, size_t *len) { if (sm == NULL || sm->eapKeyData == NULL) { *len = 0; return NULL; } *len = sm->eapKeyDataLen; return sm->eapKeyData; } /** * eap_get_eapKeyData - Get EAP response data * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * Returns: Pointer to the EAP response (eapRespData) or %NULL on failure * * Fetch EAP response (eapRespData) from the EAP state machine. This data is * available when EAP state machine has processed an incoming EAP request. The * EAP state machine does not maintain a reference to the response after this * function is called and the caller is responsible for freeing the data. */ struct wpabuf * eap_get_eapRespData(struct eap_sm *sm) { struct wpabuf *resp; if (sm == NULL || sm->eapRespData == NULL) return NULL; resp = sm->eapRespData; sm->eapRespData = NULL; return resp; } /** * eap_sm_register_scard_ctx - Notification of smart card context * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * @ctx: Context data for smart card operations * * Notify EAP state machines of context data for smart card operations. This * context data will be used as a parameter for scard_*() functions. */ void eap_register_scard_ctx(struct eap_sm *sm, void *ctx) { if (sm) sm->scard_ctx = ctx; } /** * eap_set_config_blob - Set or add a named configuration blob * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * @blob: New value for the blob * * Adds a new configuration blob or replaces the current value of an existing * blob. */ void eap_set_config_blob(struct eap_sm *sm, struct wpa_config_blob *blob) { #ifndef CONFIG_NO_CONFIG_BLOBS sm->eapol_cb->set_config_blob(sm->eapol_ctx, blob); #endif /* CONFIG_NO_CONFIG_BLOBS */ } /** * eap_get_config_blob - Get a named configuration blob * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * @name: Name of the blob * Returns: Pointer to blob data or %NULL if not found */ const struct wpa_config_blob * eap_get_config_blob(struct eap_sm *sm, const char *name) { #ifndef CONFIG_NO_CONFIG_BLOBS return sm->eapol_cb->get_config_blob(sm->eapol_ctx, name); #else /* CONFIG_NO_CONFIG_BLOBS */ return NULL; #endif /* CONFIG_NO_CONFIG_BLOBS */ } /** * eap_set_force_disabled - Set force_disabled flag * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * @disabled: 1 = EAP disabled, 0 = EAP enabled * * This function is used to force EAP state machine to be disabled when it is * not in use (e.g., with WPA-PSK or plaintext connections). */ void eap_set_force_disabled(struct eap_sm *sm, int disabled) { sm->force_disabled = disabled; } /** * eap_set_external_sim - Set external_sim flag * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * @external_sim: Whether external SIM/USIM processing is used */ void eap_set_external_sim(struct eap_sm *sm, int external_sim) { sm->external_sim = external_sim; } /** * eap_notify_pending - Notify that EAP method is ready to re-process a request * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * * An EAP method can perform a pending operation (e.g., to get a response from * an external process). Once the response is available, this function can be * used to request EAPOL state machine to retry delivering the previously * received (and still unanswered) EAP request to EAP state machine. */ void eap_notify_pending(struct eap_sm *sm) { sm->eapol_cb->notify_pending(sm->eapol_ctx); } /** * eap_invalidate_cached_session - Mark cached session data invalid * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() */ void eap_invalidate_cached_session(struct eap_sm *sm) { if (sm) eap_deinit_prev_method(sm, "invalidate"); } int eap_is_wps_pbc_enrollee(struct eap_peer_config *conf) { if (conf->identity_len != WSC_ID_ENROLLEE_LEN || os_memcmp(conf->identity, WSC_ID_ENROLLEE, WSC_ID_ENROLLEE_LEN)) return 0; /* Not a WPS Enrollee */ if (conf->phase1 == NULL || os_strstr(conf->phase1, "pbc=1") == NULL) return 0; /* Not using PBC */ return 1; } int eap_is_wps_pin_enrollee(struct eap_peer_config *conf) { if (conf->identity_len != WSC_ID_ENROLLEE_LEN || os_memcmp(conf->identity, WSC_ID_ENROLLEE, WSC_ID_ENROLLEE_LEN)) return 0; /* Not a WPS Enrollee */ if (conf->phase1 == NULL || os_strstr(conf->phase1, "pin=") == NULL) return 0; /* Not using PIN */ return 1; } void eap_sm_set_ext_pw_ctx(struct eap_sm *sm, struct ext_password_data *ext) { ext_password_free(sm->ext_pw_buf); sm->ext_pw_buf = NULL; sm->ext_pw = ext; } /** * eap_set_anon_id - Set or add anonymous identity * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * @id: Anonymous identity (e.g., EAP-SIM pseudonym) or %NULL to clear * @len: Length of anonymous identity in octets */ void eap_set_anon_id(struct eap_sm *sm, const u8 *id, size_t len) { if (sm->eapol_cb->set_anon_id) sm->eapol_cb->set_anon_id(sm->eapol_ctx, id, len); } int eap_peer_was_failure_expected(struct eap_sm *sm) { return sm->expected_failure; } wpa-2.1/src/eap_peer/eap.h000066400000000000000000000237741227414666700154640ustar00rootroot00000000000000/* * EAP peer state machine functions (RFC 4137) * Copyright (c) 2004-2012, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef EAP_H #define EAP_H #include "common/defs.h" #include "eap_common/eap_defs.h" #include "eap_peer/eap_methods.h" struct eap_sm; struct wpa_config_blob; struct wpabuf; struct eap_method_type { int vendor; u32 method; }; #ifdef IEEE8021X_EAPOL /** * enum eapol_bool_var - EAPOL boolean state variables for EAP state machine * * These variables are used in the interface between EAP peer state machine and * lower layer. These are defined in RFC 4137, Sect. 4.1. Lower layer code is * expected to maintain these variables and register a callback functions for * EAP state machine to get and set the variables. */ enum eapol_bool_var { /** * EAPOL_eapSuccess - EAP SUCCESS state reached * * EAP state machine reads and writes this value. */ EAPOL_eapSuccess, /** * EAPOL_eapRestart - Lower layer request to restart authentication * * Set to TRUE in lower layer, FALSE in EAP state machine. */ EAPOL_eapRestart, /** * EAPOL_eapFail - EAP FAILURE state reached * * EAP state machine writes this value. */ EAPOL_eapFail, /** * EAPOL_eapResp - Response to send * * Set to TRUE in EAP state machine, FALSE in lower layer. */ EAPOL_eapResp, /** * EAPOL_eapNoResp - Request has been process; no response to send * * Set to TRUE in EAP state machine, FALSE in lower layer. */ EAPOL_eapNoResp, /** * EAPOL_eapReq - EAP request available from lower layer * * Set to TRUE in lower layer, FALSE in EAP state machine. */ EAPOL_eapReq, /** * EAPOL_portEnabled - Lower layer is ready for communication * * EAP state machines reads this value. */ EAPOL_portEnabled, /** * EAPOL_altAccept - Alternate indication of success (RFC3748) * * EAP state machines reads this value. */ EAPOL_altAccept, /** * EAPOL_altReject - Alternate indication of failure (RFC3748) * * EAP state machines reads this value. */ EAPOL_altReject }; /** * enum eapol_int_var - EAPOL integer state variables for EAP state machine * * These variables are used in the interface between EAP peer state machine and * lower layer. These are defined in RFC 4137, Sect. 4.1. Lower layer code is * expected to maintain these variables and register a callback functions for * EAP state machine to get and set the variables. */ enum eapol_int_var { /** * EAPOL_idleWhile - Outside time for EAP peer timeout * * This integer variable is used to provide an outside timer that the * external (to EAP state machine) code must decrement by one every * second until the value reaches zero. This is used in the same way as * EAPOL state machine timers. EAP state machine reads and writes this * value. */ EAPOL_idleWhile }; /** * struct eapol_callbacks - Callback functions from EAP to lower layer * * This structure defines the callback functions that EAP state machine * requires from the lower layer (usually EAPOL state machine) for updating * state variables and requesting information. eapol_ctx from * eap_peer_sm_init() call will be used as the ctx parameter for these * callback functions. */ struct eapol_callbacks { /** * get_config - Get pointer to the current network configuration * @ctx: eapol_ctx from eap_peer_sm_init() call */ struct eap_peer_config * (*get_config)(void *ctx); /** * get_bool - Get a boolean EAPOL state variable * @variable: EAPOL boolean variable to get * Returns: Value of the EAPOL variable */ Boolean (*get_bool)(void *ctx, enum eapol_bool_var variable); /** * set_bool - Set a boolean EAPOL state variable * @ctx: eapol_ctx from eap_peer_sm_init() call * @variable: EAPOL boolean variable to set * @value: Value for the EAPOL variable */ void (*set_bool)(void *ctx, enum eapol_bool_var variable, Boolean value); /** * get_int - Get an integer EAPOL state variable * @ctx: eapol_ctx from eap_peer_sm_init() call * @variable: EAPOL integer variable to get * Returns: Value of the EAPOL variable */ unsigned int (*get_int)(void *ctx, enum eapol_int_var variable); /** * set_int - Set an integer EAPOL state variable * @ctx: eapol_ctx from eap_peer_sm_init() call * @variable: EAPOL integer variable to set * @value: Value for the EAPOL variable */ void (*set_int)(void *ctx, enum eapol_int_var variable, unsigned int value); /** * get_eapReqData - Get EAP-Request data * @ctx: eapol_ctx from eap_peer_sm_init() call * @len: Pointer to variable that will be set to eapReqDataLen * Returns: Reference to eapReqData (EAP state machine will not free * this) or %NULL if eapReqData not available. */ struct wpabuf * (*get_eapReqData)(void *ctx); /** * set_config_blob - Set named configuration blob * @ctx: eapol_ctx from eap_peer_sm_init() call * @blob: New value for the blob * * Adds a new configuration blob or replaces the current value of an * existing blob. */ void (*set_config_blob)(void *ctx, struct wpa_config_blob *blob); /** * get_config_blob - Get a named configuration blob * @ctx: eapol_ctx from eap_peer_sm_init() call * @name: Name of the blob * Returns: Pointer to blob data or %NULL if not found */ const struct wpa_config_blob * (*get_config_blob)(void *ctx, const char *name); /** * notify_pending - Notify that a pending request can be retried * @ctx: eapol_ctx from eap_peer_sm_init() call * * An EAP method can perform a pending operation (e.g., to get a * response from an external process). Once the response is available, * this callback function can be used to request EAPOL state machine to * retry delivering the previously received (and still unanswered) EAP * request to EAP state machine. */ void (*notify_pending)(void *ctx); /** * eap_param_needed - Notify that EAP parameter is needed * @ctx: eapol_ctx from eap_peer_sm_init() call * @field: Field indicator (e.g., WPA_CTRL_REQ_EAP_IDENTITY) * @txt: User readable text describing the required parameter */ void (*eap_param_needed)(void *ctx, enum wpa_ctrl_req_type field, const char *txt); /** * notify_cert - Notification of a peer certificate * @ctx: eapol_ctx from eap_peer_sm_init() call * @depth: Depth in certificate chain (0 = server) * @subject: Subject of the peer certificate * @cert_hash: SHA-256 hash of the certificate * @cert: Peer certificate */ void (*notify_cert)(void *ctx, int depth, const char *subject, const char *cert_hash, const struct wpabuf *cert); /** * notify_status - Notification of the current EAP state * @ctx: eapol_ctx from eap_peer_sm_init() call * @status: Step in the process of EAP authentication * @parameter: Step-specific parameter, e.g., EAP method name */ void (*notify_status)(void *ctx, const char *status, const char *parameter); /** * set_anon_id - Set or add anonymous identity * @ctx: eapol_ctx from eap_peer_sm_init() call * @id: Anonymous identity (e.g., EAP-SIM pseudonym) or %NULL to clear * @len: Length of anonymous identity in octets */ void (*set_anon_id)(void *ctx, const u8 *id, size_t len); }; /** * struct eap_config - Configuration for EAP state machine */ struct eap_config { /** * opensc_engine_path - OpenSC engine for OpenSSL engine support * * Usually, path to engine_opensc.so. */ const char *opensc_engine_path; /** * pkcs11_engine_path - PKCS#11 engine for OpenSSL engine support * * Usually, path to engine_pkcs11.so. */ const char *pkcs11_engine_path; /** * pkcs11_module_path - OpenSC PKCS#11 module for OpenSSL engine * * Usually, path to opensc-pkcs11.so. */ const char *pkcs11_module_path; /** * wps - WPS context data * * This is only used by EAP-WSC and can be left %NULL if not available. */ struct wps_context *wps; /** * cert_in_cb - Include server certificates in callback */ int cert_in_cb; }; struct eap_sm * eap_peer_sm_init(void *eapol_ctx, struct eapol_callbacks *eapol_cb, void *msg_ctx, struct eap_config *conf); void eap_peer_sm_deinit(struct eap_sm *sm); int eap_peer_sm_step(struct eap_sm *sm); void eap_sm_abort(struct eap_sm *sm); int eap_sm_get_status(struct eap_sm *sm, char *buf, size_t buflen, int verbose); const char * eap_sm_get_method_name(struct eap_sm *sm); struct wpabuf * eap_sm_buildIdentity(struct eap_sm *sm, int id, int encrypted); void eap_sm_request_identity(struct eap_sm *sm); void eap_sm_request_password(struct eap_sm *sm); void eap_sm_request_new_password(struct eap_sm *sm); void eap_sm_request_pin(struct eap_sm *sm); void eap_sm_request_otp(struct eap_sm *sm, const char *msg, size_t msg_len); void eap_sm_request_passphrase(struct eap_sm *sm); void eap_sm_request_sim(struct eap_sm *sm, const char *req); void eap_sm_notify_ctrl_attached(struct eap_sm *sm); u32 eap_get_phase2_type(const char *name, int *vendor); struct eap_method_type * eap_get_phase2_types(struct eap_peer_config *config, size_t *count); void eap_set_fast_reauth(struct eap_sm *sm, int enabled); void eap_set_workaround(struct eap_sm *sm, unsigned int workaround); void eap_set_force_disabled(struct eap_sm *sm, int disabled); void eap_set_external_sim(struct eap_sm *sm, int external_sim); int eap_key_available(struct eap_sm *sm); void eap_notify_success(struct eap_sm *sm); void eap_notify_lower_layer_success(struct eap_sm *sm); const u8 * eap_get_eapSessionId(struct eap_sm *sm, size_t *len); const u8 * eap_get_eapKeyData(struct eap_sm *sm, size_t *len); struct wpabuf * eap_get_eapRespData(struct eap_sm *sm); void eap_register_scard_ctx(struct eap_sm *sm, void *ctx); void eap_invalidate_cached_session(struct eap_sm *sm); int eap_is_wps_pbc_enrollee(struct eap_peer_config *conf); int eap_is_wps_pin_enrollee(struct eap_peer_config *conf); struct ext_password_data; void eap_sm_set_ext_pw_ctx(struct eap_sm *sm, struct ext_password_data *ext); void eap_set_anon_id(struct eap_sm *sm, const u8 *id, size_t len); int eap_peer_was_failure_expected(struct eap_sm *sm); #endif /* IEEE8021X_EAPOL */ #endif /* EAP_H */ wpa-2.1/src/eap_peer/eap_aka.c000066400000000000000000001221521227414666700162610ustar00rootroot00000000000000/* * EAP peer method: EAP-AKA (RFC 4187) and EAP-AKA' (RFC 5448) * Copyright (c) 2004-2012, 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 "pcsc_funcs.h" #include "crypto/crypto.h" #include "crypto/sha1.h" #include "crypto/sha256.h" #include "crypto/milenage.h" #include "eap_common/eap_sim_common.h" #include "eap_config.h" #include "eap_i.h" struct eap_aka_data { u8 ik[EAP_AKA_IK_LEN], ck[EAP_AKA_CK_LEN], res[EAP_AKA_RES_MAX_LEN]; size_t res_len; u8 nonce_s[EAP_SIM_NONCE_S_LEN]; u8 mk[EAP_SIM_MK_LEN]; u8 k_aut[EAP_AKA_PRIME_K_AUT_LEN]; u8 k_encr[EAP_SIM_K_ENCR_LEN]; u8 k_re[EAP_AKA_PRIME_K_RE_LEN]; /* EAP-AKA' only */ u8 msk[EAP_SIM_KEYING_DATA_LEN]; u8 emsk[EAP_EMSK_LEN]; u8 rand[EAP_AKA_RAND_LEN], autn[EAP_AKA_AUTN_LEN]; u8 auts[EAP_AKA_AUTS_LEN]; int num_id_req, num_notification; u8 *pseudonym; size_t pseudonym_len; u8 *reauth_id; size_t reauth_id_len; int reauth; unsigned int counter, counter_too_small; u8 *last_eap_identity; size_t last_eap_identity_len; enum { CONTINUE, RESULT_SUCCESS, RESULT_FAILURE, SUCCESS, FAILURE } state; struct wpabuf *id_msgs; int prev_id; int result_ind, use_result_ind; u8 eap_method; u8 *network_name; size_t network_name_len; u16 kdf; int kdf_negotiation; }; #ifndef CONFIG_NO_STDOUT_DEBUG static const char * eap_aka_state_txt(int state) { switch (state) { case CONTINUE: return "CONTINUE"; case RESULT_SUCCESS: return "RESULT_SUCCESS"; case RESULT_FAILURE: return "RESULT_FAILURE"; case SUCCESS: return "SUCCESS"; case FAILURE: return "FAILURE"; default: return "?"; } } #endif /* CONFIG_NO_STDOUT_DEBUG */ static void eap_aka_state(struct eap_aka_data *data, int state) { wpa_printf(MSG_DEBUG, "EAP-AKA: %s -> %s", eap_aka_state_txt(data->state), eap_aka_state_txt(state)); data->state = state; } static void * eap_aka_init(struct eap_sm *sm) { struct eap_aka_data *data; const char *phase1 = eap_get_config_phase1(sm); struct eap_peer_config *config = eap_get_config(sm); data = os_zalloc(sizeof(*data)); if (data == NULL) return NULL; data->eap_method = EAP_TYPE_AKA; eap_aka_state(data, CONTINUE); data->prev_id = -1; data->result_ind = phase1 && os_strstr(phase1, "result_ind=1") != NULL; if (config && config->anonymous_identity) { data->pseudonym = os_malloc(config->anonymous_identity_len); if (data->pseudonym) { os_memcpy(data->pseudonym, config->anonymous_identity, config->anonymous_identity_len); data->pseudonym_len = config->anonymous_identity_len; } } return data; } #ifdef EAP_AKA_PRIME static void * eap_aka_prime_init(struct eap_sm *sm) { struct eap_aka_data *data = eap_aka_init(sm); if (data == NULL) return NULL; data->eap_method = EAP_TYPE_AKA_PRIME; return data; } #endif /* EAP_AKA_PRIME */ static void eap_aka_deinit(struct eap_sm *sm, void *priv) { struct eap_aka_data *data = priv; if (data) { os_free(data->pseudonym); os_free(data->reauth_id); os_free(data->last_eap_identity); wpabuf_free(data->id_msgs); os_free(data->network_name); os_free(data); } } static int eap_aka_ext_sim_req(struct eap_sm *sm, struct eap_aka_data *data) { char req[200], *pos, *end; wpa_printf(MSG_DEBUG, "EAP-AKA: Use external USIM processing"); pos = req; end = pos + sizeof(req); pos += os_snprintf(pos, end - pos, "UMTS-AUTH"); pos += os_snprintf(pos, end - pos, ":"); pos += wpa_snprintf_hex(pos, end - pos, data->rand, EAP_AKA_RAND_LEN); pos += os_snprintf(pos, end - pos, ":"); pos += wpa_snprintf_hex(pos, end - pos, data->autn, EAP_AKA_AUTN_LEN); eap_sm_request_sim(sm, req); return 1; } static int eap_aka_ext_sim_result(struct eap_sm *sm, struct eap_aka_data *data, struct eap_peer_config *conf) { char *resp, *pos; wpa_printf(MSG_DEBUG, "EAP-AKA: Use result from external USIM processing"); resp = conf->external_sim_resp; conf->external_sim_resp = NULL; if (os_strncmp(resp, "UMTS-AUTS:", 10) == 0) { pos = resp + 10; if (hexstr2bin(pos, data->auts, EAP_AKA_AUTS_LEN) < 0) goto invalid; wpa_hexdump_key(MSG_DEBUG, "EAP-AKA: AUTS", data->auts, EAP_AKA_AUTS_LEN); os_free(resp); return -2; } if (os_strncmp(resp, "UMTS-AUTH:", 10) != 0) { wpa_printf(MSG_DEBUG, "EAP-AKA: Unrecognized external USIM processing response"); os_free(resp); return -1; } pos = resp + 10; wpa_hexdump(MSG_DEBUG, "EAP-AKA: RAND", data->rand, EAP_AKA_RAND_LEN); if (hexstr2bin(pos, data->ik, EAP_AKA_IK_LEN) < 0) goto invalid; wpa_hexdump_key(MSG_DEBUG, "EAP-AKA: IK", data->ik, EAP_AKA_IK_LEN); pos += EAP_AKA_IK_LEN * 2; if (*pos != ':') goto invalid; pos++; if (hexstr2bin(pos, data->ck, EAP_AKA_CK_LEN) < 0) goto invalid; wpa_hexdump_key(MSG_DEBUG, "EAP-AKA: CK", data->ck, EAP_AKA_CK_LEN); pos += EAP_AKA_CK_LEN * 2; if (*pos != ':') goto invalid; pos++; data->res_len = os_strlen(pos) / 2; if (data->res_len > EAP_AKA_RES_MAX_LEN) { data->res_len = 0; goto invalid; } if (hexstr2bin(pos, data->res, data->res_len) < 0) goto invalid; wpa_hexdump_key(MSG_DEBUG, "EAP-AKA: RES", data->res, data->res_len); os_free(resp); return 0; invalid: wpa_printf(MSG_DEBUG, "EAP-AKA: Invalid external USIM processing UMTS-AUTH response"); os_free(resp); return -1; } static int eap_aka_umts_auth(struct eap_sm *sm, struct eap_aka_data *data) { struct eap_peer_config *conf; wpa_printf(MSG_DEBUG, "EAP-AKA: UMTS authentication algorithm"); conf = eap_get_config(sm); if (conf == NULL) return -1; if (sm->external_sim) { if (conf->external_sim_resp) return eap_aka_ext_sim_result(sm, data, conf); else return eap_aka_ext_sim_req(sm, data); } if (conf->pcsc) { return scard_umts_auth(sm->scard_ctx, data->rand, data->autn, data->res, &data->res_len, data->ik, data->ck, data->auts); } #ifdef CONFIG_USIM_SIMULATOR if (conf->password) { u8 opc[16], k[16], sqn[6]; const char *pos; wpa_printf(MSG_DEBUG, "EAP-AKA: Use internal Milenage " "implementation for UMTS authentication"); if (conf->password_len < 78) { wpa_printf(MSG_DEBUG, "EAP-AKA: invalid Milenage " "password"); return -1; } pos = (const char *) conf->password; if (hexstr2bin(pos, k, 16)) return -1; pos += 32; if (*pos != ':') return -1; pos++; if (hexstr2bin(pos, opc, 16)) return -1; pos += 32; if (*pos != ':') return -1; pos++; if (hexstr2bin(pos, sqn, 6)) return -1; return milenage_check(opc, k, sqn, data->rand, data->autn, data->ik, data->ck, data->res, &data->res_len, data->auts); } #endif /* CONFIG_USIM_SIMULATOR */ #ifdef CONFIG_USIM_HARDCODED wpa_printf(MSG_DEBUG, "EAP-AKA: Use hardcoded Kc and SRES values for " "testing"); /* These hardcoded Kc and SRES values are used for testing. * Could consider making them configurable. */ os_memset(data->res, '2', EAP_AKA_RES_MAX_LEN); data->res_len = EAP_AKA_RES_MAX_LEN; os_memset(data->ik, '3', EAP_AKA_IK_LEN); os_memset(data->ck, '4', EAP_AKA_CK_LEN); { u8 autn[EAP_AKA_AUTN_LEN]; os_memset(autn, '1', EAP_AKA_AUTN_LEN); if (os_memcmp(autn, data->autn, EAP_AKA_AUTN_LEN) != 0) { wpa_printf(MSG_WARNING, "EAP-AKA: AUTN did not match " "with expected value"); return -1; } } #if 0 { static int test_resync = 1; if (test_resync) { /* Test Resynchronization */ test_resync = 0; return -2; } } #endif return 0; #else /* CONFIG_USIM_HARDCODED */ wpa_printf(MSG_DEBUG, "EAP-AKA: No UMTS authentication algorith " "enabled"); return -1; #endif /* CONFIG_USIM_HARDCODED */ } #define CLEAR_PSEUDONYM 0x01 #define CLEAR_REAUTH_ID 0x02 #define CLEAR_EAP_ID 0x04 static void eap_aka_clear_identities(struct eap_sm *sm, struct eap_aka_data *data, int id) { if ((id & CLEAR_PSEUDONYM) && data->pseudonym) { wpa_printf(MSG_DEBUG, "EAP-AKA: forgetting old pseudonym"); os_free(data->pseudonym); data->pseudonym = NULL; data->pseudonym_len = 0; eap_set_anon_id(sm, NULL, 0); } if ((id & CLEAR_REAUTH_ID) && data->reauth_id) { wpa_printf(MSG_DEBUG, "EAP-AKA: forgetting old reauth_id"); os_free(data->reauth_id); data->reauth_id = NULL; data->reauth_id_len = 0; } if ((id & CLEAR_EAP_ID) && data->last_eap_identity) { wpa_printf(MSG_DEBUG, "EAP-AKA: forgetting old eap_id"); os_free(data->last_eap_identity); data->last_eap_identity = NULL; data->last_eap_identity_len = 0; } } static int eap_aka_learn_ids(struct eap_sm *sm, struct eap_aka_data *data, struct eap_sim_attrs *attr) { if (attr->next_pseudonym) { const u8 *identity = NULL; size_t identity_len = 0; const u8 *realm = NULL; size_t realm_len = 0; wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA: (encr) AT_NEXT_PSEUDONYM", attr->next_pseudonym, attr->next_pseudonym_len); os_free(data->pseudonym); /* Look for the realm of the permanent identity */ identity = eap_get_config_identity(sm, &identity_len); if (identity) { for (realm = identity, realm_len = identity_len; realm_len > 0; realm_len--, realm++) { if (*realm == '@') break; } } data->pseudonym = os_malloc(attr->next_pseudonym_len + realm_len); if (data->pseudonym == NULL) { wpa_printf(MSG_INFO, "EAP-AKA: (encr) No memory for " "next pseudonym"); data->pseudonym_len = 0; return -1; } os_memcpy(data->pseudonym, attr->next_pseudonym, attr->next_pseudonym_len); if (realm_len) { os_memcpy(data->pseudonym + attr->next_pseudonym_len, realm, realm_len); } data->pseudonym_len = attr->next_pseudonym_len + realm_len; eap_set_anon_id(sm, data->pseudonym, data->pseudonym_len); } if (attr->next_reauth_id) { os_free(data->reauth_id); data->reauth_id = os_malloc(attr->next_reauth_id_len); if (data->reauth_id == NULL) { wpa_printf(MSG_INFO, "EAP-AKA: (encr) No memory for " "next reauth_id"); data->reauth_id_len = 0; return -1; } os_memcpy(data->reauth_id, attr->next_reauth_id, attr->next_reauth_id_len); data->reauth_id_len = attr->next_reauth_id_len; wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA: (encr) AT_NEXT_REAUTH_ID", data->reauth_id, data->reauth_id_len); } return 0; } static int eap_aka_add_id_msg(struct eap_aka_data *data, const struct wpabuf *msg) { if (msg == NULL) return -1; if (data->id_msgs == NULL) { data->id_msgs = wpabuf_dup(msg); return data->id_msgs == NULL ? -1 : 0; } if (wpabuf_resize(&data->id_msgs, wpabuf_len(msg)) < 0) return -1; wpabuf_put_buf(data->id_msgs, msg); return 0; } static void eap_aka_add_checkcode(struct eap_aka_data *data, struct eap_sim_msg *msg) { const u8 *addr; size_t len; u8 hash[SHA256_MAC_LEN]; wpa_printf(MSG_DEBUG, " AT_CHECKCODE"); if (data->id_msgs == NULL) { /* * No EAP-AKA/Identity packets were exchanged - send empty * checkcode. */ eap_sim_msg_add(msg, EAP_SIM_AT_CHECKCODE, 0, NULL, 0); return; } /* Checkcode is SHA1/SHA256 hash over all EAP-AKA/Identity packets. */ addr = wpabuf_head(data->id_msgs); len = wpabuf_len(data->id_msgs); wpa_hexdump(MSG_MSGDUMP, "EAP-AKA: AT_CHECKCODE data", addr, len); #ifdef EAP_AKA_PRIME if (data->eap_method == EAP_TYPE_AKA_PRIME) sha256_vector(1, &addr, &len, hash); else #endif /* EAP_AKA_PRIME */ sha1_vector(1, &addr, &len, hash); eap_sim_msg_add(msg, EAP_SIM_AT_CHECKCODE, 0, hash, data->eap_method == EAP_TYPE_AKA_PRIME ? EAP_AKA_PRIME_CHECKCODE_LEN : EAP_AKA_CHECKCODE_LEN); } static int eap_aka_verify_checkcode(struct eap_aka_data *data, const u8 *checkcode, size_t checkcode_len) { const u8 *addr; size_t len; u8 hash[SHA256_MAC_LEN]; size_t hash_len; if (checkcode == NULL) return -1; if (data->id_msgs == NULL) { if (checkcode_len != 0) { wpa_printf(MSG_DEBUG, "EAP-AKA: Checkcode from server " "indicates that AKA/Identity messages were " "used, but they were not"); return -1; } return 0; } hash_len = data->eap_method == EAP_TYPE_AKA_PRIME ? EAP_AKA_PRIME_CHECKCODE_LEN : EAP_AKA_CHECKCODE_LEN; if (checkcode_len != hash_len) { wpa_printf(MSG_DEBUG, "EAP-AKA: Checkcode from server " "indicates that AKA/Identity message were not " "used, but they were"); return -1; } /* Checkcode is SHA1/SHA256 hash over all EAP-AKA/Identity packets. */ addr = wpabuf_head(data->id_msgs); len = wpabuf_len(data->id_msgs); #ifdef EAP_AKA_PRIME if (data->eap_method == EAP_TYPE_AKA_PRIME) sha256_vector(1, &addr, &len, hash); else #endif /* EAP_AKA_PRIME */ sha1_vector(1, &addr, &len, hash); if (os_memcmp(hash, checkcode, hash_len) != 0) { wpa_printf(MSG_DEBUG, "EAP-AKA: Mismatch in AT_CHECKCODE"); return -1; } return 0; } static struct wpabuf * eap_aka_client_error(struct eap_aka_data *data, u8 id, int err) { struct eap_sim_msg *msg; eap_aka_state(data, FAILURE); data->num_id_req = 0; data->num_notification = 0; wpa_printf(MSG_DEBUG, "EAP-AKA: Send Client-Error (error code %d)", err); msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method, EAP_AKA_SUBTYPE_CLIENT_ERROR); eap_sim_msg_add(msg, EAP_SIM_AT_CLIENT_ERROR_CODE, err, NULL, 0); return eap_sim_msg_finish(msg, NULL, NULL, 0); } static struct wpabuf * eap_aka_authentication_reject(struct eap_aka_data *data, u8 id) { struct eap_sim_msg *msg; eap_aka_state(data, FAILURE); data->num_id_req = 0; data->num_notification = 0; wpa_printf(MSG_DEBUG, "Generating EAP-AKA Authentication-Reject " "(id=%d)", id); msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method, EAP_AKA_SUBTYPE_AUTHENTICATION_REJECT); return eap_sim_msg_finish(msg, NULL, NULL, 0); } static struct wpabuf * eap_aka_synchronization_failure( struct eap_aka_data *data, u8 id) { struct eap_sim_msg *msg; data->num_id_req = 0; data->num_notification = 0; wpa_printf(MSG_DEBUG, "Generating EAP-AKA Synchronization-Failure " "(id=%d)", id); msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method, EAP_AKA_SUBTYPE_SYNCHRONIZATION_FAILURE); wpa_printf(MSG_DEBUG, " AT_AUTS"); eap_sim_msg_add_full(msg, EAP_SIM_AT_AUTS, data->auts, EAP_AKA_AUTS_LEN); return eap_sim_msg_finish(msg, NULL, NULL, 0); } static struct wpabuf * eap_aka_response_identity(struct eap_sm *sm, struct eap_aka_data *data, u8 id, enum eap_sim_id_req id_req) { const u8 *identity = NULL; size_t identity_len = 0; struct eap_sim_msg *msg; data->reauth = 0; if (id_req == ANY_ID && data->reauth_id) { identity = data->reauth_id; identity_len = data->reauth_id_len; data->reauth = 1; } else if ((id_req == ANY_ID || id_req == FULLAUTH_ID) && data->pseudonym) { identity = data->pseudonym; identity_len = data->pseudonym_len; eap_aka_clear_identities(sm, data, CLEAR_REAUTH_ID); } else if (id_req != NO_ID_REQ) { identity = eap_get_config_identity(sm, &identity_len); if (identity) { eap_aka_clear_identities(sm, data, CLEAR_PSEUDONYM | CLEAR_REAUTH_ID); } } if (id_req != NO_ID_REQ) eap_aka_clear_identities(sm, data, CLEAR_EAP_ID); wpa_printf(MSG_DEBUG, "Generating EAP-AKA Identity (id=%d)", id); msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method, EAP_AKA_SUBTYPE_IDENTITY); if (identity) { wpa_hexdump_ascii(MSG_DEBUG, " AT_IDENTITY", identity, identity_len); eap_sim_msg_add(msg, EAP_SIM_AT_IDENTITY, identity_len, identity, identity_len); } return eap_sim_msg_finish(msg, NULL, NULL, 0); } static struct wpabuf * eap_aka_response_challenge(struct eap_aka_data *data, u8 id) { struct eap_sim_msg *msg; wpa_printf(MSG_DEBUG, "Generating EAP-AKA Challenge (id=%d)", id); msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method, EAP_AKA_SUBTYPE_CHALLENGE); wpa_printf(MSG_DEBUG, " AT_RES"); eap_sim_msg_add(msg, EAP_SIM_AT_RES, data->res_len * 8, data->res, data->res_len); eap_aka_add_checkcode(data, msg); if (data->use_result_ind) { wpa_printf(MSG_DEBUG, " AT_RESULT_IND"); eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0); } wpa_printf(MSG_DEBUG, " AT_MAC"); eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); return eap_sim_msg_finish(msg, data->k_aut, (u8 *) "", 0); } static struct wpabuf * eap_aka_response_reauth(struct eap_aka_data *data, u8 id, int counter_too_small, const u8 *nonce_s) { struct eap_sim_msg *msg; unsigned int counter; wpa_printf(MSG_DEBUG, "Generating EAP-AKA Reauthentication (id=%d)", id); msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method, EAP_AKA_SUBTYPE_REAUTHENTICATION); wpa_printf(MSG_DEBUG, " AT_IV"); wpa_printf(MSG_DEBUG, " AT_ENCR_DATA"); eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV, EAP_SIM_AT_ENCR_DATA); if (counter_too_small) { wpa_printf(MSG_DEBUG, " *AT_COUNTER_TOO_SMALL"); eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER_TOO_SMALL, 0, NULL, 0); counter = data->counter_too_small; } else counter = data->counter; wpa_printf(MSG_DEBUG, " *AT_COUNTER %d", counter); eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, counter, NULL, 0); if (eap_sim_msg_add_encr_end(msg, data->k_encr, EAP_SIM_AT_PADDING)) { wpa_printf(MSG_WARNING, "EAP-AKA: Failed to encrypt " "AT_ENCR_DATA"); eap_sim_msg_free(msg); return NULL; } eap_aka_add_checkcode(data, msg); if (data->use_result_ind) { wpa_printf(MSG_DEBUG, " AT_RESULT_IND"); eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0); } wpa_printf(MSG_DEBUG, " AT_MAC"); eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); return eap_sim_msg_finish(msg, data->k_aut, nonce_s, EAP_SIM_NONCE_S_LEN); } static struct wpabuf * eap_aka_response_notification(struct eap_aka_data *data, u8 id, u16 notification) { struct eap_sim_msg *msg; u8 *k_aut = (notification & 0x4000) == 0 ? data->k_aut : NULL; wpa_printf(MSG_DEBUG, "Generating EAP-AKA Notification (id=%d)", id); msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method, EAP_AKA_SUBTYPE_NOTIFICATION); if (k_aut && data->reauth) { wpa_printf(MSG_DEBUG, " AT_IV"); wpa_printf(MSG_DEBUG, " AT_ENCR_DATA"); eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV, EAP_SIM_AT_ENCR_DATA); wpa_printf(MSG_DEBUG, " *AT_COUNTER %d", data->counter); eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, data->counter, NULL, 0); if (eap_sim_msg_add_encr_end(msg, data->k_encr, EAP_SIM_AT_PADDING)) { wpa_printf(MSG_WARNING, "EAP-AKA: Failed to encrypt " "AT_ENCR_DATA"); eap_sim_msg_free(msg); return NULL; } } if (k_aut) { wpa_printf(MSG_DEBUG, " AT_MAC"); eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); } return eap_sim_msg_finish(msg, k_aut, (u8 *) "", 0); } static struct wpabuf * eap_aka_process_identity(struct eap_sm *sm, struct eap_aka_data *data, u8 id, const struct wpabuf *reqData, struct eap_sim_attrs *attr) { int id_error; struct wpabuf *buf; wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Identity"); id_error = 0; switch (attr->id_req) { case NO_ID_REQ: break; case ANY_ID: if (data->num_id_req > 0) id_error++; data->num_id_req++; break; case FULLAUTH_ID: if (data->num_id_req > 1) id_error++; data->num_id_req++; break; case PERMANENT_ID: if (data->num_id_req > 2) id_error++; data->num_id_req++; break; } if (id_error) { wpa_printf(MSG_INFO, "EAP-AKA: Too many ID requests " "used within one authentication"); return eap_aka_client_error(data, id, EAP_AKA_UNABLE_TO_PROCESS_PACKET); } buf = eap_aka_response_identity(sm, data, id, attr->id_req); if (data->prev_id != id) { eap_aka_add_id_msg(data, reqData); eap_aka_add_id_msg(data, buf); data->prev_id = id; } return buf; } static int eap_aka_verify_mac(struct eap_aka_data *data, const struct wpabuf *req, const u8 *mac, const u8 *extra, size_t extra_len) { if (data->eap_method == EAP_TYPE_AKA_PRIME) return eap_sim_verify_mac_sha256(data->k_aut, req, mac, extra, extra_len); return eap_sim_verify_mac(data->k_aut, req, mac, extra, extra_len); } #ifdef EAP_AKA_PRIME static struct wpabuf * eap_aka_prime_kdf_select(struct eap_aka_data *data, u8 id, u16 kdf) { struct eap_sim_msg *msg; data->kdf_negotiation = 1; data->kdf = kdf; wpa_printf(MSG_DEBUG, "Generating EAP-AKA Challenge (id=%d) (KDF " "select)", id); msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method, EAP_AKA_SUBTYPE_CHALLENGE); wpa_printf(MSG_DEBUG, " AT_KDF"); eap_sim_msg_add(msg, EAP_SIM_AT_KDF, kdf, NULL, 0); return eap_sim_msg_finish(msg, NULL, NULL, 0); } static struct wpabuf * eap_aka_prime_kdf_neg(struct eap_aka_data *data, u8 id, struct eap_sim_attrs *attr) { size_t i; for (i = 0; i < attr->kdf_count; i++) { if (attr->kdf[i] == EAP_AKA_PRIME_KDF) return eap_aka_prime_kdf_select(data, id, EAP_AKA_PRIME_KDF); } /* No matching KDF found - fail authentication as if AUTN had been * incorrect */ return eap_aka_authentication_reject(data, id); } static int eap_aka_prime_kdf_valid(struct eap_aka_data *data, struct eap_sim_attrs *attr) { size_t i, j; if (attr->kdf_count == 0) return 0; /* The only allowed (and required) duplication of a KDF is the addition * of the selected KDF into the beginning of the list. */ if (data->kdf_negotiation) { if (attr->kdf[0] != data->kdf) { wpa_printf(MSG_WARNING, "EAP-AKA': The server did not " "accept the selected KDF"); return 0; } for (i = 1; i < attr->kdf_count; i++) { if (attr->kdf[i] == data->kdf) break; } if (i == attr->kdf_count && attr->kdf_count < EAP_AKA_PRIME_KDF_MAX) { wpa_printf(MSG_WARNING, "EAP-AKA': The server did not " "duplicate the selected KDF"); return 0; } /* TODO: should check that the list is identical to the one * used in the previous Challenge message apart from the added * entry in the beginning. */ } for (i = data->kdf ? 1 : 0; i < attr->kdf_count; i++) { for (j = i + 1; j < attr->kdf_count; j++) { if (attr->kdf[i] == attr->kdf[j]) { wpa_printf(MSG_WARNING, "EAP-AKA': The server " "included a duplicated KDF"); return 0; } } } return 1; } #endif /* EAP_AKA_PRIME */ static struct wpabuf * eap_aka_process_challenge(struct eap_sm *sm, struct eap_aka_data *data, u8 id, const struct wpabuf *reqData, struct eap_sim_attrs *attr) { const u8 *identity; size_t identity_len; int res; struct eap_sim_attrs eattr; wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Challenge"); if (attr->checkcode && eap_aka_verify_checkcode(data, attr->checkcode, attr->checkcode_len)) { wpa_printf(MSG_WARNING, "EAP-AKA: Invalid AT_CHECKCODE in the " "message"); return eap_aka_client_error(data, id, EAP_AKA_UNABLE_TO_PROCESS_PACKET); } #ifdef EAP_AKA_PRIME if (data->eap_method == EAP_TYPE_AKA_PRIME) { if (!attr->kdf_input || attr->kdf_input_len == 0) { wpa_printf(MSG_WARNING, "EAP-AKA': Challenge message " "did not include non-empty AT_KDF_INPUT"); /* Fail authentication as if AUTN had been incorrect */ return eap_aka_authentication_reject(data, id); } os_free(data->network_name); data->network_name = os_malloc(attr->kdf_input_len); if (data->network_name == NULL) { wpa_printf(MSG_WARNING, "EAP-AKA': No memory for " "storing Network Name"); return eap_aka_authentication_reject(data, id); } os_memcpy(data->network_name, attr->kdf_input, attr->kdf_input_len); data->network_name_len = attr->kdf_input_len; wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA': Network Name " "(AT_KDF_INPUT)", data->network_name, data->network_name_len); /* TODO: check Network Name per 3GPP.33.402 */ if (!eap_aka_prime_kdf_valid(data, attr)) return eap_aka_authentication_reject(data, id); if (attr->kdf[0] != EAP_AKA_PRIME_KDF) return eap_aka_prime_kdf_neg(data, id, attr); data->kdf = EAP_AKA_PRIME_KDF; wpa_printf(MSG_DEBUG, "EAP-AKA': KDF %d selected", data->kdf); } if (data->eap_method == EAP_TYPE_AKA && attr->bidding) { u16 flags = WPA_GET_BE16(attr->bidding); if ((flags & EAP_AKA_BIDDING_FLAG_D) && eap_allowed_method(sm, EAP_VENDOR_IETF, EAP_TYPE_AKA_PRIME)) { wpa_printf(MSG_WARNING, "EAP-AKA: Bidding down from " "AKA' to AKA detected"); /* Fail authentication as if AUTN had been incorrect */ return eap_aka_authentication_reject(data, id); } } #endif /* EAP_AKA_PRIME */ data->reauth = 0; if (!attr->mac || !attr->rand || !attr->autn) { wpa_printf(MSG_WARNING, "EAP-AKA: Challenge message " "did not include%s%s%s", !attr->mac ? " AT_MAC" : "", !attr->rand ? " AT_RAND" : "", !attr->autn ? " AT_AUTN" : ""); return eap_aka_client_error(data, id, EAP_AKA_UNABLE_TO_PROCESS_PACKET); } os_memcpy(data->rand, attr->rand, EAP_AKA_RAND_LEN); os_memcpy(data->autn, attr->autn, EAP_AKA_AUTN_LEN); res = eap_aka_umts_auth(sm, data); if (res == -1) { wpa_printf(MSG_WARNING, "EAP-AKA: UMTS authentication " "failed (AUTN)"); return eap_aka_authentication_reject(data, id); } else if (res == -2) { wpa_printf(MSG_WARNING, "EAP-AKA: UMTS authentication " "failed (AUTN seq# -> AUTS)"); return eap_aka_synchronization_failure(data, id); } else if (res > 0) { wpa_printf(MSG_DEBUG, "EAP-AKA: Wait for external USIM processing"); return NULL; } else if (res) { wpa_printf(MSG_WARNING, "EAP-AKA: UMTS authentication failed"); return eap_aka_client_error(data, id, EAP_AKA_UNABLE_TO_PROCESS_PACKET); } #ifdef EAP_AKA_PRIME if (data->eap_method == EAP_TYPE_AKA_PRIME) { /* Note: AUTN = (SQN ^ AK) || AMF || MAC which gives us the * needed 6-octet SQN ^ AK for CK',IK' derivation */ u16 amf = WPA_GET_BE16(data->autn + 6); if (!(amf & 0x8000)) { wpa_printf(MSG_WARNING, "EAP-AKA': AMF separation bit " "not set (AMF=0x%4x)", amf); return eap_aka_authentication_reject(data, id); } eap_aka_prime_derive_ck_ik_prime(data->ck, data->ik, data->autn, data->network_name, data->network_name_len); } #endif /* EAP_AKA_PRIME */ if (data->last_eap_identity) { identity = data->last_eap_identity; identity_len = data->last_eap_identity_len; } else if (data->pseudonym) { identity = data->pseudonym; identity_len = data->pseudonym_len; } else identity = eap_get_config_identity(sm, &identity_len); wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA: Selected identity for MK " "derivation", identity, identity_len); if (data->eap_method == EAP_TYPE_AKA_PRIME) { eap_aka_prime_derive_keys(identity, identity_len, data->ik, data->ck, data->k_encr, data->k_aut, data->k_re, data->msk, data->emsk); } else { eap_aka_derive_mk(identity, identity_len, data->ik, data->ck, data->mk); eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut, data->msk, data->emsk); } if (eap_aka_verify_mac(data, reqData, attr->mac, (u8 *) "", 0)) { wpa_printf(MSG_WARNING, "EAP-AKA: Challenge message " "used invalid AT_MAC"); return eap_aka_client_error(data, id, EAP_AKA_UNABLE_TO_PROCESS_PACKET); } /* Old reauthentication identity must not be used anymore. In * other words, if no new identities are received, full * authentication will be used on next reauthentication (using * pseudonym identity or permanent identity). */ eap_aka_clear_identities(sm, data, CLEAR_REAUTH_ID | CLEAR_EAP_ID); if (attr->encr_data) { u8 *decrypted; decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data, attr->encr_data_len, attr->iv, &eattr, 0); if (decrypted == NULL) { return eap_aka_client_error( data, id, EAP_AKA_UNABLE_TO_PROCESS_PACKET); } eap_aka_learn_ids(sm, data, &eattr); os_free(decrypted); } if (data->result_ind && attr->result_ind) data->use_result_ind = 1; if (data->state != FAILURE && data->state != RESULT_FAILURE) { eap_aka_state(data, data->use_result_ind ? RESULT_SUCCESS : SUCCESS); } data->num_id_req = 0; data->num_notification = 0; /* RFC 4187 specifies that counter is initialized to one after * fullauth, but initializing it to zero makes it easier to implement * reauth verification. */ data->counter = 0; return eap_aka_response_challenge(data, id); } static int eap_aka_process_notification_reauth(struct eap_aka_data *data, struct eap_sim_attrs *attr) { struct eap_sim_attrs eattr; u8 *decrypted; if (attr->encr_data == NULL || attr->iv == NULL) { wpa_printf(MSG_WARNING, "EAP-AKA: Notification message after " "reauth did not include encrypted data"); return -1; } decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data, attr->encr_data_len, attr->iv, &eattr, 0); if (decrypted == NULL) { wpa_printf(MSG_WARNING, "EAP-AKA: Failed to parse encrypted " "data from notification message"); return -1; } if (eattr.counter < 0 || (size_t) eattr.counter != data->counter) { wpa_printf(MSG_WARNING, "EAP-AKA: Counter in notification " "message does not match with counter in reauth " "message"); os_free(decrypted); return -1; } os_free(decrypted); return 0; } static int eap_aka_process_notification_auth(struct eap_aka_data *data, const struct wpabuf *reqData, struct eap_sim_attrs *attr) { if (attr->mac == NULL) { wpa_printf(MSG_INFO, "EAP-AKA: no AT_MAC in after_auth " "Notification message"); return -1; } if (eap_aka_verify_mac(data, reqData, attr->mac, (u8 *) "", 0)) { wpa_printf(MSG_WARNING, "EAP-AKA: Notification message " "used invalid AT_MAC"); return -1; } if (data->reauth && eap_aka_process_notification_reauth(data, attr)) { wpa_printf(MSG_WARNING, "EAP-AKA: Invalid notification " "message after reauth"); return -1; } return 0; } static struct wpabuf * eap_aka_process_notification( struct eap_sm *sm, struct eap_aka_data *data, u8 id, const struct wpabuf *reqData, struct eap_sim_attrs *attr) { wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Notification"); if (data->num_notification > 0) { wpa_printf(MSG_INFO, "EAP-AKA: too many notification " "rounds (only one allowed)"); return eap_aka_client_error(data, id, EAP_AKA_UNABLE_TO_PROCESS_PACKET); } data->num_notification++; if (attr->notification == -1) { wpa_printf(MSG_INFO, "EAP-AKA: no AT_NOTIFICATION in " "Notification message"); return eap_aka_client_error(data, id, EAP_AKA_UNABLE_TO_PROCESS_PACKET); } if ((attr->notification & 0x4000) == 0 && eap_aka_process_notification_auth(data, reqData, attr)) { return eap_aka_client_error(data, id, EAP_AKA_UNABLE_TO_PROCESS_PACKET); } eap_sim_report_notification(sm->msg_ctx, attr->notification, 1); if (attr->notification >= 0 && attr->notification < 32768) { eap_aka_state(data, FAILURE); } else if (attr->notification == EAP_SIM_SUCCESS && data->state == RESULT_SUCCESS) eap_aka_state(data, SUCCESS); return eap_aka_response_notification(data, id, attr->notification); } static struct wpabuf * eap_aka_process_reauthentication( struct eap_sm *sm, struct eap_aka_data *data, u8 id, const struct wpabuf *reqData, struct eap_sim_attrs *attr) { struct eap_sim_attrs eattr; u8 *decrypted; wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Reauthentication"); if (attr->checkcode && eap_aka_verify_checkcode(data, attr->checkcode, attr->checkcode_len)) { wpa_printf(MSG_WARNING, "EAP-AKA: Invalid AT_CHECKCODE in the " "message"); return eap_aka_client_error(data, id, EAP_AKA_UNABLE_TO_PROCESS_PACKET); } if (data->reauth_id == NULL) { wpa_printf(MSG_WARNING, "EAP-AKA: Server is trying " "reauthentication, but no reauth_id available"); return eap_aka_client_error(data, id, EAP_AKA_UNABLE_TO_PROCESS_PACKET); } data->reauth = 1; if (eap_aka_verify_mac(data, reqData, attr->mac, (u8 *) "", 0)) { wpa_printf(MSG_WARNING, "EAP-AKA: Reauthentication " "did not have valid AT_MAC"); return eap_aka_client_error(data, id, EAP_AKA_UNABLE_TO_PROCESS_PACKET); } if (attr->encr_data == NULL || attr->iv == NULL) { wpa_printf(MSG_WARNING, "EAP-AKA: Reauthentication " "message did not include encrypted data"); return eap_aka_client_error(data, id, EAP_AKA_UNABLE_TO_PROCESS_PACKET); } decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data, attr->encr_data_len, attr->iv, &eattr, 0); if (decrypted == NULL) { wpa_printf(MSG_WARNING, "EAP-AKA: Failed to parse encrypted " "data from reauthentication message"); return eap_aka_client_error(data, id, EAP_AKA_UNABLE_TO_PROCESS_PACKET); } if (eattr.nonce_s == NULL || eattr.counter < 0) { wpa_printf(MSG_INFO, "EAP-AKA: (encr) No%s%s in reauth packet", !eattr.nonce_s ? " AT_NONCE_S" : "", eattr.counter < 0 ? " AT_COUNTER" : ""); os_free(decrypted); return eap_aka_client_error(data, id, EAP_AKA_UNABLE_TO_PROCESS_PACKET); } if (eattr.counter < 0 || (size_t) eattr.counter <= data->counter) { struct wpabuf *res; wpa_printf(MSG_INFO, "EAP-AKA: (encr) Invalid counter " "(%d <= %d)", eattr.counter, data->counter); data->counter_too_small = eattr.counter; /* Reply using Re-auth w/ AT_COUNTER_TOO_SMALL. The current * reauth_id must not be used to start a new reauthentication. * However, since it was used in the last EAP-Response-Identity * packet, it has to saved for the following fullauth to be * used in MK derivation. */ os_free(data->last_eap_identity); data->last_eap_identity = data->reauth_id; data->last_eap_identity_len = data->reauth_id_len; data->reauth_id = NULL; data->reauth_id_len = 0; res = eap_aka_response_reauth(data, id, 1, eattr.nonce_s); os_free(decrypted); return res; } data->counter = eattr.counter; os_memcpy(data->nonce_s, eattr.nonce_s, EAP_SIM_NONCE_S_LEN); wpa_hexdump(MSG_DEBUG, "EAP-AKA: (encr) AT_NONCE_S", data->nonce_s, EAP_SIM_NONCE_S_LEN); if (data->eap_method == EAP_TYPE_AKA_PRIME) { eap_aka_prime_derive_keys_reauth(data->k_re, data->counter, data->reauth_id, data->reauth_id_len, data->nonce_s, data->msk, data->emsk); } else { eap_sim_derive_keys_reauth(data->counter, data->reauth_id, data->reauth_id_len, data->nonce_s, data->mk, data->msk, data->emsk); } eap_aka_clear_identities(sm, data, CLEAR_REAUTH_ID | CLEAR_EAP_ID); eap_aka_learn_ids(sm, data, &eattr); if (data->result_ind && attr->result_ind) data->use_result_ind = 1; if (data->state != FAILURE && data->state != RESULT_FAILURE) { eap_aka_state(data, data->use_result_ind ? RESULT_SUCCESS : SUCCESS); } data->num_id_req = 0; data->num_notification = 0; if (data->counter > EAP_AKA_MAX_FAST_REAUTHS) { wpa_printf(MSG_DEBUG, "EAP-AKA: Maximum number of " "fast reauths performed - force fullauth"); eap_aka_clear_identities(sm, data, CLEAR_REAUTH_ID | CLEAR_EAP_ID); } os_free(decrypted); return eap_aka_response_reauth(data, id, 0, data->nonce_s); } static struct wpabuf * eap_aka_process(struct eap_sm *sm, void *priv, struct eap_method_ret *ret, const struct wpabuf *reqData) { struct eap_aka_data *data = priv; const struct eap_hdr *req; u8 subtype, id; struct wpabuf *res; const u8 *pos; struct eap_sim_attrs attr; size_t len; wpa_hexdump_buf(MSG_DEBUG, "EAP-AKA: EAP data", reqData); if (eap_get_config_identity(sm, &len) == NULL) { wpa_printf(MSG_INFO, "EAP-AKA: Identity not configured"); eap_sm_request_identity(sm); ret->ignore = TRUE; return NULL; } pos = eap_hdr_validate(EAP_VENDOR_IETF, data->eap_method, reqData, &len); if (pos == NULL || len < 1) { ret->ignore = TRUE; return NULL; } req = wpabuf_head(reqData); id = req->identifier; len = be_to_host16(req->length); ret->ignore = FALSE; ret->methodState = METHOD_MAY_CONT; ret->decision = DECISION_FAIL; ret->allowNotifications = TRUE; subtype = *pos++; wpa_printf(MSG_DEBUG, "EAP-AKA: Subtype=%d", subtype); pos += 2; /* Reserved */ if (eap_sim_parse_attr(pos, wpabuf_head_u8(reqData) + len, &attr, data->eap_method == EAP_TYPE_AKA_PRIME ? 2 : 1, 0)) { res = eap_aka_client_error(data, id, EAP_AKA_UNABLE_TO_PROCESS_PACKET); goto done; } switch (subtype) { case EAP_AKA_SUBTYPE_IDENTITY: res = eap_aka_process_identity(sm, data, id, reqData, &attr); break; case EAP_AKA_SUBTYPE_CHALLENGE: res = eap_aka_process_challenge(sm, data, id, reqData, &attr); break; case EAP_AKA_SUBTYPE_NOTIFICATION: res = eap_aka_process_notification(sm, data, id, reqData, &attr); break; case EAP_AKA_SUBTYPE_REAUTHENTICATION: res = eap_aka_process_reauthentication(sm, data, id, reqData, &attr); break; case EAP_AKA_SUBTYPE_CLIENT_ERROR: wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Client-Error"); res = eap_aka_client_error(data, id, EAP_AKA_UNABLE_TO_PROCESS_PACKET); break; default: wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown subtype=%d", subtype); res = eap_aka_client_error(data, id, EAP_AKA_UNABLE_TO_PROCESS_PACKET); break; } done: if (data->state == FAILURE) { ret->decision = DECISION_FAIL; ret->methodState = METHOD_DONE; } else if (data->state == SUCCESS) { ret->decision = data->use_result_ind ? DECISION_UNCOND_SUCC : DECISION_COND_SUCC; /* * It is possible for the server to reply with AKA * Notification, so we must allow the method to continue and * not only accept EAP-Success at this point. */ ret->methodState = data->use_result_ind ? METHOD_DONE : METHOD_MAY_CONT; } else if (data->state == RESULT_FAILURE) ret->methodState = METHOD_CONT; else if (data->state == RESULT_SUCCESS) ret->methodState = METHOD_CONT; if (ret->methodState == METHOD_DONE) { ret->allowNotifications = FALSE; } return res; } static Boolean eap_aka_has_reauth_data(struct eap_sm *sm, void *priv) { struct eap_aka_data *data = priv; return data->pseudonym || data->reauth_id; } static void eap_aka_deinit_for_reauth(struct eap_sm *sm, void *priv) { struct eap_aka_data *data = priv; eap_aka_clear_identities(sm, data, CLEAR_EAP_ID); data->prev_id = -1; wpabuf_free(data->id_msgs); data->id_msgs = NULL; data->use_result_ind = 0; data->kdf_negotiation = 0; } static void * eap_aka_init_for_reauth(struct eap_sm *sm, void *priv) { struct eap_aka_data *data = priv; data->num_id_req = 0; data->num_notification = 0; eap_aka_state(data, CONTINUE); return priv; } static const u8 * eap_aka_get_identity(struct eap_sm *sm, void *priv, size_t *len) { struct eap_aka_data *data = priv; if (data->reauth_id) { *len = data->reauth_id_len; return data->reauth_id; } if (data->pseudonym) { *len = data->pseudonym_len; return data->pseudonym; } return NULL; } static Boolean eap_aka_isKeyAvailable(struct eap_sm *sm, void *priv) { struct eap_aka_data *data = priv; return data->state == SUCCESS; } static u8 * eap_aka_getKey(struct eap_sm *sm, void *priv, size_t *len) { struct eap_aka_data *data = priv; u8 *key; if (data->state != SUCCESS) return NULL; key = os_malloc(EAP_SIM_KEYING_DATA_LEN); if (key == NULL) return NULL; *len = EAP_SIM_KEYING_DATA_LEN; os_memcpy(key, data->msk, EAP_SIM_KEYING_DATA_LEN); return key; } static u8 * eap_aka_get_session_id(struct eap_sm *sm, void *priv, size_t *len) { struct eap_aka_data *data = priv; u8 *id; if (data->state != SUCCESS) return NULL; *len = 1 + EAP_AKA_RAND_LEN + EAP_AKA_AUTN_LEN; id = os_malloc(*len); if (id == NULL) return NULL; id[0] = data->eap_method; os_memcpy(id + 1, data->rand, EAP_AKA_RAND_LEN); os_memcpy(id + 1 + EAP_AKA_RAND_LEN, data->autn, EAP_AKA_AUTN_LEN); wpa_hexdump(MSG_DEBUG, "EAP-AKA: Derived Session-Id", id, *len); return id; } static u8 * eap_aka_get_emsk(struct eap_sm *sm, void *priv, size_t *len) { struct eap_aka_data *data = priv; u8 *key; if (data->state != SUCCESS) return NULL; key = os_malloc(EAP_EMSK_LEN); if (key == NULL) return NULL; *len = EAP_EMSK_LEN; os_memcpy(key, data->emsk, EAP_EMSK_LEN); return key; } int eap_peer_aka_register(void) { struct eap_method *eap; int ret; eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_AKA, "AKA"); if (eap == NULL) return -1; eap->init = eap_aka_init; eap->deinit = eap_aka_deinit; eap->process = eap_aka_process; eap->isKeyAvailable = eap_aka_isKeyAvailable; eap->getKey = eap_aka_getKey; eap->getSessionId = eap_aka_get_session_id; eap->has_reauth_data = eap_aka_has_reauth_data; eap->deinit_for_reauth = eap_aka_deinit_for_reauth; eap->init_for_reauth = eap_aka_init_for_reauth; eap->get_identity = eap_aka_get_identity; eap->get_emsk = eap_aka_get_emsk; ret = eap_peer_method_register(eap); if (ret) eap_peer_method_free(eap); return ret; } #ifdef EAP_AKA_PRIME int eap_peer_aka_prime_register(void) { struct eap_method *eap; int ret; eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_AKA_PRIME, "AKA'"); if (eap == NULL) return -1; eap->init = eap_aka_prime_init; eap->deinit = eap_aka_deinit; eap->process = eap_aka_process; eap->isKeyAvailable = eap_aka_isKeyAvailable; eap->getKey = eap_aka_getKey; eap->getSessionId = eap_aka_get_session_id; eap->has_reauth_data = eap_aka_has_reauth_data; eap->deinit_for_reauth = eap_aka_deinit_for_reauth; eap->init_for_reauth = eap_aka_init_for_reauth; eap->get_identity = eap_aka_get_identity; eap->get_emsk = eap_aka_get_emsk; ret = eap_peer_method_register(eap); if (ret) eap_peer_method_free(eap); return ret; } #endif /* EAP_AKA_PRIME */ wpa-2.1/src/eap_peer/eap_config.h000066400000000000000000000540131227414666700167770ustar00rootroot00000000000000/* * EAP peer configuration data * Copyright (c) 2003-2013, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef EAP_CONFIG_H #define EAP_CONFIG_H /** * struct eap_peer_config - EAP peer configuration/credentials */ struct eap_peer_config { /** * identity - EAP Identity * * This field is used to set the real user identity or NAI (for * EAP-PSK/PAX/SAKE/GPSK). */ u8 *identity; /** * identity_len - EAP Identity length */ size_t identity_len; /** * anonymous_identity - Anonymous EAP Identity * * This field is used for unencrypted use with EAP types that support * different tunnelled identity, e.g., EAP-TTLS, in order to reveal the * real identity (identity field) only to the authentication server. * * If not set, the identity field will be used for both unencrypted and * protected fields. * * This field can also be used with EAP-SIM/AKA/AKA' to store the * pseudonym identity. */ u8 *anonymous_identity; /** * anonymous_identity_len - Length of anonymous_identity */ size_t anonymous_identity_len; /** * password - Password string for EAP * * This field can include either the plaintext password (default * option) or a NtPasswordHash (16-byte MD4 hash of the unicode * presentation of the password) if flags field has * EAP_CONFIG_FLAGS_PASSWORD_NTHASH bit set to 1. NtPasswordHash can * only be used with authentication mechanism that use this hash as the * starting point for operation: MSCHAP and MSCHAPv2 (EAP-MSCHAPv2, * EAP-TTLS/MSCHAPv2, EAP-TTLS/MSCHAP, LEAP). * * In addition, this field is used to configure a pre-shared key for * EAP-PSK/PAX/SAKE/GPSK. The length of the PSK must be 16 for EAP-PSK * and EAP-PAX and 32 for EAP-SAKE. EAP-GPSK can use a variable length * PSK. */ u8 *password; /** * password_len - Length of password field */ size_t password_len; /** * ca_cert - File path to CA certificate file (PEM/DER) * * This file can have one or more trusted CA certificates. If ca_cert * and ca_path are not included, server certificate will not be * verified. This is insecure and a trusted CA certificate should * always be configured when using EAP-TLS/TTLS/PEAP. Full path to the * file should be used since working directory may change when * wpa_supplicant is run in the background. * * Alternatively, a named configuration blob can be used by setting * this to blob://blob_name. * * Alternatively, this can be used to only perform matching of the * server certificate (SHA-256 hash of the DER encoded X.509 * certificate). In this case, the possible CA certificates in the * server certificate chain are ignored and only the server certificate * is verified. This is configured with the following format: * hash:://server/sha256/cert_hash_in_hex * For example: "hash://server/sha256/ * 5a1bc1296205e6fdbe3979728efe3920798885c1c4590b5f90f43222d239ca6a" * * On Windows, trusted CA certificates can be loaded from the system * certificate store by setting this to cert_store://name, e.g., * ca_cert="cert_store://CA" or ca_cert="cert_store://ROOT". * Note that when running wpa_supplicant as an application, the user * certificate store (My user account) is used, whereas computer store * (Computer account) is used when running wpasvc as a service. */ u8 *ca_cert; /** * ca_path - Directory path for CA certificate files (PEM) * * This path may contain multiple CA certificates in OpenSSL format. * Common use for this is to point to system trusted CA list which is * often installed into directory like /etc/ssl/certs. If configured, * these certificates are added to the list of trusted CAs. ca_cert * may also be included in that case, but it is not required. */ u8 *ca_path; /** * client_cert - File path to client certificate file (PEM/DER) * * This field is used with EAP method that use TLS authentication. * Usually, this is only configured for EAP-TLS, even though this could * in theory be used with EAP-TTLS and EAP-PEAP, too. Full path to the * file should be used since working directory may change when * wpa_supplicant is run in the background. * * Alternatively, a named configuration blob can be used by setting * this to blob://blob_name. */ u8 *client_cert; /** * private_key - File path to client private key file (PEM/DER/PFX) * * When PKCS#12/PFX file (.p12/.pfx) is used, client_cert should be * commented out. Both the private key and certificate will be read * from the PKCS#12 file in this case. Full path to the file should be * used since working directory may change when wpa_supplicant is run * in the background. * * Windows certificate store can be used by leaving client_cert out and * configuring private_key in one of the following formats: * * cert://substring_to_match * * hash://certificate_thumbprint_in_hex * * For example: private_key="hash://63093aa9c47f56ae88334c7b65a4" * * Note that when running wpa_supplicant as an application, the user * certificate store (My user account) is used, whereas computer store * (Computer account) is used when running wpasvc as a service. * * Alternatively, a named configuration blob can be used by setting * this to blob://blob_name. */ u8 *private_key; /** * private_key_passwd - Password for private key file * * If left out, this will be asked through control interface. */ u8 *private_key_passwd; /** * 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. In most cases, the default RSA * authentication does not use this configuration. However, it is * possible setup RSA to use ephemeral DH key exchange. In addition, * ciphers with DSA keys always use ephemeral DH keys. This can be used * to achieve forward secrecy. If the file is in DSA parameters format, * it will be automatically converted into DH params. Full path to the * file should be used since working directory may change when * wpa_supplicant is run in the background. * * Alternatively, a named configuration blob can be used by setting * this to blob://blob_name. */ u8 *dh_file; /** * subject_match - Constraint for server certificate subject * * This substring is matched against the subject of the authentication * server certificate. If this string is set, the server sertificate is * only accepted if it contains this string in the subject. The subject * string is in following format: * * /C=US/ST=CA/L=San Francisco/CN=Test AS/emailAddress=as@n.example.com */ u8 *subject_match; /** * altsubject_match - Constraint for server certificate alt. subject * * Semicolon separated string of entries to be matched against the * alternative subject name of the authentication server certificate. * If this string is set, the server sertificate is only accepted if it * contains one of the entries in an alternative subject name * extension. * * altSubjectName string is in following format: TYPE:VALUE * * Example: EMAIL:server@example.com * Example: DNS:server.example.com;DNS:server2.example.com * * Following types are supported: EMAIL, DNS, URI */ u8 *altsubject_match; /** * domain_suffix_match - Constraint for server domain name * * If set, this FQDN is used as a suffix match requirement for the * server certificate in SubjectAltName dNSName element(s). If a * matching dNSName is found, this constraint is met. If no dNSName * values are present, this constraint is matched against SubjetName CN * using same suffix match comparison. Suffix match here means that the * host/domain name is compared one label at a time starting from the * top-level domain and all the labels in domain_suffix_match shall be * included in the certificate. The certificate may include additional * sub-level labels in addition to the required labels. * * For example, domain_suffix_match=example.com would match * test.example.com but would not match test-example.com. */ char *domain_suffix_match; /** * ca_cert2 - File path to CA certificate file (PEM/DER) (Phase 2) * * This file can have one or more trusted CA certificates. If ca_cert2 * and ca_path2 are not included, server certificate will not be * verified. This is insecure and a trusted CA certificate should * always be configured. Full path to the file should be used since * working directory may change when wpa_supplicant is run in the * background. * * This field is like ca_cert, but used for phase 2 (inside * EAP-TTLS/PEAP/FAST tunnel) authentication. * * Alternatively, a named configuration blob can be used by setting * this to blob://blob_name. */ u8 *ca_cert2; /** * ca_path2 - Directory path for CA certificate files (PEM) (Phase 2) * * This path may contain multiple CA certificates in OpenSSL format. * Common use for this is to point to system trusted CA list which is * often installed into directory like /etc/ssl/certs. If configured, * these certificates are added to the list of trusted CAs. ca_cert * may also be included in that case, but it is not required. * * This field is like ca_path, but used for phase 2 (inside * EAP-TTLS/PEAP/FAST tunnel) authentication. */ u8 *ca_path2; /** * client_cert2 - File path to client certificate file * * This field is like client_cert, but used for phase 2 (inside * EAP-TTLS/PEAP/FAST tunnel) authentication. Full path to the * file should be used since working directory may change when * wpa_supplicant is run in the background. * * Alternatively, a named configuration blob can be used by setting * this to blob://blob_name. */ u8 *client_cert2; /** * private_key2 - File path to client private key file * * This field is like private_key, but used for phase 2 (inside * EAP-TTLS/PEAP/FAST tunnel) authentication. Full path to the * file should be used since working directory may change when * wpa_supplicant is run in the background. * * Alternatively, a named configuration blob can be used by setting * this to blob://blob_name. */ u8 *private_key2; /** * private_key2_passwd - Password for private key file * * This field is like private_key_passwd, but used for phase 2 (inside * EAP-TTLS/PEAP/FAST tunnel) authentication. */ u8 *private_key2_passwd; /** * dh_file2 - File path to DH/DSA parameters file (in PEM format) * * This field is like dh_file, but used for phase 2 (inside * EAP-TTLS/PEAP/FAST tunnel) authentication. Full path to the * file should be used since working directory may change when * wpa_supplicant is run in the background. * * Alternatively, a named configuration blob can be used by setting * this to blob://blob_name. */ u8 *dh_file2; /** * subject_match2 - Constraint for server certificate subject * * This field is like subject_match, but used for phase 2 (inside * EAP-TTLS/PEAP/FAST tunnel) authentication. */ u8 *subject_match2; /** * altsubject_match2 - Constraint for server certificate alt. subject * * This field is like altsubject_match, but used for phase 2 (inside * EAP-TTLS/PEAP/FAST tunnel) authentication. */ u8 *altsubject_match2; /** * domain_suffix_match2 - Constraint for server domain name * * This field is like domain_suffix_match, but used for phase 2 (inside * EAP-TTLS/PEAP/FAST tunnel) authentication. */ char *domain_suffix_match2; /** * eap_methods - Allowed EAP methods * * (vendor=EAP_VENDOR_IETF,method=EAP_TYPE_NONE) terminated list of * allowed EAP methods or %NULL if all methods are accepted. */ struct eap_method_type *eap_methods; /** * phase1 - Phase 1 (outer authentication) parameters * * String with field-value pairs, e.g., "peapver=0" or * "peapver=1 peaplabel=1". * * 'peapver' can be used to force which PEAP version (0 or 1) is used. * * 'peaplabel=1' can be used to force new label, "client PEAP * encryption", to be used during key derivation when PEAPv1 or newer. * * Most existing PEAPv1 implementation seem to be using the old label, * "client EAP encryption", and wpa_supplicant is now using that as the * default value. * * Some servers, e.g., Radiator, may require peaplabel=1 configuration * to interoperate with PEAPv1; see eap_testing.txt for more details. * * 'peap_outer_success=0' can be used to terminate PEAP authentication * on tunneled EAP-Success. This is required with some RADIUS servers * that implement draft-josefsson-pppext-eap-tls-eap-05.txt (e.g., * Lucent NavisRadius v4.4.0 with PEAP in "IETF Draft 5" mode). * * include_tls_length=1 can be used to force wpa_supplicant to include * TLS Message Length field in all TLS messages even if they are not * fragmented. * * sim_min_num_chal=3 can be used to configure EAP-SIM to require three * challenges (by default, it accepts 2 or 3). * * result_ind=1 can be used to enable EAP-SIM and EAP-AKA to use * protected result indication. * * fast_provisioning option can be used to enable in-line provisioning * of EAP-FAST credentials (PAC): * 0 = disabled, * 1 = allow unauthenticated provisioning, * 2 = allow authenticated provisioning, * 3 = allow both unauthenticated and authenticated provisioning * * fast_max_pac_list_len=num option can be used to set the maximum * number of PAC entries to store in a PAC list (default: 10). * * fast_pac_format=binary option can be used to select binary format * for storing PAC entries in order to save some space (the default * text format uses about 2.5 times the size of minimal binary format). * * crypto_binding option can be used to control PEAPv0 cryptobinding * behavior: * 0 = do not use cryptobinding (default) * 1 = use cryptobinding if server supports it * 2 = require cryptobinding * * EAP-WSC (WPS) uses following options: pin=Device_Password and * uuid=Device_UUID */ char *phase1; /** * phase2 - Phase2 (inner authentication with TLS tunnel) parameters * * String with field-value pairs, e.g., "auth=MSCHAPV2" for EAP-PEAP or * "autheap=MSCHAPV2 autheap=MD5" for EAP-TTLS. */ char *phase2; /** * pcsc - Parameters for PC/SC smartcard interface for USIM and GSM SIM * * This field is used to configure PC/SC smartcard interface. * Currently, the only configuration is whether this field is %NULL (do * not use PC/SC) or non-NULL (e.g., "") to enable PC/SC. * * This field is used for EAP-SIM and EAP-AKA. */ char *pcsc; /** * pin - PIN for USIM, GSM SIM, and smartcards * * This field is used to configure PIN for SIM and smartcards for * EAP-SIM and EAP-AKA. In addition, this is used with EAP-TLS if a * smartcard is used for private key operations. * * If left out, this will be asked through control interface. */ char *pin; /** * engine - Enable OpenSSL engine (e.g., for smartcard access) * * This is used if private key operations for EAP-TLS are performed * using a smartcard. */ int engine; /** * engine_id - Engine ID for OpenSSL engine * * "opensc" to select OpenSC engine or "pkcs11" to select PKCS#11 * engine. * * This is used if private key operations for EAP-TLS are performed * using a smartcard. */ char *engine_id; /** * engine2 - Enable OpenSSL engine (e.g., for smartcard) (Phase 2) * * This is used if private key operations for EAP-TLS are performed * using a smartcard. * * This field is like engine, but used for phase 2 (inside * EAP-TTLS/PEAP/FAST tunnel) authentication. */ int engine2; /** * pin2 - PIN for USIM, GSM SIM, and smartcards (Phase 2) * * This field is used to configure PIN for SIM and smartcards for * EAP-SIM and EAP-AKA. In addition, this is used with EAP-TLS if a * smartcard is used for private key operations. * * This field is like pin2, but used for phase 2 (inside * EAP-TTLS/PEAP/FAST tunnel) authentication. * * If left out, this will be asked through control interface. */ char *pin2; /** * engine2_id - Engine ID for OpenSSL engine (Phase 2) * * "opensc" to select OpenSC engine or "pkcs11" to select PKCS#11 * engine. * * This is used if private key operations for EAP-TLS are performed * using a smartcard. * * This field is like engine_id, but used for phase 2 (inside * EAP-TTLS/PEAP/FAST tunnel) authentication. */ char *engine2_id; /** * key_id - Key ID for OpenSSL engine * * This is used if private key operations for EAP-TLS are performed * using a smartcard. */ char *key_id; /** * cert_id - Cert ID for OpenSSL engine * * This is used if the certificate operations for EAP-TLS are performed * using a smartcard. */ char *cert_id; /** * ca_cert_id - CA Cert ID for OpenSSL engine * * This is used if the CA certificate for EAP-TLS is on a smartcard. */ char *ca_cert_id; /** * key2_id - Key ID for OpenSSL engine (phase2) * * This is used if private key operations for EAP-TLS are performed * using a smartcard. */ char *key2_id; /** * cert2_id - Cert ID for OpenSSL engine (phase2) * * This is used if the certificate operations for EAP-TLS are performed * using a smartcard. */ char *cert2_id; /** * ca_cert2_id - CA Cert ID for OpenSSL engine (phase2) * * This is used if the CA certificate for EAP-TLS is on a smartcard. */ char *ca_cert2_id; /** * otp - One-time-password * * This field should not be set in configuration step. It is only used * internally when OTP is entered through the control interface. */ u8 *otp; /** * otp_len - Length of the otp field */ size_t otp_len; /** * pending_req_identity - Whether there is a pending identity request * * This field should not be set in configuration step. It is only used * internally when control interface is used to request needed * information. */ int pending_req_identity; /** * pending_req_password - Whether there is a pending password request * * This field should not be set in configuration step. It is only used * internally when control interface is used to request needed * information. */ int pending_req_password; /** * pending_req_pin - Whether there is a pending PIN request * * This field should not be set in configuration step. It is only used * internally when control interface is used to request needed * information. */ int pending_req_pin; /** * pending_req_new_password - Pending password update request * * This field should not be set in configuration step. It is only used * internally when control interface is used to request needed * information. */ int pending_req_new_password; /** * pending_req_passphrase - Pending passphrase request * * This field should not be set in configuration step. It is only used * internally when control interface is used to request needed * information. */ int pending_req_passphrase; /** * pending_req_otp - Whether there is a pending OTP request * * This field should not be set in configuration step. It is only used * internally when control interface is used to request needed * information. */ char *pending_req_otp; /** * pending_req_otp_len - Length of the pending OTP request */ size_t pending_req_otp_len; /** * pac_file - File path or blob name for the PAC entries (EAP-FAST) * * wpa_supplicant will need to be able to create this file and write * updates to it when PAC is being provisioned or refreshed. Full path * to the file should be used since working directory may change when * wpa_supplicant is run in the background. * Alternatively, a named configuration blob can be used by setting * this to blob://blob_name. */ char *pac_file; /** * mschapv2_retry - MSCHAPv2 retry in progress * * This field is used internally by EAP-MSCHAPv2 and should not be set * as part of configuration. */ int mschapv2_retry; /** * new_password - New password for password update * * This field is used during MSCHAPv2 password update. This is normally * requested from the user through the control interface and not set * from configuration. */ u8 *new_password; /** * new_password_len - Length of new_password field */ size_t new_password_len; /** * fragment_size - Maximum EAP fragment size in bytes (default 1398) * * This value limits the fragment size for EAP methods that support * fragmentation (e.g., EAP-TLS and EAP-PEAP). This value should be set * small enough to make the EAP messages fit in MTU of the network * interface used for EAPOL. The default value is suitable for most * cases. */ int fragment_size; #define EAP_CONFIG_FLAGS_PASSWORD_NTHASH BIT(0) #define EAP_CONFIG_FLAGS_EXT_PASSWORD BIT(1) /** * flags - Network configuration flags (bitfield) * * This variable is used for internal flags to describe further details * for the network parameters. * bit 0 = password is represented as a 16-byte NtPasswordHash value * instead of plaintext password * bit 1 = password is stored in external storage; the value in the * password field is the name of that external entry */ u32 flags; /** * ocsp - Whether to use/require OCSP to check server certificate * * 0 = do not use OCSP stapling (TLS certificate status extension) * 1 = try to use OCSP stapling, but not require response * 2 = require valid OCSP stapling response */ int ocsp; /** * external_sim_resp - Response from external SIM processing * * This field should not be set in configuration step. It is only used * internally when control interface is used to request external * SIM/USIM processing. */ char *external_sim_resp; }; /** * struct wpa_config_blob - Named configuration blob * * This data structure is used to provide storage for binary objects to store * abstract information like certificates and private keys inlined with the * configuration data. */ struct wpa_config_blob { /** * name - Blob name */ char *name; /** * data - Pointer to binary data */ u8 *data; /** * len - Length of binary data */ size_t len; /** * next - Pointer to next blob in the configuration */ struct wpa_config_blob *next; }; #endif /* EAP_CONFIG_H */ wpa-2.1/src/eap_peer/eap_eke.c000066400000000000000000000475341227414666700163030ustar00rootroot00000000000000/* * EAP peer method: EAP-EKE (RFC 6124) * Copyright (c) 2013, 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/random.h" #include "eap_peer/eap_i.h" #include "eap_common/eap_eke_common.h" struct eap_eke_data { enum { IDENTITY, COMMIT, CONFIRM, SUCCESS, FAILURE } state; u8 msk[EAP_MSK_LEN]; u8 emsk[EAP_EMSK_LEN]; u8 *peerid; size_t peerid_len; u8 *serverid; size_t serverid_len; u8 dh_priv[EAP_EKE_MAX_DH_LEN]; struct eap_eke_session sess; u8 nonce_p[EAP_EKE_MAX_NONCE_LEN]; u8 nonce_s[EAP_EKE_MAX_NONCE_LEN]; struct wpabuf *msgs; u8 dhgroup; /* forced DH group or 0 to allow all supported */ u8 encr; /* forced encryption algorithm or 0 to allow all supported */ u8 prf; /* forced PRF or 0 to allow all supported */ u8 mac; /* forced MAC or 0 to allow all supported */ }; static const char * eap_eke_state_txt(int state) { switch (state) { case IDENTITY: return "IDENTITY"; case COMMIT: return "COMMIT"; case CONFIRM: return "CONFIRM"; case SUCCESS: return "SUCCESS"; case FAILURE: return "FAILURE"; default: return "?"; } } static void eap_eke_state(struct eap_eke_data *data, int state) { wpa_printf(MSG_DEBUG, "EAP-EKE: %s -> %s", eap_eke_state_txt(data->state), eap_eke_state_txt(state)); data->state = state; } static void eap_eke_deinit(struct eap_sm *sm, void *priv); static void * eap_eke_init(struct eap_sm *sm) { struct eap_eke_data *data; const u8 *identity, *password; size_t identity_len, password_len; const char *phase1; password = eap_get_config_password(sm, &password_len); if (!password) { wpa_printf(MSG_INFO, "EAP-EKE: No password configured"); return NULL; } data = os_zalloc(sizeof(*data)); if (data == NULL) return NULL; eap_eke_state(data, IDENTITY); identity = eap_get_config_identity(sm, &identity_len); if (identity) { data->peerid = os_malloc(identity_len); if (data->peerid == NULL) { eap_eke_deinit(sm, data); return NULL; } os_memcpy(data->peerid, identity, identity_len); data->peerid_len = identity_len; } phase1 = eap_get_config_phase1(sm); if (phase1) { const char *pos; pos = os_strstr(phase1, "dhgroup="); if (pos) { data->dhgroup = atoi(pos + 8); wpa_printf(MSG_DEBUG, "EAP-EKE: Forced dhgroup %u", data->dhgroup); } pos = os_strstr(phase1, "encr="); if (pos) { data->encr = atoi(pos + 5); wpa_printf(MSG_DEBUG, "EAP-EKE: Forced encr %u", data->encr); } pos = os_strstr(phase1, "prf="); if (pos) { data->prf = atoi(pos + 4); wpa_printf(MSG_DEBUG, "EAP-EKE: Forced prf %u", data->prf); } pos = os_strstr(phase1, "mac="); if (pos) { data->mac = atoi(pos + 4); wpa_printf(MSG_DEBUG, "EAP-EKE: Forced mac %u", data->mac); } } return data; } static void eap_eke_deinit(struct eap_sm *sm, void *priv) { struct eap_eke_data *data = priv; eap_eke_session_clean(&data->sess); os_free(data->serverid); os_free(data->peerid); wpabuf_free(data->msgs); os_free(data); } static struct wpabuf * eap_eke_build_msg(struct eap_eke_data *data, int id, size_t length, u8 eke_exch) { struct wpabuf *msg; size_t plen; plen = 1 + length; msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_EKE, plen, EAP_CODE_RESPONSE, id); if (msg == NULL) { wpa_printf(MSG_ERROR, "EAP-EKE: Failed to allocate memory"); return NULL; } wpabuf_put_u8(msg, eke_exch); return msg; } static int eap_eke_supp_dhgroup(u8 dhgroup) { return dhgroup == EAP_EKE_DHGROUP_EKE_2 || dhgroup == EAP_EKE_DHGROUP_EKE_5 || dhgroup == EAP_EKE_DHGROUP_EKE_14 || dhgroup == EAP_EKE_DHGROUP_EKE_15 || dhgroup == EAP_EKE_DHGROUP_EKE_16; } static int eap_eke_supp_encr(u8 encr) { return encr == EAP_EKE_ENCR_AES128_CBC; } static int eap_eke_supp_prf(u8 prf) { return prf == EAP_EKE_PRF_HMAC_SHA1 || prf == EAP_EKE_PRF_HMAC_SHA2_256; } static int eap_eke_supp_mac(u8 mac) { return mac == EAP_EKE_MAC_HMAC_SHA1 || mac == EAP_EKE_MAC_HMAC_SHA2_256; } static struct wpabuf * eap_eke_build_fail(struct eap_eke_data *data, struct eap_method_ret *ret, const struct wpabuf *reqData, u32 failure_code) { struct wpabuf *resp; wpa_printf(MSG_DEBUG, "EAP-EKE: Sending EAP-EKE-Failure/Response - code=0x%x", failure_code); resp = eap_eke_build_msg(data, eap_get_id(reqData), 4, EAP_EKE_FAILURE); if (resp) wpabuf_put_be32(resp, failure_code); os_memset(data->dh_priv, 0, sizeof(data->dh_priv)); eap_eke_session_clean(&data->sess); eap_eke_state(data, FAILURE); ret->methodState = METHOD_DONE; ret->decision = DECISION_FAIL; ret->allowNotifications = FALSE; return resp; } static struct wpabuf * eap_eke_process_id(struct eap_eke_data *data, struct eap_method_ret *ret, const struct wpabuf *reqData, const u8 *payload, size_t payload_len) { struct wpabuf *resp; unsigned num_prop, i; const u8 *pos, *end; const u8 *prop = NULL; u8 idtype; if (data->state != IDENTITY) { return eap_eke_build_fail(data, ret, reqData, EAP_EKE_FAIL_PROTO_ERROR); } wpa_printf(MSG_DEBUG, "EAP-EKE: Received EAP-EKE-ID/Request"); if (payload_len < 2 + 4) { wpa_printf(MSG_DEBUG, "EAP-EKE: Too short ID/Request Data"); return eap_eke_build_fail(data, ret, reqData, EAP_EKE_FAIL_PROTO_ERROR); } pos = payload; end = payload + payload_len; num_prop = *pos++; pos++; /* Ignore Reserved field */ if (pos + num_prop * 4 > end) { wpa_printf(MSG_DEBUG, "EAP-EKE: Too short ID/Request Data (num_prop=%u)", num_prop); return eap_eke_build_fail(data, ret, reqData, EAP_EKE_FAIL_PROTO_ERROR); } for (i = 0; i < num_prop; i++) { const u8 *tmp = pos; wpa_printf(MSG_DEBUG, "EAP-EKE: Proposal #%u: dh=%u encr=%u prf=%u mac=%u", i, pos[0], pos[1], pos[2], pos[3]); pos += 4; if ((data->dhgroup && data->dhgroup != *tmp) || !eap_eke_supp_dhgroup(*tmp)) continue; tmp++; if ((data->encr && data->encr != *tmp) || !eap_eke_supp_encr(*tmp)) continue; tmp++; if ((data->prf && data->prf != *tmp) || !eap_eke_supp_prf(*tmp)) continue; tmp++; if ((data->mac && data->mac != *tmp) || !eap_eke_supp_mac(*tmp)) continue; prop = tmp - 3; if (eap_eke_session_init(&data->sess, prop[0], prop[1], prop[2], prop[3]) < 0) { prop = NULL; continue; } wpa_printf(MSG_DEBUG, "EAP-EKE: Selected proposal"); break; } if (prop == NULL) { wpa_printf(MSG_DEBUG, "EAP-EKE: No acceptable proposal found"); return eap_eke_build_fail(data, ret, reqData, EAP_EKE_FAIL_NO_PROPOSAL_CHOSEN); } pos += (num_prop - i - 1) * 4; if (pos == end) { wpa_printf(MSG_DEBUG, "EAP-EKE: Too short ID/Request Data to include IDType/Identity"); return eap_eke_build_fail(data, ret, reqData, EAP_EKE_FAIL_PROTO_ERROR); } idtype = *pos++; wpa_printf(MSG_DEBUG, "EAP-EKE: Server IDType %u", idtype); wpa_hexdump_ascii(MSG_DEBUG, "EAP-EKE: Server Identity", pos, end - pos); os_free(data->serverid); data->serverid = os_malloc(end - pos); if (data->serverid == NULL) { return eap_eke_build_fail(data, ret, reqData, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); } os_memcpy(data->serverid, pos, end - pos); data->serverid_len = end - pos; wpa_printf(MSG_DEBUG, "EAP-EKE: Sending EAP-EKE-ID/Response"); resp = eap_eke_build_msg(data, eap_get_id(reqData), 2 + 4 + 1 + data->peerid_len, EAP_EKE_ID); if (resp == NULL) { return eap_eke_build_fail(data, ret, reqData, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); } wpabuf_put_u8(resp, 1); /* NumProposals */ wpabuf_put_u8(resp, 0); /* Reserved */ wpabuf_put_data(resp, prop, 4); /* Selected Proposal */ wpabuf_put_u8(resp, EAP_EKE_ID_NAI); if (data->peerid) wpabuf_put_data(resp, data->peerid, data->peerid_len); wpabuf_free(data->msgs); data->msgs = wpabuf_alloc(wpabuf_len(reqData) + wpabuf_len(resp)); if (data->msgs == NULL) { wpabuf_free(resp); return eap_eke_build_fail(data, ret, reqData, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); } wpabuf_put_buf(data->msgs, reqData); wpabuf_put_buf(data->msgs, resp); eap_eke_state(data, COMMIT); return resp; } static struct wpabuf * eap_eke_process_commit(struct eap_sm *sm, struct eap_eke_data *data, struct eap_method_ret *ret, const struct wpabuf *reqData, const u8 *payload, size_t payload_len) { struct wpabuf *resp; const u8 *pos, *end, *dhcomp; size_t prot_len; u8 *rpos; u8 key[EAP_EKE_MAX_KEY_LEN]; u8 pub[EAP_EKE_MAX_DH_LEN]; const u8 *password; size_t password_len; if (data->state != COMMIT) { wpa_printf(MSG_DEBUG, "EAP-EKE: EAP-EKE-Commit/Request received in unexpected state (%d)", data->state); return eap_eke_build_fail(data, ret, reqData, EAP_EKE_FAIL_PROTO_ERROR); } wpa_printf(MSG_DEBUG, "EAP-EKE: Received EAP-EKE-Commit/Request"); password = eap_get_config_password(sm, &password_len); if (password == NULL) { wpa_printf(MSG_INFO, "EAP-EKE: No password configured!"); return eap_eke_build_fail(data, ret, reqData, EAP_EKE_FAIL_PASSWD_NOT_FOUND); } pos = payload; end = payload + payload_len; if (pos + data->sess.dhcomp_len > end) { wpa_printf(MSG_DEBUG, "EAP-EKE: Too short EAP-EKE-Commit"); return eap_eke_build_fail(data, ret, reqData, EAP_EKE_FAIL_PROTO_ERROR); } wpa_hexdump(MSG_DEBUG, "EAP-EKE: DHComponent_S", pos, data->sess.dhcomp_len); dhcomp = pos; pos += data->sess.dhcomp_len; wpa_hexdump(MSG_DEBUG, "EAP-EKE: CBValue", pos, end - pos); /* * temp = prf(0+, password) * key = prf+(temp, ID_S | ID_P) */ if (eap_eke_derive_key(&data->sess, password, password_len, data->serverid, data->serverid_len, data->peerid, data->peerid_len, key) < 0) { wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive key"); return eap_eke_build_fail(data, ret, reqData, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); } /* * y_p = g ^ x_p (mod p) * x_p = random number 2 .. p-1 */ if (eap_eke_dh_init(data->sess.dhgroup, data->dh_priv, pub) < 0) { wpa_printf(MSG_INFO, "EAP-EKE: Failed to initialize DH"); os_memset(key, 0, sizeof(key)); return eap_eke_build_fail(data, ret, reqData, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); } if (eap_eke_shared_secret(&data->sess, key, data->dh_priv, dhcomp) < 0) { wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive shared secret"); os_memset(key, 0, sizeof(key)); return eap_eke_build_fail(data, ret, reqData, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); } if (eap_eke_derive_ke_ki(&data->sess, data->serverid, data->serverid_len, data->peerid, data->peerid_len) < 0) { wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive Ke/Ki"); os_memset(key, 0, sizeof(key)); return eap_eke_build_fail(data, ret, reqData, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); } wpa_printf(MSG_DEBUG, "EAP-EKE: Sending EAP-EKE-Commit/Response"); resp = eap_eke_build_msg(data, eap_get_id(reqData), data->sess.dhcomp_len + data->sess.pnonce_len, EAP_EKE_COMMIT); if (resp == NULL) { os_memset(key, 0, sizeof(key)); return eap_eke_build_fail(data, ret, reqData, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); } /* DHComponent_P = Encr(key, y_p) */ rpos = wpabuf_put(resp, data->sess.dhcomp_len); if (eap_eke_dhcomp(&data->sess, key, pub, rpos) < 0) { wpa_printf(MSG_INFO, "EAP-EKE: Failed to build DHComponent_S"); os_memset(key, 0, sizeof(key)); return eap_eke_build_fail(data, ret, reqData, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); } os_memset(key, 0, sizeof(key)); wpa_hexdump(MSG_DEBUG, "EAP-EKE: DHComponent_P", rpos, data->sess.dhcomp_len); if (random_get_bytes(data->nonce_p, data->sess.nonce_len)) { wpabuf_free(resp); return eap_eke_build_fail(data, ret, reqData, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); } wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Nonce_P", data->nonce_p, data->sess.nonce_len); prot_len = wpabuf_tailroom(resp); if (eap_eke_prot(&data->sess, data->nonce_p, data->sess.nonce_len, wpabuf_put(resp, 0), &prot_len) < 0) { wpabuf_free(resp); return eap_eke_build_fail(data, ret, reqData, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); } wpa_hexdump(MSG_DEBUG, "EAP-EKE: PNonce_P", wpabuf_put(resp, 0), prot_len); wpabuf_put(resp, prot_len); /* TODO: CBValue */ if (wpabuf_resize(&data->msgs, wpabuf_len(reqData) + wpabuf_len(resp)) < 0) { wpabuf_free(resp); return eap_eke_build_fail(data, ret, reqData, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); } wpabuf_put_buf(data->msgs, reqData); wpabuf_put_buf(data->msgs, resp); eap_eke_state(data, CONFIRM); return resp; } static struct wpabuf * eap_eke_process_confirm(struct eap_eke_data *data, struct eap_method_ret *ret, const struct wpabuf *reqData, const u8 *payload, size_t payload_len) { struct wpabuf *resp; const u8 *pos, *end; size_t prot_len; u8 nonces[2 * EAP_EKE_MAX_NONCE_LEN]; u8 auth_s[EAP_EKE_MAX_HASH_LEN]; size_t decrypt_len; u8 *auth; if (data->state != CONFIRM) { wpa_printf(MSG_DEBUG, "EAP-EKE: EAP-EKE-Confirm/Request received in unexpected state (%d)", data->state); return eap_eke_build_fail(data, ret, reqData, EAP_EKE_FAIL_PROTO_ERROR); } wpa_printf(MSG_DEBUG, "EAP-EKE: Received EAP-EKE-Confirm/Request"); pos = payload; end = payload + payload_len; if (pos + data->sess.pnonce_ps_len + data->sess.prf_len > end) { wpa_printf(MSG_DEBUG, "EAP-EKE: Too short EAP-EKE-Commit"); return eap_eke_build_fail(data, ret, reqData, EAP_EKE_FAIL_PROTO_ERROR); } decrypt_len = sizeof(nonces); if (eap_eke_decrypt_prot(&data->sess, pos, data->sess.pnonce_ps_len, nonces, &decrypt_len) < 0) { wpa_printf(MSG_INFO, "EAP-EKE: Failed to decrypt PNonce_PS"); return eap_eke_build_fail(data, ret, reqData, EAP_EKE_FAIL_AUTHENTICATION_FAIL); } if (decrypt_len != (size_t) 2 * data->sess.nonce_len) { wpa_printf(MSG_INFO, "EAP-EKE: PNonce_PS protected data length does not match length of Nonce_P and Nonce_S"); return eap_eke_build_fail(data, ret, reqData, EAP_EKE_FAIL_AUTHENTICATION_FAIL); } wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Received Nonce_P | Nonce_S", nonces, 2 * data->sess.nonce_len); if (os_memcmp(data->nonce_p, nonces, data->sess.nonce_len) != 0) { wpa_printf(MSG_INFO, "EAP-EKE: Received Nonce_P does not match trnsmitted Nonce_P"); return eap_eke_build_fail(data, ret, reqData, EAP_EKE_FAIL_AUTHENTICATION_FAIL); } os_memcpy(data->nonce_s, nonces + data->sess.nonce_len, data->sess.nonce_len); wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Nonce_S", data->nonce_s, data->sess.nonce_len); if (eap_eke_derive_ka(&data->sess, data->serverid, data->serverid_len, data->peerid, data->peerid_len, data->nonce_p, data->nonce_s) < 0) { return eap_eke_build_fail(data, ret, reqData, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); } if (eap_eke_auth(&data->sess, "EAP-EKE server", data->msgs, auth_s) < 0) { return eap_eke_build_fail(data, ret, reqData, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); } wpa_hexdump(MSG_DEBUG, "EAP-EKE: Auth_S", auth_s, data->sess.prf_len); if (os_memcmp(auth_s, pos + data->sess.pnonce_ps_len, data->sess.prf_len) != 0) { wpa_printf(MSG_INFO, "EAP-EKE: Auth_S does not match"); return eap_eke_build_fail(data, ret, reqData, EAP_EKE_FAIL_AUTHENTICATION_FAIL); } wpa_printf(MSG_DEBUG, "EAP-EKE: Sending EAP-EKE-Confirm/Response"); resp = eap_eke_build_msg(data, eap_get_id(reqData), data->sess.pnonce_len + data->sess.prf_len, EAP_EKE_CONFIRM); if (resp == NULL) { return eap_eke_build_fail(data, ret, reqData, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); } prot_len = wpabuf_tailroom(resp); if (eap_eke_prot(&data->sess, data->nonce_s, data->sess.nonce_len, wpabuf_put(resp, 0), &prot_len) < 0) { wpabuf_free(resp); return eap_eke_build_fail(data, ret, reqData, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); } wpabuf_put(resp, prot_len); auth = wpabuf_put(resp, data->sess.prf_len); if (eap_eke_auth(&data->sess, "EAP-EKE peer", data->msgs, auth) < 0) { wpabuf_free(resp); return eap_eke_build_fail(data, ret, reqData, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); } wpa_hexdump(MSG_DEBUG, "EAP-EKE: Auth_P", auth, data->sess.prf_len); if (eap_eke_derive_msk(&data->sess, data->serverid, data->serverid_len, data->peerid, data->peerid_len, data->nonce_s, data->nonce_p, data->msk, data->emsk) < 0) { wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive MSK/EMSK"); wpabuf_free(resp); return eap_eke_build_fail(data, ret, reqData, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); } os_memset(data->dh_priv, 0, sizeof(data->dh_priv)); eap_eke_session_clean(&data->sess); eap_eke_state(data, SUCCESS); ret->methodState = METHOD_MAY_CONT; ret->decision = DECISION_COND_SUCC; ret->allowNotifications = FALSE; return resp; } static struct wpabuf * eap_eke_process_failure(struct eap_eke_data *data, struct eap_method_ret *ret, const struct wpabuf *reqData, const u8 *payload, size_t payload_len) { wpa_printf(MSG_DEBUG, "EAP-EKE: Received EAP-EKE-Failure/Request"); if (payload_len < 4) { wpa_printf(MSG_DEBUG, "EAP-EKE: Too short EAP-EKE-Failure"); } else { u32 code; code = WPA_GET_BE32(payload); wpa_printf(MSG_INFO, "EAP-EKE: Failure-Code 0x%x", code); } return eap_eke_build_fail(data, ret, reqData, EAP_EKE_FAIL_NO_ERROR); } static struct wpabuf * eap_eke_process(struct eap_sm *sm, void *priv, struct eap_method_ret *ret, const struct wpabuf *reqData) { struct eap_eke_data *data = priv; struct wpabuf *resp; const u8 *pos, *end; size_t len; u8 eke_exch; pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_EKE, reqData, &len); if (pos == NULL || len < 1) { ret->ignore = TRUE; return NULL; } end = pos + len; eke_exch = *pos++; wpa_printf(MSG_DEBUG, "EAP-EKE: Received frame: exch %d", eke_exch); wpa_hexdump(MSG_DEBUG, "EAP-EKE: Received Data", pos, end - pos); ret->ignore = FALSE; ret->methodState = METHOD_MAY_CONT; ret->decision = DECISION_FAIL; ret->allowNotifications = TRUE; switch (eke_exch) { case EAP_EKE_ID: resp = eap_eke_process_id(data, ret, reqData, pos, end - pos); break; case EAP_EKE_COMMIT: resp = eap_eke_process_commit(sm, data, ret, reqData, pos, end - pos); break; case EAP_EKE_CONFIRM: resp = eap_eke_process_confirm(data, ret, reqData, pos, end - pos); break; case EAP_EKE_FAILURE: resp = eap_eke_process_failure(data, ret, reqData, pos, end - pos); break; default: wpa_printf(MSG_DEBUG, "EAP-EKE: Ignoring message with unknown EKE-Exch %d", eke_exch); ret->ignore = TRUE; return NULL; } if (ret->methodState == METHOD_DONE) ret->allowNotifications = FALSE; return resp; } static Boolean eap_eke_isKeyAvailable(struct eap_sm *sm, void *priv) { struct eap_eke_data *data = priv; return data->state == SUCCESS; } static u8 * eap_eke_getKey(struct eap_sm *sm, void *priv, size_t *len) { struct eap_eke_data *data = priv; u8 *key; if (data->state != SUCCESS) return NULL; key = os_malloc(EAP_MSK_LEN); if (key == NULL) return NULL; os_memcpy(key, data->msk, EAP_MSK_LEN); *len = EAP_MSK_LEN; return key; } static u8 * eap_eke_get_emsk(struct eap_sm *sm, void *priv, size_t *len) { struct eap_eke_data *data = priv; u8 *key; if (data->state != SUCCESS) return NULL; key = os_malloc(EAP_EMSK_LEN); if (key == NULL) return NULL; os_memcpy(key, data->emsk, EAP_EMSK_LEN); *len = EAP_EMSK_LEN; return key; } int eap_peer_eke_register(void) { struct eap_method *eap; int ret; eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_EKE, "EKE"); if (eap == NULL) return -1; eap->init = eap_eke_init; eap->deinit = eap_eke_deinit; eap->process = eap_eke_process; eap->isKeyAvailable = eap_eke_isKeyAvailable; eap->getKey = eap_eke_getKey; eap->get_emsk = eap_eke_get_emsk; ret = eap_peer_method_register(eap); if (ret) eap_peer_method_free(eap); return ret; } wpa-2.1/src/eap_peer/eap_fast.c000066400000000000000000001342171227414666700164670ustar00rootroot00000000000000/* * EAP peer method: EAP-FAST (RFC 4851) * Copyright (c) 2004-2008, 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/tls.h" #include "crypto/sha1.h" #include "eap_common/eap_tlv_common.h" #include "eap_i.h" #include "eap_tls_common.h" #include "eap_config.h" #include "eap_fast_pac.h" #ifdef EAP_FAST_DYNAMIC #include "eap_fast_pac.c" #endif /* EAP_FAST_DYNAMIC */ /* TODO: * - test session resumption and enable it if it interoperates * - password change (pending mschapv2 packet; replay decrypted packet) */ static void eap_fast_deinit(struct eap_sm *sm, void *priv); struct eap_fast_data { struct eap_ssl_data ssl; int fast_version; const struct eap_method *phase2_method; void *phase2_priv; int phase2_success; struct eap_method_type phase2_type; struct eap_method_type *phase2_types; size_t num_phase2_types; int resuming; /* starting a resumed session */ struct eap_fast_key_block_provisioning *key_block_p; #define EAP_FAST_PROV_UNAUTH 1 #define EAP_FAST_PROV_AUTH 2 int provisioning_allowed; /* Allowed PAC provisioning modes */ int provisioning; /* doing PAC provisioning (not the normal auth) */ int anon_provisioning; /* doing anonymous (unauthenticated) * provisioning */ int session_ticket_used; u8 key_data[EAP_FAST_KEY_LEN]; u8 *session_id; size_t id_len; u8 emsk[EAP_EMSK_LEN]; int success; struct eap_fast_pac *pac; struct eap_fast_pac *current_pac; size_t max_pac_list_len; int use_pac_binary_format; u8 simck[EAP_FAST_SIMCK_LEN]; int simck_idx; struct wpabuf *pending_phase2_req; }; static int eap_fast_session_ticket_cb(void *ctx, const u8 *ticket, size_t len, const u8 *client_random, const u8 *server_random, u8 *master_secret) { struct eap_fast_data *data = ctx; wpa_printf(MSG_DEBUG, "EAP-FAST: SessionTicket callback"); if (client_random == NULL || server_random == NULL || master_secret == NULL) { wpa_printf(MSG_DEBUG, "EAP-FAST: SessionTicket failed - fall " "back to full TLS handshake"); data->session_ticket_used = 0; if (data->provisioning_allowed) { wpa_printf(MSG_DEBUG, "EAP-FAST: Try to provision a " "new PAC-Key"); data->provisioning = 1; data->current_pac = NULL; } return 0; } wpa_hexdump(MSG_DEBUG, "EAP-FAST: SessionTicket", ticket, len); if (data->current_pac == NULL) { wpa_printf(MSG_DEBUG, "EAP-FAST: No PAC-Key available for " "using SessionTicket"); data->session_ticket_used = 0; return 0; } eap_fast_derive_master_secret(data->current_pac->pac_key, server_random, client_random, master_secret); data->session_ticket_used = 1; return 1; } static int eap_fast_parse_phase1(struct eap_fast_data *data, const char *phase1) { const char *pos; pos = os_strstr(phase1, "fast_provisioning="); if (pos) { data->provisioning_allowed = atoi(pos + 18); wpa_printf(MSG_DEBUG, "EAP-FAST: Automatic PAC provisioning " "mode: %d", data->provisioning_allowed); } pos = os_strstr(phase1, "fast_max_pac_list_len="); if (pos) { data->max_pac_list_len = atoi(pos + 22); if (data->max_pac_list_len == 0) data->max_pac_list_len = 1; wpa_printf(MSG_DEBUG, "EAP-FAST: Maximum PAC list length: %lu", (unsigned long) data->max_pac_list_len); } pos = os_strstr(phase1, "fast_pac_format=binary"); if (pos) { data->use_pac_binary_format = 1; wpa_printf(MSG_DEBUG, "EAP-FAST: Using binary format for PAC " "list"); } return 0; } static void * eap_fast_init(struct eap_sm *sm) { struct eap_fast_data *data; struct eap_peer_config *config = eap_get_config(sm); data = os_zalloc(sizeof(*data)); if (data == NULL) return NULL; data->fast_version = EAP_FAST_VERSION; data->max_pac_list_len = 10; if (config && config->phase1 && eap_fast_parse_phase1(data, config->phase1) < 0) { eap_fast_deinit(sm, data); return NULL; } if (eap_peer_select_phase2_methods(config, "auth=", &data->phase2_types, &data->num_phase2_types) < 0) { eap_fast_deinit(sm, data); return NULL; } data->phase2_type.vendor = EAP_VENDOR_IETF; data->phase2_type.method = EAP_TYPE_NONE; if (eap_peer_tls_ssl_init(sm, &data->ssl, config, EAP_TYPE_FAST)) { wpa_printf(MSG_INFO, "EAP-FAST: Failed to initialize SSL."); eap_fast_deinit(sm, data); return NULL; } if (tls_connection_set_session_ticket_cb(sm->ssl_ctx, data->ssl.conn, eap_fast_session_ticket_cb, data) < 0) { wpa_printf(MSG_INFO, "EAP-FAST: Failed to set SessionTicket " "callback"); eap_fast_deinit(sm, data); return NULL; } /* * The local RADIUS server in a Cisco AP does not seem to like empty * fragments before data, so disable that workaround for CBC. * TODO: consider making this configurable */ if (tls_connection_enable_workaround(sm->ssl_ctx, data->ssl.conn)) { wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to enable TLS " "workarounds"); } if (!config->pac_file) { wpa_printf(MSG_INFO, "EAP-FAST: No PAC file configured"); eap_fast_deinit(sm, data); return NULL; } if (data->use_pac_binary_format && eap_fast_load_pac_bin(sm, &data->pac, config->pac_file) < 0) { wpa_printf(MSG_INFO, "EAP-FAST: Failed to load PAC file"); eap_fast_deinit(sm, data); return NULL; } if (!data->use_pac_binary_format && eap_fast_load_pac(sm, &data->pac, config->pac_file) < 0) { wpa_printf(MSG_INFO, "EAP-FAST: Failed to load PAC file"); eap_fast_deinit(sm, data); return NULL; } eap_fast_pac_list_truncate(data->pac, data->max_pac_list_len); if (data->pac == NULL && !data->provisioning_allowed) { wpa_printf(MSG_INFO, "EAP-FAST: No PAC configured and " "provisioning disabled"); eap_fast_deinit(sm, data); return NULL; } return data; } static void eap_fast_deinit(struct eap_sm *sm, void *priv) { struct eap_fast_data *data = priv; struct eap_fast_pac *pac, *prev; if (data == NULL) return; if (data->phase2_priv && data->phase2_method) data->phase2_method->deinit(sm, data->phase2_priv); os_free(data->phase2_types); os_free(data->key_block_p); eap_peer_tls_ssl_deinit(sm, &data->ssl); pac = data->pac; prev = NULL; while (pac) { prev = pac; pac = pac->next; eap_fast_free_pac(prev); } os_free(data->session_id); wpabuf_free(data->pending_phase2_req); os_free(data); } static int eap_fast_derive_msk(struct eap_fast_data *data) { eap_fast_derive_eap_msk(data->simck, data->key_data); eap_fast_derive_eap_emsk(data->simck, data->emsk); data->success = 1; return 0; } static void eap_fast_derive_key_auth(struct eap_sm *sm, struct eap_fast_data *data) { u8 *sks; /* RFC 4851, Section 5.1: * Extra key material after TLS key_block: session_key_seed[40] */ sks = eap_fast_derive_key(sm->ssl_ctx, data->ssl.conn, "key expansion", EAP_FAST_SKS_LEN); if (sks == NULL) { wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to derive " "session_key_seed"); return; } /* * RFC 4851, Section 5.2: * S-IMCK[0] = session_key_seed */ wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: session_key_seed (SKS = S-IMCK[0])", sks, EAP_FAST_SKS_LEN); data->simck_idx = 0; os_memcpy(data->simck, sks, EAP_FAST_SIMCK_LEN); os_free(sks); } static void eap_fast_derive_key_provisioning(struct eap_sm *sm, struct eap_fast_data *data) { os_free(data->key_block_p); data->key_block_p = (struct eap_fast_key_block_provisioning *) eap_fast_derive_key(sm->ssl_ctx, data->ssl.conn, "key expansion", sizeof(*data->key_block_p)); if (data->key_block_p == NULL) { wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to derive key block"); return; } /* * RFC 4851, Section 5.2: * S-IMCK[0] = session_key_seed */ wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: session_key_seed (SKS = S-IMCK[0])", data->key_block_p->session_key_seed, sizeof(data->key_block_p->session_key_seed)); data->simck_idx = 0; os_memcpy(data->simck, data->key_block_p->session_key_seed, EAP_FAST_SIMCK_LEN); wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: server_challenge", data->key_block_p->server_challenge, sizeof(data->key_block_p->server_challenge)); wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: client_challenge", data->key_block_p->client_challenge, sizeof(data->key_block_p->client_challenge)); } static void eap_fast_derive_keys(struct eap_sm *sm, struct eap_fast_data *data) { if (data->anon_provisioning) eap_fast_derive_key_provisioning(sm, data); else eap_fast_derive_key_auth(sm, data); } static int eap_fast_init_phase2_method(struct eap_sm *sm, struct eap_fast_data *data) { data->phase2_method = eap_peer_get_eap_method(data->phase2_type.vendor, data->phase2_type.method); if (data->phase2_method == NULL) return -1; if (data->key_block_p) { sm->auth_challenge = data->key_block_p->server_challenge; sm->peer_challenge = data->key_block_p->client_challenge; } sm->init_phase2 = 1; data->phase2_priv = data->phase2_method->init(sm); sm->init_phase2 = 0; sm->auth_challenge = NULL; sm->peer_challenge = NULL; return data->phase2_priv == NULL ? -1 : 0; } static int eap_fast_select_phase2_method(struct eap_fast_data *data, u8 type) { size_t i; /* TODO: TNC with anonymous provisioning; need to require both * completed MSCHAPv2 and TNC */ if (data->anon_provisioning && type != EAP_TYPE_MSCHAPV2) { wpa_printf(MSG_INFO, "EAP-FAST: Only EAP-MSCHAPv2 is allowed " "during unauthenticated provisioning; reject phase2" " type %d", type); return -1; } #ifdef EAP_TNC if (type == EAP_TYPE_TNC) { data->phase2_type.vendor = EAP_VENDOR_IETF; data->phase2_type.method = EAP_TYPE_TNC; wpa_printf(MSG_DEBUG, "EAP-FAST: Selected Phase 2 EAP " "vendor %d method %d for TNC", data->phase2_type.vendor, data->phase2_type.method); return 0; } #endif /* EAP_TNC */ for (i = 0; i < data->num_phase2_types; i++) { if (data->phase2_types[i].vendor != EAP_VENDOR_IETF || data->phase2_types[i].method != type) continue; data->phase2_type.vendor = data->phase2_types[i].vendor; data->phase2_type.method = data->phase2_types[i].method; wpa_printf(MSG_DEBUG, "EAP-FAST: Selected Phase 2 EAP " "vendor %d method %d", data->phase2_type.vendor, data->phase2_type.method); break; } if (type != data->phase2_type.method || type == EAP_TYPE_NONE) return -1; return 0; } static int eap_fast_phase2_request(struct eap_sm *sm, struct eap_fast_data *data, struct eap_method_ret *ret, struct eap_hdr *hdr, struct wpabuf **resp) { size_t len = be_to_host16(hdr->length); u8 *pos; struct eap_method_ret iret; struct eap_peer_config *config = eap_get_config(sm); struct wpabuf msg; if (len <= sizeof(struct eap_hdr)) { wpa_printf(MSG_INFO, "EAP-FAST: too short " "Phase 2 request (len=%lu)", (unsigned long) len); return -1; } pos = (u8 *) (hdr + 1); wpa_printf(MSG_DEBUG, "EAP-FAST: Phase 2 Request: type=%d", *pos); if (*pos == EAP_TYPE_IDENTITY) { *resp = eap_sm_buildIdentity(sm, hdr->identifier, 1); return 0; } if (data->phase2_priv && data->phase2_method && *pos != data->phase2_type.method) { wpa_printf(MSG_DEBUG, "EAP-FAST: Phase 2 EAP sequence - " "deinitialize previous method"); data->phase2_method->deinit(sm, data->phase2_priv); data->phase2_method = NULL; data->phase2_priv = NULL; data->phase2_type.vendor = EAP_VENDOR_IETF; data->phase2_type.method = EAP_TYPE_NONE; } if (data->phase2_type.vendor == EAP_VENDOR_IETF && data->phase2_type.method == EAP_TYPE_NONE && eap_fast_select_phase2_method(data, *pos) < 0) { if (eap_peer_tls_phase2_nak(data->phase2_types, data->num_phase2_types, hdr, resp)) return -1; return 0; } if ((data->phase2_priv == NULL && eap_fast_init_phase2_method(sm, data) < 0) || data->phase2_method == NULL) { wpa_printf(MSG_INFO, "EAP-FAST: Failed to initialize " "Phase 2 EAP method %d", *pos); ret->methodState = METHOD_DONE; ret->decision = DECISION_FAIL; return -1; } os_memset(&iret, 0, sizeof(iret)); wpabuf_set(&msg, hdr, len); *resp = data->phase2_method->process(sm, data->phase2_priv, &iret, &msg); if (*resp == NULL || (iret.methodState == METHOD_DONE && iret.decision == DECISION_FAIL)) { ret->methodState = METHOD_DONE; ret->decision = DECISION_FAIL; } else if ((iret.methodState == METHOD_DONE || iret.methodState == METHOD_MAY_CONT) && (iret.decision == DECISION_UNCOND_SUCC || iret.decision == DECISION_COND_SUCC)) { data->phase2_success = 1; } if (*resp == NULL && config && (config->pending_req_identity || config->pending_req_password || config->pending_req_otp || config->pending_req_new_password)) { wpabuf_free(data->pending_phase2_req); data->pending_phase2_req = wpabuf_alloc_copy(hdr, len); } else if (*resp == NULL) return -1; return 0; } static struct wpabuf * eap_fast_tlv_nak(int vendor_id, int tlv_type) { struct wpabuf *buf; struct eap_tlv_nak_tlv *nak; buf = wpabuf_alloc(sizeof(*nak)); if (buf == NULL) return NULL; nak = wpabuf_put(buf, sizeof(*nak)); nak->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY | EAP_TLV_NAK_TLV); nak->length = host_to_be16(6); nak->vendor_id = host_to_be32(vendor_id); nak->nak_type = host_to_be16(tlv_type); return buf; } static struct wpabuf * eap_fast_tlv_result(int status, int intermediate) { struct wpabuf *buf; struct eap_tlv_intermediate_result_tlv *result; buf = wpabuf_alloc(sizeof(*result)); if (buf == NULL) return NULL; wpa_printf(MSG_DEBUG, "EAP-FAST: Add %sResult TLV(status=%d)", intermediate ? "Intermediate " : "", status); result = wpabuf_put(buf, sizeof(*result)); result->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY | (intermediate ? EAP_TLV_INTERMEDIATE_RESULT_TLV : EAP_TLV_RESULT_TLV)); result->length = host_to_be16(2); result->status = host_to_be16(status); return buf; } static struct wpabuf * eap_fast_tlv_pac_ack(void) { struct wpabuf *buf; struct eap_tlv_result_tlv *res; struct eap_tlv_pac_ack_tlv *ack; buf = wpabuf_alloc(sizeof(*res) + sizeof(*ack)); if (buf == NULL) return NULL; wpa_printf(MSG_DEBUG, "EAP-FAST: Add PAC TLV (ack)"); ack = wpabuf_put(buf, sizeof(*ack)); ack->tlv_type = host_to_be16(EAP_TLV_PAC_TLV | EAP_TLV_TYPE_MANDATORY); ack->length = host_to_be16(sizeof(*ack) - sizeof(struct eap_tlv_hdr)); ack->pac_type = host_to_be16(PAC_TYPE_PAC_ACKNOWLEDGEMENT); ack->pac_len = host_to_be16(2); ack->result = host_to_be16(EAP_TLV_RESULT_SUCCESS); return buf; } static struct wpabuf * eap_fast_process_eap_payload_tlv( struct eap_sm *sm, struct eap_fast_data *data, struct eap_method_ret *ret, u8 *eap_payload_tlv, size_t eap_payload_tlv_len) { struct eap_hdr *hdr; struct wpabuf *resp = NULL; if (eap_payload_tlv_len < sizeof(*hdr)) { wpa_printf(MSG_DEBUG, "EAP-FAST: too short EAP " "Payload TLV (len=%lu)", (unsigned long) eap_payload_tlv_len); return NULL; } hdr = (struct eap_hdr *) eap_payload_tlv; if (be_to_host16(hdr->length) > eap_payload_tlv_len) { wpa_printf(MSG_DEBUG, "EAP-FAST: EAP packet overflow in " "EAP Payload TLV"); return NULL; } if (hdr->code != EAP_CODE_REQUEST) { wpa_printf(MSG_INFO, "EAP-FAST: Unexpected code=%d in " "Phase 2 EAP header", hdr->code); return NULL; } if (eap_fast_phase2_request(sm, data, ret, hdr, &resp)) { wpa_printf(MSG_INFO, "EAP-FAST: Phase2 Request processing " "failed"); return NULL; } return eap_fast_tlv_eap_payload(resp); } static int eap_fast_validate_crypto_binding( struct eap_tlv_crypto_binding_tlv *_bind) { wpa_printf(MSG_DEBUG, "EAP-FAST: Crypto-Binding TLV: Version %d " "Received Version %d SubType %d", _bind->version, _bind->received_version, _bind->subtype); wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: NONCE", _bind->nonce, sizeof(_bind->nonce)); wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Compound MAC", _bind->compound_mac, sizeof(_bind->compound_mac)); if (_bind->version != EAP_FAST_VERSION || _bind->received_version != EAP_FAST_VERSION || _bind->subtype != EAP_TLV_CRYPTO_BINDING_SUBTYPE_REQUEST) { wpa_printf(MSG_INFO, "EAP-FAST: Invalid version/subtype in " "Crypto-Binding TLV: Version %d " "Received Version %d SubType %d", _bind->version, _bind->received_version, _bind->subtype); return -1; } return 0; } static void eap_fast_write_crypto_binding( struct eap_tlv_crypto_binding_tlv *rbind, struct eap_tlv_crypto_binding_tlv *_bind, const u8 *cmk) { rbind->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY | EAP_TLV_CRYPTO_BINDING_TLV); rbind->length = host_to_be16(sizeof(*rbind) - sizeof(struct eap_tlv_hdr)); rbind->version = EAP_FAST_VERSION; rbind->received_version = _bind->version; rbind->subtype = EAP_TLV_CRYPTO_BINDING_SUBTYPE_RESPONSE; os_memcpy(rbind->nonce, _bind->nonce, sizeof(_bind->nonce)); inc_byte_array(rbind->nonce, sizeof(rbind->nonce)); hmac_sha1(cmk, EAP_FAST_CMK_LEN, (u8 *) rbind, sizeof(*rbind), rbind->compound_mac); wpa_printf(MSG_DEBUG, "EAP-FAST: Reply Crypto-Binding TLV: Version %d " "Received Version %d SubType %d", rbind->version, rbind->received_version, rbind->subtype); wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: NONCE", rbind->nonce, sizeof(rbind->nonce)); wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Compound MAC", rbind->compound_mac, sizeof(rbind->compound_mac)); } static int eap_fast_get_phase2_key(struct eap_sm *sm, struct eap_fast_data *data, u8 *isk, size_t isk_len) { u8 *key; size_t key_len; os_memset(isk, 0, isk_len); if (data->phase2_method == NULL || data->phase2_priv == NULL) { wpa_printf(MSG_DEBUG, "EAP-FAST: Phase 2 method not " "available"); return -1; } if (data->phase2_method->isKeyAvailable == NULL || data->phase2_method->getKey == NULL) return 0; if (!data->phase2_method->isKeyAvailable(sm, data->phase2_priv) || (key = data->phase2_method->getKey(sm, data->phase2_priv, &key_len)) == NULL) { wpa_printf(MSG_DEBUG, "EAP-FAST: Could not get key material " "from Phase 2"); return -1; } if (key_len > isk_len) key_len = isk_len; if (key_len == 32 && data->phase2_method->vendor == EAP_VENDOR_IETF && data->phase2_method->method == EAP_TYPE_MSCHAPV2) { /* * EAP-FAST uses reverse order for MS-MPPE keys when deriving * MSK from EAP-MSCHAPv2. Swap the keys here to get the correct * ISK for EAP-FAST cryptobinding. */ os_memcpy(isk, key + 16, 16); os_memcpy(isk + 16, key, 16); } else os_memcpy(isk, key, key_len); os_free(key); return 0; } static int eap_fast_get_cmk(struct eap_sm *sm, struct eap_fast_data *data, u8 *cmk) { u8 isk[32], imck[60]; wpa_printf(MSG_DEBUG, "EAP-FAST: Determining CMK[%d] for Compound MIC " "calculation", data->simck_idx + 1); /* * RFC 4851, Section 5.2: * IMCK[j] = T-PRF(S-IMCK[j-1], "Inner Methods Compound Keys", * MSK[j], 60) * S-IMCK[j] = first 40 octets of IMCK[j] * CMK[j] = last 20 octets of IMCK[j] */ if (eap_fast_get_phase2_key(sm, data, isk, sizeof(isk)) < 0) return -1; wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: ISK[j]", isk, sizeof(isk)); sha1_t_prf(data->simck, EAP_FAST_SIMCK_LEN, "Inner Methods Compound Keys", isk, sizeof(isk), imck, sizeof(imck)); data->simck_idx++; os_memcpy(data->simck, imck, EAP_FAST_SIMCK_LEN); wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: S-IMCK[j]", data->simck, EAP_FAST_SIMCK_LEN); os_memcpy(cmk, imck + EAP_FAST_SIMCK_LEN, EAP_FAST_CMK_LEN); wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: CMK[j]", cmk, EAP_FAST_CMK_LEN); return 0; } static u8 * eap_fast_write_pac_request(u8 *pos, u16 pac_type) { struct eap_tlv_hdr *pac; struct eap_tlv_request_action_tlv *act; struct eap_tlv_pac_type_tlv *type; act = (struct eap_tlv_request_action_tlv *) pos; act->tlv_type = host_to_be16(EAP_TLV_REQUEST_ACTION_TLV); act->length = host_to_be16(2); act->action = host_to_be16(EAP_TLV_ACTION_PROCESS_TLV); pac = (struct eap_tlv_hdr *) (act + 1); pac->tlv_type = host_to_be16(EAP_TLV_PAC_TLV); pac->length = host_to_be16(sizeof(*type)); type = (struct eap_tlv_pac_type_tlv *) (pac + 1); type->tlv_type = host_to_be16(PAC_TYPE_PAC_TYPE); type->length = host_to_be16(2); type->pac_type = host_to_be16(pac_type); return (u8 *) (type + 1); } static struct wpabuf * eap_fast_process_crypto_binding( struct eap_sm *sm, struct eap_fast_data *data, struct eap_method_ret *ret, struct eap_tlv_crypto_binding_tlv *_bind, size_t bind_len) { struct wpabuf *resp; u8 *pos; u8 cmk[EAP_FAST_CMK_LEN], cmac[SHA1_MAC_LEN]; int res; size_t len; if (eap_fast_validate_crypto_binding(_bind) < 0) return NULL; if (eap_fast_get_cmk(sm, data, cmk) < 0) return NULL; /* Validate received Compound MAC */ os_memcpy(cmac, _bind->compound_mac, sizeof(cmac)); os_memset(_bind->compound_mac, 0, sizeof(cmac)); wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Crypto-Binding TLV for Compound " "MAC calculation", (u8 *) _bind, bind_len); hmac_sha1(cmk, EAP_FAST_CMK_LEN, (u8 *) _bind, bind_len, _bind->compound_mac); res = os_memcmp(cmac, _bind->compound_mac, sizeof(cmac)); wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Received Compound MAC", cmac, sizeof(cmac)); wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Calculated Compound MAC", _bind->compound_mac, sizeof(cmac)); if (res != 0) { wpa_printf(MSG_INFO, "EAP-FAST: Compound MAC did not match"); os_memcpy(_bind->compound_mac, cmac, sizeof(cmac)); return NULL; } /* * Compound MAC was valid, so authentication succeeded. Reply with * crypto binding to allow server to complete authentication. */ len = sizeof(struct eap_tlv_crypto_binding_tlv); resp = wpabuf_alloc(len); if (resp == NULL) return NULL; if (!data->anon_provisioning && data->phase2_success && eap_fast_derive_msk(data) < 0) { wpa_printf(MSG_INFO, "EAP-FAST: Failed to generate MSK"); ret->methodState = METHOD_DONE; ret->decision = DECISION_FAIL; data->phase2_success = 0; wpabuf_free(resp); return NULL; } if (!data->anon_provisioning && data->phase2_success) { os_free(data->session_id); data->session_id = eap_peer_tls_derive_session_id( sm, &data->ssl, EAP_TYPE_FAST, &data->id_len); if (data->session_id) { wpa_hexdump(MSG_DEBUG, "EAP-FAST: Derived Session-Id", data->session_id, data->id_len); } else { wpa_printf(MSG_ERROR, "EAP-FAST: Failed to derive " "Session-Id"); wpabuf_free(resp); return NULL; } } pos = wpabuf_put(resp, sizeof(struct eap_tlv_crypto_binding_tlv)); eap_fast_write_crypto_binding((struct eap_tlv_crypto_binding_tlv *) pos, _bind, cmk); return resp; } static void eap_fast_parse_pac_tlv(struct eap_fast_pac *entry, int type, u8 *pos, size_t len, int *pac_key_found) { switch (type & 0x7fff) { case PAC_TYPE_PAC_KEY: wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: PAC-Key", pos, len); if (len != EAP_FAST_PAC_KEY_LEN) { wpa_printf(MSG_DEBUG, "EAP-FAST: Invalid PAC-Key " "length %lu", (unsigned long) len); break; } *pac_key_found = 1; os_memcpy(entry->pac_key, pos, len); break; case PAC_TYPE_PAC_OPAQUE: wpa_hexdump(MSG_DEBUG, "EAP-FAST: PAC-Opaque", pos, len); entry->pac_opaque = pos; entry->pac_opaque_len = len; break; case PAC_TYPE_PAC_INFO: wpa_hexdump(MSG_DEBUG, "EAP-FAST: PAC-Info", pos, len); entry->pac_info = pos; entry->pac_info_len = len; break; default: wpa_printf(MSG_DEBUG, "EAP-FAST: Ignored unknown PAC type %d", type); break; } } static int eap_fast_process_pac_tlv(struct eap_fast_pac *entry, u8 *pac, size_t pac_len) { struct pac_tlv_hdr *hdr; u8 *pos; size_t left, len; int type, pac_key_found = 0; pos = pac; left = pac_len; while (left > sizeof(*hdr)) { hdr = (struct pac_tlv_hdr *) pos; type = be_to_host16(hdr->type); len = be_to_host16(hdr->len); pos += sizeof(*hdr); left -= sizeof(*hdr); if (len > left) { wpa_printf(MSG_DEBUG, "EAP-FAST: PAC TLV overrun " "(type=%d len=%lu left=%lu)", type, (unsigned long) len, (unsigned long) left); return -1; } eap_fast_parse_pac_tlv(entry, type, pos, len, &pac_key_found); pos += len; left -= len; } if (!pac_key_found || !entry->pac_opaque || !entry->pac_info) { wpa_printf(MSG_DEBUG, "EAP-FAST: PAC TLV does not include " "all the required fields"); return -1; } return 0; } static int eap_fast_parse_pac_info(struct eap_fast_pac *entry, int type, u8 *pos, size_t len) { u16 pac_type; u32 lifetime; struct os_time now; switch (type & 0x7fff) { case PAC_TYPE_CRED_LIFETIME: if (len != 4) { wpa_hexdump(MSG_DEBUG, "EAP-FAST: PAC-Info - " "Invalid CRED_LIFETIME length - ignored", pos, len); return 0; } /* * This is not currently saved separately in PAC files since * the server can automatically initiate PAC update when * needed. Anyway, the information is available from PAC-Info * dump if it is needed for something in the future. */ lifetime = WPA_GET_BE32(pos); os_get_time(&now); wpa_printf(MSG_DEBUG, "EAP-FAST: PAC-Info - CRED_LIFETIME %d " "(%d days)", lifetime, (lifetime - (u32) now.sec) / 86400); break; case PAC_TYPE_A_ID: wpa_hexdump_ascii(MSG_DEBUG, "EAP-FAST: PAC-Info - A-ID", pos, len); entry->a_id = pos; entry->a_id_len = len; break; case PAC_TYPE_I_ID: wpa_hexdump_ascii(MSG_DEBUG, "EAP-FAST: PAC-Info - I-ID", pos, len); entry->i_id = pos; entry->i_id_len = len; break; case PAC_TYPE_A_ID_INFO: wpa_hexdump_ascii(MSG_DEBUG, "EAP-FAST: PAC-Info - A-ID-Info", pos, len); entry->a_id_info = pos; entry->a_id_info_len = len; break; case PAC_TYPE_PAC_TYPE: /* RFC 5422, Section 4.2.6 - PAC-Type TLV */ if (len != 2) { wpa_printf(MSG_INFO, "EAP-FAST: Invalid PAC-Type " "length %lu (expected 2)", (unsigned long) len); wpa_hexdump_ascii(MSG_DEBUG, "EAP-FAST: PAC-Info - PAC-Type", pos, len); return -1; } pac_type = WPA_GET_BE16(pos); if (pac_type != PAC_TYPE_TUNNEL_PAC && pac_type != PAC_TYPE_USER_AUTHORIZATION && pac_type != PAC_TYPE_MACHINE_AUTHENTICATION) { wpa_printf(MSG_INFO, "EAP-FAST: Unsupported PAC Type " "%d", pac_type); return -1; } wpa_printf(MSG_DEBUG, "EAP-FAST: PAC-Info - PAC-Type %d", pac_type); entry->pac_type = pac_type; break; default: wpa_printf(MSG_DEBUG, "EAP-FAST: Ignored unknown PAC-Info " "type %d", type); break; } return 0; } static int eap_fast_process_pac_info(struct eap_fast_pac *entry) { struct pac_tlv_hdr *hdr; u8 *pos; size_t left, len; int type; /* RFC 5422, Section 4.2.4 */ /* PAC-Type defaults to Tunnel PAC (Type 1) */ entry->pac_type = PAC_TYPE_TUNNEL_PAC; pos = entry->pac_info; left = entry->pac_info_len; while (left > sizeof(*hdr)) { hdr = (struct pac_tlv_hdr *) pos; type = be_to_host16(hdr->type); len = be_to_host16(hdr->len); pos += sizeof(*hdr); left -= sizeof(*hdr); if (len > left) { wpa_printf(MSG_DEBUG, "EAP-FAST: PAC-Info overrun " "(type=%d len=%lu left=%lu)", type, (unsigned long) len, (unsigned long) left); return -1; } if (eap_fast_parse_pac_info(entry, type, pos, len) < 0) return -1; pos += len; left -= len; } if (entry->a_id == NULL || entry->a_id_info == NULL) { wpa_printf(MSG_DEBUG, "EAP-FAST: PAC-Info does not include " "all the required fields"); return -1; } return 0; } static struct wpabuf * eap_fast_process_pac(struct eap_sm *sm, struct eap_fast_data *data, struct eap_method_ret *ret, u8 *pac, size_t pac_len) { struct eap_peer_config *config = eap_get_config(sm); struct eap_fast_pac entry; os_memset(&entry, 0, sizeof(entry)); if (eap_fast_process_pac_tlv(&entry, pac, pac_len) || eap_fast_process_pac_info(&entry)) return NULL; eap_fast_add_pac(&data->pac, &data->current_pac, &entry); eap_fast_pac_list_truncate(data->pac, data->max_pac_list_len); if (data->use_pac_binary_format) eap_fast_save_pac_bin(sm, data->pac, config->pac_file); else eap_fast_save_pac(sm, data->pac, config->pac_file); if (data->provisioning) { if (data->anon_provisioning) { /* * Unauthenticated provisioning does not provide keying * material and must end with an EAP-Failure. * Authentication will be done separately after this. */ data->success = 0; ret->decision = DECISION_FAIL; } else { /* * Server may or may not allow authenticated * provisioning also for key generation. */ ret->decision = DECISION_COND_SUCC; } wpa_printf(MSG_DEBUG, "EAP-FAST: Send PAC-Acknowledgement TLV " "- Provisioning completed successfully"); sm->expected_failure = 1; } else { /* * This is PAC refreshing, i.e., normal authentication that is * expected to be completed with an EAP-Success. However, * RFC 5422, Section 3.5 allows EAP-Failure to be sent even * after protected success exchange in case of EAP-Fast * provisioning, so we better use DECISION_COND_SUCC here * instead of DECISION_UNCOND_SUCC. */ wpa_printf(MSG_DEBUG, "EAP-FAST: Send PAC-Acknowledgement TLV " "- PAC refreshing completed successfully"); ret->decision = DECISION_COND_SUCC; } ret->methodState = METHOD_DONE; return eap_fast_tlv_pac_ack(); } static int eap_fast_parse_decrypted(struct wpabuf *decrypted, struct eap_fast_tlv_parse *tlv, struct wpabuf **resp) { int mandatory, tlv_type, len, res; u8 *pos, *end; os_memset(tlv, 0, sizeof(*tlv)); /* Parse TLVs from the decrypted Phase 2 data */ pos = wpabuf_mhead(decrypted); end = pos + wpabuf_len(decrypted); while (pos + 4 < end) { mandatory = pos[0] & 0x80; tlv_type = WPA_GET_BE16(pos) & 0x3fff; pos += 2; len = WPA_GET_BE16(pos); pos += 2; if (pos + len > end) { wpa_printf(MSG_INFO, "EAP-FAST: TLV overflow"); return -1; } wpa_printf(MSG_DEBUG, "EAP-FAST: Received Phase 2: " "TLV type %d length %d%s", tlv_type, len, mandatory ? " (mandatory)" : ""); res = eap_fast_parse_tlv(tlv, tlv_type, pos, len); if (res == -2) break; if (res < 0) { if (mandatory) { wpa_printf(MSG_DEBUG, "EAP-FAST: Nak unknown " "mandatory TLV type %d", tlv_type); *resp = eap_fast_tlv_nak(0, tlv_type); break; } else { wpa_printf(MSG_DEBUG, "EAP-FAST: ignored " "unknown optional TLV type %d", tlv_type); } } pos += len; } return 0; } static int eap_fast_encrypt_response(struct eap_sm *sm, struct eap_fast_data *data, struct wpabuf *resp, u8 identifier, struct wpabuf **out_data) { if (resp == NULL) return 0; wpa_hexdump_buf(MSG_DEBUG, "EAP-FAST: Encrypting Phase 2 data", resp); if (eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_FAST, data->fast_version, identifier, resp, out_data)) { wpa_printf(MSG_INFO, "EAP-FAST: Failed to encrypt a Phase 2 " "frame"); } wpabuf_free(resp); return 0; } static struct wpabuf * eap_fast_pac_request(void) { struct wpabuf *tmp; u8 *pos, *pos2; tmp = wpabuf_alloc(sizeof(struct eap_tlv_hdr) + sizeof(struct eap_tlv_request_action_tlv) + sizeof(struct eap_tlv_pac_type_tlv)); if (tmp == NULL) return NULL; pos = wpabuf_put(tmp, 0); pos2 = eap_fast_write_pac_request(pos, PAC_TYPE_TUNNEL_PAC); wpabuf_put(tmp, pos2 - pos); return tmp; } static int eap_fast_process_decrypted(struct eap_sm *sm, struct eap_fast_data *data, struct eap_method_ret *ret, const struct eap_hdr *req, struct wpabuf *decrypted, struct wpabuf **out_data) { struct wpabuf *resp = NULL, *tmp; struct eap_fast_tlv_parse tlv; int failed = 0; if (eap_fast_parse_decrypted(decrypted, &tlv, &resp) < 0) return 0; if (resp) return eap_fast_encrypt_response(sm, data, resp, req->identifier, out_data); if (tlv.result == EAP_TLV_RESULT_FAILURE) { resp = eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 0); return eap_fast_encrypt_response(sm, data, resp, req->identifier, out_data); } if (tlv.iresult == EAP_TLV_RESULT_FAILURE) { resp = eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 1); return eap_fast_encrypt_response(sm, data, resp, req->identifier, out_data); } if (tlv.crypto_binding) { tmp = eap_fast_process_crypto_binding(sm, data, ret, tlv.crypto_binding, tlv.crypto_binding_len); if (tmp == NULL) failed = 1; else resp = wpabuf_concat(resp, tmp); } if (tlv.iresult == EAP_TLV_RESULT_SUCCESS) { tmp = eap_fast_tlv_result(failed ? EAP_TLV_RESULT_FAILURE : EAP_TLV_RESULT_SUCCESS, 1); resp = wpabuf_concat(resp, tmp); } if (tlv.eap_payload_tlv) { tmp = eap_fast_process_eap_payload_tlv( sm, data, ret, tlv.eap_payload_tlv, tlv.eap_payload_tlv_len); resp = wpabuf_concat(resp, tmp); } if (tlv.pac && tlv.result != EAP_TLV_RESULT_SUCCESS) { wpa_printf(MSG_DEBUG, "EAP-FAST: PAC TLV without Result TLV " "acknowledging success"); failed = 1; } else if (tlv.pac && tlv.result == EAP_TLV_RESULT_SUCCESS) { tmp = eap_fast_process_pac(sm, data, ret, tlv.pac, tlv.pac_len); resp = wpabuf_concat(resp, tmp); } if (data->current_pac == NULL && data->provisioning && !data->anon_provisioning && !tlv.pac && (tlv.iresult == EAP_TLV_RESULT_SUCCESS || tlv.result == EAP_TLV_RESULT_SUCCESS)) { /* * Need to request Tunnel PAC when using authenticated * provisioning. */ wpa_printf(MSG_DEBUG, "EAP-FAST: Request Tunnel PAC"); tmp = eap_fast_pac_request(); resp = wpabuf_concat(resp, tmp); } if (tlv.result == EAP_TLV_RESULT_SUCCESS && !failed) { tmp = eap_fast_tlv_result(EAP_TLV_RESULT_SUCCESS, 0); resp = wpabuf_concat(tmp, resp); } else if (failed) { tmp = eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 0); resp = wpabuf_concat(tmp, resp); } if (resp && tlv.result == EAP_TLV_RESULT_SUCCESS && !failed && tlv.crypto_binding && data->phase2_success) { if (data->anon_provisioning) { wpa_printf(MSG_DEBUG, "EAP-FAST: Unauthenticated " "provisioning completed successfully."); ret->methodState = METHOD_DONE; ret->decision = DECISION_FAIL; sm->expected_failure = 1; } else { wpa_printf(MSG_DEBUG, "EAP-FAST: Authentication " "completed successfully."); if (data->provisioning) ret->methodState = METHOD_MAY_CONT; else ret->methodState = METHOD_DONE; ret->decision = DECISION_UNCOND_SUCC; } } if (resp == NULL) { wpa_printf(MSG_DEBUG, "EAP-FAST: No recognized TLVs - send " "empty response packet"); resp = wpabuf_alloc(1); } return eap_fast_encrypt_response(sm, data, resp, req->identifier, out_data); } static int eap_fast_decrypt(struct eap_sm *sm, struct eap_fast_data *data, struct eap_method_ret *ret, const struct eap_hdr *req, const struct wpabuf *in_data, struct wpabuf **out_data) { struct wpabuf *in_decrypted; int res; wpa_printf(MSG_DEBUG, "EAP-FAST: Received %lu bytes encrypted data for" " Phase 2", (unsigned long) wpabuf_len(in_data)); if (data->pending_phase2_req) { wpa_printf(MSG_DEBUG, "EAP-FAST: Pending Phase 2 request - " "skip decryption and use old data"); /* Clear TLS reassembly state. */ eap_peer_tls_reset_input(&data->ssl); in_decrypted = data->pending_phase2_req; data->pending_phase2_req = NULL; goto continue_req; } if (wpabuf_len(in_data) == 0) { /* Received TLS ACK - requesting more fragments */ return eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_FAST, data->fast_version, req->identifier, NULL, out_data); } res = eap_peer_tls_decrypt(sm, &data->ssl, in_data, &in_decrypted); if (res) return res; continue_req: wpa_hexdump_buf(MSG_MSGDUMP, "EAP-FAST: Decrypted Phase 2 TLV(s)", in_decrypted); if (wpabuf_len(in_decrypted) < 4) { wpa_printf(MSG_INFO, "EAP-FAST: Too short Phase 2 " "TLV frame (len=%lu)", (unsigned long) wpabuf_len(in_decrypted)); wpabuf_free(in_decrypted); return -1; } res = eap_fast_process_decrypted(sm, data, ret, req, in_decrypted, out_data); wpabuf_free(in_decrypted); return res; } static const u8 * eap_fast_get_a_id(const u8 *buf, size_t len, size_t *id_len) { const u8 *a_id; struct pac_tlv_hdr *hdr; /* * Parse authority identity (A-ID) from the EAP-FAST/Start. This * supports both raw A-ID and one inside an A-ID TLV. */ a_id = buf; *id_len = len; if (len > sizeof(*hdr)) { int tlen; hdr = (struct pac_tlv_hdr *) buf; tlen = be_to_host16(hdr->len); if (be_to_host16(hdr->type) == PAC_TYPE_A_ID && sizeof(*hdr) + tlen <= len) { wpa_printf(MSG_DEBUG, "EAP-FAST: A-ID was in TLV " "(Start)"); a_id = (u8 *) (hdr + 1); *id_len = tlen; } } wpa_hexdump_ascii(MSG_DEBUG, "EAP-FAST: A-ID", a_id, *id_len); return a_id; } static void eap_fast_select_pac(struct eap_fast_data *data, const u8 *a_id, size_t a_id_len) { data->current_pac = eap_fast_get_pac(data->pac, a_id, a_id_len, PAC_TYPE_TUNNEL_PAC); if (data->current_pac == NULL) { /* * Tunnel PAC was not available for this A-ID. Try to use * Machine Authentication PAC, if one is available. */ data->current_pac = eap_fast_get_pac( data->pac, a_id, a_id_len, PAC_TYPE_MACHINE_AUTHENTICATION); } if (data->current_pac) { wpa_printf(MSG_DEBUG, "EAP-FAST: PAC found for this A-ID " "(PAC-Type %d)", data->current_pac->pac_type); wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-FAST: A-ID-Info", data->current_pac->a_id_info, data->current_pac->a_id_info_len); } } static int eap_fast_use_pac_opaque(struct eap_sm *sm, struct eap_fast_data *data, struct eap_fast_pac *pac) { u8 *tlv; size_t tlv_len, olen; struct eap_tlv_hdr *ehdr; olen = pac->pac_opaque_len; tlv_len = sizeof(*ehdr) + olen; tlv = os_malloc(tlv_len); if (tlv) { ehdr = (struct eap_tlv_hdr *) tlv; ehdr->tlv_type = host_to_be16(PAC_TYPE_PAC_OPAQUE); ehdr->length = host_to_be16(olen); os_memcpy(ehdr + 1, pac->pac_opaque, olen); } if (tlv == NULL || tls_connection_client_hello_ext(sm->ssl_ctx, data->ssl.conn, TLS_EXT_PAC_OPAQUE, tlv, tlv_len) < 0) { wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to add PAC-Opaque TLS " "extension"); os_free(tlv); return -1; } os_free(tlv); return 0; } static int eap_fast_clear_pac_opaque_ext(struct eap_sm *sm, struct eap_fast_data *data) { if (tls_connection_client_hello_ext(sm->ssl_ctx, data->ssl.conn, TLS_EXT_PAC_OPAQUE, NULL, 0) < 0) { wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to remove PAC-Opaque " "TLS extension"); return -1; } return 0; } static int eap_fast_set_provisioning_ciphers(struct eap_sm *sm, struct eap_fast_data *data) { u8 ciphers[5]; int count = 0; if (data->provisioning_allowed & EAP_FAST_PROV_UNAUTH) { wpa_printf(MSG_DEBUG, "EAP-FAST: Enabling unauthenticated " "provisioning TLS cipher suites"); ciphers[count++] = TLS_CIPHER_ANON_DH_AES128_SHA; } if (data->provisioning_allowed & EAP_FAST_PROV_AUTH) { wpa_printf(MSG_DEBUG, "EAP-FAST: Enabling authenticated " "provisioning TLS cipher suites"); ciphers[count++] = TLS_CIPHER_RSA_DHE_AES128_SHA; ciphers[count++] = TLS_CIPHER_AES128_SHA; ciphers[count++] = TLS_CIPHER_RC4_SHA; } ciphers[count++] = TLS_CIPHER_NONE; if (tls_connection_set_cipher_list(sm->ssl_ctx, data->ssl.conn, ciphers)) { wpa_printf(MSG_INFO, "EAP-FAST: Could not configure TLS " "cipher suites for provisioning"); return -1; } return 0; } static int eap_fast_process_start(struct eap_sm *sm, struct eap_fast_data *data, u8 flags, const u8 *pos, size_t left) { const u8 *a_id; size_t a_id_len; /* EAP-FAST Version negotiation (section 3.1) */ wpa_printf(MSG_DEBUG, "EAP-FAST: Start (server ver=%d, own ver=%d)", flags & EAP_TLS_VERSION_MASK, data->fast_version); if ((flags & EAP_TLS_VERSION_MASK) < data->fast_version) data->fast_version = flags & EAP_TLS_VERSION_MASK; wpa_printf(MSG_DEBUG, "EAP-FAST: Using FAST version %d", data->fast_version); a_id = eap_fast_get_a_id(pos, left, &a_id_len); eap_fast_select_pac(data, a_id, a_id_len); if (data->resuming && data->current_pac) { wpa_printf(MSG_DEBUG, "EAP-FAST: Trying to resume session - " "do not add PAC-Opaque to TLS ClientHello"); if (eap_fast_clear_pac_opaque_ext(sm, data) < 0) return -1; } else if (data->current_pac) { /* * PAC found for the A-ID and we are not resuming an old * session, so add PAC-Opaque extension to ClientHello. */ if (eap_fast_use_pac_opaque(sm, data, data->current_pac) < 0) return -1; } else { /* No PAC found, so we must provision one. */ if (!data->provisioning_allowed) { wpa_printf(MSG_DEBUG, "EAP-FAST: No PAC found and " "provisioning disabled"); return -1; } wpa_printf(MSG_DEBUG, "EAP-FAST: No PAC found - " "starting provisioning"); if (eap_fast_set_provisioning_ciphers(sm, data) < 0 || eap_fast_clear_pac_opaque_ext(sm, data) < 0) return -1; data->provisioning = 1; } return 0; } static struct wpabuf * eap_fast_process(struct eap_sm *sm, void *priv, struct eap_method_ret *ret, const struct wpabuf *reqData) { const struct eap_hdr *req; size_t left; int res; u8 flags, id; struct wpabuf *resp; const u8 *pos; struct eap_fast_data *data = priv; pos = eap_peer_tls_process_init(sm, &data->ssl, EAP_TYPE_FAST, ret, reqData, &left, &flags); if (pos == NULL) return NULL; req = wpabuf_head(reqData); id = req->identifier; if (flags & EAP_TLS_FLAGS_START) { if (eap_fast_process_start(sm, data, flags, pos, left) < 0) return NULL; left = 0; /* A-ID is not used in further packet processing */ } resp = NULL; if (tls_connection_established(sm->ssl_ctx, data->ssl.conn) && !data->resuming) { /* Process tunneled (encrypted) phase 2 data. */ struct wpabuf msg; wpabuf_set(&msg, pos, left); res = eap_fast_decrypt(sm, data, ret, req, &msg, &resp); if (res < 0) { ret->methodState = METHOD_DONE; ret->decision = DECISION_FAIL; /* * Ack possible Alert that may have caused failure in * decryption. */ res = 1; } } else { /* Continue processing TLS handshake (phase 1). */ res = eap_peer_tls_process_helper(sm, &data->ssl, EAP_TYPE_FAST, data->fast_version, id, pos, left, &resp); if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) { char cipher[80]; wpa_printf(MSG_DEBUG, "EAP-FAST: TLS done, proceed to Phase 2"); if (data->provisioning && (!(data->provisioning_allowed & EAP_FAST_PROV_AUTH) || tls_get_cipher(sm->ssl_ctx, data->ssl.conn, cipher, sizeof(cipher)) < 0 || os_strstr(cipher, "ADH-") || os_strstr(cipher, "anon"))) { wpa_printf(MSG_DEBUG, "EAP-FAST: Using " "anonymous (unauthenticated) " "provisioning"); data->anon_provisioning = 1; } else data->anon_provisioning = 0; data->resuming = 0; eap_fast_derive_keys(sm, data); } if (res == 2) { struct wpabuf msg; /* * Application data included in the handshake message. */ wpabuf_free(data->pending_phase2_req); data->pending_phase2_req = resp; resp = NULL; wpabuf_set(&msg, pos, left); res = eap_fast_decrypt(sm, data, ret, req, &msg, &resp); } } if (res == 1) { wpabuf_free(resp); return eap_peer_tls_build_ack(id, EAP_TYPE_FAST, data->fast_version); } return resp; } #if 0 /* FIX */ static Boolean eap_fast_has_reauth_data(struct eap_sm *sm, void *priv) { struct eap_fast_data *data = priv; return tls_connection_established(sm->ssl_ctx, data->ssl.conn); } static void eap_fast_deinit_for_reauth(struct eap_sm *sm, void *priv) { struct eap_fast_data *data = priv; os_free(data->key_block_p); data->key_block_p = NULL; wpabuf_free(data->pending_phase2_req); data->pending_phase2_req = NULL; } static void * eap_fast_init_for_reauth(struct eap_sm *sm, void *priv) { struct eap_fast_data *data = priv; if (eap_peer_tls_reauth_init(sm, &data->ssl)) { os_free(data); return NULL; } os_free(data->session_id); data->session_id = NULL; if (data->phase2_priv && data->phase2_method && data->phase2_method->init_for_reauth) data->phase2_method->init_for_reauth(sm, data->phase2_priv); data->phase2_success = 0; data->resuming = 1; data->provisioning = 0; data->anon_provisioning = 0; data->simck_idx = 0; return priv; } #endif static int eap_fast_get_status(struct eap_sm *sm, void *priv, char *buf, size_t buflen, int verbose) { struct eap_fast_data *data = priv; int len, ret; len = eap_peer_tls_status(sm, &data->ssl, buf, buflen, verbose); if (data->phase2_method) { ret = os_snprintf(buf + len, buflen - len, "EAP-FAST Phase2 method=%s\n", data->phase2_method->name); if (ret < 0 || (size_t) ret >= buflen - len) return len; len += ret; } return len; } static Boolean eap_fast_isKeyAvailable(struct eap_sm *sm, void *priv) { struct eap_fast_data *data = priv; return data->success; } static u8 * eap_fast_getKey(struct eap_sm *sm, void *priv, size_t *len) { struct eap_fast_data *data = priv; u8 *key; if (!data->success) return NULL; key = os_malloc(EAP_FAST_KEY_LEN); if (key == NULL) return NULL; *len = EAP_FAST_KEY_LEN; os_memcpy(key, data->key_data, EAP_FAST_KEY_LEN); return key; } static u8 * eap_fast_get_session_id(struct eap_sm *sm, void *priv, size_t *len) { struct eap_fast_data *data = priv; u8 *id; if (!data->success) return NULL; id = os_malloc(data->id_len); if (id == NULL) return NULL; *len = data->id_len; os_memcpy(id, data->session_id, data->id_len); return id; } static u8 * eap_fast_get_emsk(struct eap_sm *sm, void *priv, size_t *len) { struct eap_fast_data *data = priv; u8 *key; if (!data->success) return NULL; key = os_malloc(EAP_EMSK_LEN); if (key == NULL) return NULL; *len = EAP_EMSK_LEN; os_memcpy(key, data->emsk, EAP_EMSK_LEN); return key; } int eap_peer_fast_register(void) { struct eap_method *eap; int ret; eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_FAST, "FAST"); if (eap == NULL) return -1; eap->init = eap_fast_init; eap->deinit = eap_fast_deinit; eap->process = eap_fast_process; eap->isKeyAvailable = eap_fast_isKeyAvailable; eap->getKey = eap_fast_getKey; eap->getSessionId = eap_fast_get_session_id; eap->get_status = eap_fast_get_status; #if 0 eap->has_reauth_data = eap_fast_has_reauth_data; eap->deinit_for_reauth = eap_fast_deinit_for_reauth; eap->init_for_reauth = eap_fast_init_for_reauth; #endif eap->get_emsk = eap_fast_get_emsk; ret = eap_peer_method_register(eap); if (ret) eap_peer_method_free(eap); return ret; } wpa-2.1/src/eap_peer/eap_fast_pac.c000066400000000000000000000513071227414666700173100ustar00rootroot00000000000000/* * EAP peer method: EAP-FAST PAC file processing * Copyright (c) 2004-2006, 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_config.h" #include "eap_i.h" #include "eap_fast_pac.h" /* TODO: encrypt PAC-Key in the PAC file */ /* Text data format */ static const char *pac_file_hdr = "wpa_supplicant EAP-FAST PAC file - version 1"; /* * Binary data format * 4-octet magic value: 6A E4 92 0C * 2-octet version (big endian) * * * version=0: * Sequence of PAC entries: * 2-octet PAC-Type (big endian) * 32-octet PAC-Key * 2-octet PAC-Opaque length (big endian) * PAC-Opaque data (length bytes) * 2-octet PAC-Info length (big endian) * PAC-Info data (length bytes) */ #define EAP_FAST_PAC_BINARY_MAGIC 0x6ae4920c #define EAP_FAST_PAC_BINARY_FORMAT_VERSION 0 /** * eap_fast_free_pac - Free PAC data * @pac: Pointer to the PAC entry * * Note that the PAC entry must not be in a list since this function does not * remove the list links. */ void eap_fast_free_pac(struct eap_fast_pac *pac) { os_free(pac->pac_opaque); os_free(pac->pac_info); os_free(pac->a_id); os_free(pac->i_id); os_free(pac->a_id_info); os_free(pac); } /** * eap_fast_get_pac - Get a PAC entry based on A-ID * @pac_root: Pointer to root of the PAC list * @a_id: A-ID to search for * @a_id_len: Length of A-ID * @pac_type: PAC-Type to search for * Returns: Pointer to the PAC entry, or %NULL if A-ID not found */ struct eap_fast_pac * eap_fast_get_pac(struct eap_fast_pac *pac_root, const u8 *a_id, size_t a_id_len, u16 pac_type) { struct eap_fast_pac *pac = pac_root; while (pac) { if (pac->pac_type == pac_type && pac->a_id_len == a_id_len && os_memcmp(pac->a_id, a_id, a_id_len) == 0) { return pac; } pac = pac->next; } return NULL; } static void eap_fast_remove_pac(struct eap_fast_pac **pac_root, struct eap_fast_pac **pac_current, const u8 *a_id, size_t a_id_len, u16 pac_type) { struct eap_fast_pac *pac, *prev; pac = *pac_root; prev = NULL; while (pac) { if (pac->pac_type == pac_type && pac->a_id_len == a_id_len && os_memcmp(pac->a_id, a_id, a_id_len) == 0) { if (prev == NULL) *pac_root = pac->next; else prev->next = pac->next; if (*pac_current == pac) *pac_current = NULL; eap_fast_free_pac(pac); break; } prev = pac; pac = pac->next; } } static int eap_fast_copy_buf(u8 **dst, size_t *dst_len, const u8 *src, size_t src_len) { if (src) { *dst = os_malloc(src_len); if (*dst == NULL) return -1; os_memcpy(*dst, src, src_len); *dst_len = src_len; } return 0; } /** * eap_fast_add_pac - Add a copy of a PAC entry to a list * @pac_root: Pointer to PAC list root pointer * @pac_current: Pointer to the current PAC pointer * @entry: New entry to clone and add to the list * Returns: 0 on success, -1 on failure * * This function makes a clone of the given PAC entry and adds this copied * entry to the list (pac_root). If an old entry for the same A-ID is found, * it will be removed from the PAC list and in this case, pac_current entry * is set to %NULL if it was the removed entry. */ int eap_fast_add_pac(struct eap_fast_pac **pac_root, struct eap_fast_pac **pac_current, struct eap_fast_pac *entry) { struct eap_fast_pac *pac; if (entry == NULL || entry->a_id == NULL) return -1; /* Remove a possible old entry for the matching A-ID. */ eap_fast_remove_pac(pac_root, pac_current, entry->a_id, entry->a_id_len, entry->pac_type); /* Allocate a new entry and add it to the list of PACs. */ pac = os_zalloc(sizeof(*pac)); if (pac == NULL) return -1; pac->pac_type = entry->pac_type; os_memcpy(pac->pac_key, entry->pac_key, EAP_FAST_PAC_KEY_LEN); if (eap_fast_copy_buf(&pac->pac_opaque, &pac->pac_opaque_len, entry->pac_opaque, entry->pac_opaque_len) < 0 || eap_fast_copy_buf(&pac->pac_info, &pac->pac_info_len, entry->pac_info, entry->pac_info_len) < 0 || eap_fast_copy_buf(&pac->a_id, &pac->a_id_len, entry->a_id, entry->a_id_len) < 0 || eap_fast_copy_buf(&pac->i_id, &pac->i_id_len, entry->i_id, entry->i_id_len) < 0 || eap_fast_copy_buf(&pac->a_id_info, &pac->a_id_info_len, entry->a_id_info, entry->a_id_info_len) < 0) { eap_fast_free_pac(pac); return -1; } pac->next = *pac_root; *pac_root = pac; return 0; } struct eap_fast_read_ctx { FILE *f; const char *pos; const char *end; int line; char *buf; size_t buf_len; }; static int eap_fast_read_line(struct eap_fast_read_ctx *rc, char **value) { char *pos; rc->line++; if (rc->f) { if (fgets(rc->buf, rc->buf_len, rc->f) == NULL) return -1; } else { const char *l_end; size_t len; if (rc->pos >= rc->end) return -1; l_end = rc->pos; while (l_end < rc->end && *l_end != '\n') l_end++; len = l_end - rc->pos; if (len >= rc->buf_len) len = rc->buf_len - 1; os_memcpy(rc->buf, rc->pos, len); rc->buf[len] = '\0'; rc->pos = l_end + 1; } rc->buf[rc->buf_len - 1] = '\0'; pos = rc->buf; while (*pos != '\0') { if (*pos == '\n' || *pos == '\r') { *pos = '\0'; break; } pos++; } pos = os_strchr(rc->buf, '='); if (pos) *pos++ = '\0'; *value = pos; return 0; } static u8 * eap_fast_parse_hex(const char *value, size_t *len) { int hlen; u8 *buf; if (value == NULL) return NULL; hlen = os_strlen(value); if (hlen & 1) return NULL; *len = hlen / 2; buf = os_malloc(*len); if (buf == NULL) return NULL; if (hexstr2bin(value, buf, *len)) { os_free(buf); return NULL; } return buf; } static int eap_fast_init_pac_data(struct eap_sm *sm, const char *pac_file, struct eap_fast_read_ctx *rc) { os_memset(rc, 0, sizeof(*rc)); rc->buf_len = 2048; rc->buf = os_malloc(rc->buf_len); if (rc->buf == NULL) return -1; if (os_strncmp(pac_file, "blob://", 7) == 0) { const struct wpa_config_blob *blob; blob = eap_get_config_blob(sm, pac_file + 7); if (blob == NULL) { wpa_printf(MSG_INFO, "EAP-FAST: No PAC blob '%s' - " "assume no PAC entries have been " "provisioned", pac_file + 7); os_free(rc->buf); return -1; } rc->pos = (char *) blob->data; rc->end = (char *) blob->data + blob->len; } else { rc->f = fopen(pac_file, "rb"); if (rc->f == NULL) { wpa_printf(MSG_INFO, "EAP-FAST: No PAC file '%s' - " "assume no PAC entries have been " "provisioned", pac_file); os_free(rc->buf); return -1; } } return 0; } static void eap_fast_deinit_pac_data(struct eap_fast_read_ctx *rc) { os_free(rc->buf); if (rc->f) fclose(rc->f); } static const char * eap_fast_parse_start(struct eap_fast_pac **pac) { if (*pac) return "START line without END"; *pac = os_zalloc(sizeof(struct eap_fast_pac)); if (*pac == NULL) return "No memory for PAC entry"; (*pac)->pac_type = PAC_TYPE_TUNNEL_PAC; return NULL; } static const char * eap_fast_parse_end(struct eap_fast_pac **pac_root, struct eap_fast_pac **pac) { if (*pac == NULL) return "END line without START"; if (*pac_root) { struct eap_fast_pac *end = *pac_root; while (end->next) end = end->next; end->next = *pac; } else *pac_root = *pac; *pac = NULL; return NULL; } static const char * eap_fast_parse_pac_type(struct eap_fast_pac *pac, char *pos) { pac->pac_type = atoi(pos); if (pac->pac_type != PAC_TYPE_TUNNEL_PAC && pac->pac_type != PAC_TYPE_USER_AUTHORIZATION && pac->pac_type != PAC_TYPE_MACHINE_AUTHENTICATION) return "Unrecognized PAC-Type"; return NULL; } static const char * eap_fast_parse_pac_key(struct eap_fast_pac *pac, char *pos) { u8 *key; size_t key_len; key = eap_fast_parse_hex(pos, &key_len); if (key == NULL || key_len != EAP_FAST_PAC_KEY_LEN) { os_free(key); return "Invalid PAC-Key"; } os_memcpy(pac->pac_key, key, EAP_FAST_PAC_KEY_LEN); os_free(key); return NULL; } static const char * eap_fast_parse_pac_opaque(struct eap_fast_pac *pac, char *pos) { os_free(pac->pac_opaque); pac->pac_opaque = eap_fast_parse_hex(pos, &pac->pac_opaque_len); if (pac->pac_opaque == NULL) return "Invalid PAC-Opaque"; return NULL; } static const char * eap_fast_parse_a_id(struct eap_fast_pac *pac, char *pos) { os_free(pac->a_id); pac->a_id = eap_fast_parse_hex(pos, &pac->a_id_len); if (pac->a_id == NULL) return "Invalid A-ID"; return NULL; } static const char * eap_fast_parse_i_id(struct eap_fast_pac *pac, char *pos) { os_free(pac->i_id); pac->i_id = eap_fast_parse_hex(pos, &pac->i_id_len); if (pac->i_id == NULL) return "Invalid I-ID"; return NULL; } static const char * eap_fast_parse_a_id_info(struct eap_fast_pac *pac, char *pos) { os_free(pac->a_id_info); pac->a_id_info = eap_fast_parse_hex(pos, &pac->a_id_info_len); if (pac->a_id_info == NULL) return "Invalid A-ID-Info"; return NULL; } /** * eap_fast_load_pac - Load PAC entries (text format) * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * @pac_root: Pointer to root of the PAC list (to be filled) * @pac_file: Name of the PAC file/blob to load * Returns: 0 on success, -1 on failure */ int eap_fast_load_pac(struct eap_sm *sm, struct eap_fast_pac **pac_root, const char *pac_file) { struct eap_fast_read_ctx rc; struct eap_fast_pac *pac = NULL; int count = 0; char *pos; const char *err = NULL; if (pac_file == NULL) return -1; if (eap_fast_init_pac_data(sm, pac_file, &rc) < 0) return 0; if (eap_fast_read_line(&rc, &pos) < 0) { /* empty file - assume it is fine to overwrite */ eap_fast_deinit_pac_data(&rc); return 0; } if (os_strcmp(pac_file_hdr, rc.buf) != 0) err = "Unrecognized header line"; while (!err && eap_fast_read_line(&rc, &pos) == 0) { if (os_strcmp(rc.buf, "START") == 0) err = eap_fast_parse_start(&pac); else if (os_strcmp(rc.buf, "END") == 0) { err = eap_fast_parse_end(pac_root, &pac); count++; } else if (!pac) err = "Unexpected line outside START/END block"; else if (os_strcmp(rc.buf, "PAC-Type") == 0) err = eap_fast_parse_pac_type(pac, pos); else if (os_strcmp(rc.buf, "PAC-Key") == 0) err = eap_fast_parse_pac_key(pac, pos); else if (os_strcmp(rc.buf, "PAC-Opaque") == 0) err = eap_fast_parse_pac_opaque(pac, pos); else if (os_strcmp(rc.buf, "A-ID") == 0) err = eap_fast_parse_a_id(pac, pos); else if (os_strcmp(rc.buf, "I-ID") == 0) err = eap_fast_parse_i_id(pac, pos); else if (os_strcmp(rc.buf, "A-ID-Info") == 0) err = eap_fast_parse_a_id_info(pac, pos); } if (pac) { err = "PAC block not terminated with END"; eap_fast_free_pac(pac); } eap_fast_deinit_pac_data(&rc); if (err) { wpa_printf(MSG_INFO, "EAP-FAST: %s in '%s:%d'", err, pac_file, rc.line); return -1; } wpa_printf(MSG_DEBUG, "EAP-FAST: Read %d PAC entries from '%s'", count, pac_file); return 0; } static void eap_fast_write(char **buf, char **pos, size_t *buf_len, const char *field, const u8 *data, size_t len, int txt) { size_t i, need; int ret; char *end; if (data == NULL || buf == NULL || *buf == NULL || pos == NULL || *pos == NULL || *pos < *buf) return; need = os_strlen(field) + len * 2 + 30; if (txt) need += os_strlen(field) + len + 20; if (*pos - *buf + need > *buf_len) { char *nbuf = os_realloc(*buf, *buf_len + need); if (nbuf == NULL) { os_free(*buf); *buf = NULL; return; } *pos = nbuf + (*pos - *buf); *buf = nbuf; *buf_len += need; } end = *buf + *buf_len; ret = os_snprintf(*pos, end - *pos, "%s=", field); if (ret < 0 || ret >= end - *pos) return; *pos += ret; *pos += wpa_snprintf_hex(*pos, end - *pos, data, len); ret = os_snprintf(*pos, end - *pos, "\n"); if (ret < 0 || ret >= end - *pos) return; *pos += ret; if (txt) { ret = os_snprintf(*pos, end - *pos, "%s-txt=", field); if (ret < 0 || ret >= end - *pos) return; *pos += ret; for (i = 0; i < len; i++) { ret = os_snprintf(*pos, end - *pos, "%c", data[i]); if (ret < 0 || ret >= end - *pos) return; *pos += ret; } ret = os_snprintf(*pos, end - *pos, "\n"); if (ret < 0 || ret >= end - *pos) return; *pos += ret; } } static int eap_fast_write_pac(struct eap_sm *sm, const char *pac_file, char *buf, size_t len) { if (os_strncmp(pac_file, "blob://", 7) == 0) { struct wpa_config_blob *blob; blob = os_zalloc(sizeof(*blob)); if (blob == NULL) return -1; blob->data = (u8 *) buf; blob->len = len; buf = NULL; blob->name = os_strdup(pac_file + 7); if (blob->name == NULL) { os_free(blob); return -1; } eap_set_config_blob(sm, blob); } else { FILE *f; f = fopen(pac_file, "wb"); if (f == NULL) { wpa_printf(MSG_INFO, "EAP-FAST: Failed to open PAC " "file '%s' for writing", pac_file); return -1; } if (fwrite(buf, 1, len, f) != len) { wpa_printf(MSG_INFO, "EAP-FAST: Failed to write all " "PACs into '%s'", pac_file); fclose(f); return -1; } os_free(buf); fclose(f); } return 0; } static int eap_fast_add_pac_data(struct eap_fast_pac *pac, char **buf, char **pos, size_t *buf_len) { int ret; ret = os_snprintf(*pos, *buf + *buf_len - *pos, "START\nPAC-Type=%d\n", pac->pac_type); if (ret < 0 || ret >= *buf + *buf_len - *pos) return -1; *pos += ret; eap_fast_write(buf, pos, buf_len, "PAC-Key", pac->pac_key, EAP_FAST_PAC_KEY_LEN, 0); eap_fast_write(buf, pos, buf_len, "PAC-Opaque", pac->pac_opaque, pac->pac_opaque_len, 0); eap_fast_write(buf, pos, buf_len, "PAC-Info", pac->pac_info, pac->pac_info_len, 0); eap_fast_write(buf, pos, buf_len, "A-ID", pac->a_id, pac->a_id_len, 0); eap_fast_write(buf, pos, buf_len, "I-ID", pac->i_id, pac->i_id_len, 1); eap_fast_write(buf, pos, buf_len, "A-ID-Info", pac->a_id_info, pac->a_id_info_len, 1); if (*buf == NULL) { wpa_printf(MSG_DEBUG, "EAP-FAST: No memory for PAC " "data"); return -1; } ret = os_snprintf(*pos, *buf + *buf_len - *pos, "END\n"); if (ret < 0 || ret >= *buf + *buf_len - *pos) return -1; *pos += ret; return 0; } /** * eap_fast_save_pac - Save PAC entries (text format) * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * @pac_root: Root of the PAC list * @pac_file: Name of the PAC file/blob * Returns: 0 on success, -1 on failure */ int eap_fast_save_pac(struct eap_sm *sm, struct eap_fast_pac *pac_root, const char *pac_file) { struct eap_fast_pac *pac; int ret, count = 0; char *buf, *pos; size_t buf_len; if (pac_file == NULL) return -1; buf_len = 1024; pos = buf = os_malloc(buf_len); if (buf == NULL) return -1; ret = os_snprintf(pos, buf + buf_len - pos, "%s\n", pac_file_hdr); if (ret < 0 || ret >= buf + buf_len - pos) { os_free(buf); return -1; } pos += ret; pac = pac_root; while (pac) { if (eap_fast_add_pac_data(pac, &buf, &pos, &buf_len)) { os_free(buf); return -1; } count++; pac = pac->next; } if (eap_fast_write_pac(sm, pac_file, buf, pos - buf)) { os_free(buf); return -1; } wpa_printf(MSG_DEBUG, "EAP-FAST: Wrote %d PAC entries into '%s'", count, pac_file); return 0; } /** * eap_fast_pac_list_truncate - Truncate a PAC list to the given length * @pac_root: Root of the PAC list * @max_len: Maximum length of the list (>= 1) * Returns: Number of PAC entries removed */ size_t eap_fast_pac_list_truncate(struct eap_fast_pac *pac_root, size_t max_len) { struct eap_fast_pac *pac, *prev; size_t count; pac = pac_root; prev = NULL; count = 0; while (pac) { count++; if (count > max_len) break; prev = pac; pac = pac->next; } if (count <= max_len || prev == NULL) return 0; count = 0; prev->next = NULL; while (pac) { prev = pac; pac = pac->next; eap_fast_free_pac(prev); count++; } return count; } static void eap_fast_pac_get_a_id(struct eap_fast_pac *pac) { u8 *pos, *end; u16 type, len; pos = pac->pac_info; end = pos + pac->pac_info_len; while (pos + 4 < end) { type = WPA_GET_BE16(pos); pos += 2; len = WPA_GET_BE16(pos); pos += 2; if (pos + len > end) break; if (type == PAC_TYPE_A_ID) { os_free(pac->a_id); pac->a_id = os_malloc(len); if (pac->a_id == NULL) break; os_memcpy(pac->a_id, pos, len); pac->a_id_len = len; } if (type == PAC_TYPE_A_ID_INFO) { os_free(pac->a_id_info); pac->a_id_info = os_malloc(len); if (pac->a_id_info == NULL) break; os_memcpy(pac->a_id_info, pos, len); pac->a_id_info_len = len; } pos += len; } } /** * eap_fast_load_pac_bin - Load PAC entries (binary format) * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * @pac_root: Pointer to root of the PAC list (to be filled) * @pac_file: Name of the PAC file/blob to load * Returns: 0 on success, -1 on failure */ int eap_fast_load_pac_bin(struct eap_sm *sm, struct eap_fast_pac **pac_root, const char *pac_file) { const struct wpa_config_blob *blob = NULL; u8 *buf, *end, *pos; size_t len, count = 0; struct eap_fast_pac *pac, *prev; *pac_root = NULL; if (pac_file == NULL) return -1; if (os_strncmp(pac_file, "blob://", 7) == 0) { blob = eap_get_config_blob(sm, pac_file + 7); if (blob == NULL) { wpa_printf(MSG_INFO, "EAP-FAST: No PAC blob '%s' - " "assume no PAC entries have been " "provisioned", pac_file + 7); return 0; } buf = blob->data; len = blob->len; } else { buf = (u8 *) os_readfile(pac_file, &len); if (buf == NULL) { wpa_printf(MSG_INFO, "EAP-FAST: No PAC file '%s' - " "assume no PAC entries have been " "provisioned", pac_file); return 0; } } if (len == 0) { if (blob == NULL) os_free(buf); return 0; } if (len < 6 || WPA_GET_BE32(buf) != EAP_FAST_PAC_BINARY_MAGIC || WPA_GET_BE16(buf + 4) != EAP_FAST_PAC_BINARY_FORMAT_VERSION) { wpa_printf(MSG_INFO, "EAP-FAST: Invalid PAC file '%s' (bin)", pac_file); if (blob == NULL) os_free(buf); return -1; } pac = prev = NULL; pos = buf + 6; end = buf + len; while (pos < end) { if (end - pos < 2 + 32 + 2 + 2) goto parse_fail; pac = os_zalloc(sizeof(*pac)); if (pac == NULL) goto parse_fail; pac->pac_type = WPA_GET_BE16(pos); pos += 2; os_memcpy(pac->pac_key, pos, EAP_FAST_PAC_KEY_LEN); pos += EAP_FAST_PAC_KEY_LEN; pac->pac_opaque_len = WPA_GET_BE16(pos); pos += 2; if (pos + pac->pac_opaque_len + 2 > end) goto parse_fail; pac->pac_opaque = os_malloc(pac->pac_opaque_len); if (pac->pac_opaque == NULL) goto parse_fail; os_memcpy(pac->pac_opaque, pos, pac->pac_opaque_len); pos += pac->pac_opaque_len; pac->pac_info_len = WPA_GET_BE16(pos); pos += 2; if (pos + pac->pac_info_len > end) goto parse_fail; pac->pac_info = os_malloc(pac->pac_info_len); if (pac->pac_info == NULL) goto parse_fail; os_memcpy(pac->pac_info, pos, pac->pac_info_len); pos += pac->pac_info_len; eap_fast_pac_get_a_id(pac); count++; if (prev) prev->next = pac; else *pac_root = pac; prev = pac; } if (blob == NULL) os_free(buf); wpa_printf(MSG_DEBUG, "EAP-FAST: Read %lu PAC entries from '%s' (bin)", (unsigned long) count, pac_file); return 0; parse_fail: wpa_printf(MSG_INFO, "EAP-FAST: Failed to parse PAC file '%s' (bin)", pac_file); if (blob == NULL) os_free(buf); if (pac) eap_fast_free_pac(pac); return -1; } /** * eap_fast_save_pac_bin - Save PAC entries (binary format) * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * @pac_root: Root of the PAC list * @pac_file: Name of the PAC file/blob * Returns: 0 on success, -1 on failure */ int eap_fast_save_pac_bin(struct eap_sm *sm, struct eap_fast_pac *pac_root, const char *pac_file) { size_t len, count = 0; struct eap_fast_pac *pac; u8 *buf, *pos; len = 6; pac = pac_root; while (pac) { if (pac->pac_opaque_len > 65535 || pac->pac_info_len > 65535) return -1; len += 2 + EAP_FAST_PAC_KEY_LEN + 2 + pac->pac_opaque_len + 2 + pac->pac_info_len; pac = pac->next; } buf = os_malloc(len); if (buf == NULL) return -1; pos = buf; WPA_PUT_BE32(pos, EAP_FAST_PAC_BINARY_MAGIC); pos += 4; WPA_PUT_BE16(pos, EAP_FAST_PAC_BINARY_FORMAT_VERSION); pos += 2; pac = pac_root; while (pac) { WPA_PUT_BE16(pos, pac->pac_type); pos += 2; os_memcpy(pos, pac->pac_key, EAP_FAST_PAC_KEY_LEN); pos += EAP_FAST_PAC_KEY_LEN; WPA_PUT_BE16(pos, pac->pac_opaque_len); pos += 2; os_memcpy(pos, pac->pac_opaque, pac->pac_opaque_len); pos += pac->pac_opaque_len; WPA_PUT_BE16(pos, pac->pac_info_len); pos += 2; os_memcpy(pos, pac->pac_info, pac->pac_info_len); pos += pac->pac_info_len; pac = pac->next; count++; } if (eap_fast_write_pac(sm, pac_file, (char *) buf, len)) { os_free(buf); return -1; } wpa_printf(MSG_DEBUG, "EAP-FAST: Wrote %lu PAC entries into '%s' " "(bin)", (unsigned long) count, pac_file); return 0; } wpa-2.1/src/eap_peer/eap_fast_pac.h000066400000000000000000000026371227414666700173170ustar00rootroot00000000000000/* * EAP peer method: EAP-FAST PAC file processing * Copyright (c) 2004-2007, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef EAP_FAST_PAC_H #define EAP_FAST_PAC_H #include "eap_common/eap_fast_common.h" struct eap_fast_pac { struct eap_fast_pac *next; u8 pac_key[EAP_FAST_PAC_KEY_LEN]; u8 *pac_opaque; size_t pac_opaque_len; u8 *pac_info; size_t pac_info_len; u8 *a_id; size_t a_id_len; u8 *i_id; size_t i_id_len; u8 *a_id_info; size_t a_id_info_len; u16 pac_type; }; void eap_fast_free_pac(struct eap_fast_pac *pac); struct eap_fast_pac * eap_fast_get_pac(struct eap_fast_pac *pac_root, const u8 *a_id, size_t a_id_len, u16 pac_type); int eap_fast_add_pac(struct eap_fast_pac **pac_root, struct eap_fast_pac **pac_current, struct eap_fast_pac *entry); int eap_fast_load_pac(struct eap_sm *sm, struct eap_fast_pac **pac_root, const char *pac_file); int eap_fast_save_pac(struct eap_sm *sm, struct eap_fast_pac *pac_root, const char *pac_file); size_t eap_fast_pac_list_truncate(struct eap_fast_pac *pac_root, size_t max_len); int eap_fast_load_pac_bin(struct eap_sm *sm, struct eap_fast_pac **pac_root, const char *pac_file); int eap_fast_save_pac_bin(struct eap_sm *sm, struct eap_fast_pac *pac_root, const char *pac_file); #endif /* EAP_FAST_PAC_H */ wpa-2.1/src/eap_peer/eap_gpsk.c000066400000000000000000000457371227414666700165060ustar00rootroot00000000000000/* * EAP peer method: EAP-GPSK (RFC 5433) * Copyright (c) 2006-2014, 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/random.h" #include "eap_peer/eap_i.h" #include "eap_common/eap_gpsk_common.h" struct eap_gpsk_data { enum { GPSK_1, GPSK_3, SUCCESS, FAILURE } state; u8 rand_server[EAP_GPSK_RAND_LEN]; u8 rand_peer[EAP_GPSK_RAND_LEN]; u8 msk[EAP_MSK_LEN]; u8 emsk[EAP_EMSK_LEN]; u8 sk[EAP_GPSK_MAX_SK_LEN]; size_t sk_len; u8 pk[EAP_GPSK_MAX_PK_LEN]; size_t pk_len; u8 session_id[128]; size_t id_len; u8 *id_peer; size_t id_peer_len; u8 *id_server; size_t id_server_len; int vendor; /* CSuite/Specifier */ int specifier; /* CSuite/Specifier */ u8 *psk; size_t psk_len; u16 forced_cipher; /* force cipher or 0 to allow all supported */ }; static struct wpabuf * eap_gpsk_send_gpsk_2(struct eap_gpsk_data *data, u8 identifier, const u8 *csuite_list, size_t csuite_list_len); static struct wpabuf * eap_gpsk_send_gpsk_4(struct eap_gpsk_data *data, u8 identifier); #ifndef CONFIG_NO_STDOUT_DEBUG static const char * eap_gpsk_state_txt(int state) { switch (state) { case GPSK_1: return "GPSK-1"; case GPSK_3: return "GPSK-3"; case SUCCESS: return "SUCCESS"; case FAILURE: return "FAILURE"; default: return "?"; } } #endif /* CONFIG_NO_STDOUT_DEBUG */ static void eap_gpsk_state(struct eap_gpsk_data *data, int state) { wpa_printf(MSG_DEBUG, "EAP-GPSK: %s -> %s", eap_gpsk_state_txt(data->state), eap_gpsk_state_txt(state)); data->state = state; } static void eap_gpsk_deinit(struct eap_sm *sm, void *priv); static void * eap_gpsk_init(struct eap_sm *sm) { struct eap_gpsk_data *data; const u8 *identity, *password; size_t identity_len, password_len; const char *phase1; password = eap_get_config_password(sm, &password_len); if (password == NULL) { wpa_printf(MSG_INFO, "EAP-GPSK: No key (password) configured"); return NULL; } data = os_zalloc(sizeof(*data)); if (data == NULL) return NULL; data->state = GPSK_1; identity = eap_get_config_identity(sm, &identity_len); if (identity) { data->id_peer = os_malloc(identity_len); if (data->id_peer == NULL) { eap_gpsk_deinit(sm, data); return NULL; } os_memcpy(data->id_peer, identity, identity_len); data->id_peer_len = identity_len; } phase1 = eap_get_config_phase1(sm); if (phase1) { const char *pos; pos = os_strstr(phase1, "cipher="); if (pos) { data->forced_cipher = atoi(pos + 7); wpa_printf(MSG_DEBUG, "EAP-GPSK: Forced cipher %u", data->forced_cipher); } } data->psk = os_malloc(password_len); if (data->psk == NULL) { eap_gpsk_deinit(sm, data); return NULL; } os_memcpy(data->psk, password, password_len); data->psk_len = password_len; return data; } static void eap_gpsk_deinit(struct eap_sm *sm, void *priv) { struct eap_gpsk_data *data = priv; os_free(data->id_server); os_free(data->id_peer); os_free(data->psk); os_free(data); } static const u8 * eap_gpsk_process_id_server(struct eap_gpsk_data *data, const u8 *pos, const u8 *end) { u16 alen; if (end - pos < 2) { wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short GPSK-1 packet"); return NULL; } alen = WPA_GET_BE16(pos); pos += 2; if (end - pos < alen) { wpa_printf(MSG_DEBUG, "EAP-GPSK: ID_Server overflow"); return NULL; } os_free(data->id_server); data->id_server = os_malloc(alen); if (data->id_server == NULL) { wpa_printf(MSG_DEBUG, "EAP-GPSK: No memory for ID_Server"); return NULL; } os_memcpy(data->id_server, pos, alen); data->id_server_len = alen; wpa_hexdump_ascii(MSG_DEBUG, "EAP-GPSK: ID_Server", data->id_server, data->id_server_len); pos += alen; return pos; } static const u8 * eap_gpsk_process_rand_server(struct eap_gpsk_data *data, const u8 *pos, const u8 *end) { if (pos == NULL) return NULL; if (end - pos < EAP_GPSK_RAND_LEN) { wpa_printf(MSG_DEBUG, "EAP-GPSK: RAND_Server overflow"); return NULL; } os_memcpy(data->rand_server, pos, EAP_GPSK_RAND_LEN); wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Server", data->rand_server, EAP_GPSK_RAND_LEN); pos += EAP_GPSK_RAND_LEN; return pos; } static int eap_gpsk_select_csuite(struct eap_sm *sm, struct eap_gpsk_data *data, const u8 *csuite_list, size_t csuite_list_len) { struct eap_gpsk_csuite *csuite; int i, count; count = csuite_list_len / sizeof(struct eap_gpsk_csuite); data->vendor = EAP_GPSK_VENDOR_IETF; data->specifier = EAP_GPSK_CIPHER_RESERVED; csuite = (struct eap_gpsk_csuite *) csuite_list; for (i = 0; i < count; i++) { int vendor, specifier; vendor = WPA_GET_BE32(csuite->vendor); specifier = WPA_GET_BE16(csuite->specifier); wpa_printf(MSG_DEBUG, "EAP-GPSK: CSuite[%d]: %d:%d", i, vendor, specifier); if (data->vendor == EAP_GPSK_VENDOR_IETF && data->specifier == EAP_GPSK_CIPHER_RESERVED && eap_gpsk_supported_ciphersuite(vendor, specifier) && (!data->forced_cipher || data->forced_cipher == specifier)) { data->vendor = vendor; data->specifier = specifier; } csuite++; } if (data->vendor == EAP_GPSK_VENDOR_IETF && data->specifier == EAP_GPSK_CIPHER_RESERVED) { wpa_msg(sm->msg_ctx, MSG_INFO, "EAP-GPSK: No supported " "ciphersuite found"); return -1; } wpa_printf(MSG_DEBUG, "EAP-GPSK: Selected ciphersuite %d:%d", data->vendor, data->specifier); return 0; } static const u8 * eap_gpsk_process_csuite_list(struct eap_sm *sm, struct eap_gpsk_data *data, const u8 **list, size_t *list_len, const u8 *pos, const u8 *end) { if (pos == NULL) return NULL; if (end - pos < 2) { wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short GPSK-1 packet"); return NULL; } *list_len = WPA_GET_BE16(pos); pos += 2; if (end - pos < (int) *list_len) { wpa_printf(MSG_DEBUG, "EAP-GPSK: CSuite_List overflow"); return NULL; } if (*list_len == 0 || (*list_len % sizeof(struct eap_gpsk_csuite))) { wpa_printf(MSG_DEBUG, "EAP-GPSK: Invalid CSuite_List len %lu", (unsigned long) *list_len); return NULL; } *list = pos; pos += *list_len; if (eap_gpsk_select_csuite(sm, data, *list, *list_len) < 0) return NULL; return pos; } static struct wpabuf * eap_gpsk_process_gpsk_1(struct eap_sm *sm, struct eap_gpsk_data *data, struct eap_method_ret *ret, const struct wpabuf *reqData, const u8 *payload, size_t payload_len) { size_t csuite_list_len; const u8 *csuite_list, *pos, *end; struct wpabuf *resp; if (data->state != GPSK_1) { ret->ignore = TRUE; return NULL; } wpa_printf(MSG_DEBUG, "EAP-GPSK: Received Request/GPSK-1"); end = payload + payload_len; pos = eap_gpsk_process_id_server(data, payload, end); pos = eap_gpsk_process_rand_server(data, pos, end); pos = eap_gpsk_process_csuite_list(sm, data, &csuite_list, &csuite_list_len, pos, end); if (pos == NULL) { ret->methodState = METHOD_DONE; eap_gpsk_state(data, FAILURE); return NULL; } resp = eap_gpsk_send_gpsk_2(data, eap_get_id(reqData), csuite_list, csuite_list_len); if (resp == NULL) return NULL; eap_gpsk_state(data, GPSK_3); return resp; } static struct wpabuf * eap_gpsk_send_gpsk_2(struct eap_gpsk_data *data, u8 identifier, const u8 *csuite_list, size_t csuite_list_len) { struct wpabuf *resp; size_t len, miclen; u8 *rpos, *start; struct eap_gpsk_csuite *csuite; wpa_printf(MSG_DEBUG, "EAP-GPSK: Sending Response/GPSK-2"); miclen = eap_gpsk_mic_len(data->vendor, data->specifier); len = 1 + 2 + data->id_peer_len + 2 + data->id_server_len + 2 * EAP_GPSK_RAND_LEN + 2 + csuite_list_len + sizeof(struct eap_gpsk_csuite) + 2 + miclen; resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GPSK, len, EAP_CODE_RESPONSE, identifier); if (resp == NULL) return NULL; wpabuf_put_u8(resp, EAP_GPSK_OPCODE_GPSK_2); start = wpabuf_put(resp, 0); wpa_hexdump_ascii(MSG_DEBUG, "EAP-GPSK: ID_Peer", data->id_peer, data->id_peer_len); wpabuf_put_be16(resp, data->id_peer_len); wpabuf_put_data(resp, data->id_peer, data->id_peer_len); wpabuf_put_be16(resp, data->id_server_len); wpabuf_put_data(resp, data->id_server, data->id_server_len); if (random_get_bytes(data->rand_peer, EAP_GPSK_RAND_LEN)) { wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to get random data " "for RAND_Peer"); eap_gpsk_state(data, FAILURE); wpabuf_free(resp); return NULL; } wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Peer", data->rand_peer, EAP_GPSK_RAND_LEN); wpabuf_put_data(resp, data->rand_peer, EAP_GPSK_RAND_LEN); wpabuf_put_data(resp, data->rand_server, EAP_GPSK_RAND_LEN); wpabuf_put_be16(resp, csuite_list_len); wpabuf_put_data(resp, csuite_list, csuite_list_len); csuite = wpabuf_put(resp, sizeof(*csuite)); WPA_PUT_BE32(csuite->vendor, data->vendor); WPA_PUT_BE16(csuite->specifier, data->specifier); if (eap_gpsk_derive_keys(data->psk, data->psk_len, data->vendor, data->specifier, data->rand_peer, data->rand_server, data->id_peer, data->id_peer_len, data->id_server, data->id_server_len, data->msk, data->emsk, data->sk, &data->sk_len, data->pk, &data->pk_len) < 0) { wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to derive keys"); eap_gpsk_state(data, FAILURE); wpabuf_free(resp); return NULL; } if (eap_gpsk_derive_session_id(data->psk, data->psk_len, data->vendor, data->specifier, data->rand_peer, data->rand_server, data->id_peer, data->id_peer_len, data->id_server, data->id_server_len, EAP_TYPE_GPSK, data->session_id, &data->id_len) < 0) { wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to derive Session-Id"); eap_gpsk_state(data, FAILURE); wpabuf_free(resp); return NULL; } wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Derived Session-Id", data->session_id, data->id_len); /* No PD_Payload_1 */ wpabuf_put_be16(resp, 0); rpos = wpabuf_put(resp, miclen); if (eap_gpsk_compute_mic(data->sk, data->sk_len, data->vendor, data->specifier, start, rpos - start, rpos) < 0) { eap_gpsk_state(data, FAILURE); wpabuf_free(resp); return NULL; } return resp; } static const u8 * eap_gpsk_validate_rand(struct eap_gpsk_data *data, const u8 *pos, const u8 *end) { if (end - pos < EAP_GPSK_RAND_LEN) { wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for " "RAND_Peer"); return NULL; } if (os_memcmp(pos, data->rand_peer, EAP_GPSK_RAND_LEN) != 0) { wpa_printf(MSG_DEBUG, "EAP-GPSK: RAND_Peer in GPSK-2 and " "GPSK-3 did not match"); wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Peer in GPSK-2", data->rand_peer, EAP_GPSK_RAND_LEN); wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Peer in GPSK-3", pos, EAP_GPSK_RAND_LEN); return NULL; } pos += EAP_GPSK_RAND_LEN; if (end - pos < EAP_GPSK_RAND_LEN) { wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for " "RAND_Server"); return NULL; } if (os_memcmp(pos, data->rand_server, EAP_GPSK_RAND_LEN) != 0) { wpa_printf(MSG_DEBUG, "EAP-GPSK: RAND_Server in GPSK-1 and " "GPSK-3 did not match"); wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Server in GPSK-1", data->rand_server, EAP_GPSK_RAND_LEN); wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Server in GPSK-3", pos, EAP_GPSK_RAND_LEN); return NULL; } pos += EAP_GPSK_RAND_LEN; return pos; } static const u8 * eap_gpsk_validate_id_server(struct eap_gpsk_data *data, const u8 *pos, const u8 *end) { size_t len; if (pos == NULL) return NULL; if (end - pos < (int) 2) { wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for " "length(ID_Server)"); return NULL; } len = WPA_GET_BE16(pos); pos += 2; if (end - pos < (int) len) { wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for " "ID_Server"); return NULL; } if (len != data->id_server_len || os_memcmp(pos, data->id_server, len) != 0) { wpa_printf(MSG_INFO, "EAP-GPSK: ID_Server did not match with " "the one used in GPSK-1"); wpa_hexdump_ascii(MSG_DEBUG, "EAP-GPSK: ID_Server in GPSK-1", data->id_server, data->id_server_len); wpa_hexdump_ascii(MSG_DEBUG, "EAP-GPSK: ID_Server in GPSK-3", pos, len); return NULL; } pos += len; return pos; } static const u8 * eap_gpsk_validate_csuite(struct eap_gpsk_data *data, const u8 *pos, const u8 *end) { int vendor, specifier; const struct eap_gpsk_csuite *csuite; if (pos == NULL) return NULL; if (end - pos < (int) sizeof(*csuite)) { wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for " "CSuite_Sel"); return NULL; } csuite = (const struct eap_gpsk_csuite *) pos; vendor = WPA_GET_BE32(csuite->vendor); specifier = WPA_GET_BE16(csuite->specifier); pos += sizeof(*csuite); if (vendor != data->vendor || specifier != data->specifier) { wpa_printf(MSG_DEBUG, "EAP-GPSK: CSuite_Sel (%d:%d) does not " "match with the one sent in GPSK-2 (%d:%d)", vendor, specifier, data->vendor, data->specifier); return NULL; } return pos; } static const u8 * eap_gpsk_validate_pd_payload_2(struct eap_gpsk_data *data, const u8 *pos, const u8 *end) { u16 alen; if (pos == NULL) return NULL; if (end - pos < 2) { wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for " "PD_Payload_2 length"); return NULL; } alen = WPA_GET_BE16(pos); pos += 2; if (end - pos < alen) { wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for " "%d-octet PD_Payload_2", alen); return NULL; } wpa_hexdump(MSG_DEBUG, "EAP-GPSK: PD_Payload_2", pos, alen); pos += alen; return pos; } static const u8 * eap_gpsk_validate_gpsk_3_mic(struct eap_gpsk_data *data, const u8 *payload, const u8 *pos, const u8 *end) { size_t miclen; u8 mic[EAP_GPSK_MAX_MIC_LEN]; if (pos == NULL) return NULL; miclen = eap_gpsk_mic_len(data->vendor, data->specifier); if (end - pos < (int) miclen) { wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for MIC " "(left=%lu miclen=%lu)", (unsigned long) (end - pos), (unsigned long) miclen); return NULL; } if (eap_gpsk_compute_mic(data->sk, data->sk_len, data->vendor, data->specifier, payload, pos - payload, mic) < 0) { wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to compute MIC"); return NULL; } if (os_memcmp(mic, pos, miclen) != 0) { wpa_printf(MSG_INFO, "EAP-GPSK: Incorrect MIC in GPSK-3"); wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Received MIC", pos, miclen); wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Computed MIC", mic, miclen); return NULL; } pos += miclen; return pos; } static struct wpabuf * eap_gpsk_process_gpsk_3(struct eap_sm *sm, struct eap_gpsk_data *data, struct eap_method_ret *ret, const struct wpabuf *reqData, const u8 *payload, size_t payload_len) { struct wpabuf *resp; const u8 *pos, *end; if (data->state != GPSK_3) { ret->ignore = TRUE; return NULL; } wpa_printf(MSG_DEBUG, "EAP-GPSK: Received Request/GPSK-3"); end = payload + payload_len; pos = eap_gpsk_validate_rand(data, payload, end); pos = eap_gpsk_validate_id_server(data, pos, end); pos = eap_gpsk_validate_csuite(data, pos, end); pos = eap_gpsk_validate_pd_payload_2(data, pos, end); pos = eap_gpsk_validate_gpsk_3_mic(data, payload, pos, end); if (pos == NULL) { eap_gpsk_state(data, FAILURE); return NULL; } if (pos != end) { wpa_printf(MSG_DEBUG, "EAP-GPSK: Ignored %lu bytes of extra " "data in the end of GPSK-2", (unsigned long) (end - pos)); } resp = eap_gpsk_send_gpsk_4(data, eap_get_id(reqData)); if (resp == NULL) return NULL; eap_gpsk_state(data, SUCCESS); ret->methodState = METHOD_DONE; ret->decision = DECISION_UNCOND_SUCC; return resp; } static struct wpabuf * eap_gpsk_send_gpsk_4(struct eap_gpsk_data *data, u8 identifier) { struct wpabuf *resp; u8 *rpos, *start; size_t mlen; wpa_printf(MSG_DEBUG, "EAP-GPSK: Sending Response/GPSK-4"); mlen = eap_gpsk_mic_len(data->vendor, data->specifier); resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GPSK, 1 + 2 + mlen, EAP_CODE_RESPONSE, identifier); if (resp == NULL) return NULL; wpabuf_put_u8(resp, EAP_GPSK_OPCODE_GPSK_4); start = wpabuf_put(resp, 0); /* No PD_Payload_3 */ wpabuf_put_be16(resp, 0); rpos = wpabuf_put(resp, mlen); if (eap_gpsk_compute_mic(data->sk, data->sk_len, data->vendor, data->specifier, start, rpos - start, rpos) < 0) { eap_gpsk_state(data, FAILURE); wpabuf_free(resp); return NULL; } return resp; } static struct wpabuf * eap_gpsk_process(struct eap_sm *sm, void *priv, struct eap_method_ret *ret, const struct wpabuf *reqData) { struct eap_gpsk_data *data = priv; struct wpabuf *resp; const u8 *pos; size_t len; pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_GPSK, reqData, &len); if (pos == NULL || len < 1) { ret->ignore = TRUE; return NULL; } wpa_printf(MSG_DEBUG, "EAP-GPSK: Received frame: opcode %d", *pos); ret->ignore = FALSE; ret->methodState = METHOD_MAY_CONT; ret->decision = DECISION_FAIL; ret->allowNotifications = FALSE; switch (*pos) { case EAP_GPSK_OPCODE_GPSK_1: resp = eap_gpsk_process_gpsk_1(sm, data, ret, reqData, pos + 1, len - 1); break; case EAP_GPSK_OPCODE_GPSK_3: resp = eap_gpsk_process_gpsk_3(sm, data, ret, reqData, pos + 1, len - 1); break; default: wpa_printf(MSG_DEBUG, "EAP-GPSK: Ignoring message with " "unknown opcode %d", *pos); ret->ignore = TRUE; return NULL; } return resp; } static Boolean eap_gpsk_isKeyAvailable(struct eap_sm *sm, void *priv) { struct eap_gpsk_data *data = priv; return data->state == SUCCESS; } static u8 * eap_gpsk_getKey(struct eap_sm *sm, void *priv, size_t *len) { struct eap_gpsk_data *data = priv; u8 *key; if (data->state != SUCCESS) return NULL; key = os_malloc(EAP_MSK_LEN); if (key == NULL) return NULL; os_memcpy(key, data->msk, EAP_MSK_LEN); *len = EAP_MSK_LEN; return key; } static u8 * eap_gpsk_get_emsk(struct eap_sm *sm, void *priv, size_t *len) { struct eap_gpsk_data *data = priv; u8 *key; if (data->state != SUCCESS) return NULL; key = os_malloc(EAP_EMSK_LEN); if (key == NULL) return NULL; os_memcpy(key, data->emsk, EAP_EMSK_LEN); *len = EAP_EMSK_LEN; return key; } static u8 * eap_gpsk_get_session_id(struct eap_sm *sm, void *priv, size_t *len) { struct eap_gpsk_data *data = priv; u8 *sid; if (data->state != SUCCESS) return NULL; sid = os_malloc(data->id_len); if (sid == NULL) return NULL; os_memcpy(sid, data->session_id, data->id_len); *len = data->id_len; return sid; } int eap_peer_gpsk_register(void) { struct eap_method *eap; int ret; eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_GPSK, "GPSK"); if (eap == NULL) return -1; eap->init = eap_gpsk_init; eap->deinit = eap_gpsk_deinit; eap->process = eap_gpsk_process; eap->isKeyAvailable = eap_gpsk_isKeyAvailable; eap->getKey = eap_gpsk_getKey; eap->get_emsk = eap_gpsk_get_emsk; eap->getSessionId = eap_gpsk_get_session_id; ret = eap_peer_method_register(eap); if (ret) eap_peer_method_free(eap); return ret; } wpa-2.1/src/eap_peer/eap_gtc.c000066400000000000000000000066401227414666700163050ustar00rootroot00000000000000/* * EAP peer method: EAP-GTC (RFC 3748) * Copyright (c) 2004-2006, 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_i.h" struct eap_gtc_data { int prefix; }; static void * eap_gtc_init(struct eap_sm *sm) { struct eap_gtc_data *data; data = os_zalloc(sizeof(*data)); if (data == NULL) return NULL; if (sm->m && sm->m->vendor == EAP_VENDOR_IETF && sm->m->method == EAP_TYPE_FAST) { wpa_printf(MSG_DEBUG, "EAP-GTC: EAP-FAST tunnel - use prefix " "with challenge/response"); data->prefix = 1; } return data; } static void eap_gtc_deinit(struct eap_sm *sm, void *priv) { struct eap_gtc_data *data = priv; os_free(data); } static struct wpabuf * eap_gtc_process(struct eap_sm *sm, void *priv, struct eap_method_ret *ret, const struct wpabuf *reqData) { struct eap_gtc_data *data = priv; struct wpabuf *resp; const u8 *pos, *password, *identity; size_t password_len, identity_len, len, plen; int otp; u8 id; pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_GTC, reqData, &len); if (pos == NULL) { ret->ignore = TRUE; return NULL; } id = eap_get_id(reqData); wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-GTC: Request message", pos, len); if (data->prefix && (len < 10 || os_memcmp(pos, "CHALLENGE=", 10) != 0)) { wpa_printf(MSG_DEBUG, "EAP-GTC: Challenge did not start with " "expected prefix"); /* Send an empty response in order to allow tunneled * acknowledgement of the failure. This will also cover the * error case which seems to use EAP-MSCHAPv2 like error * reporting with EAP-GTC inside EAP-FAST tunnel. */ resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GTC, 0, EAP_CODE_RESPONSE, id); return resp; } password = eap_get_config_otp(sm, &password_len); if (password) otp = 1; else { password = eap_get_config_password(sm, &password_len); otp = 0; } if (password == NULL) { wpa_printf(MSG_INFO, "EAP-GTC: Password not configured"); eap_sm_request_otp(sm, (const char *) pos, len); ret->ignore = TRUE; return NULL; } ret->ignore = FALSE; ret->methodState = data->prefix ? METHOD_MAY_CONT : METHOD_DONE; ret->decision = DECISION_COND_SUCC; ret->allowNotifications = FALSE; plen = password_len; identity = eap_get_config_identity(sm, &identity_len); if (identity == NULL) return NULL; if (data->prefix) plen += 9 + identity_len + 1; resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GTC, plen, EAP_CODE_RESPONSE, id); if (resp == NULL) return NULL; if (data->prefix) { wpabuf_put_data(resp, "RESPONSE=", 9); wpabuf_put_data(resp, identity, identity_len); wpabuf_put_u8(resp, '\0'); } wpabuf_put_data(resp, password, password_len); wpa_hexdump_ascii_key(MSG_MSGDUMP, "EAP-GTC: Response", wpabuf_head_u8(resp) + sizeof(struct eap_hdr) + 1, plen); if (otp) { wpa_printf(MSG_DEBUG, "EAP-GTC: Forgetting used password"); eap_clear_config_otp(sm); } return resp; } int eap_peer_gtc_register(void) { struct eap_method *eap; int ret; eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_GTC, "GTC"); if (eap == NULL) return -1; eap->init = eap_gtc_init; eap->deinit = eap_gtc_deinit; eap->process = eap_gtc_process; ret = eap_peer_method_register(eap); if (ret) eap_peer_method_free(eap); return ret; } wpa-2.1/src/eap_peer/eap_i.h000066400000000000000000000312411227414666700157600ustar00rootroot00000000000000/* * EAP peer state machines internal structures (RFC 4137) * Copyright (c) 2004-2007, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef EAP_I_H #define EAP_I_H #include "wpabuf.h" #include "eap_peer/eap.h" #include "eap_common/eap_common.h" /* RFC 4137 - EAP Peer state machine */ typedef enum { DECISION_FAIL, DECISION_COND_SUCC, DECISION_UNCOND_SUCC } EapDecision; typedef enum { METHOD_NONE, METHOD_INIT, METHOD_CONT, METHOD_MAY_CONT, METHOD_DONE } EapMethodState; /** * struct eap_method_ret - EAP return values from struct eap_method::process() * * These structure contains OUT variables for the interface between peer state * machine and methods (RFC 4137, Sect. 4.2). eapRespData will be returned as * the return value of struct eap_method::process() so it is not included in * this structure. */ struct eap_method_ret { /** * ignore - Whether method decided to drop the current packed (OUT) */ Boolean ignore; /** * methodState - Method-specific state (IN/OUT) */ EapMethodState methodState; /** * decision - Authentication decision (OUT) */ EapDecision decision; /** * allowNotifications - Whether method allows notifications (OUT) */ Boolean allowNotifications; }; /** * struct eap_method - EAP method interface * This structure defines the EAP method interface. Each method will need to * register its own EAP type, EAP name, and set of function pointers for method * specific operations. This interface is based on section 4.4 of RFC 4137. */ struct eap_method { /** * vendor - EAP Vendor-ID (EAP_VENDOR_*) (0 = IETF) */ int vendor; /** * method - EAP type number (EAP_TYPE_*) */ EapType method; /** * name - Name of the method (e.g., "TLS") */ const char *name; /** * init - Initialize an EAP method * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * Returns: Pointer to allocated private data, or %NULL on failure * * This function is used to initialize the EAP method explicitly * instead of using METHOD_INIT state as specific in RFC 4137. The * method is expected to initialize it method-specific state and return * a pointer that will be used as the priv argument to other calls. */ void * (*init)(struct eap_sm *sm); /** * deinit - Deinitialize an EAP method * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * @priv: Pointer to private EAP method data from eap_method::init() * * Deinitialize the EAP method and free any allocated private data. */ void (*deinit)(struct eap_sm *sm, void *priv); /** * process - Process an EAP request * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * @priv: Pointer to private EAP method data from eap_method::init() * @ret: Return values from EAP request validation and processing * @reqData: EAP request to be processed (eapReqData) * Returns: Pointer to allocated EAP response packet (eapRespData) * * This function is a combination of m.check(), m.process(), and * m.buildResp() procedures defined in section 4.4 of RFC 4137 In other * words, this function validates the incoming request, processes it, * and build a response packet. m.check() and m.process() return values * are returned through struct eap_method_ret *ret variable. Caller is * responsible for freeing the returned EAP response packet. */ struct wpabuf * (*process)(struct eap_sm *sm, void *priv, struct eap_method_ret *ret, const struct wpabuf *reqData); /** * isKeyAvailable - Find out whether EAP method has keying material * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * @priv: Pointer to private EAP method data from eap_method::init() * Returns: %TRUE if key material (eapKeyData) is available */ Boolean (*isKeyAvailable)(struct eap_sm *sm, void *priv); /** * getKey - Get EAP method specific keying material (eapKeyData) * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * @priv: Pointer to private EAP method data from eap_method::init() * @len: Pointer to variable to store key length (eapKeyDataLen) * Returns: Keying material (eapKeyData) or %NULL if not available * * This function can be used to get the keying material from the EAP * method. The key may already be stored in the method-specific private * data or this function may derive the key. */ u8 * (*getKey)(struct eap_sm *sm, void *priv, size_t *len); /** * get_status - Get EAP method status * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * @priv: Pointer to private EAP method data from eap_method::init() * @buf: Buffer for status information * @buflen: Maximum buffer length * @verbose: Whether to include verbose status information * Returns: Number of bytes written to buf * * Query EAP method for status information. This function fills in a * text area with current status information from the EAP method. If * the buffer (buf) is not large enough, status information will be * truncated to fit the buffer. */ int (*get_status)(struct eap_sm *sm, void *priv, char *buf, size_t buflen, int verbose); /** * has_reauth_data - Whether method is ready for fast reauthentication * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * @priv: Pointer to private EAP method data from eap_method::init() * Returns: %TRUE or %FALSE based on whether fast reauthentication is * possible * * This function is an optional handler that only EAP methods * supporting fast re-authentication need to implement. */ Boolean (*has_reauth_data)(struct eap_sm *sm, void *priv); /** * deinit_for_reauth - Release data that is not needed for fast re-auth * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * @priv: Pointer to private EAP method data from eap_method::init() * * This function is an optional handler that only EAP methods * supporting fast re-authentication need to implement. This is called * when authentication has been completed and EAP state machine is * requesting that enough state information is maintained for fast * re-authentication */ void (*deinit_for_reauth)(struct eap_sm *sm, void *priv); /** * init_for_reauth - Prepare for start of fast re-authentication * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * @priv: Pointer to private EAP method data from eap_method::init() * * This function is an optional handler that only EAP methods * supporting fast re-authentication need to implement. This is called * when EAP authentication is started and EAP state machine is * requesting fast re-authentication to be used. */ void * (*init_for_reauth)(struct eap_sm *sm, void *priv); /** * get_identity - Get method specific identity for re-authentication * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * @priv: Pointer to private EAP method data from eap_method::init() * @len: Length of the returned identity * Returns: Pointer to the method specific identity or %NULL if default * identity is to be used * * This function is an optional handler that only EAP methods * that use method specific identity need to implement. */ const u8 * (*get_identity)(struct eap_sm *sm, void *priv, size_t *len); /** * free - Free EAP method data * @method: Pointer to the method data registered with * eap_peer_method_register(). * * This function will be called when the EAP method is being * unregistered. If the EAP method allocated resources during * registration (e.g., allocated struct eap_method), they should be * freed in this function. No other method functions will be called * after this call. If this function is not defined (i.e., function * pointer is %NULL), a default handler is used to release the method * data with free(method). This is suitable for most cases. */ void (*free)(struct eap_method *method); #define EAP_PEER_METHOD_INTERFACE_VERSION 1 /** * version - Version of the EAP peer method interface * * The EAP peer method implementation should set this variable to * EAP_PEER_METHOD_INTERFACE_VERSION. This is used to verify that the * EAP method is using supported API version when using dynamically * loadable EAP methods. */ int version; /** * next - Pointer to the next EAP method * * This variable is used internally in the EAP method registration code * to create a linked list of registered EAP methods. */ struct eap_method *next; #ifdef CONFIG_DYNAMIC_EAP_METHODS /** * dl_handle - Handle for the dynamic library * * This variable is used internally in the EAP method registration code * to store a handle for the dynamic library. If the method is linked * in statically, this is %NULL. */ void *dl_handle; #endif /* CONFIG_DYNAMIC_EAP_METHODS */ /** * get_emsk - Get EAP method specific keying extended material (EMSK) * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * @priv: Pointer to private EAP method data from eap_method::init() * @len: Pointer to a variable to store EMSK length * Returns: EMSK or %NULL if not available * * This function can be used to get the extended keying material from * the EAP method. The key may already be stored in the method-specific * private data or this function may derive the key. */ u8 * (*get_emsk)(struct eap_sm *sm, void *priv, size_t *len); /** * getSessionId - Get EAP method specific Session-Id * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * @priv: Pointer to private EAP method data from eap_method::init() * @len: Pointer to a variable to store Session-Id length * Returns: Session-Id or %NULL if not available * * This function can be used to get the Session-Id from the EAP method. * The Session-Id may already be stored in the method-specific private * data or this function may derive the Session-Id. */ u8 * (*getSessionId)(struct eap_sm *sm, void *priv, size_t *len); }; /** * struct eap_sm - EAP state machine data */ struct eap_sm { enum { EAP_INITIALIZE, EAP_DISABLED, EAP_IDLE, EAP_RECEIVED, EAP_GET_METHOD, EAP_METHOD, EAP_SEND_RESPONSE, EAP_DISCARD, EAP_IDENTITY, EAP_NOTIFICATION, EAP_RETRANSMIT, EAP_SUCCESS, EAP_FAILURE } EAP_state; /* Long-term local variables */ EapType selectedMethod; EapMethodState methodState; int lastId; struct wpabuf *lastRespData; EapDecision decision; /* Short-term local variables */ Boolean rxReq; Boolean rxSuccess; Boolean rxFailure; int reqId; EapType reqMethod; int reqVendor; u32 reqVendorMethod; Boolean ignore; /* Constants */ int ClientTimeout; /* Miscellaneous variables */ Boolean allowNotifications; /* peer state machine <-> methods */ struct wpabuf *eapRespData; /* peer to lower layer */ Boolean eapKeyAvailable; /* peer to lower layer */ u8 *eapKeyData; /* peer to lower layer */ size_t eapKeyDataLen; /* peer to lower layer */ u8 *eapSessionId; /* peer to lower layer */ size_t eapSessionIdLen; /* peer to lower layer */ const struct eap_method *m; /* selected EAP method */ /* not defined in RFC 4137 */ Boolean changed; void *eapol_ctx; struct eapol_callbacks *eapol_cb; void *eap_method_priv; int init_phase2; int fast_reauth; Boolean rxResp /* LEAP only */; Boolean leap_done; Boolean peap_done; u8 req_md5[16]; /* MD5() of the current EAP packet */ u8 last_md5[16]; /* MD5() of the previously received EAP packet; used * in duplicate request detection. */ void *msg_ctx; void *scard_ctx; void *ssl_ctx; void *ssl_ctx2; unsigned int workaround; /* Optional challenges generated in Phase 1 (EAP-FAST) */ u8 *peer_challenge, *auth_challenge; int num_rounds; int force_disabled; struct wps_context *wps; int prev_failure; struct ext_password_data *ext_pw; struct wpabuf *ext_pw_buf; int external_sim; unsigned int expected_failure:1; }; const u8 * eap_get_config_identity(struct eap_sm *sm, size_t *len); const u8 * eap_get_config_password(struct eap_sm *sm, size_t *len); const u8 * eap_get_config_password2(struct eap_sm *sm, size_t *len, int *hash); const u8 * eap_get_config_new_password(struct eap_sm *sm, size_t *len); const u8 * eap_get_config_otp(struct eap_sm *sm, size_t *len); void eap_clear_config_otp(struct eap_sm *sm); const char * eap_get_config_phase1(struct eap_sm *sm); const char * eap_get_config_phase2(struct eap_sm *sm); int eap_get_config_fragment_size(struct eap_sm *sm); struct eap_peer_config * eap_get_config(struct eap_sm *sm); void eap_set_config_blob(struct eap_sm *sm, struct wpa_config_blob *blob); const struct wpa_config_blob * eap_get_config_blob(struct eap_sm *sm, const char *name); void eap_notify_pending(struct eap_sm *sm); int eap_allowed_method(struct eap_sm *sm, int vendor, u32 method); #endif /* EAP_I_H */ wpa-2.1/src/eap_peer/eap_ikev2.c000066400000000000000000000317321227414666700165500ustar00rootroot00000000000000/* * EAP-IKEv2 peer (RFC 5106) * Copyright (c) 2007-2014, 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_i.h" #include "eap_common/eap_ikev2_common.h" #include "ikev2.h" struct eap_ikev2_data { struct ikev2_responder_data ikev2; enum { WAIT_START, PROC_MSG, WAIT_FRAG_ACK, DONE, FAIL } state; struct wpabuf *in_buf; struct wpabuf *out_buf; size_t out_used; size_t fragment_size; int keys_ready; u8 keymat[EAP_MSK_LEN + EAP_EMSK_LEN]; int keymat_ok; }; static const char * eap_ikev2_state_txt(int state) { switch (state) { case WAIT_START: return "WAIT_START"; case PROC_MSG: return "PROC_MSG"; case WAIT_FRAG_ACK: return "WAIT_FRAG_ACK"; case DONE: return "DONE"; case FAIL: return "FAIL"; default: return "?"; } } static void eap_ikev2_state(struct eap_ikev2_data *data, int state) { wpa_printf(MSG_DEBUG, "EAP-IKEV2: %s -> %s", eap_ikev2_state_txt(data->state), eap_ikev2_state_txt(state)); data->state = state; } static void * eap_ikev2_init(struct eap_sm *sm) { struct eap_ikev2_data *data; const u8 *identity, *password; size_t identity_len, password_len; int fragment_size; identity = eap_get_config_identity(sm, &identity_len); if (identity == NULL) { wpa_printf(MSG_INFO, "EAP-IKEV2: No identity available"); return NULL; } data = os_zalloc(sizeof(*data)); if (data == NULL) return NULL; data->state = WAIT_START; fragment_size = eap_get_config_fragment_size(sm); if (fragment_size <= 0) data->fragment_size = IKEV2_FRAGMENT_SIZE; else data->fragment_size = fragment_size; data->ikev2.state = SA_INIT; data->ikev2.peer_auth = PEER_AUTH_SECRET; data->ikev2.key_pad = (u8 *) os_strdup("Key Pad for EAP-IKEv2"); if (data->ikev2.key_pad == NULL) goto failed; data->ikev2.key_pad_len = 21; data->ikev2.IDr = os_malloc(identity_len); if (data->ikev2.IDr == NULL) goto failed; os_memcpy(data->ikev2.IDr, identity, identity_len); data->ikev2.IDr_len = identity_len; password = eap_get_config_password(sm, &password_len); if (password) { data->ikev2.shared_secret = os_malloc(password_len); if (data->ikev2.shared_secret == NULL) goto failed; os_memcpy(data->ikev2.shared_secret, password, password_len); data->ikev2.shared_secret_len = password_len; } return data; failed: ikev2_responder_deinit(&data->ikev2); os_free(data); return NULL; } static void eap_ikev2_deinit(struct eap_sm *sm, void *priv) { struct eap_ikev2_data *data = priv; wpabuf_free(data->in_buf); wpabuf_free(data->out_buf); ikev2_responder_deinit(&data->ikev2); os_free(data); } static int eap_ikev2_peer_keymat(struct eap_ikev2_data *data) { if (eap_ikev2_derive_keymat( data->ikev2.proposal.prf, &data->ikev2.keys, data->ikev2.i_nonce, data->ikev2.i_nonce_len, data->ikev2.r_nonce, data->ikev2.r_nonce_len, data->keymat) < 0) { wpa_printf(MSG_DEBUG, "EAP-IKEV2: Failed to " "derive key material"); return -1; } data->keymat_ok = 1; return 0; } static struct wpabuf * eap_ikev2_build_msg(struct eap_ikev2_data *data, struct eap_method_ret *ret, u8 id) { struct wpabuf *resp; u8 flags; size_t send_len, plen, icv_len = 0; ret->ignore = FALSE; wpa_printf(MSG_DEBUG, "EAP-IKEV2: Generating Response"); ret->allowNotifications = TRUE; flags = 0; send_len = wpabuf_len(data->out_buf) - data->out_used; if (1 + send_len > data->fragment_size) { send_len = data->fragment_size - 1; flags |= IKEV2_FLAGS_MORE_FRAGMENTS; if (data->out_used == 0) { flags |= IKEV2_FLAGS_LENGTH_INCLUDED; send_len -= 4; } } #ifdef CCNS_PL /* Some issues figuring out the length of the message if Message Length * field not included?! */ if (!(flags & IKEV2_FLAGS_LENGTH_INCLUDED)) flags |= IKEV2_FLAGS_LENGTH_INCLUDED; #endif /* CCNS_PL */ plen = 1 + send_len; if (flags & IKEV2_FLAGS_LENGTH_INCLUDED) plen += 4; if (data->keys_ready) { const struct ikev2_integ_alg *integ; wpa_printf(MSG_DEBUG, "EAP-IKEV2: Add Integrity Checksum " "Data"); flags |= IKEV2_FLAGS_ICV_INCLUDED; integ = ikev2_get_integ(data->ikev2.proposal.integ); if (integ == NULL) { wpa_printf(MSG_DEBUG, "EAP-IKEV2: Unknown INTEG " "transform / cannot generate ICV"); return NULL; } icv_len = integ->hash_len; plen += icv_len; } resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_IKEV2, plen, EAP_CODE_RESPONSE, id); if (resp == NULL) return NULL; wpabuf_put_u8(resp, flags); /* Flags */ if (flags & IKEV2_FLAGS_LENGTH_INCLUDED) wpabuf_put_be32(resp, wpabuf_len(data->out_buf)); wpabuf_put_data(resp, wpabuf_head_u8(data->out_buf) + data->out_used, send_len); data->out_used += send_len; if (flags & IKEV2_FLAGS_ICV_INCLUDED) { const u8 *msg = wpabuf_head(resp); size_t len = wpabuf_len(resp); ikev2_integ_hash(data->ikev2.proposal.integ, data->ikev2.keys.SK_ar, data->ikev2.keys.SK_integ_len, msg, len, wpabuf_put(resp, icv_len)); } ret->methodState = METHOD_MAY_CONT; ret->decision = DECISION_FAIL; if (data->out_used == wpabuf_len(data->out_buf)) { wpa_printf(MSG_DEBUG, "EAP-IKEV2: Sending out %lu bytes " "(message sent completely)", (unsigned long) send_len); wpabuf_free(data->out_buf); data->out_buf = NULL; data->out_used = 0; switch (data->ikev2.state) { case SA_AUTH: /* SA_INIT was sent out, so message have to be * integrity protected from now on. */ data->keys_ready = 1; break; case IKEV2_DONE: ret->methodState = METHOD_DONE; if (data->state == FAIL) break; ret->decision = DECISION_COND_SUCC; wpa_printf(MSG_DEBUG, "EAP-IKEV2: Authentication " "completed successfully"); if (eap_ikev2_peer_keymat(data)) break; eap_ikev2_state(data, DONE); break; case IKEV2_FAILED: wpa_printf(MSG_DEBUG, "EAP-IKEV2: Authentication " "failed"); ret->methodState = METHOD_DONE; ret->decision = DECISION_FAIL; break; default: break; } } else { wpa_printf(MSG_DEBUG, "EAP-IKEV2: Sending out %lu bytes " "(%lu more to send)", (unsigned long) send_len, (unsigned long) wpabuf_len(data->out_buf) - data->out_used); eap_ikev2_state(data, WAIT_FRAG_ACK); } return resp; } static int eap_ikev2_process_icv(struct eap_ikev2_data *data, const struct wpabuf *reqData, u8 flags, const u8 *pos, const u8 **end) { if (flags & IKEV2_FLAGS_ICV_INCLUDED) { int icv_len = eap_ikev2_validate_icv( data->ikev2.proposal.integ, &data->ikev2.keys, 1, reqData, pos, *end); if (icv_len < 0) return -1; /* Hide Integrity Checksum Data from further processing */ *end -= icv_len; } else if (data->keys_ready) { wpa_printf(MSG_INFO, "EAP-IKEV2: The message should have " "included integrity checksum"); return -1; } return 0; } static int eap_ikev2_process_cont(struct eap_ikev2_data *data, const u8 *buf, size_t len) { /* Process continuation of a pending message */ if (len > wpabuf_tailroom(data->in_buf)) { wpa_printf(MSG_DEBUG, "EAP-IKEV2: Fragment overflow"); eap_ikev2_state(data, FAIL); return -1; } wpabuf_put_data(data->in_buf, buf, len); wpa_printf(MSG_DEBUG, "EAP-IKEV2: Received %lu bytes, waiting " "for %lu bytes more", (unsigned long) len, (unsigned long) wpabuf_tailroom(data->in_buf)); return 0; } static struct wpabuf * eap_ikev2_process_fragment(struct eap_ikev2_data *data, struct eap_method_ret *ret, u8 id, u8 flags, u32 message_length, const u8 *buf, size_t len) { /* Process a fragment that is not the last one of the message */ if (data->in_buf == NULL && !(flags & IKEV2_FLAGS_LENGTH_INCLUDED)) { wpa_printf(MSG_DEBUG, "EAP-IKEV2: No Message Length field in " "a fragmented packet"); ret->ignore = TRUE; return NULL; } if (data->in_buf == NULL) { /* First fragment of the message */ data->in_buf = wpabuf_alloc(message_length); if (data->in_buf == NULL) { wpa_printf(MSG_DEBUG, "EAP-IKEV2: No memory for " "message"); ret->ignore = TRUE; return NULL; } wpabuf_put_data(data->in_buf, buf, len); wpa_printf(MSG_DEBUG, "EAP-IKEV2: Received %lu bytes in first " "fragment, waiting for %lu bytes more", (unsigned long) len, (unsigned long) wpabuf_tailroom(data->in_buf)); } return eap_ikev2_build_frag_ack(id, EAP_CODE_RESPONSE); } static struct wpabuf * eap_ikev2_process(struct eap_sm *sm, void *priv, struct eap_method_ret *ret, const struct wpabuf *reqData) { struct eap_ikev2_data *data = priv; const u8 *start, *pos, *end; size_t len; u8 flags, id; u32 message_length = 0; struct wpabuf tmpbuf; pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_IKEV2, reqData, &len); if (pos == NULL) { ret->ignore = TRUE; return NULL; } id = eap_get_id(reqData); start = pos; end = start + len; if (len == 0) flags = 0; /* fragment ack */ else flags = *pos++; if (eap_ikev2_process_icv(data, reqData, flags, pos, &end) < 0) { ret->ignore = TRUE; return NULL; } if (flags & IKEV2_FLAGS_LENGTH_INCLUDED) { if (end - pos < 4) { wpa_printf(MSG_DEBUG, "EAP-IKEV2: Message underflow"); ret->ignore = TRUE; return NULL; } message_length = WPA_GET_BE32(pos); pos += 4; if (message_length < (u32) (end - pos)) { wpa_printf(MSG_DEBUG, "EAP-IKEV2: Invalid Message " "Length (%d; %ld remaining in this msg)", message_length, (long) (end - pos)); ret->ignore = TRUE; return NULL; } } wpa_printf(MSG_DEBUG, "EAP-IKEV2: Received packet: Flags 0x%x " "Message Length %u", flags, message_length); if (data->state == WAIT_FRAG_ACK) { #ifdef CCNS_PL if (len > 1) /* Empty Flags field included in ACK */ #else /* CCNS_PL */ if (len != 0) #endif /* CCNS_PL */ { wpa_printf(MSG_DEBUG, "EAP-IKEV2: Unexpected payload " "in WAIT_FRAG_ACK state"); ret->ignore = TRUE; return NULL; } wpa_printf(MSG_DEBUG, "EAP-IKEV2: Fragment acknowledged"); eap_ikev2_state(data, PROC_MSG); return eap_ikev2_build_msg(data, ret, id); } if (data->in_buf && eap_ikev2_process_cont(data, pos, end - pos) < 0) { ret->ignore = TRUE; return NULL; } if (flags & IKEV2_FLAGS_MORE_FRAGMENTS) { return eap_ikev2_process_fragment(data, ret, id, flags, message_length, pos, end - pos); } if (data->in_buf == NULL) { /* Wrap unfragmented messages as wpabuf without extra copy */ wpabuf_set(&tmpbuf, pos, end - pos); data->in_buf = &tmpbuf; } if (ikev2_responder_process(&data->ikev2, data->in_buf) < 0) { if (data->in_buf == &tmpbuf) data->in_buf = NULL; eap_ikev2_state(data, FAIL); return NULL; } if (data->in_buf != &tmpbuf) wpabuf_free(data->in_buf); data->in_buf = NULL; if (data->out_buf == NULL) { data->out_buf = ikev2_responder_build(&data->ikev2); if (data->out_buf == NULL) { wpa_printf(MSG_DEBUG, "EAP-IKEV2: Failed to generate " "IKEv2 message"); return NULL; } data->out_used = 0; } eap_ikev2_state(data, PROC_MSG); return eap_ikev2_build_msg(data, ret, id); } static Boolean eap_ikev2_isKeyAvailable(struct eap_sm *sm, void *priv) { struct eap_ikev2_data *data = priv; return data->state == DONE && data->keymat_ok; } static u8 * eap_ikev2_getKey(struct eap_sm *sm, void *priv, size_t *len) { struct eap_ikev2_data *data = priv; u8 *key; if (data->state != DONE || !data->keymat_ok) return NULL; key = os_malloc(EAP_MSK_LEN); if (key) { os_memcpy(key, data->keymat, EAP_MSK_LEN); *len = EAP_MSK_LEN; } return key; } static u8 * eap_ikev2_get_emsk(struct eap_sm *sm, void *priv, size_t *len) { struct eap_ikev2_data *data = priv; u8 *key; if (data->state != DONE || !data->keymat_ok) return NULL; key = os_malloc(EAP_EMSK_LEN); if (key) { os_memcpy(key, data->keymat + EAP_MSK_LEN, EAP_EMSK_LEN); *len = EAP_EMSK_LEN; } return key; } static u8 * eap_ikev2_get_session_id(struct eap_sm *sm, void *priv, size_t *len) { struct eap_ikev2_data *data = priv; u8 *sid; size_t sid_len; size_t offset; if (data->state != DONE || !data->keymat_ok) return NULL; sid_len = 1 + data->ikev2.i_nonce_len + data->ikev2.r_nonce_len; sid = os_malloc(sid_len); if (sid) { offset = 0; sid[offset] = EAP_TYPE_IKEV2; offset++; os_memcpy(sid + offset, data->ikev2.i_nonce, data->ikev2.i_nonce_len); offset += data->ikev2.i_nonce_len; os_memcpy(sid + offset, data->ikev2.r_nonce, data->ikev2.r_nonce_len); *len = sid_len; wpa_hexdump(MSG_DEBUG, "EAP-IKEV2: Derived Session-Id", sid, sid_len); } return sid; } int eap_peer_ikev2_register(void) { struct eap_method *eap; int ret; eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_IKEV2, "IKEV2"); if (eap == NULL) return -1; eap->init = eap_ikev2_init; eap->deinit = eap_ikev2_deinit; eap->process = eap_ikev2_process; eap->isKeyAvailable = eap_ikev2_isKeyAvailable; eap->getKey = eap_ikev2_getKey; eap->get_emsk = eap_ikev2_get_emsk; eap->getSessionId = eap_ikev2_get_session_id; ret = eap_peer_method_register(eap); if (ret) eap_peer_method_free(eap); return ret; } wpa-2.1/src/eap_peer/eap_leap.c000066400000000000000000000251241227414666700164470ustar00rootroot00000000000000/* * EAP peer method: LEAP * Copyright (c) 2004-2007, 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" #include "crypto/crypto.h" #include "crypto/random.h" #include "eap_i.h" #define LEAP_VERSION 1 #define LEAP_CHALLENGE_LEN 8 #define LEAP_RESPONSE_LEN 24 #define LEAP_KEY_LEN 16 struct eap_leap_data { enum { LEAP_WAIT_CHALLENGE, LEAP_WAIT_SUCCESS, LEAP_WAIT_RESPONSE, LEAP_DONE } state; u8 peer_challenge[LEAP_CHALLENGE_LEN]; u8 peer_response[LEAP_RESPONSE_LEN]; u8 ap_challenge[LEAP_CHALLENGE_LEN]; u8 ap_response[LEAP_RESPONSE_LEN]; }; static void * eap_leap_init(struct eap_sm *sm) { struct eap_leap_data *data; data = os_zalloc(sizeof(*data)); if (data == NULL) return NULL; data->state = LEAP_WAIT_CHALLENGE; sm->leap_done = FALSE; return data; } static void eap_leap_deinit(struct eap_sm *sm, void *priv) { os_free(priv); } static struct wpabuf * eap_leap_process_request(struct eap_sm *sm, void *priv, struct eap_method_ret *ret, const struct wpabuf *reqData) { struct eap_leap_data *data = priv; struct wpabuf *resp; const u8 *pos, *challenge, *identity, *password; u8 challenge_len, *rpos; size_t identity_len, password_len, len; int pwhash; wpa_printf(MSG_DEBUG, "EAP-LEAP: Processing EAP-Request"); identity = eap_get_config_identity(sm, &identity_len); password = eap_get_config_password2(sm, &password_len, &pwhash); if (identity == NULL || password == NULL) return NULL; pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_LEAP, reqData, &len); if (pos == NULL || len < 3) { wpa_printf(MSG_INFO, "EAP-LEAP: Invalid EAP-Request frame"); ret->ignore = TRUE; return NULL; } if (*pos != LEAP_VERSION) { wpa_printf(MSG_WARNING, "EAP-LEAP: Unsupported LEAP version " "%d", *pos); ret->ignore = TRUE; return NULL; } pos++; pos++; /* skip unused byte */ challenge_len = *pos++; if (challenge_len != LEAP_CHALLENGE_LEN || challenge_len > len - 3) { wpa_printf(MSG_INFO, "EAP-LEAP: Invalid challenge " "(challenge_len=%d reqDataLen=%lu)", challenge_len, (unsigned long) wpabuf_len(reqData)); ret->ignore = TRUE; return NULL; } challenge = pos; os_memcpy(data->peer_challenge, challenge, LEAP_CHALLENGE_LEN); wpa_hexdump(MSG_MSGDUMP, "EAP-LEAP: Challenge from AP", challenge, LEAP_CHALLENGE_LEN); wpa_printf(MSG_DEBUG, "EAP-LEAP: Generating Challenge Response"); resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_LEAP, 3 + LEAP_RESPONSE_LEN + identity_len, EAP_CODE_RESPONSE, eap_get_id(reqData)); if (resp == NULL) return NULL; wpabuf_put_u8(resp, LEAP_VERSION); wpabuf_put_u8(resp, 0); /* unused */ wpabuf_put_u8(resp, LEAP_RESPONSE_LEN); rpos = wpabuf_put(resp, LEAP_RESPONSE_LEN); if (pwhash) challenge_response(challenge, password, rpos); else nt_challenge_response(challenge, password, password_len, rpos); os_memcpy(data->peer_response, rpos, LEAP_RESPONSE_LEN); wpa_hexdump(MSG_MSGDUMP, "EAP-LEAP: Response", rpos, LEAP_RESPONSE_LEN); wpabuf_put_data(resp, identity, identity_len); data->state = LEAP_WAIT_SUCCESS; return resp; } static struct wpabuf * eap_leap_process_success(struct eap_sm *sm, void *priv, struct eap_method_ret *ret, const struct wpabuf *reqData) { struct eap_leap_data *data = priv; struct wpabuf *resp; u8 *pos; const u8 *identity; size_t identity_len; wpa_printf(MSG_DEBUG, "EAP-LEAP: Processing EAP-Success"); identity = eap_get_config_identity(sm, &identity_len); if (identity == NULL) return NULL; if (data->state != LEAP_WAIT_SUCCESS) { wpa_printf(MSG_INFO, "EAP-LEAP: EAP-Success received in " "unexpected state (%d) - ignored", data->state); ret->ignore = TRUE; return NULL; } resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_LEAP, 3 + LEAP_CHALLENGE_LEN + identity_len, EAP_CODE_REQUEST, eap_get_id(reqData)); if (resp == NULL) return NULL; wpabuf_put_u8(resp, LEAP_VERSION); wpabuf_put_u8(resp, 0); /* unused */ wpabuf_put_u8(resp, LEAP_CHALLENGE_LEN); pos = wpabuf_put(resp, LEAP_CHALLENGE_LEN); if (random_get_bytes(pos, LEAP_CHALLENGE_LEN)) { wpa_printf(MSG_WARNING, "EAP-LEAP: Failed to read random data " "for challenge"); wpabuf_free(resp); ret->ignore = TRUE; return NULL; } os_memcpy(data->ap_challenge, pos, LEAP_CHALLENGE_LEN); wpa_hexdump(MSG_MSGDUMP, "EAP-LEAP: Challenge to AP/AS", pos, LEAP_CHALLENGE_LEN); wpabuf_put_data(resp, identity, identity_len); data->state = LEAP_WAIT_RESPONSE; return resp; } static struct wpabuf * eap_leap_process_response(struct eap_sm *sm, void *priv, struct eap_method_ret *ret, const struct wpabuf *reqData) { struct eap_leap_data *data = priv; const u8 *pos, *password; u8 response_len, pw_hash[16], pw_hash_hash[16], expected[LEAP_RESPONSE_LEN]; size_t password_len, len; int pwhash; wpa_printf(MSG_DEBUG, "EAP-LEAP: Processing EAP-Response"); password = eap_get_config_password2(sm, &password_len, &pwhash); if (password == NULL) return NULL; pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_LEAP, reqData, &len); if (pos == NULL || len < 3) { wpa_printf(MSG_INFO, "EAP-LEAP: Invalid EAP-Response frame"); ret->ignore = TRUE; return NULL; } if (*pos != LEAP_VERSION) { wpa_printf(MSG_WARNING, "EAP-LEAP: Unsupported LEAP version " "%d", *pos); ret->ignore = TRUE; return NULL; } pos++; pos++; /* skip unused byte */ response_len = *pos++; if (response_len != LEAP_RESPONSE_LEN || response_len > len - 3) { wpa_printf(MSG_INFO, "EAP-LEAP: Invalid response " "(response_len=%d reqDataLen=%lu)", response_len, (unsigned long) wpabuf_len(reqData)); ret->ignore = TRUE; return NULL; } wpa_hexdump(MSG_DEBUG, "EAP-LEAP: Response from AP", pos, LEAP_RESPONSE_LEN); os_memcpy(data->ap_response, pos, LEAP_RESPONSE_LEN); if (pwhash) { if (hash_nt_password_hash(password, pw_hash_hash)) { ret->ignore = TRUE; return NULL; } } else { if (nt_password_hash(password, password_len, pw_hash) || hash_nt_password_hash(pw_hash, pw_hash_hash)) { ret->ignore = TRUE; return NULL; } } challenge_response(data->ap_challenge, pw_hash_hash, expected); ret->methodState = METHOD_DONE; ret->allowNotifications = FALSE; if (os_memcmp(pos, expected, LEAP_RESPONSE_LEN) != 0) { wpa_printf(MSG_WARNING, "EAP-LEAP: AP sent an invalid " "response - authentication failed"); wpa_hexdump(MSG_DEBUG, "EAP-LEAP: Expected response from AP", expected, LEAP_RESPONSE_LEN); ret->decision = DECISION_FAIL; return NULL; } ret->decision = DECISION_UNCOND_SUCC; /* LEAP is somewhat odd method since it sends EAP-Success in the middle * of the authentication. Use special variable to transit EAP state * machine to SUCCESS state. */ sm->leap_done = TRUE; data->state = LEAP_DONE; /* No more authentication messages expected; AP will send EAPOL-Key * frames if encryption is enabled. */ return NULL; } static struct wpabuf * eap_leap_process(struct eap_sm *sm, void *priv, struct eap_method_ret *ret, const struct wpabuf *reqData) { const struct eap_hdr *eap; size_t password_len; const u8 *password; password = eap_get_config_password(sm, &password_len); if (password == NULL) { wpa_printf(MSG_INFO, "EAP-LEAP: Password not configured"); eap_sm_request_password(sm); ret->ignore = TRUE; return NULL; } /* * LEAP needs to be able to handle EAP-Success frame which does not * include Type field. Consequently, eap_hdr_validate() cannot be used * here. This validation will be done separately for EAP-Request and * EAP-Response frames. */ eap = wpabuf_head(reqData); if (wpabuf_len(reqData) < sizeof(*eap) || be_to_host16(eap->length) > wpabuf_len(reqData)) { wpa_printf(MSG_INFO, "EAP-LEAP: Invalid frame"); ret->ignore = TRUE; return NULL; } ret->ignore = FALSE; ret->allowNotifications = TRUE; ret->methodState = METHOD_MAY_CONT; ret->decision = DECISION_FAIL; sm->leap_done = FALSE; switch (eap->code) { case EAP_CODE_REQUEST: return eap_leap_process_request(sm, priv, ret, reqData); case EAP_CODE_SUCCESS: return eap_leap_process_success(sm, priv, ret, reqData); case EAP_CODE_RESPONSE: return eap_leap_process_response(sm, priv, ret, reqData); default: wpa_printf(MSG_INFO, "EAP-LEAP: Unexpected EAP code (%d) - " "ignored", eap->code); ret->ignore = TRUE; return NULL; } } static Boolean eap_leap_isKeyAvailable(struct eap_sm *sm, void *priv) { struct eap_leap_data *data = priv; return data->state == LEAP_DONE; } static u8 * eap_leap_getKey(struct eap_sm *sm, void *priv, size_t *len) { struct eap_leap_data *data = priv; u8 *key, pw_hash_hash[16], pw_hash[16]; const u8 *addr[5], *password; size_t elen[5], password_len; int pwhash; if (data->state != LEAP_DONE) return NULL; password = eap_get_config_password2(sm, &password_len, &pwhash); if (password == NULL) return NULL; key = os_malloc(LEAP_KEY_LEN); if (key == NULL) return NULL; if (pwhash) { if (hash_nt_password_hash(password, pw_hash_hash)) { os_free(key); return NULL; } } else { if (nt_password_hash(password, password_len, pw_hash) || hash_nt_password_hash(pw_hash, pw_hash_hash)) { os_free(key); return NULL; } } wpa_hexdump_key(MSG_DEBUG, "EAP-LEAP: pw_hash_hash", pw_hash_hash, 16); wpa_hexdump(MSG_DEBUG, "EAP-LEAP: peer_challenge", data->peer_challenge, LEAP_CHALLENGE_LEN); wpa_hexdump(MSG_DEBUG, "EAP-LEAP: peer_response", data->peer_response, LEAP_RESPONSE_LEN); wpa_hexdump(MSG_DEBUG, "EAP-LEAP: ap_challenge", data->ap_challenge, LEAP_CHALLENGE_LEN); wpa_hexdump(MSG_DEBUG, "EAP-LEAP: ap_response", data->ap_response, LEAP_RESPONSE_LEN); addr[0] = pw_hash_hash; elen[0] = 16; addr[1] = data->ap_challenge; elen[1] = LEAP_CHALLENGE_LEN; addr[2] = data->ap_response; elen[2] = LEAP_RESPONSE_LEN; addr[3] = data->peer_challenge; elen[3] = LEAP_CHALLENGE_LEN; addr[4] = data->peer_response; elen[4] = LEAP_RESPONSE_LEN; md5_vector(5, addr, elen, key); wpa_hexdump_key(MSG_DEBUG, "EAP-LEAP: master key", key, LEAP_KEY_LEN); *len = LEAP_KEY_LEN; return key; } int eap_peer_leap_register(void) { struct eap_method *eap; int ret; eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_LEAP, "LEAP"); if (eap == NULL) return -1; eap->init = eap_leap_init; eap->deinit = eap_leap_deinit; eap->process = eap_leap_process; eap->isKeyAvailable = eap_leap_isKeyAvailable; eap->getKey = eap_leap_getKey; ret = eap_peer_method_register(eap); if (ret) eap_peer_method_free(eap); return ret; } wpa-2.1/src/eap_peer/eap_md5.c000066400000000000000000000056041227414666700162140ustar00rootroot00000000000000/* * EAP peer method: EAP-MD5 (RFC 3748 and RFC 1994) * Copyright (c) 2004-2012, 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_i.h" #include "eap_common/chap.h" static void * eap_md5_init(struct eap_sm *sm) { /* No need for private data. However, must return non-NULL to indicate * success. */ return (void *) 1; } static void eap_md5_deinit(struct eap_sm *sm, void *priv) { } static struct wpabuf * eap_md5_process(struct eap_sm *sm, void *priv, struct eap_method_ret *ret, const struct wpabuf *reqData) { struct wpabuf *resp; const u8 *pos, *challenge, *password; u8 *rpos, id; size_t len, challenge_len, password_len; password = eap_get_config_password(sm, &password_len); if (password == NULL) { wpa_printf(MSG_INFO, "EAP-MD5: Password not configured"); eap_sm_request_password(sm); ret->ignore = TRUE; return NULL; } pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MD5, reqData, &len); if (pos == NULL || len == 0) { wpa_printf(MSG_INFO, "EAP-MD5: Invalid frame (pos=%p len=%lu)", pos, (unsigned long) len); ret->ignore = TRUE; return NULL; } /* * CHAP Challenge: * Value-Size (1 octet) | Value(Challenge) | Name(optional) */ challenge_len = *pos++; if (challenge_len == 0 || challenge_len > len - 1) { wpa_printf(MSG_INFO, "EAP-MD5: Invalid challenge " "(challenge_len=%lu len=%lu)", (unsigned long) challenge_len, (unsigned long) len); ret->ignore = TRUE; return NULL; } ret->ignore = FALSE; challenge = pos; wpa_hexdump(MSG_MSGDUMP, "EAP-MD5: Challenge", challenge, challenge_len); wpa_printf(MSG_DEBUG, "EAP-MD5: Generating Challenge Response"); ret->methodState = METHOD_DONE; ret->decision = DECISION_COND_SUCC; ret->allowNotifications = TRUE; resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MD5, 1 + CHAP_MD5_LEN, EAP_CODE_RESPONSE, eap_get_id(reqData)); if (resp == NULL) return NULL; /* * CHAP Response: * Value-Size (1 octet) | Value(Response) | Name(optional) */ wpabuf_put_u8(resp, CHAP_MD5_LEN); id = eap_get_id(resp); rpos = wpabuf_put(resp, CHAP_MD5_LEN); if (chap_md5(id, password, password_len, challenge, challenge_len, rpos)) { wpa_printf(MSG_INFO, "EAP-MD5: CHAP MD5 operation failed"); ret->ignore = TRUE; wpabuf_free(resp); return NULL; } wpa_hexdump(MSG_MSGDUMP, "EAP-MD5: Response", rpos, CHAP_MD5_LEN); return resp; } int eap_peer_md5_register(void) { struct eap_method *eap; int ret; eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_MD5, "MD5"); if (eap == NULL) return -1; eap->init = eap_md5_init; eap->deinit = eap_md5_deinit; eap->process = eap_md5_process; ret = eap_peer_method_register(eap); if (ret) eap_peer_method_free(eap); return ret; } wpa-2.1/src/eap_peer/eap_methods.c000066400000000000000000000206021227414666700171650ustar00rootroot00000000000000/* * EAP peer: Method registration * Copyright (c) 2004-2007, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "includes.h" #ifdef CONFIG_DYNAMIC_EAP_METHODS #include #endif /* CONFIG_DYNAMIC_EAP_METHODS */ #include "common.h" #include "eap_i.h" #include "eap_methods.h" static struct eap_method *eap_methods = NULL; /** * eap_peer_get_eap_method - Get EAP method based on type number * @vendor: EAP Vendor-Id (0 = IETF) * @method: EAP type number * Returns: Pointer to EAP method or %NULL if not found */ const struct eap_method * eap_peer_get_eap_method(int vendor, EapType method) { struct eap_method *m; for (m = eap_methods; m; m = m->next) { if (m->vendor == vendor && m->method == method) return m; } return NULL; } /** * eap_peer_get_type - Get EAP type for the given EAP method name * @name: EAP method name, e.g., TLS * @vendor: Buffer for returning EAP Vendor-Id * Returns: EAP method type or %EAP_TYPE_NONE if not found * * This function maps EAP type names into EAP type numbers based on the list of * EAP methods included in the build. */ EapType eap_peer_get_type(const char *name, int *vendor) { struct eap_method *m; for (m = eap_methods; m; m = m->next) { if (os_strcmp(m->name, name) == 0) { *vendor = m->vendor; return m->method; } } *vendor = EAP_VENDOR_IETF; return EAP_TYPE_NONE; } /** * eap_get_name - Get EAP method name for the given EAP type * @vendor: EAP Vendor-Id (0 = IETF) * @type: EAP method type * Returns: EAP method name, e.g., TLS, or %NULL if not found * * This function maps EAP type numbers into EAP type names based on the list of * EAP methods included in the build. */ const char * eap_get_name(int vendor, EapType type) { struct eap_method *m; if (vendor == EAP_VENDOR_IETF && type == EAP_TYPE_EXPANDED) return "expanded"; for (m = eap_methods; m; m = m->next) { if (m->vendor == vendor && m->method == type) return m->name; } return NULL; } /** * eap_get_names - Get space separated list of names for supported EAP methods * @buf: Buffer for names * @buflen: Buffer length * Returns: Number of characters written into buf (not including nul * termination) */ size_t eap_get_names(char *buf, size_t buflen) { char *pos, *end; struct eap_method *m; int ret; if (buflen == 0) return 0; pos = buf; end = pos + buflen; for (m = eap_methods; m; m = m->next) { ret = os_snprintf(pos, end - pos, "%s%s", m == eap_methods ? "" : " ", m->name); if (ret < 0 || ret >= end - pos) break; pos += ret; } buf[buflen - 1] = '\0'; return pos - buf; } /** * eap_get_names_as_string_array - Get supported EAP methods as string array * @num: Buffer for returning the number of items in array, not including %NULL * terminator. This parameter can be %NULL if the length is not needed. * Returns: A %NULL-terminated array of strings, or %NULL on error. * * This function returns the list of names for all supported EAP methods as an * array of strings. The caller must free the returned array items and the * array. */ char ** eap_get_names_as_string_array(size_t *num) { struct eap_method *m; size_t array_len = 0; char **array; int i = 0, j; for (m = eap_methods; m; m = m->next) array_len++; array = os_zalloc(sizeof(char *) * (array_len + 1)); if (array == NULL) return NULL; for (m = eap_methods; m; m = m->next) { array[i++] = os_strdup(m->name); if (array[i - 1] == NULL) { for (j = 0; j < i; j++) os_free(array[j]); os_free(array); return NULL; } } array[i] = NULL; if (num) *num = array_len; return array; } /** * eap_peer_get_methods - Get a list of enabled EAP peer methods * @count: Set to number of available methods * Returns: List of enabled EAP peer methods */ const struct eap_method * eap_peer_get_methods(size_t *count) { int c = 0; struct eap_method *m; for (m = eap_methods; m; m = m->next) c++; *count = c; return eap_methods; } #ifdef CONFIG_DYNAMIC_EAP_METHODS /** * eap_peer_method_load - Load a dynamic EAP method library (shared object) * @so: File path for the shared object file to load * Returns: 0 on success, -1 on failure */ int eap_peer_method_load(const char *so) { void *handle; int (*dyn_init)(void); int ret; handle = dlopen(so, RTLD_LAZY); if (handle == NULL) { wpa_printf(MSG_ERROR, "EAP: Failed to open dynamic EAP method " "'%s': %s", so, dlerror()); return -1; } dyn_init = dlsym(handle, "eap_peer_method_dynamic_init"); if (dyn_init == NULL) { dlclose(handle); wpa_printf(MSG_ERROR, "EAP: Invalid EAP method '%s' - no " "eap_peer_method_dynamic_init()", so); return -1; } ret = dyn_init(); if (ret) { dlclose(handle); wpa_printf(MSG_ERROR, "EAP: Failed to add EAP method '%s' - " "ret %d", so, ret); return ret; } /* Store the handle for this shared object. It will be freed with * dlclose() when the EAP method is unregistered. */ eap_methods->dl_handle = handle; wpa_printf(MSG_DEBUG, "EAP: Loaded dynamic EAP method: '%s'", so); return 0; } /** * eap_peer_method_unload - Unload a dynamic EAP method library (shared object) * @method: Pointer to the dynamically loaded EAP method * Returns: 0 on success, -1 on failure * * This function can be used to unload EAP methods that have been previously * loaded with eap_peer_method_load(). Before unloading the method, all * references to the method must be removed to make sure that no dereferences * of freed memory will occur after unloading. */ int eap_peer_method_unload(struct eap_method *method) { struct eap_method *m, *prev; void *handle; m = eap_methods; prev = NULL; while (m) { if (m == method) break; prev = m; m = m->next; } if (m == NULL || m->dl_handle == NULL) return -1; if (prev) prev->next = m->next; else eap_methods = m->next; handle = m->dl_handle; if (m->free) m->free(m); else eap_peer_method_free(m); dlclose(handle); return 0; } #endif /* CONFIG_DYNAMIC_EAP_METHODS */ /** * eap_peer_method_alloc - Allocate EAP peer method structure * @version: Version of the EAP peer method interface (set to * EAP_PEER_METHOD_INTERFACE_VERSION) * @vendor: EAP Vendor-ID (EAP_VENDOR_*) (0 = IETF) * @method: EAP type number (EAP_TYPE_*) * @name: Name of the method (e.g., "TLS") * Returns: Allocated EAP method structure or %NULL on failure * * The returned structure should be freed with eap_peer_method_free() when it * is not needed anymore. */ struct eap_method * eap_peer_method_alloc(int version, int vendor, EapType method, const char *name) { struct eap_method *eap; eap = os_zalloc(sizeof(*eap)); if (eap == NULL) return NULL; eap->version = version; eap->vendor = vendor; eap->method = method; eap->name = name; return eap; } /** * eap_peer_method_free - Free EAP peer method structure * @method: Method structure allocated with eap_peer_method_alloc() */ void eap_peer_method_free(struct eap_method *method) { os_free(method); } /** * eap_peer_method_register - Register an EAP peer method * @method: EAP method to register * Returns: 0 on success, -1 on invalid method, or -2 if a matching EAP method * has already been registered * * Each EAP peer method needs to call this function to register itself as a * supported EAP method. */ int eap_peer_method_register(struct eap_method *method) { struct eap_method *m, *last = NULL; if (method == NULL || method->name == NULL || method->version != EAP_PEER_METHOD_INTERFACE_VERSION) return -1; for (m = eap_methods; m; m = m->next) { if ((m->vendor == method->vendor && m->method == method->method) || os_strcmp(m->name, method->name) == 0) return -2; last = m; } if (last) last->next = method; else eap_methods = method; return 0; } /** * eap_peer_unregister_methods - Unregister EAP peer methods * * This function is called at program termination to unregister all EAP peer * methods. */ void eap_peer_unregister_methods(void) { struct eap_method *m; #ifdef CONFIG_DYNAMIC_EAP_METHODS void *handle; #endif /* CONFIG_DYNAMIC_EAP_METHODS */ while (eap_methods) { m = eap_methods; eap_methods = eap_methods->next; #ifdef CONFIG_DYNAMIC_EAP_METHODS handle = m->dl_handle; #endif /* CONFIG_DYNAMIC_EAP_METHODS */ if (m->free) m->free(m); else eap_peer_method_free(m); #ifdef CONFIG_DYNAMIC_EAP_METHODS if (handle) dlclose(handle); #endif /* CONFIG_DYNAMIC_EAP_METHODS */ } } wpa-2.1/src/eap_peer/eap_methods.h000066400000000000000000000052651227414666700172020ustar00rootroot00000000000000/* * EAP peer: Method registration * Copyright (c) 2004-2007, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef EAP_METHODS_H #define EAP_METHODS_H #include "eap_common/eap_defs.h" const struct eap_method * eap_peer_get_eap_method(int vendor, EapType method); const struct eap_method * eap_peer_get_methods(size_t *count); struct eap_method * eap_peer_method_alloc(int version, int vendor, EapType method, const char *name); void eap_peer_method_free(struct eap_method *method); int eap_peer_method_register(struct eap_method *method); #ifdef IEEE8021X_EAPOL EapType eap_peer_get_type(const char *name, int *vendor); const char * eap_get_name(int vendor, EapType type); size_t eap_get_names(char *buf, size_t buflen); char ** eap_get_names_as_string_array(size_t *num); void eap_peer_unregister_methods(void); #else /* IEEE8021X_EAPOL */ static inline EapType eap_peer_get_type(const char *name, int *vendor) { *vendor = EAP_VENDOR_IETF; return EAP_TYPE_NONE; } static inline const char * eap_get_name(int vendor, EapType type) { return NULL; } static inline size_t eap_get_names(char *buf, size_t buflen) { return 0; } static inline int eap_peer_register_methods(void) { return 0; } static inline void eap_peer_unregister_methods(void) { } static inline char ** eap_get_names_as_string_array(size_t *num) { return NULL; } #endif /* IEEE8021X_EAPOL */ #ifdef CONFIG_DYNAMIC_EAP_METHODS int eap_peer_method_load(const char *so); int eap_peer_method_unload(struct eap_method *method); #else /* CONFIG_DYNAMIC_EAP_METHODS */ static inline int eap_peer_method_load(const char *so) { return 0; } static inline int eap_peer_method_unload(struct eap_method *method) { return 0; } #endif /* CONFIG_DYNAMIC_EAP_METHODS */ /* EAP peer method registration calls for statically linked in methods */ int eap_peer_md5_register(void); int eap_peer_tls_register(void); int eap_peer_unauth_tls_register(void); int eap_peer_mschapv2_register(void); int eap_peer_peap_register(void); int eap_peer_ttls_register(void); int eap_peer_gtc_register(void); int eap_peer_otp_register(void); int eap_peer_sim_register(void); int eap_peer_leap_register(void); int eap_peer_psk_register(void); int eap_peer_aka_register(void); int eap_peer_aka_prime_register(void); int eap_peer_fast_register(void); int eap_peer_pax_register(void); int eap_peer_sake_register(void); int eap_peer_gpsk_register(void); int eap_peer_wsc_register(void); int eap_peer_ikev2_register(void); int eap_peer_vendor_test_register(void); int eap_peer_tnc_register(void); int eap_peer_pwd_register(void); int eap_peer_eke_register(void); #endif /* EAP_METHODS_H */ wpa-2.1/src/eap_peer/eap_mschapv2.c000066400000000000000000000613341227414666700172540ustar00rootroot00000000000000/* * EAP peer method: EAP-MSCHAPV2 (draft-kamath-pppext-eap-mschapv2-00.txt) * Copyright (c) 2004-2008, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. * * This file implements EAP peer part of EAP-MSCHAPV2 method (EAP type 26). * draft-kamath-pppext-eap-mschapv2-00.txt defines the Microsoft EAP CHAP * Extensions Protocol, Version 2, for mutual authentication and key * derivation. This encapsulates MS-CHAP-v2 protocol which is defined in * RFC 2759. Use of EAP-MSCHAPV2 derived keys with MPPE cipher is described in * RFC 3079. */ #include "includes.h" #include "common.h" #include "crypto/ms_funcs.h" #include "crypto/random.h" #include "common/wpa_ctrl.h" #include "mschapv2.h" #include "eap_i.h" #include "eap_config.h" #ifdef _MSC_VER #pragma pack(push, 1) #endif /* _MSC_VER */ struct eap_mschapv2_hdr { u8 op_code; /* MSCHAPV2_OP_* */ u8 mschapv2_id; /* usually same as EAP identifier; must be changed * for challenges, but not for success/failure */ u8 ms_length[2]; /* Note: misaligned; length - 5 */ /* followed by data */ } STRUCT_PACKED; /* Response Data field */ struct ms_response { u8 peer_challenge[MSCHAPV2_CHAL_LEN]; u8 reserved[8]; u8 nt_response[MSCHAPV2_NT_RESPONSE_LEN]; u8 flags; } STRUCT_PACKED; /* Change-Password Data field */ struct ms_change_password { u8 encr_password[516]; u8 encr_hash[16]; u8 peer_challenge[MSCHAPV2_CHAL_LEN]; u8 reserved[8]; u8 nt_response[MSCHAPV2_NT_RESPONSE_LEN]; u8 flags[2]; } STRUCT_PACKED; #ifdef _MSC_VER #pragma pack(pop) #endif /* _MSC_VER */ #define MSCHAPV2_OP_CHALLENGE 1 #define MSCHAPV2_OP_RESPONSE 2 #define MSCHAPV2_OP_SUCCESS 3 #define MSCHAPV2_OP_FAILURE 4 #define MSCHAPV2_OP_CHANGE_PASSWORD 7 #define ERROR_RESTRICTED_LOGON_HOURS 646 #define ERROR_ACCT_DISABLED 647 #define ERROR_PASSWD_EXPIRED 648 #define ERROR_NO_DIALIN_PERMISSION 649 #define ERROR_AUTHENTICATION_FAILURE 691 #define ERROR_CHANGING_PASSWORD 709 #define PASSWD_CHANGE_CHAL_LEN 16 #define MSCHAPV2_KEY_LEN 16 struct eap_mschapv2_data { u8 auth_response[MSCHAPV2_AUTH_RESPONSE_LEN]; int auth_response_valid; int prev_error; u8 passwd_change_challenge[PASSWD_CHANGE_CHAL_LEN]; int passwd_change_challenge_valid; int passwd_change_version; /* Optional challenge values generated in EAP-FAST Phase 1 negotiation */ u8 *peer_challenge; u8 *auth_challenge; int phase2; u8 master_key[MSCHAPV2_MASTER_KEY_LEN]; int master_key_valid; int success; struct wpabuf *prev_challenge; }; static void eap_mschapv2_deinit(struct eap_sm *sm, void *priv); static void * eap_mschapv2_init(struct eap_sm *sm) { struct eap_mschapv2_data *data; data = os_zalloc(sizeof(*data)); if (data == NULL) return NULL; if (sm->peer_challenge) { data->peer_challenge = os_malloc(MSCHAPV2_CHAL_LEN); if (data->peer_challenge == NULL) { eap_mschapv2_deinit(sm, data); return NULL; } os_memcpy(data->peer_challenge, sm->peer_challenge, MSCHAPV2_CHAL_LEN); } if (sm->auth_challenge) { data->auth_challenge = os_malloc(MSCHAPV2_CHAL_LEN); if (data->auth_challenge == NULL) { eap_mschapv2_deinit(sm, data); return NULL; } os_memcpy(data->auth_challenge, sm->auth_challenge, MSCHAPV2_CHAL_LEN); } data->phase2 = sm->init_phase2; return data; } static void eap_mschapv2_deinit(struct eap_sm *sm, void *priv) { struct eap_mschapv2_data *data = priv; os_free(data->peer_challenge); os_free(data->auth_challenge); wpabuf_free(data->prev_challenge); os_free(data); } static struct wpabuf * eap_mschapv2_challenge_reply( struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id, u8 mschapv2_id, const u8 *auth_challenge) { struct wpabuf *resp; struct eap_mschapv2_hdr *ms; u8 *peer_challenge; int ms_len; struct ms_response *r; size_t identity_len, password_len; const u8 *identity, *password; int pwhash; wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Generating Challenge Response"); identity = eap_get_config_identity(sm, &identity_len); password = eap_get_config_password2(sm, &password_len, &pwhash); if (identity == NULL || password == NULL) return NULL; ms_len = sizeof(*ms) + 1 + sizeof(*r) + identity_len; resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len, EAP_CODE_RESPONSE, id); if (resp == NULL) return NULL; ms = wpabuf_put(resp, sizeof(*ms)); ms->op_code = MSCHAPV2_OP_RESPONSE; ms->mschapv2_id = mschapv2_id; if (data->prev_error) { /* * TODO: this does not seem to be enough when processing two * or more failure messages. IAS did not increment mschapv2_id * in its own packets, but it seemed to expect the peer to * increment this for all packets(?). */ ms->mschapv2_id++; } WPA_PUT_BE16(ms->ms_length, ms_len); wpabuf_put_u8(resp, sizeof(*r)); /* Value-Size */ /* Response */ r = wpabuf_put(resp, sizeof(*r)); peer_challenge = r->peer_challenge; if (data->peer_challenge) { wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: peer_challenge generated " "in Phase 1"); peer_challenge = data->peer_challenge; os_memset(r->peer_challenge, 0, MSCHAPV2_CHAL_LEN); } else if (random_get_bytes(peer_challenge, MSCHAPV2_CHAL_LEN)) { wpabuf_free(resp); return NULL; } os_memset(r->reserved, 0, 8); if (data->auth_challenge) { wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: auth_challenge generated " "in Phase 1"); auth_challenge = data->auth_challenge; } if (mschapv2_derive_response(identity, identity_len, password, password_len, pwhash, auth_challenge, peer_challenge, r->nt_response, data->auth_response, data->master_key)) { wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to derive " "response"); wpabuf_free(resp); return NULL; } data->auth_response_valid = 1; data->master_key_valid = 1; r->flags = 0; /* reserved, must be zero */ wpabuf_put_data(resp, identity, identity_len); wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: TX identifier %d mschapv2_id %d " "(response)", id, ms->mschapv2_id); return resp; } /** * eap_mschapv2_process - Process an EAP-MSCHAPv2 challenge message * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * @data: Pointer to private EAP method data from eap_mschapv2_init() * @ret: Return values from EAP request validation and processing * @req: Pointer to EAP-MSCHAPv2 header from the request * @req_len: Length of the EAP-MSCHAPv2 data * @id: EAP identifier used in the request * Returns: Pointer to allocated EAP response packet (eapRespData) or %NULL if * no reply available */ static struct wpabuf * eap_mschapv2_challenge( struct eap_sm *sm, struct eap_mschapv2_data *data, struct eap_method_ret *ret, const struct eap_mschapv2_hdr *req, size_t req_len, u8 id) { size_t len, challenge_len; const u8 *pos, *challenge; if (eap_get_config_identity(sm, &len) == NULL || eap_get_config_password(sm, &len) == NULL) return NULL; wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received challenge"); if (req_len < sizeof(*req) + 1) { wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Too short challenge data " "(len %lu)", (unsigned long) req_len); ret->ignore = TRUE; return NULL; } pos = (const u8 *) (req + 1); challenge_len = *pos++; len = req_len - sizeof(*req) - 1; if (challenge_len != MSCHAPV2_CHAL_LEN) { wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid challenge length " "%lu", (unsigned long) challenge_len); ret->ignore = TRUE; return NULL; } if (len < challenge_len) { wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Too short challenge" " packet: len=%lu challenge_len=%lu", (unsigned long) len, (unsigned long) challenge_len); ret->ignore = TRUE; return NULL; } if (data->passwd_change_challenge_valid) { wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Using challenge from the " "failure message"); challenge = data->passwd_change_challenge; } else challenge = pos; pos += challenge_len; len -= challenge_len; wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Authentication Servername", pos, len); ret->ignore = FALSE; ret->methodState = METHOD_MAY_CONT; ret->decision = DECISION_FAIL; ret->allowNotifications = TRUE; return eap_mschapv2_challenge_reply(sm, data, id, req->mschapv2_id, challenge); } static void eap_mschapv2_password_changed(struct eap_sm *sm, struct eap_mschapv2_data *data) { struct eap_peer_config *config = eap_get_config(sm); if (config && config->new_password) { wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_PASSWORD_CHANGED "EAP-MSCHAPV2: Password changed successfully"); data->prev_error = 0; os_free(config->password); if (config->flags & EAP_CONFIG_FLAGS_EXT_PASSWORD) { /* TODO: update external storage */ } else if (config->flags & EAP_CONFIG_FLAGS_PASSWORD_NTHASH) { config->password = os_malloc(16); config->password_len = 16; if (config->password) { nt_password_hash(config->new_password, config->new_password_len, config->password); } os_free(config->new_password); } else { config->password = config->new_password; config->password_len = config->new_password_len; } config->new_password = NULL; config->new_password_len = 0; } } /** * eap_mschapv2_process - Process an EAP-MSCHAPv2 success message * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * @data: Pointer to private EAP method data from eap_mschapv2_init() * @ret: Return values from EAP request validation and processing * @req: Pointer to EAP-MSCHAPv2 header from the request * @req_len: Length of the EAP-MSCHAPv2 data * @id: EAP identifier used in th erequest * Returns: Pointer to allocated EAP response packet (eapRespData) or %NULL if * no reply available */ static struct wpabuf * eap_mschapv2_success(struct eap_sm *sm, struct eap_mschapv2_data *data, struct eap_method_ret *ret, const struct eap_mschapv2_hdr *req, size_t req_len, u8 id) { struct wpabuf *resp; const u8 *pos; size_t len; wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received success"); len = req_len - sizeof(*req); pos = (const u8 *) (req + 1); if (!data->auth_response_valid || mschapv2_verify_auth_response(data->auth_response, pos, len)) { wpa_printf(MSG_WARNING, "EAP-MSCHAPV2: Invalid authenticator " "response in success request"); ret->methodState = METHOD_DONE; ret->decision = DECISION_FAIL; return NULL; } pos += 2 + 2 * MSCHAPV2_AUTH_RESPONSE_LEN; len -= 2 + 2 * MSCHAPV2_AUTH_RESPONSE_LEN; while (len > 0 && *pos == ' ') { pos++; len--; } wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Success message", pos, len); wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Authentication succeeded"); /* Note: Only op_code of the EAP-MSCHAPV2 header is included in success * message. */ resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, 1, EAP_CODE_RESPONSE, id); if (resp == NULL) { wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Failed to allocate " "buffer for success response"); ret->ignore = TRUE; return NULL; } wpabuf_put_u8(resp, MSCHAPV2_OP_SUCCESS); /* op_code */ ret->methodState = METHOD_DONE; ret->decision = DECISION_UNCOND_SUCC; ret->allowNotifications = FALSE; data->success = 1; if (data->prev_error == ERROR_PASSWD_EXPIRED) eap_mschapv2_password_changed(sm, data); return resp; } static int eap_mschapv2_failure_txt(struct eap_sm *sm, struct eap_mschapv2_data *data, char *txt) { char *pos, *msg = ""; int retry = 1; struct eap_peer_config *config = eap_get_config(sm); /* For example: * E=691 R=1 C=<32 octets hex challenge> V=3 M=Authentication Failure */ pos = txt; if (pos && os_strncmp(pos, "E=", 2) == 0) { pos += 2; data->prev_error = atoi(pos); wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: error %d", data->prev_error); pos = os_strchr(pos, ' '); if (pos) pos++; } if (pos && os_strncmp(pos, "R=", 2) == 0) { pos += 2; retry = atoi(pos); wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: retry is %sallowed", retry == 1 ? "" : "not "); pos = os_strchr(pos, ' '); if (pos) pos++; } if (pos && os_strncmp(pos, "C=", 2) == 0) { int hex_len; pos += 2; hex_len = os_strchr(pos, ' ') - (char *) pos; if (hex_len == PASSWD_CHANGE_CHAL_LEN * 2) { if (hexstr2bin(pos, data->passwd_change_challenge, PASSWD_CHANGE_CHAL_LEN)) { wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: invalid " "failure challenge"); } else { wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: failure " "challenge", data->passwd_change_challenge, PASSWD_CHANGE_CHAL_LEN); data->passwd_change_challenge_valid = 1; } } else { wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: invalid failure " "challenge len %d", hex_len); } pos = os_strchr(pos, ' '); if (pos) pos++; } else { wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: required challenge field " "was not present in failure message"); } if (pos && os_strncmp(pos, "V=", 2) == 0) { pos += 2; data->passwd_change_version = atoi(pos); wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: password changing " "protocol version %d", data->passwd_change_version); pos = os_strchr(pos, ' '); if (pos) pos++; } if (pos && os_strncmp(pos, "M=", 2) == 0) { pos += 2; msg = pos; } wpa_msg(sm->msg_ctx, MSG_WARNING, "EAP-MSCHAPV2: failure message: '%s' (retry %sallowed, error " "%d)", msg, retry == 1 ? "" : "not ", data->prev_error); if (data->prev_error == ERROR_PASSWD_EXPIRED && data->passwd_change_version == 3 && config) { if (config->new_password == NULL) { wpa_msg(sm->msg_ctx, MSG_INFO, "EAP-MSCHAPV2: Password expired - password " "change required"); eap_sm_request_new_password(sm); } } else if (retry == 1 && config) { /* TODO: could prevent the current password from being used * again at least for some period of time */ if (!config->mschapv2_retry) eap_sm_request_identity(sm); eap_sm_request_password(sm); config->mschapv2_retry = 1; } else if (config) { /* TODO: prevent retries using same username/password */ config->mschapv2_retry = 0; } return retry == 1; } static struct wpabuf * eap_mschapv2_change_password( struct eap_sm *sm, struct eap_mschapv2_data *data, struct eap_method_ret *ret, const struct eap_mschapv2_hdr *req, u8 id) { struct wpabuf *resp; int ms_len; const u8 *username, *password, *new_password; size_t username_len, password_len, new_password_len; struct eap_mschapv2_hdr *ms; struct ms_change_password *cp; u8 password_hash[16], password_hash_hash[16]; int pwhash; username = eap_get_config_identity(sm, &username_len); password = eap_get_config_password2(sm, &password_len, &pwhash); new_password = eap_get_config_new_password(sm, &new_password_len); if (username == NULL || password == NULL || new_password == NULL) return NULL; username = mschapv2_remove_domain(username, &username_len); ret->ignore = FALSE; ret->methodState = METHOD_MAY_CONT; ret->decision = DECISION_COND_SUCC; ret->allowNotifications = TRUE; ms_len = sizeof(*ms) + sizeof(*cp); resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len, EAP_CODE_RESPONSE, id); if (resp == NULL) return NULL; ms = wpabuf_put(resp, sizeof(*ms)); ms->op_code = MSCHAPV2_OP_CHANGE_PASSWORD; ms->mschapv2_id = req->mschapv2_id + 1; WPA_PUT_BE16(ms->ms_length, ms_len); cp = wpabuf_put(resp, sizeof(*cp)); /* Encrypted-Password */ if (pwhash) { if (encrypt_pw_block_with_password_hash( new_password, new_password_len, password, cp->encr_password)) goto fail; } else { if (new_password_encrypted_with_old_nt_password_hash( new_password, new_password_len, password, password_len, cp->encr_password)) goto fail; } /* Encrypted-Hash */ if (pwhash) { u8 new_password_hash[16]; nt_password_hash(new_password, new_password_len, new_password_hash); nt_password_hash_encrypted_with_block(password, new_password_hash, cp->encr_hash); } else { old_nt_password_hash_encrypted_with_new_nt_password_hash( new_password, new_password_len, password, password_len, cp->encr_hash); } /* Peer-Challenge */ if (random_get_bytes(cp->peer_challenge, MSCHAPV2_CHAL_LEN)) goto fail; /* Reserved, must be zero */ os_memset(cp->reserved, 0, 8); /* NT-Response */ wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: auth_challenge", data->passwd_change_challenge, PASSWD_CHANGE_CHAL_LEN); wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: peer_challenge", cp->peer_challenge, MSCHAPV2_CHAL_LEN); wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: username", username, username_len); wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-MSCHAPV2: new password", new_password, new_password_len); generate_nt_response(data->passwd_change_challenge, cp->peer_challenge, username, username_len, new_password, new_password_len, cp->nt_response); wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: NT-Response", cp->nt_response, MSCHAPV2_NT_RESPONSE_LEN); /* Authenticator response is not really needed yet, but calculate it * here so that challenges need not be saved. */ generate_authenticator_response(new_password, new_password_len, cp->peer_challenge, data->passwd_change_challenge, username, username_len, cp->nt_response, data->auth_response); data->auth_response_valid = 1; /* Likewise, generate master_key here since we have the needed data * available. */ nt_password_hash(new_password, new_password_len, password_hash); hash_nt_password_hash(password_hash, password_hash_hash); get_master_key(password_hash_hash, cp->nt_response, data->master_key); data->master_key_valid = 1; /* Flags */ os_memset(cp->flags, 0, 2); wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: TX identifier %d mschapv2_id %d " "(change pw)", id, ms->mschapv2_id); return resp; fail: wpabuf_free(resp); return NULL; } /** * eap_mschapv2_process - Process an EAP-MSCHAPv2 failure message * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * @data: Pointer to private EAP method data from eap_mschapv2_init() * @ret: Return values from EAP request validation and processing * @req: Pointer to EAP-MSCHAPv2 header from the request * @req_len: Length of the EAP-MSCHAPv2 data * @id: EAP identifier used in th erequest * Returns: Pointer to allocated EAP response packet (eapRespData) or %NULL if * no reply available */ static struct wpabuf * eap_mschapv2_failure(struct eap_sm *sm, struct eap_mschapv2_data *data, struct eap_method_ret *ret, const struct eap_mschapv2_hdr *req, size_t req_len, u8 id) { struct wpabuf *resp; const u8 *msdata = (const u8 *) (req + 1); char *buf; size_t len = req_len - sizeof(*req); int retry = 0; wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received failure"); wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Failure data", msdata, len); /* * eap_mschapv2_failure_txt() expects a nul terminated string, so we * must allocate a large enough temporary buffer to create that since * the received message does not include nul termination. */ buf = dup_binstr(msdata, len); if (buf) { retry = eap_mschapv2_failure_txt(sm, data, buf); os_free(buf); } ret->ignore = FALSE; ret->methodState = METHOD_DONE; ret->decision = DECISION_FAIL; ret->allowNotifications = FALSE; if (data->prev_error == ERROR_PASSWD_EXPIRED && data->passwd_change_version == 3) { struct eap_peer_config *config = eap_get_config(sm); if (config && config->new_password) return eap_mschapv2_change_password(sm, data, ret, req, id); if (config && config->pending_req_new_password) return NULL; } else if (retry && data->prev_error == ERROR_AUTHENTICATION_FAILURE) { /* TODO: could try to retry authentication, e.g, after having * changed the username/password. In this case, EAP MS-CHAP-v2 * Failure Response would not be sent here. */ return NULL; } /* Note: Only op_code of the EAP-MSCHAPV2 header is included in failure * message. */ resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, 1, EAP_CODE_RESPONSE, id); if (resp == NULL) return NULL; wpabuf_put_u8(resp, MSCHAPV2_OP_FAILURE); /* op_code */ return resp; } static int eap_mschapv2_check_config(struct eap_sm *sm) { size_t len; if (eap_get_config_identity(sm, &len) == NULL) { wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Identity not configured"); eap_sm_request_identity(sm); return -1; } if (eap_get_config_password(sm, &len) == NULL) { wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Password not configured"); eap_sm_request_password(sm); return -1; } return 0; } static int eap_mschapv2_check_mslen(struct eap_sm *sm, size_t len, const struct eap_mschapv2_hdr *ms) { size_t ms_len = WPA_GET_BE16(ms->ms_length); if (ms_len == len) return 0; wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid header: len=%lu " "ms_len=%lu", (unsigned long) len, (unsigned long) ms_len); if (sm->workaround) { /* Some authentication servers use invalid ms_len, * ignore it for interoperability. */ wpa_printf(MSG_INFO, "EAP-MSCHAPV2: workaround, ignore" " invalid ms_len %lu (len %lu)", (unsigned long) ms_len, (unsigned long) len); return 0; } return -1; } static void eap_mschapv2_copy_challenge(struct eap_mschapv2_data *data, const struct wpabuf *reqData) { /* * Store a copy of the challenge message, so that it can be processed * again in case retry is allowed after a possible failure. */ wpabuf_free(data->prev_challenge); data->prev_challenge = wpabuf_dup(reqData); } /** * eap_mschapv2_process - Process an EAP-MSCHAPv2 request * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * @priv: Pointer to private EAP method data from eap_mschapv2_init() * @ret: Return values from EAP request validation and processing * @reqData: EAP request to be processed (eapReqData) * Returns: Pointer to allocated EAP response packet (eapRespData) or %NULL if * no reply available */ static struct wpabuf * eap_mschapv2_process(struct eap_sm *sm, void *priv, struct eap_method_ret *ret, const struct wpabuf *reqData) { struct eap_mschapv2_data *data = priv; struct eap_peer_config *config = eap_get_config(sm); const struct eap_mschapv2_hdr *ms; int using_prev_challenge = 0; const u8 *pos; size_t len; u8 id; if (eap_mschapv2_check_config(sm)) { ret->ignore = TRUE; return NULL; } if (config->mschapv2_retry && data->prev_challenge && data->prev_error == ERROR_AUTHENTICATION_FAILURE) { wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Replacing pending packet " "with the previous challenge"); reqData = data->prev_challenge; using_prev_challenge = 1; config->mschapv2_retry = 0; } pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, reqData, &len); if (pos == NULL || len < sizeof(*ms) + 1) { ret->ignore = TRUE; return NULL; } ms = (const struct eap_mschapv2_hdr *) pos; if (eap_mschapv2_check_mslen(sm, len, ms)) { ret->ignore = TRUE; return NULL; } id = eap_get_id(reqData); wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: RX identifier %d mschapv2_id %d", id, ms->mschapv2_id); switch (ms->op_code) { case MSCHAPV2_OP_CHALLENGE: if (!using_prev_challenge) eap_mschapv2_copy_challenge(data, reqData); return eap_mschapv2_challenge(sm, data, ret, ms, len, id); case MSCHAPV2_OP_SUCCESS: return eap_mschapv2_success(sm, data, ret, ms, len, id); case MSCHAPV2_OP_FAILURE: return eap_mschapv2_failure(sm, data, ret, ms, len, id); default: wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Unknown op %d - ignored", ms->op_code); ret->ignore = TRUE; return NULL; } } static Boolean eap_mschapv2_isKeyAvailable(struct eap_sm *sm, void *priv) { struct eap_mschapv2_data *data = priv; return data->success && data->master_key_valid; } static u8 * eap_mschapv2_getKey(struct eap_sm *sm, void *priv, size_t *len) { struct eap_mschapv2_data *data = priv; u8 *key; int key_len; if (!data->master_key_valid || !data->success) return NULL; key_len = 2 * MSCHAPV2_KEY_LEN; key = os_malloc(key_len); if (key == NULL) return NULL; /* MSK = server MS-MPPE-Recv-Key | MS-MPPE-Send-Key, i.e., * peer MS-MPPE-Send-Key | MS-MPPE-Recv-Key */ get_asymetric_start_key(data->master_key, key, MSCHAPV2_KEY_LEN, 1, 0); get_asymetric_start_key(data->master_key, key + MSCHAPV2_KEY_LEN, MSCHAPV2_KEY_LEN, 0, 0); wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived key", key, key_len); *len = key_len; return key; } /** * eap_peer_mschapv2_register - Register EAP-MSCHAPv2 peer method * Returns: 0 on success, -1 on failure * * This function is used to register EAP-MSCHAPv2 peer method into the EAP * method list. */ int eap_peer_mschapv2_register(void) { struct eap_method *eap; int ret; eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, "MSCHAPV2"); if (eap == NULL) return -1; eap->init = eap_mschapv2_init; eap->deinit = eap_mschapv2_deinit; eap->process = eap_mschapv2_process; eap->isKeyAvailable = eap_mschapv2_isKeyAvailable; eap->getKey = eap_mschapv2_getKey; ret = eap_peer_method_register(eap); if (ret) eap_peer_method_free(eap); return ret; } wpa-2.1/src/eap_peer/eap_otp.c000066400000000000000000000042461227414666700163320ustar00rootroot00000000000000/* * EAP peer method: EAP-OTP (RFC 3748) * Copyright (c) 2004-2006, 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_i.h" static void * eap_otp_init(struct eap_sm *sm) { /* No need for private data. However, must return non-NULL to indicate * success. */ return (void *) 1; } static void eap_otp_deinit(struct eap_sm *sm, void *priv) { } static struct wpabuf * eap_otp_process(struct eap_sm *sm, void *priv, struct eap_method_ret *ret, const struct wpabuf *reqData) { struct wpabuf *resp; const u8 *pos, *password; size_t password_len, len; int otp; pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_OTP, reqData, &len); if (pos == NULL) { ret->ignore = TRUE; return NULL; } wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-OTP: Request message", pos, len); password = eap_get_config_otp(sm, &password_len); if (password) otp = 1; else { password = eap_get_config_password(sm, &password_len); otp = 0; } if (password == NULL) { wpa_printf(MSG_INFO, "EAP-OTP: Password not configured"); eap_sm_request_otp(sm, (const char *) pos, len); ret->ignore = TRUE; return NULL; } ret->ignore = FALSE; ret->methodState = METHOD_DONE; ret->decision = DECISION_COND_SUCC; ret->allowNotifications = FALSE; resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_OTP, password_len, EAP_CODE_RESPONSE, eap_get_id(reqData)); if (resp == NULL) return NULL; wpabuf_put_data(resp, password, password_len); wpa_hexdump_ascii_key(MSG_MSGDUMP, "EAP-OTP: Response", password, password_len); if (otp) { wpa_printf(MSG_DEBUG, "EAP-OTP: Forgetting used password"); eap_clear_config_otp(sm); } return resp; } int eap_peer_otp_register(void) { struct eap_method *eap; int ret; eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_OTP, "OTP"); if (eap == NULL) return -1; eap->init = eap_otp_init; eap->deinit = eap_otp_deinit; eap->process = eap_otp_process; ret = eap_peer_method_register(eap); if (ret) eap_peer_method_free(eap); return ret; } wpa-2.1/src/eap_peer/eap_pax.c000066400000000000000000000324671227414666700163260ustar00rootroot00000000000000/* * EAP peer method: EAP-PAX (RFC 4746) * Copyright (c) 2005-2008, 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/random.h" #include "eap_common/eap_pax_common.h" #include "eap_i.h" /* * Note: only PAX_STD subprotocol is currently supported * * TODO: Add support with PAX_SEC with the mandatory to implement ciphersuite * (HMAC_SHA1_128, IANA DH Group 14 (2048 bits), RSA-PKCS1-V1_5) and * recommended ciphersuite (HMAC_SHA256_128, IANA DH Group 15 (3072 bits), * RSAES-OAEP). */ struct eap_pax_data { enum { PAX_INIT, PAX_STD_2_SENT, PAX_DONE } state; u8 mac_id, dh_group_id, public_key_id; union { u8 e[2 * EAP_PAX_RAND_LEN]; struct { u8 x[EAP_PAX_RAND_LEN]; /* server rand */ u8 y[EAP_PAX_RAND_LEN]; /* client rand */ } r; } rand; char *cid; size_t cid_len; u8 ak[EAP_PAX_AK_LEN]; u8 mk[EAP_PAX_MK_LEN]; u8 ck[EAP_PAX_CK_LEN]; u8 ick[EAP_PAX_ICK_LEN]; }; static void eap_pax_deinit(struct eap_sm *sm, void *priv); static void * eap_pax_init(struct eap_sm *sm) { struct eap_pax_data *data; const u8 *identity, *password; size_t identity_len, password_len; identity = eap_get_config_identity(sm, &identity_len); password = eap_get_config_password(sm, &password_len); if (!identity || !password) { wpa_printf(MSG_INFO, "EAP-PAX: CID (nai) or key (password) " "not configured"); return NULL; } if (password_len != EAP_PAX_AK_LEN) { wpa_printf(MSG_INFO, "EAP-PAX: Invalid PSK length"); return NULL; } data = os_zalloc(sizeof(*data)); if (data == NULL) return NULL; data->state = PAX_INIT; data->cid = os_malloc(identity_len); if (data->cid == NULL) { eap_pax_deinit(sm, data); return NULL; } os_memcpy(data->cid, identity, identity_len); data->cid_len = identity_len; os_memcpy(data->ak, password, EAP_PAX_AK_LEN); return data; } static void eap_pax_deinit(struct eap_sm *sm, void *priv) { struct eap_pax_data *data = priv; os_free(data->cid); os_free(data); } static struct wpabuf * eap_pax_alloc_resp(const struct eap_pax_hdr *req, u8 id, u8 op_code, size_t plen) { struct wpabuf *resp; struct eap_pax_hdr *pax; resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PAX, sizeof(*pax) + plen, EAP_CODE_RESPONSE, id); if (resp == NULL) return NULL; pax = wpabuf_put(resp, sizeof(*pax)); pax->op_code = op_code; pax->flags = 0; pax->mac_id = req->mac_id; pax->dh_group_id = req->dh_group_id; pax->public_key_id = req->public_key_id; return resp; } static struct wpabuf * eap_pax_process_std_1(struct eap_pax_data *data, struct eap_method_ret *ret, u8 id, const struct eap_pax_hdr *req, size_t req_plen) { struct wpabuf *resp; const u8 *pos; u8 *rpos; size_t left, plen; wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-1 (received)"); if (data->state != PAX_INIT) { wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-1 received in " "unexpected state (%d) - ignored", data->state); ret->ignore = TRUE; return NULL; } if (req->flags & EAP_PAX_FLAGS_CE) { wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-1 with CE flag set - " "ignored"); ret->ignore = TRUE; return NULL; } left = req_plen - sizeof(*req); if (left < 2 + EAP_PAX_RAND_LEN) { wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-1 with too short " "payload"); ret->ignore = TRUE; return NULL; } pos = (const u8 *) (req + 1); if (WPA_GET_BE16(pos) != EAP_PAX_RAND_LEN) { wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-1 with incorrect A " "length %d (expected %d)", WPA_GET_BE16(pos), EAP_PAX_RAND_LEN); ret->ignore = TRUE; return NULL; } pos += 2; left -= 2; os_memcpy(data->rand.r.x, pos, EAP_PAX_RAND_LEN); wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: X (server rand)", data->rand.r.x, EAP_PAX_RAND_LEN); pos += EAP_PAX_RAND_LEN; left -= EAP_PAX_RAND_LEN; if (left > 0) { wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ignored extra payload", pos, left); } if (random_get_bytes(data->rand.r.y, EAP_PAX_RAND_LEN)) { wpa_printf(MSG_ERROR, "EAP-PAX: Failed to get random data"); ret->ignore = TRUE; return NULL; } wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Y (client rand)", data->rand.r.y, EAP_PAX_RAND_LEN); if (eap_pax_initial_key_derivation(req->mac_id, data->ak, data->rand.e, data->mk, data->ck, data->ick) < 0) { ret->ignore = TRUE; return NULL; } wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-2 (sending)"); plen = 2 + EAP_PAX_RAND_LEN + 2 + data->cid_len + 2 + EAP_PAX_MAC_LEN + EAP_PAX_ICV_LEN; resp = eap_pax_alloc_resp(req, id, EAP_PAX_OP_STD_2, plen); if (resp == NULL) return NULL; wpabuf_put_be16(resp, EAP_PAX_RAND_LEN); wpabuf_put_data(resp, data->rand.r.y, EAP_PAX_RAND_LEN); wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: B = Y (client rand)", data->rand.r.y, EAP_PAX_RAND_LEN); wpabuf_put_be16(resp, data->cid_len); wpabuf_put_data(resp, data->cid, data->cid_len); wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-PAX: CID", (u8 *) data->cid, data->cid_len); wpabuf_put_be16(resp, EAP_PAX_MAC_LEN); rpos = wpabuf_put(resp, EAP_PAX_MAC_LEN); eap_pax_mac(req->mac_id, data->ck, EAP_PAX_CK_LEN, data->rand.r.x, EAP_PAX_RAND_LEN, data->rand.r.y, EAP_PAX_RAND_LEN, (u8 *) data->cid, data->cid_len, rpos); wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: MAC_CK(A, B, CID)", rpos, EAP_PAX_MAC_LEN); /* Optional ADE could be added here, if needed */ rpos = wpabuf_put(resp, EAP_PAX_ICV_LEN); eap_pax_mac(req->mac_id, data->ick, EAP_PAX_ICK_LEN, wpabuf_head(resp), wpabuf_len(resp) - EAP_PAX_ICV_LEN, NULL, 0, NULL, 0, rpos); wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", rpos, EAP_PAX_ICV_LEN); data->state = PAX_STD_2_SENT; data->mac_id = req->mac_id; data->dh_group_id = req->dh_group_id; data->public_key_id = req->public_key_id; return resp; } static struct wpabuf * eap_pax_process_std_3(struct eap_pax_data *data, struct eap_method_ret *ret, u8 id, const struct eap_pax_hdr *req, size_t req_plen) { struct wpabuf *resp; u8 *rpos, mac[EAP_PAX_MAC_LEN]; const u8 *pos; size_t left; wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-3 (received)"); if (data->state != PAX_STD_2_SENT) { wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-3 received in " "unexpected state (%d) - ignored", data->state); ret->ignore = TRUE; return NULL; } if (req->flags & EAP_PAX_FLAGS_CE) { wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-3 with CE flag set - " "ignored"); ret->ignore = TRUE; return NULL; } left = req_plen - sizeof(*req); if (left < 2 + EAP_PAX_MAC_LEN) { wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-3 with too short " "payload"); ret->ignore = TRUE; return NULL; } pos = (const u8 *) (req + 1); if (WPA_GET_BE16(pos) != EAP_PAX_MAC_LEN) { wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-3 with incorrect " "MAC_CK length %d (expected %d)", WPA_GET_BE16(pos), EAP_PAX_MAC_LEN); ret->ignore = TRUE; return NULL; } pos += 2; left -= 2; wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: MAC_CK(B, CID)", pos, EAP_PAX_MAC_LEN); eap_pax_mac(data->mac_id, data->ck, EAP_PAX_CK_LEN, data->rand.r.y, EAP_PAX_RAND_LEN, (u8 *) data->cid, data->cid_len, NULL, 0, mac); if (os_memcmp(pos, mac, EAP_PAX_MAC_LEN) != 0) { wpa_printf(MSG_INFO, "EAP-PAX: Invalid MAC_CK(B, CID) " "received"); wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: expected MAC_CK(B, CID)", mac, EAP_PAX_MAC_LEN); ret->methodState = METHOD_DONE; ret->decision = DECISION_FAIL; return NULL; } pos += EAP_PAX_MAC_LEN; left -= EAP_PAX_MAC_LEN; if (left > 0) { wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ignored extra payload", pos, left); } wpa_printf(MSG_DEBUG, "EAP-PAX: PAX-ACK (sending)"); resp = eap_pax_alloc_resp(req, id, EAP_PAX_OP_ACK, EAP_PAX_ICV_LEN); if (resp == NULL) return NULL; /* Optional ADE could be added here, if needed */ rpos = wpabuf_put(resp, EAP_PAX_ICV_LEN); eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN, wpabuf_head(resp), wpabuf_len(resp) - EAP_PAX_ICV_LEN, NULL, 0, NULL, 0, rpos); wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", rpos, EAP_PAX_ICV_LEN); data->state = PAX_DONE; ret->methodState = METHOD_DONE; ret->decision = DECISION_UNCOND_SUCC; ret->allowNotifications = FALSE; return resp; } static struct wpabuf * eap_pax_process(struct eap_sm *sm, void *priv, struct eap_method_ret *ret, const struct wpabuf *reqData) { struct eap_pax_data *data = priv; const struct eap_pax_hdr *req; struct wpabuf *resp; u8 icvbuf[EAP_PAX_ICV_LEN], id; const u8 *icv, *pos; size_t len; u16 flen, mlen; pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PAX, reqData, &len); if (pos == NULL || len < EAP_PAX_ICV_LEN) { ret->ignore = TRUE; return NULL; } id = eap_get_id(reqData); req = (const struct eap_pax_hdr *) pos; flen = len - EAP_PAX_ICV_LEN; mlen = wpabuf_len(reqData) - EAP_PAX_ICV_LEN; wpa_printf(MSG_DEBUG, "EAP-PAX: received frame: op_code 0x%x " "flags 0x%x mac_id 0x%x dh_group_id 0x%x " "public_key_id 0x%x", req->op_code, req->flags, req->mac_id, req->dh_group_id, req->public_key_id); wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: received payload", pos, len - EAP_PAX_ICV_LEN); if (data->state != PAX_INIT && data->mac_id != req->mac_id) { wpa_printf(MSG_INFO, "EAP-PAX: MAC ID changed during " "authentication (was 0x%d, is 0x%d)", data->mac_id, req->mac_id); ret->ignore = TRUE; return NULL; } if (data->state != PAX_INIT && data->dh_group_id != req->dh_group_id) { wpa_printf(MSG_INFO, "EAP-PAX: DH Group ID changed during " "authentication (was 0x%d, is 0x%d)", data->dh_group_id, req->dh_group_id); ret->ignore = TRUE; return NULL; } if (data->state != PAX_INIT && data->public_key_id != req->public_key_id) { wpa_printf(MSG_INFO, "EAP-PAX: Public Key ID changed during " "authentication (was 0x%d, is 0x%d)", data->public_key_id, req->public_key_id); ret->ignore = TRUE; return NULL; } /* TODO: add support EAP_PAX_HMAC_SHA256_128 */ if (req->mac_id != EAP_PAX_MAC_HMAC_SHA1_128) { wpa_printf(MSG_INFO, "EAP-PAX: Unsupported MAC ID 0x%x", req->mac_id); ret->ignore = TRUE; return NULL; } if (req->dh_group_id != EAP_PAX_DH_GROUP_NONE) { wpa_printf(MSG_INFO, "EAP-PAX: Unsupported DH Group ID 0x%x", req->dh_group_id); ret->ignore = TRUE; return NULL; } if (req->public_key_id != EAP_PAX_PUBLIC_KEY_NONE) { wpa_printf(MSG_INFO, "EAP-PAX: Unsupported Public Key ID 0x%x", req->public_key_id); ret->ignore = TRUE; return NULL; } if (req->flags & EAP_PAX_FLAGS_MF) { /* TODO: add support for reassembling fragments */ wpa_printf(MSG_INFO, "EAP-PAX: fragmentation not supported - " "ignored packet"); ret->ignore = TRUE; return NULL; } icv = pos + len - EAP_PAX_ICV_LEN; wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", icv, EAP_PAX_ICV_LEN); if (req->op_code == EAP_PAX_OP_STD_1) { eap_pax_mac(req->mac_id, (u8 *) "", 0, wpabuf_head(reqData), mlen, NULL, 0, NULL, 0, icvbuf); } else { eap_pax_mac(req->mac_id, data->ick, EAP_PAX_ICK_LEN, wpabuf_head(reqData), mlen, NULL, 0, NULL, 0, icvbuf); } if (os_memcmp(icv, icvbuf, EAP_PAX_ICV_LEN) != 0) { wpa_printf(MSG_DEBUG, "EAP-PAX: invalid ICV - ignoring the " "message"); wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: expected ICV", icvbuf, EAP_PAX_ICV_LEN); ret->ignore = TRUE; return NULL; } ret->ignore = FALSE; ret->methodState = METHOD_MAY_CONT; ret->decision = DECISION_FAIL; ret->allowNotifications = TRUE; switch (req->op_code) { case EAP_PAX_OP_STD_1: resp = eap_pax_process_std_1(data, ret, id, req, flen); break; case EAP_PAX_OP_STD_3: resp = eap_pax_process_std_3(data, ret, id, req, flen); break; default: wpa_printf(MSG_DEBUG, "EAP-PAX: ignoring message with unknown " "op_code %d", req->op_code); ret->ignore = TRUE; return NULL; } if (ret->methodState == METHOD_DONE) { ret->allowNotifications = FALSE; } return resp; } static Boolean eap_pax_isKeyAvailable(struct eap_sm *sm, void *priv) { struct eap_pax_data *data = priv; return data->state == PAX_DONE; } static u8 * eap_pax_getKey(struct eap_sm *sm, void *priv, size_t *len) { struct eap_pax_data *data = priv; u8 *key; if (data->state != PAX_DONE) return NULL; key = os_malloc(EAP_MSK_LEN); if (key == NULL) return NULL; *len = EAP_MSK_LEN; eap_pax_kdf(data->mac_id, data->mk, EAP_PAX_MK_LEN, "Master Session Key", data->rand.e, 2 * EAP_PAX_RAND_LEN, EAP_MSK_LEN, key); return key; } static u8 * eap_pax_get_emsk(struct eap_sm *sm, void *priv, size_t *len) { struct eap_pax_data *data = priv; u8 *key; if (data->state != PAX_DONE) return NULL; key = os_malloc(EAP_EMSK_LEN); if (key == NULL) return NULL; *len = EAP_EMSK_LEN; eap_pax_kdf(data->mac_id, data->mk, EAP_PAX_MK_LEN, "Extended Master Session Key", data->rand.e, 2 * EAP_PAX_RAND_LEN, EAP_EMSK_LEN, key); return key; } int eap_peer_pax_register(void) { struct eap_method *eap; int ret; eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_PAX, "PAX"); if (eap == NULL) return -1; eap->init = eap_pax_init; eap->deinit = eap_pax_deinit; eap->process = eap_pax_process; eap->isKeyAvailable = eap_pax_isKeyAvailable; eap->getKey = eap_pax_getKey; eap->get_emsk = eap_pax_get_emsk; ret = eap_peer_method_register(eap); if (ret) eap_peer_method_free(eap); return ret; } wpa-2.1/src/eap_peer/eap_peap.c000066400000000000000000001053271227414666700164570ustar00rootroot00000000000000/* * EAP peer method: EAP-PEAP (draft-josefsson-pppext-eap-tls-eap-10.txt) * Copyright (c) 2004-2008, 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/sha1.h" #include "crypto/tls.h" #include "eap_common/eap_tlv_common.h" #include "eap_common/eap_peap_common.h" #include "eap_i.h" #include "eap_tls_common.h" #include "eap_config.h" #include "tncc.h" /* Maximum supported PEAP version * 0 = Microsoft's PEAP version 0; draft-kamath-pppext-peapv0-00.txt * 1 = draft-josefsson-ppext-eap-tls-eap-05.txt */ #define EAP_PEAP_VERSION 1 static void eap_peap_deinit(struct eap_sm *sm, void *priv); struct eap_peap_data { struct eap_ssl_data ssl; int peap_version, force_peap_version, force_new_label; const struct eap_method *phase2_method; void *phase2_priv; int phase2_success; int phase2_eap_success; int phase2_eap_started; struct eap_method_type phase2_type; struct eap_method_type *phase2_types; size_t num_phase2_types; int peap_outer_success; /* 0 = PEAP terminated on Phase 2 inner * EAP-Success * 1 = reply with tunneled EAP-Success to inner * EAP-Success and expect AS to send outer * (unencrypted) EAP-Success after this * 2 = reply with PEAP/TLS ACK to inner * EAP-Success and expect AS to send outer * (unencrypted) EAP-Success after this */ int resuming; /* starting a resumed session */ int reauth; /* reauthentication */ u8 *key_data; u8 *session_id; size_t id_len; struct wpabuf *pending_phase2_req; enum { NO_BINDING, OPTIONAL_BINDING, REQUIRE_BINDING } crypto_binding; int crypto_binding_used; u8 binding_nonce[32]; u8 ipmk[40]; u8 cmk[20]; int soh; /* Whether IF-TNCCS-SOH (Statement of Health; Microsoft NAP) * is enabled. */ }; static int eap_peap_parse_phase1(struct eap_peap_data *data, const char *phase1) { const char *pos; pos = os_strstr(phase1, "peapver="); if (pos) { data->force_peap_version = atoi(pos + 8); data->peap_version = data->force_peap_version; wpa_printf(MSG_DEBUG, "EAP-PEAP: Forced PEAP version %d", data->force_peap_version); } if (os_strstr(phase1, "peaplabel=1")) { data->force_new_label = 1; wpa_printf(MSG_DEBUG, "EAP-PEAP: Force new label for key " "derivation"); } if (os_strstr(phase1, "peap_outer_success=0")) { data->peap_outer_success = 0; wpa_printf(MSG_DEBUG, "EAP-PEAP: terminate authentication on " "tunneled EAP-Success"); } else if (os_strstr(phase1, "peap_outer_success=1")) { data->peap_outer_success = 1; wpa_printf(MSG_DEBUG, "EAP-PEAP: send tunneled EAP-Success " "after receiving tunneled EAP-Success"); } else if (os_strstr(phase1, "peap_outer_success=2")) { data->peap_outer_success = 2; wpa_printf(MSG_DEBUG, "EAP-PEAP: send PEAP/TLS ACK after " "receiving tunneled EAP-Success"); } if (os_strstr(phase1, "crypto_binding=0")) { data->crypto_binding = NO_BINDING; wpa_printf(MSG_DEBUG, "EAP-PEAP: Do not use cryptobinding"); } else if (os_strstr(phase1, "crypto_binding=1")) { data->crypto_binding = OPTIONAL_BINDING; wpa_printf(MSG_DEBUG, "EAP-PEAP: Optional cryptobinding"); } else if (os_strstr(phase1, "crypto_binding=2")) { data->crypto_binding = REQUIRE_BINDING; wpa_printf(MSG_DEBUG, "EAP-PEAP: Require cryptobinding"); } #ifdef EAP_TNC if (os_strstr(phase1, "tnc=soh2")) { data->soh = 2; wpa_printf(MSG_DEBUG, "EAP-PEAP: SoH version 2 enabled"); } else if (os_strstr(phase1, "tnc=soh1")) { data->soh = 1; wpa_printf(MSG_DEBUG, "EAP-PEAP: SoH version 1 enabled"); } else if (os_strstr(phase1, "tnc=soh")) { data->soh = 2; wpa_printf(MSG_DEBUG, "EAP-PEAP: SoH version 2 enabled"); } #endif /* EAP_TNC */ return 0; } static void * eap_peap_init(struct eap_sm *sm) { struct eap_peap_data *data; struct eap_peer_config *config = eap_get_config(sm); data = os_zalloc(sizeof(*data)); if (data == NULL) return NULL; sm->peap_done = FALSE; data->peap_version = EAP_PEAP_VERSION; data->force_peap_version = -1; data->peap_outer_success = 2; data->crypto_binding = OPTIONAL_BINDING; if (config && config->phase1 && eap_peap_parse_phase1(data, config->phase1) < 0) { eap_peap_deinit(sm, data); return NULL; } if (eap_peer_select_phase2_methods(config, "auth=", &data->phase2_types, &data->num_phase2_types) < 0) { eap_peap_deinit(sm, data); return NULL; } data->phase2_type.vendor = EAP_VENDOR_IETF; data->phase2_type.method = EAP_TYPE_NONE; if (eap_peer_tls_ssl_init(sm, &data->ssl, config, EAP_TYPE_PEAP)) { wpa_printf(MSG_INFO, "EAP-PEAP: Failed to initialize SSL."); eap_peap_deinit(sm, data); return NULL; } return data; } static void eap_peap_deinit(struct eap_sm *sm, void *priv) { struct eap_peap_data *data = priv; if (data == NULL) return; if (data->phase2_priv && data->phase2_method) data->phase2_method->deinit(sm, data->phase2_priv); os_free(data->phase2_types); eap_peer_tls_ssl_deinit(sm, &data->ssl); os_free(data->key_data); os_free(data->session_id); wpabuf_free(data->pending_phase2_req); os_free(data); } /** * eap_tlv_build_nak - Build EAP-TLV NAK message * @id: EAP identifier for the header * @nak_type: TLV type (EAP_TLV_*) * Returns: Buffer to the allocated EAP-TLV NAK message or %NULL on failure * * This function builds an EAP-TLV NAK message. The caller is responsible for * freeing the returned buffer. */ static struct wpabuf * eap_tlv_build_nak(int id, u16 nak_type) { struct wpabuf *msg; msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TLV, 10, EAP_CODE_RESPONSE, id); if (msg == NULL) return NULL; wpabuf_put_u8(msg, 0x80); /* Mandatory */ wpabuf_put_u8(msg, EAP_TLV_NAK_TLV); wpabuf_put_be16(msg, 6); /* Length */ wpabuf_put_be32(msg, 0); /* Vendor-Id */ wpabuf_put_be16(msg, nak_type); /* NAK-Type */ return msg; } static int eap_peap_get_isk(struct eap_sm *sm, struct eap_peap_data *data, u8 *isk, size_t isk_len) { u8 *key; size_t key_len; os_memset(isk, 0, isk_len); if (data->phase2_method == NULL || data->phase2_priv == NULL || data->phase2_method->isKeyAvailable == NULL || data->phase2_method->getKey == NULL) return 0; if (!data->phase2_method->isKeyAvailable(sm, data->phase2_priv) || (key = data->phase2_method->getKey(sm, data->phase2_priv, &key_len)) == NULL) { wpa_printf(MSG_DEBUG, "EAP-PEAP: Could not get key material " "from Phase 2"); return -1; } if (key_len > isk_len) key_len = isk_len; os_memcpy(isk, key, key_len); os_free(key); return 0; } static int eap_peap_derive_cmk(struct eap_sm *sm, struct eap_peap_data *data) { u8 *tk; u8 isk[32], imck[60]; /* * Tunnel key (TK) is the first 60 octets of the key generated by * phase 1 of PEAP (based on TLS). */ tk = data->key_data; if (tk == NULL) return -1; wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TK", tk, 60); if (data->reauth && tls_connection_resumed(sm->ssl_ctx, data->ssl.conn)) { /* Fast-connect: IPMK|CMK = TK */ os_memcpy(data->ipmk, tk, 40); wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IPMK from TK", data->ipmk, 40); os_memcpy(data->cmk, tk + 40, 20); wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CMK from TK", data->cmk, 20); return 0; } if (eap_peap_get_isk(sm, data, isk, sizeof(isk)) < 0) return -1; wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: ISK", isk, sizeof(isk)); /* * IPMK Seed = "Inner Methods Compound Keys" | ISK * TempKey = First 40 octets of TK * IPMK|CMK = PRF+(TempKey, IPMK Seed, 60) * (note: draft-josefsson-pppext-eap-tls-eap-10.txt includes a space * in the end of the label just before ISK; is that just a typo?) */ wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TempKey", tk, 40); if (peap_prfplus(data->peap_version, tk, 40, "Inner Methods Compound Keys", isk, sizeof(isk), imck, sizeof(imck)) < 0) return -1; wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IMCK (IPMKj)", imck, sizeof(imck)); os_memcpy(data->ipmk, imck, 40); wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IPMK (S-IPMKj)", data->ipmk, 40); os_memcpy(data->cmk, imck + 40, 20); wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CMK (CMKj)", data->cmk, 20); return 0; } static int eap_tlv_add_cryptobinding(struct eap_sm *sm, struct eap_peap_data *data, struct wpabuf *buf) { u8 *mac; u8 eap_type = EAP_TYPE_PEAP; const u8 *addr[2]; size_t len[2]; u16 tlv_type; /* Compound_MAC: HMAC-SHA1-160(cryptobinding TLV | EAP type) */ addr[0] = wpabuf_put(buf, 0); len[0] = 60; addr[1] = &eap_type; len[1] = 1; tlv_type = EAP_TLV_CRYPTO_BINDING_TLV; wpabuf_put_be16(buf, tlv_type); wpabuf_put_be16(buf, 56); wpabuf_put_u8(buf, 0); /* Reserved */ wpabuf_put_u8(buf, data->peap_version); /* Version */ wpabuf_put_u8(buf, data->peap_version); /* RecvVersion */ wpabuf_put_u8(buf, 1); /* SubType: 0 = Request, 1 = Response */ wpabuf_put_data(buf, data->binding_nonce, 32); /* Nonce */ mac = wpabuf_put(buf, 20); /* Compound_MAC */ wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC CMK", data->cmk, 20); wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC data 1", addr[0], len[0]); wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC data 2", addr[1], len[1]); hmac_sha1_vector(data->cmk, 20, 2, addr, len, mac); wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC", mac, SHA1_MAC_LEN); data->crypto_binding_used = 1; return 0; } /** * eap_tlv_build_result - Build EAP-TLV Result message * @id: EAP identifier for the header * @status: Status (EAP_TLV_RESULT_SUCCESS or EAP_TLV_RESULT_FAILURE) * Returns: Buffer to the allocated EAP-TLV Result message or %NULL on failure * * This function builds an EAP-TLV Result message. The caller is responsible * for freeing the returned buffer. */ static struct wpabuf * eap_tlv_build_result(struct eap_sm *sm, struct eap_peap_data *data, int crypto_tlv_used, int id, u16 status) { struct wpabuf *msg; size_t len; if (data->crypto_binding == NO_BINDING) crypto_tlv_used = 0; len = 6; if (crypto_tlv_used) len += 60; /* Cryptobinding TLV */ msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TLV, len, EAP_CODE_RESPONSE, id); if (msg == NULL) return NULL; wpabuf_put_u8(msg, 0x80); /* Mandatory */ wpabuf_put_u8(msg, EAP_TLV_RESULT_TLV); wpabuf_put_be16(msg, 2); /* Length */ wpabuf_put_be16(msg, status); /* Status */ if (crypto_tlv_used && eap_tlv_add_cryptobinding(sm, data, msg)) { wpabuf_free(msg); return NULL; } return msg; } static int eap_tlv_validate_cryptobinding(struct eap_sm *sm, struct eap_peap_data *data, const u8 *crypto_tlv, size_t crypto_tlv_len) { u8 buf[61], mac[SHA1_MAC_LEN]; const u8 *pos; if (eap_peap_derive_cmk(sm, data) < 0) { wpa_printf(MSG_DEBUG, "EAP-PEAP: Could not derive CMK"); return -1; } if (crypto_tlv_len != 4 + 56) { wpa_printf(MSG_DEBUG, "EAP-PEAP: Invalid cryptobinding TLV " "length %d", (int) crypto_tlv_len); return -1; } pos = crypto_tlv; pos += 4; /* TLV header */ if (pos[1] != data->peap_version) { wpa_printf(MSG_DEBUG, "EAP-PEAP: Cryptobinding TLV Version " "mismatch (was %d; expected %d)", pos[1], data->peap_version); return -1; } if (pos[3] != 0) { wpa_printf(MSG_DEBUG, "EAP-PEAP: Unexpected Cryptobinding TLV " "SubType %d", pos[3]); return -1; } pos += 4; os_memcpy(data->binding_nonce, pos, 32); pos += 32; /* Nonce */ /* Compound_MAC: HMAC-SHA1-160(cryptobinding TLV | EAP type) */ os_memcpy(buf, crypto_tlv, 60); os_memset(buf + 4 + 4 + 32, 0, 20); /* Compound_MAC */ buf[60] = EAP_TYPE_PEAP; wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Compound_MAC data", buf, sizeof(buf)); hmac_sha1(data->cmk, 20, buf, sizeof(buf), mac); if (os_memcmp(mac, pos, SHA1_MAC_LEN) != 0) { wpa_printf(MSG_DEBUG, "EAP-PEAP: Invalid Compound_MAC in " "cryptobinding TLV"); wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Received MAC", pos, SHA1_MAC_LEN); wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Expected MAC", mac, SHA1_MAC_LEN); return -1; } wpa_printf(MSG_DEBUG, "EAP-PEAP: Valid cryptobinding TLV received"); return 0; } /** * eap_tlv_process - Process a received EAP-TLV message and generate a response * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * @ret: Return values from EAP request validation and processing * @req: EAP-TLV request to be processed. The caller must have validated that * the buffer is large enough to contain full request (hdr->length bytes) and * that the EAP type is EAP_TYPE_TLV. * @resp: Buffer to return a pointer to the allocated response message. This * field should be initialized to %NULL before the call. The value will be * updated if a response message is generated. The caller is responsible for * freeing the allocated message. * @force_failure: Force negotiation to fail * Returns: 0 on success, -1 on failure */ static int eap_tlv_process(struct eap_sm *sm, struct eap_peap_data *data, struct eap_method_ret *ret, const struct wpabuf *req, struct wpabuf **resp, int force_failure) { size_t left, tlv_len; const u8 *pos; const u8 *result_tlv = NULL, *crypto_tlv = NULL; size_t result_tlv_len = 0, crypto_tlv_len = 0; int tlv_type, mandatory; /* Parse TLVs */ pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TLV, req, &left); if (pos == NULL) return -1; wpa_hexdump(MSG_DEBUG, "EAP-TLV: Received TLVs", pos, left); while (left >= 4) { mandatory = !!(pos[0] & 0x80); tlv_type = WPA_GET_BE16(pos) & 0x3fff; pos += 2; tlv_len = WPA_GET_BE16(pos); pos += 2; left -= 4; if (tlv_len > left) { wpa_printf(MSG_DEBUG, "EAP-TLV: TLV underrun " "(tlv_len=%lu left=%lu)", (unsigned long) tlv_len, (unsigned long) left); return -1; } switch (tlv_type) { case EAP_TLV_RESULT_TLV: result_tlv = pos; result_tlv_len = tlv_len; break; case EAP_TLV_CRYPTO_BINDING_TLV: crypto_tlv = pos; crypto_tlv_len = tlv_len; break; default: wpa_printf(MSG_DEBUG, "EAP-TLV: Unsupported TLV Type " "%d%s", tlv_type, mandatory ? " (mandatory)" : ""); if (mandatory) { /* NAK TLV and ignore all TLVs in this packet. */ *resp = eap_tlv_build_nak(eap_get_id(req), tlv_type); return *resp == NULL ? -1 : 0; } /* Ignore this TLV, but process other TLVs */ break; } pos += tlv_len; left -= tlv_len; } if (left) { wpa_printf(MSG_DEBUG, "EAP-TLV: Last TLV too short in " "Request (left=%lu)", (unsigned long) left); return -1; } /* Process supported TLVs */ if (crypto_tlv && data->crypto_binding != NO_BINDING) { wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Cryptobinding TLV", crypto_tlv, crypto_tlv_len); if (eap_tlv_validate_cryptobinding(sm, data, crypto_tlv - 4, crypto_tlv_len + 4) < 0) { if (result_tlv == NULL) return -1; force_failure = 1; crypto_tlv = NULL; /* do not include Cryptobinding TLV * in response, if the received * cryptobinding was invalid. */ } } else if (!crypto_tlv && data->crypto_binding == REQUIRE_BINDING) { wpa_printf(MSG_DEBUG, "EAP-PEAP: No cryptobinding TLV"); return -1; } if (result_tlv) { int status, resp_status; wpa_hexdump(MSG_DEBUG, "EAP-TLV: Result TLV", result_tlv, result_tlv_len); if (result_tlv_len < 2) { wpa_printf(MSG_INFO, "EAP-TLV: Too short Result TLV " "(len=%lu)", (unsigned long) result_tlv_len); return -1; } status = WPA_GET_BE16(result_tlv); if (status == EAP_TLV_RESULT_SUCCESS) { wpa_printf(MSG_INFO, "EAP-TLV: TLV Result - Success " "- EAP-TLV/Phase2 Completed"); if (force_failure) { wpa_printf(MSG_INFO, "EAP-TLV: Earlier failure" " - force failed Phase 2"); resp_status = EAP_TLV_RESULT_FAILURE; ret->decision = DECISION_FAIL; } else { resp_status = EAP_TLV_RESULT_SUCCESS; ret->decision = DECISION_UNCOND_SUCC; } } else if (status == EAP_TLV_RESULT_FAILURE) { wpa_printf(MSG_INFO, "EAP-TLV: TLV Result - Failure"); resp_status = EAP_TLV_RESULT_FAILURE; ret->decision = DECISION_FAIL; } else { wpa_printf(MSG_INFO, "EAP-TLV: Unknown TLV Result " "Status %d", status); resp_status = EAP_TLV_RESULT_FAILURE; ret->decision = DECISION_FAIL; } ret->methodState = METHOD_DONE; *resp = eap_tlv_build_result(sm, data, crypto_tlv != NULL, eap_get_id(req), resp_status); } return 0; } static int eap_peap_phase2_request(struct eap_sm *sm, struct eap_peap_data *data, struct eap_method_ret *ret, struct wpabuf *req, struct wpabuf **resp) { struct eap_hdr *hdr = wpabuf_mhead(req); size_t len = be_to_host16(hdr->length); u8 *pos; struct eap_method_ret iret; struct eap_peer_config *config = eap_get_config(sm); if (len <= sizeof(struct eap_hdr)) { wpa_printf(MSG_INFO, "EAP-PEAP: too short " "Phase 2 request (len=%lu)", (unsigned long) len); return -1; } pos = (u8 *) (hdr + 1); wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Request: type=%d", *pos); switch (*pos) { case EAP_TYPE_IDENTITY: *resp = eap_sm_buildIdentity(sm, hdr->identifier, 1); break; case EAP_TYPE_TLV: os_memset(&iret, 0, sizeof(iret)); if (eap_tlv_process(sm, data, &iret, req, resp, data->phase2_eap_started && !data->phase2_eap_success)) { ret->methodState = METHOD_DONE; ret->decision = DECISION_FAIL; return -1; } if (iret.methodState == METHOD_DONE || iret.methodState == METHOD_MAY_CONT) { ret->methodState = iret.methodState; ret->decision = iret.decision; data->phase2_success = 1; } break; case EAP_TYPE_EXPANDED: #ifdef EAP_TNC if (data->soh) { const u8 *epos; size_t eleft; epos = eap_hdr_validate(EAP_VENDOR_MICROSOFT, 0x21, req, &eleft); if (epos) { struct wpabuf *buf; wpa_printf(MSG_DEBUG, "EAP-PEAP: SoH EAP Extensions"); buf = tncc_process_soh_request(data->soh, epos, eleft); if (buf) { *resp = eap_msg_alloc( EAP_VENDOR_MICROSOFT, 0x21, wpabuf_len(buf), EAP_CODE_RESPONSE, hdr->identifier); if (*resp == NULL) { ret->methodState = METHOD_DONE; ret->decision = DECISION_FAIL; return -1; } wpabuf_put_buf(*resp, buf); wpabuf_free(buf); break; } } } #endif /* EAP_TNC */ /* fall through */ default: if (data->phase2_type.vendor == EAP_VENDOR_IETF && data->phase2_type.method == EAP_TYPE_NONE) { size_t i; for (i = 0; i < data->num_phase2_types; i++) { if (data->phase2_types[i].vendor != EAP_VENDOR_IETF || data->phase2_types[i].method != *pos) continue; data->phase2_type.vendor = data->phase2_types[i].vendor; data->phase2_type.method = data->phase2_types[i].method; wpa_printf(MSG_DEBUG, "EAP-PEAP: Selected " "Phase 2 EAP vendor %d method %d", data->phase2_type.vendor, data->phase2_type.method); break; } } if (*pos != data->phase2_type.method || *pos == EAP_TYPE_NONE) { if (eap_peer_tls_phase2_nak(data->phase2_types, data->num_phase2_types, hdr, resp)) return -1; return 0; } if (data->phase2_priv == NULL) { data->phase2_method = eap_peer_get_eap_method( data->phase2_type.vendor, data->phase2_type.method); if (data->phase2_method) { sm->init_phase2 = 1; data->phase2_priv = data->phase2_method->init(sm); sm->init_phase2 = 0; } } if (data->phase2_priv == NULL || data->phase2_method == NULL) { wpa_printf(MSG_INFO, "EAP-PEAP: failed to initialize " "Phase 2 EAP method %d", *pos); ret->methodState = METHOD_DONE; ret->decision = DECISION_FAIL; return -1; } data->phase2_eap_started = 1; os_memset(&iret, 0, sizeof(iret)); *resp = data->phase2_method->process(sm, data->phase2_priv, &iret, req); if ((iret.methodState == METHOD_DONE || iret.methodState == METHOD_MAY_CONT) && (iret.decision == DECISION_UNCOND_SUCC || iret.decision == DECISION_COND_SUCC)) { data->phase2_eap_success = 1; data->phase2_success = 1; } break; } if (*resp == NULL && (config->pending_req_identity || config->pending_req_password || config->pending_req_otp || config->pending_req_new_password)) { wpabuf_free(data->pending_phase2_req); data->pending_phase2_req = wpabuf_alloc_copy(hdr, len); } return 0; } static int eap_peap_decrypt(struct eap_sm *sm, struct eap_peap_data *data, struct eap_method_ret *ret, const struct eap_hdr *req, const struct wpabuf *in_data, struct wpabuf **out_data) { struct wpabuf *in_decrypted = NULL; int res, skip_change = 0; struct eap_hdr *hdr, *rhdr; struct wpabuf *resp = NULL; size_t len; wpa_printf(MSG_DEBUG, "EAP-PEAP: received %lu bytes encrypted data for" " Phase 2", (unsigned long) wpabuf_len(in_data)); if (data->pending_phase2_req) { wpa_printf(MSG_DEBUG, "EAP-PEAP: Pending Phase 2 request - " "skip decryption and use old data"); /* Clear TLS reassembly state. */ eap_peer_tls_reset_input(&data->ssl); in_decrypted = data->pending_phase2_req; data->pending_phase2_req = NULL; skip_change = 1; goto continue_req; } if (wpabuf_len(in_data) == 0 && sm->workaround && data->phase2_success) { /* * Cisco ACS seems to be using TLS ACK to terminate * EAP-PEAPv0/GTC. Try to reply with TLS ACK. */ wpa_printf(MSG_DEBUG, "EAP-PEAP: Received TLS ACK, but " "expected data - acknowledge with TLS ACK since " "Phase 2 has been completed"); ret->decision = DECISION_COND_SUCC; ret->methodState = METHOD_DONE; return 1; } else if (wpabuf_len(in_data) == 0) { /* Received TLS ACK - requesting more fragments */ return eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_PEAP, data->peap_version, req->identifier, NULL, out_data); } res = eap_peer_tls_decrypt(sm, &data->ssl, in_data, &in_decrypted); if (res) return res; continue_req: wpa_hexdump_buf(MSG_DEBUG, "EAP-PEAP: Decrypted Phase 2 EAP", in_decrypted); hdr = wpabuf_mhead(in_decrypted); if (wpabuf_len(in_decrypted) == 5 && hdr->code == EAP_CODE_REQUEST && be_to_host16(hdr->length) == 5 && eap_get_type(in_decrypted) == EAP_TYPE_IDENTITY) { /* At least FreeRADIUS seems to send full EAP header with * EAP Request Identity */ skip_change = 1; } if (wpabuf_len(in_decrypted) >= 5 && hdr->code == EAP_CODE_REQUEST && eap_get_type(in_decrypted) == EAP_TYPE_TLV) { skip_change = 1; } if (data->peap_version == 0 && !skip_change) { struct eap_hdr *nhdr; struct wpabuf *nmsg = wpabuf_alloc(sizeof(struct eap_hdr) + wpabuf_len(in_decrypted)); if (nmsg == NULL) { wpabuf_free(in_decrypted); return 0; } nhdr = wpabuf_put(nmsg, sizeof(*nhdr)); wpabuf_put_buf(nmsg, in_decrypted); nhdr->code = req->code; nhdr->identifier = req->identifier; nhdr->length = host_to_be16(sizeof(struct eap_hdr) + wpabuf_len(in_decrypted)); wpabuf_free(in_decrypted); in_decrypted = nmsg; } hdr = wpabuf_mhead(in_decrypted); if (wpabuf_len(in_decrypted) < sizeof(*hdr)) { wpa_printf(MSG_INFO, "EAP-PEAP: Too short Phase 2 " "EAP frame (len=%lu)", (unsigned long) wpabuf_len(in_decrypted)); wpabuf_free(in_decrypted); return 0; } len = be_to_host16(hdr->length); if (len > wpabuf_len(in_decrypted)) { wpa_printf(MSG_INFO, "EAP-PEAP: Length mismatch in " "Phase 2 EAP frame (len=%lu hdr->length=%lu)", (unsigned long) wpabuf_len(in_decrypted), (unsigned long) len); wpabuf_free(in_decrypted); return 0; } if (len < wpabuf_len(in_decrypted)) { wpa_printf(MSG_INFO, "EAP-PEAP: Odd.. Phase 2 EAP header has " "shorter length than full decrypted data " "(%lu < %lu)", (unsigned long) len, (unsigned long) wpabuf_len(in_decrypted)); } wpa_printf(MSG_DEBUG, "EAP-PEAP: received Phase 2: code=%d " "identifier=%d length=%lu", hdr->code, hdr->identifier, (unsigned long) len); switch (hdr->code) { case EAP_CODE_REQUEST: if (eap_peap_phase2_request(sm, data, ret, in_decrypted, &resp)) { wpabuf_free(in_decrypted); wpa_printf(MSG_INFO, "EAP-PEAP: Phase2 Request " "processing failed"); return 0; } break; case EAP_CODE_SUCCESS: wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Success"); if (data->peap_version == 1) { /* EAP-Success within TLS tunnel is used to indicate * shutdown of the TLS channel. The authentication has * been completed. */ if (data->phase2_eap_started && !data->phase2_eap_success) { wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 " "Success used to indicate success, " "but Phase 2 EAP was not yet " "completed successfully"); ret->methodState = METHOD_DONE; ret->decision = DECISION_FAIL; wpabuf_free(in_decrypted); return 0; } wpa_printf(MSG_DEBUG, "EAP-PEAP: Version 1 - " "EAP-Success within TLS tunnel - " "authentication completed"); ret->decision = DECISION_UNCOND_SUCC; ret->methodState = METHOD_DONE; data->phase2_success = 1; if (data->peap_outer_success == 2) { wpabuf_free(in_decrypted); wpa_printf(MSG_DEBUG, "EAP-PEAP: Use TLS ACK " "to finish authentication"); return 1; } else if (data->peap_outer_success == 1) { /* Reply with EAP-Success within the TLS * channel to complete the authentication. */ resp = wpabuf_alloc(sizeof(struct eap_hdr)); if (resp) { rhdr = wpabuf_put(resp, sizeof(*rhdr)); rhdr->code = EAP_CODE_SUCCESS; rhdr->identifier = hdr->identifier; rhdr->length = host_to_be16(sizeof(*rhdr)); } } else { /* No EAP-Success expected for Phase 1 (outer, * unencrypted auth), so force EAP state * machine to SUCCESS state. */ sm->peap_done = TRUE; } } else { /* FIX: ? */ } break; case EAP_CODE_FAILURE: wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Failure"); ret->decision = DECISION_FAIL; ret->methodState = METHOD_MAY_CONT; ret->allowNotifications = FALSE; /* Reply with EAP-Failure within the TLS channel to complete * failure reporting. */ resp = wpabuf_alloc(sizeof(struct eap_hdr)); if (resp) { rhdr = wpabuf_put(resp, sizeof(*rhdr)); rhdr->code = EAP_CODE_FAILURE; rhdr->identifier = hdr->identifier; rhdr->length = host_to_be16(sizeof(*rhdr)); } break; default: wpa_printf(MSG_INFO, "EAP-PEAP: Unexpected code=%d in " "Phase 2 EAP header", hdr->code); break; } wpabuf_free(in_decrypted); if (resp) { int skip_change2 = 0; struct wpabuf *rmsg, buf; wpa_hexdump_buf_key(MSG_DEBUG, "EAP-PEAP: Encrypting Phase 2 data", resp); /* PEAP version changes */ if (wpabuf_len(resp) >= 5 && wpabuf_head_u8(resp)[0] == EAP_CODE_RESPONSE && eap_get_type(resp) == EAP_TYPE_TLV) skip_change2 = 1; rmsg = resp; if (data->peap_version == 0 && !skip_change2) { wpabuf_set(&buf, wpabuf_head_u8(resp) + sizeof(struct eap_hdr), wpabuf_len(resp) - sizeof(struct eap_hdr)); rmsg = &buf; } if (eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_PEAP, data->peap_version, req->identifier, rmsg, out_data)) { wpa_printf(MSG_INFO, "EAP-PEAP: Failed to encrypt " "a Phase 2 frame"); } wpabuf_free(resp); } return 0; } static struct wpabuf * eap_peap_process(struct eap_sm *sm, void *priv, struct eap_method_ret *ret, const struct wpabuf *reqData) { const struct eap_hdr *req; size_t left; int res; u8 flags, id; struct wpabuf *resp; const u8 *pos; struct eap_peap_data *data = priv; pos = eap_peer_tls_process_init(sm, &data->ssl, EAP_TYPE_PEAP, ret, reqData, &left, &flags); if (pos == NULL) return NULL; req = wpabuf_head(reqData); id = req->identifier; if (flags & EAP_TLS_FLAGS_START) { wpa_printf(MSG_DEBUG, "EAP-PEAP: Start (server ver=%d, own " "ver=%d)", flags & EAP_TLS_VERSION_MASK, data->peap_version); if ((flags & EAP_TLS_VERSION_MASK) < data->peap_version) data->peap_version = flags & EAP_TLS_VERSION_MASK; if (data->force_peap_version >= 0 && data->force_peap_version != data->peap_version) { wpa_printf(MSG_WARNING, "EAP-PEAP: Failed to select " "forced PEAP version %d", data->force_peap_version); ret->methodState = METHOD_DONE; ret->decision = DECISION_FAIL; ret->allowNotifications = FALSE; return NULL; } wpa_printf(MSG_DEBUG, "EAP-PEAP: Using PEAP version %d", data->peap_version); left = 0; /* make sure that this frame is empty, even though it * should always be, anyway */ } resp = NULL; if (tls_connection_established(sm->ssl_ctx, data->ssl.conn) && !data->resuming) { struct wpabuf msg; wpabuf_set(&msg, pos, left); res = eap_peap_decrypt(sm, data, ret, req, &msg, &resp); } else { res = eap_peer_tls_process_helper(sm, &data->ssl, EAP_TYPE_PEAP, data->peap_version, id, pos, left, &resp); if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) { char *label; wpa_printf(MSG_DEBUG, "EAP-PEAP: TLS done, proceed to Phase 2"); os_free(data->key_data); /* draft-josefsson-ppext-eap-tls-eap-05.txt * specifies that PEAPv1 would use "client PEAP * encryption" as the label. However, most existing * PEAPv1 implementations seem to be using the old * label, "client EAP encryption", instead. Use the old * label by default, but allow it to be configured with * phase1 parameter peaplabel=1. */ if (data->force_new_label) label = "client PEAP encryption"; else label = "client EAP encryption"; wpa_printf(MSG_DEBUG, "EAP-PEAP: using label '%s' in " "key derivation", label); data->key_data = eap_peer_tls_derive_key(sm, &data->ssl, label, EAP_TLS_KEY_LEN); if (data->key_data) { wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: Derived key", data->key_data, EAP_TLS_KEY_LEN); } else { wpa_printf(MSG_DEBUG, "EAP-PEAP: Failed to " "derive key"); } os_free(data->session_id); data->session_id = eap_peer_tls_derive_session_id(sm, &data->ssl, EAP_TYPE_PEAP, &data->id_len); if (data->session_id) { wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Derived Session-Id", data->session_id, data->id_len); } else { wpa_printf(MSG_ERROR, "EAP-PEAP: Failed to " "derive Session-Id"); } if (sm->workaround && data->resuming) { /* * At least few RADIUS servers (Aegis v1.1.6; * but not v1.1.4; and Cisco ACS) seem to be * terminating PEAPv1 (Aegis) or PEAPv0 (Cisco * ACS) session resumption with outer * EAP-Success. This does not seem to follow * draft-josefsson-pppext-eap-tls-eap-05.txt * section 4.2, so only allow this if EAP * workarounds are enabled. */ wpa_printf(MSG_DEBUG, "EAP-PEAP: Workaround - " "allow outer EAP-Success to " "terminate PEAP resumption"); ret->decision = DECISION_COND_SUCC; data->phase2_success = 1; } data->resuming = 0; } if (res == 2) { struct wpabuf msg; /* * Application data included in the handshake message. */ wpabuf_free(data->pending_phase2_req); data->pending_phase2_req = resp; resp = NULL; wpabuf_set(&msg, pos, left); res = eap_peap_decrypt(sm, data, ret, req, &msg, &resp); } } if (ret->methodState == METHOD_DONE) { ret->allowNotifications = FALSE; } if (res == 1) { wpabuf_free(resp); return eap_peer_tls_build_ack(id, EAP_TYPE_PEAP, data->peap_version); } return resp; } static Boolean eap_peap_has_reauth_data(struct eap_sm *sm, void *priv) { struct eap_peap_data *data = priv; return tls_connection_established(sm->ssl_ctx, data->ssl.conn) && data->phase2_success; } static void eap_peap_deinit_for_reauth(struct eap_sm *sm, void *priv) { struct eap_peap_data *data = priv; wpabuf_free(data->pending_phase2_req); data->pending_phase2_req = NULL; data->crypto_binding_used = 0; } static void * eap_peap_init_for_reauth(struct eap_sm *sm, void *priv) { struct eap_peap_data *data = priv; os_free(data->key_data); data->key_data = NULL; os_free(data->session_id); data->session_id = NULL; if (eap_peer_tls_reauth_init(sm, &data->ssl)) { os_free(data); return NULL; } if (data->phase2_priv && data->phase2_method && data->phase2_method->init_for_reauth) data->phase2_method->init_for_reauth(sm, data->phase2_priv); data->phase2_success = 0; data->phase2_eap_success = 0; data->phase2_eap_started = 0; data->resuming = 1; data->reauth = 1; sm->peap_done = FALSE; return priv; } static int eap_peap_get_status(struct eap_sm *sm, void *priv, char *buf, size_t buflen, int verbose) { struct eap_peap_data *data = priv; int len, ret; len = eap_peer_tls_status(sm, &data->ssl, buf, buflen, verbose); if (data->phase2_method) { ret = os_snprintf(buf + len, buflen - len, "EAP-PEAPv%d Phase2 method=%s\n", data->peap_version, data->phase2_method->name); if (ret < 0 || (size_t) ret >= buflen - len) return len; len += ret; } return len; } static Boolean eap_peap_isKeyAvailable(struct eap_sm *sm, void *priv) { struct eap_peap_data *data = priv; return data->key_data != NULL && data->phase2_success; } static u8 * eap_peap_getKey(struct eap_sm *sm, void *priv, size_t *len) { struct eap_peap_data *data = priv; u8 *key; if (data->key_data == NULL || !data->phase2_success) return NULL; key = os_malloc(EAP_TLS_KEY_LEN); if (key == NULL) return NULL; *len = EAP_TLS_KEY_LEN; if (data->crypto_binding_used) { u8 csk[128]; /* * Note: It looks like Microsoft implementation requires null * termination for this label while the one used for deriving * IPMK|CMK did not use null termination. */ if (peap_prfplus(data->peap_version, data->ipmk, 40, "Session Key Generating Function", (u8 *) "\00", 1, csk, sizeof(csk)) < 0) { os_free(key); return NULL; } wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CSK", csk, sizeof(csk)); os_memcpy(key, csk, EAP_TLS_KEY_LEN); wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Derived key", key, EAP_TLS_KEY_LEN); } else os_memcpy(key, data->key_data, EAP_TLS_KEY_LEN); return key; } static u8 * eap_peap_get_session_id(struct eap_sm *sm, void *priv, size_t *len) { struct eap_peap_data *data = priv; u8 *id; if (data->session_id == NULL || !data->phase2_success) return NULL; id = os_malloc(data->id_len); if (id == NULL) return NULL; *len = data->id_len; os_memcpy(id, data->session_id, data->id_len); return id; } int eap_peer_peap_register(void) { struct eap_method *eap; int ret; eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_PEAP, "PEAP"); if (eap == NULL) return -1; eap->init = eap_peap_init; eap->deinit = eap_peap_deinit; eap->process = eap_peap_process; eap->isKeyAvailable = eap_peap_isKeyAvailable; eap->getKey = eap_peap_getKey; eap->get_status = eap_peap_get_status; eap->has_reauth_data = eap_peap_has_reauth_data; eap->deinit_for_reauth = eap_peap_deinit_for_reauth; eap->init_for_reauth = eap_peap_init_for_reauth; eap->getSessionId = eap_peap_get_session_id; ret = eap_peer_method_register(eap); if (ret) eap_peer_method_free(eap); return ret; } wpa-2.1/src/eap_peer/eap_proxy.h000066400000000000000000000023651227414666700167160ustar00rootroot00000000000000/* * EAP proxy definitions * Copyright (c) 2011-2013 Qualcomm Atheros, Inc. * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef EAP_PROXY_H #define EAP_PROXY_H struct eap_proxy_sm; struct eapol_callbacks; struct eap_sm; struct eap_peer_config; enum eap_proxy_status { EAP_PROXY_FAILURE = 0x00, EAP_PROXY_SUCCESS }; struct eap_proxy_sm * eap_proxy_init(void *eapol_ctx, struct eapol_callbacks *eapol_cb, void *msg_ctx); void eap_proxy_deinit(struct eap_proxy_sm *eap_proxy); int eap_proxy_key_available(struct eap_proxy_sm *sm); const u8 * eap_proxy_get_eapKeyData(struct eap_proxy_sm *sm, size_t *len); struct wpabuf * eap_proxy_get_eapRespData(struct eap_proxy_sm *sm); int eap_proxy_sm_step(struct eap_proxy_sm *sm, struct eap_sm *eap_sm); enum eap_proxy_status eap_proxy_packet_update(struct eap_proxy_sm *eap_proxy, u8 *eapReqData, int eapReqDataLen); int eap_proxy_sm_get_status(struct eap_proxy_sm *sm, char *buf, size_t buflen, int verbose); int eap_proxy_get_imsi(struct eap_proxy_sm *eap_proxy, char *imsi_buf, size_t *imsi_len); int eap_proxy_notify_config(struct eap_proxy_sm *sm, struct eap_peer_config *config); #endif /* EAP_PROXY_H */ wpa-2.1/src/eap_peer/eap_proxy_dummy.c000066400000000000000000000024231227414666700201170ustar00rootroot00000000000000/* * EAP proxy - dummy implementation for build testing * Copyright (c) 2013 Qualcomm Atheros, Inc. * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "includes.h" #include "common.h" #include "eap_proxy.h" struct eap_proxy_sm * eap_proxy_init(void *eapol_ctx, struct eapol_callbacks *eapol_cb, void *msg_ctx) { return NULL; } void eap_proxy_deinit(struct eap_proxy_sm *eap_proxy) { } int eap_proxy_key_available(struct eap_proxy_sm *sm) { return 0; } const u8 * eap_proxy_get_eapKeyData(struct eap_proxy_sm *sm, size_t *len) { return NULL; } struct wpabuf * eap_proxy_get_eapRespData(struct eap_proxy_sm *sm) { return NULL; } int eap_proxy_sm_step(struct eap_proxy_sm *sm, struct eap_sm *eap_sm) { return 0; } enum eap_proxy_status eap_proxy_packet_update(struct eap_proxy_sm *eap_proxy, u8 *eapReqData, int eapReqDataLen) { return EAP_PROXY_FAILURE; } int eap_proxy_sm_get_status(struct eap_proxy_sm *sm, char *buf, size_t buflen, int verbose) { return 0; } int eap_proxy_get_imsi(struct eap_proxy_sm *eap_proxy, char *imsi_buf, size_t *imsi_len) { return -1; } int eap_proxy_notify_config(struct eap_proxy_sm *sm, struct eap_peer_config *config) { return -1; } wpa-2.1/src/eap_peer/eap_psk.c000066400000000000000000000324471227414666700163310ustar00rootroot00000000000000/* * EAP peer method: EAP-PSK (RFC 4764) * Copyright (c) 2004-2008, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. * * Note: EAP-PSK is an EAP authentication method and as such, completely * different from WPA-PSK. This file is not needed for WPA-PSK functionality. */ #include "includes.h" #include "common.h" #include "crypto/aes_wrap.h" #include "crypto/random.h" #include "eap_common/eap_psk_common.h" #include "eap_i.h" struct eap_psk_data { enum { PSK_INIT, PSK_MAC_SENT, PSK_DONE } state; u8 rand_p[EAP_PSK_RAND_LEN]; u8 rand_s[EAP_PSK_RAND_LEN]; u8 ak[EAP_PSK_AK_LEN], kdk[EAP_PSK_KDK_LEN], tek[EAP_PSK_TEK_LEN]; u8 *id_s, *id_p; size_t id_s_len, id_p_len; u8 msk[EAP_MSK_LEN]; u8 emsk[EAP_EMSK_LEN]; }; static void * eap_psk_init(struct eap_sm *sm) { struct eap_psk_data *data; const u8 *identity, *password; size_t identity_len, password_len; password = eap_get_config_password(sm, &password_len); if (!password || password_len != 16) { wpa_printf(MSG_INFO, "EAP-PSK: 16-octet pre-shared key not " "configured"); return NULL; } data = os_zalloc(sizeof(*data)); if (data == NULL) return NULL; if (eap_psk_key_setup(password, data->ak, data->kdk)) { os_free(data); return NULL; } wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: AK", data->ak, EAP_PSK_AK_LEN); wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: KDK", data->kdk, EAP_PSK_KDK_LEN); data->state = PSK_INIT; identity = eap_get_config_identity(sm, &identity_len); if (identity) { data->id_p = os_malloc(identity_len); if (data->id_p) os_memcpy(data->id_p, identity, identity_len); data->id_p_len = identity_len; } if (data->id_p == NULL) { wpa_printf(MSG_INFO, "EAP-PSK: could not get own identity"); os_free(data); return NULL; } return data; } static void eap_psk_deinit(struct eap_sm *sm, void *priv) { struct eap_psk_data *data = priv; os_free(data->id_s); os_free(data->id_p); os_free(data); } static struct wpabuf * eap_psk_process_1(struct eap_psk_data *data, struct eap_method_ret *ret, const struct wpabuf *reqData) { const struct eap_psk_hdr_1 *hdr1; struct eap_psk_hdr_2 *hdr2; struct wpabuf *resp; u8 *buf, *pos; size_t buflen, len; const u8 *cpos; wpa_printf(MSG_DEBUG, "EAP-PSK: in INIT state"); cpos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PSK, reqData, &len); hdr1 = (const struct eap_psk_hdr_1 *) cpos; if (cpos == NULL || len < sizeof(*hdr1)) { wpa_printf(MSG_INFO, "EAP-PSK: Invalid first message " "length (%lu; expected %lu or more)", (unsigned long) len, (unsigned long) sizeof(*hdr1)); ret->ignore = TRUE; return NULL; } wpa_printf(MSG_DEBUG, "EAP-PSK: Flags=0x%x", hdr1->flags); if (EAP_PSK_FLAGS_GET_T(hdr1->flags) != 0) { wpa_printf(MSG_INFO, "EAP-PSK: Unexpected T=%d (expected 0)", EAP_PSK_FLAGS_GET_T(hdr1->flags)); ret->methodState = METHOD_DONE; ret->decision = DECISION_FAIL; return NULL; } wpa_hexdump(MSG_DEBUG, "EAP-PSK: RAND_S", hdr1->rand_s, EAP_PSK_RAND_LEN); os_memcpy(data->rand_s, hdr1->rand_s, EAP_PSK_RAND_LEN); os_free(data->id_s); data->id_s_len = len - sizeof(*hdr1); data->id_s = os_malloc(data->id_s_len); if (data->id_s == NULL) { wpa_printf(MSG_ERROR, "EAP-PSK: Failed to allocate memory for " "ID_S (len=%lu)", (unsigned long) data->id_s_len); ret->ignore = TRUE; return NULL; } os_memcpy(data->id_s, (u8 *) (hdr1 + 1), data->id_s_len); wpa_hexdump_ascii(MSG_DEBUG, "EAP-PSK: ID_S", data->id_s, data->id_s_len); if (random_get_bytes(data->rand_p, EAP_PSK_RAND_LEN)) { wpa_printf(MSG_ERROR, "EAP-PSK: Failed to get random data"); ret->ignore = TRUE; return NULL; } resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PSK, sizeof(*hdr2) + data->id_p_len, EAP_CODE_RESPONSE, eap_get_id(reqData)); if (resp == NULL) return NULL; hdr2 = wpabuf_put(resp, sizeof(*hdr2)); hdr2->flags = EAP_PSK_FLAGS_SET_T(1); /* T=1 */ os_memcpy(hdr2->rand_s, hdr1->rand_s, EAP_PSK_RAND_LEN); os_memcpy(hdr2->rand_p, data->rand_p, EAP_PSK_RAND_LEN); wpabuf_put_data(resp, data->id_p, data->id_p_len); /* MAC_P = OMAC1-AES-128(AK, ID_P||ID_S||RAND_S||RAND_P) */ buflen = data->id_p_len + data->id_s_len + 2 * EAP_PSK_RAND_LEN; buf = os_malloc(buflen); if (buf == NULL) { wpabuf_free(resp); return NULL; } os_memcpy(buf, data->id_p, data->id_p_len); pos = buf + data->id_p_len; os_memcpy(pos, data->id_s, data->id_s_len); pos += data->id_s_len; os_memcpy(pos, hdr1->rand_s, EAP_PSK_RAND_LEN); pos += EAP_PSK_RAND_LEN; os_memcpy(pos, data->rand_p, EAP_PSK_RAND_LEN); if (omac1_aes_128(data->ak, buf, buflen, hdr2->mac_p)) { os_free(buf); wpabuf_free(resp); return NULL; } os_free(buf); wpa_hexdump(MSG_DEBUG, "EAP-PSK: RAND_P", hdr2->rand_p, EAP_PSK_RAND_LEN); wpa_hexdump(MSG_DEBUG, "EAP-PSK: MAC_P", hdr2->mac_p, EAP_PSK_MAC_LEN); wpa_hexdump_ascii(MSG_DEBUG, "EAP-PSK: ID_P", data->id_p, data->id_p_len); data->state = PSK_MAC_SENT; return resp; } static struct wpabuf * eap_psk_process_3(struct eap_psk_data *data, struct eap_method_ret *ret, const struct wpabuf *reqData) { const struct eap_psk_hdr_3 *hdr3; struct eap_psk_hdr_4 *hdr4; struct wpabuf *resp; u8 *buf, *rpchannel, nonce[16], *decrypted; const u8 *pchannel, *tag, *msg; u8 mac[EAP_PSK_MAC_LEN]; size_t buflen, left, data_len, len, plen; int failed = 0; const u8 *pos; wpa_printf(MSG_DEBUG, "EAP-PSK: in MAC_SENT state"); pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PSK, reqData, &len); hdr3 = (const struct eap_psk_hdr_3 *) pos; if (pos == NULL || len < sizeof(*hdr3)) { wpa_printf(MSG_INFO, "EAP-PSK: Invalid third message " "length (%lu; expected %lu or more)", (unsigned long) len, (unsigned long) sizeof(*hdr3)); ret->ignore = TRUE; return NULL; } left = len - sizeof(*hdr3); pchannel = (const u8 *) (hdr3 + 1); wpa_printf(MSG_DEBUG, "EAP-PSK: Flags=0x%x", hdr3->flags); if (EAP_PSK_FLAGS_GET_T(hdr3->flags) != 2) { wpa_printf(MSG_INFO, "EAP-PSK: Unexpected T=%d (expected 2)", EAP_PSK_FLAGS_GET_T(hdr3->flags)); ret->methodState = METHOD_DONE; ret->decision = DECISION_FAIL; return NULL; } wpa_hexdump(MSG_DEBUG, "EAP-PSK: RAND_S", hdr3->rand_s, EAP_PSK_RAND_LEN); wpa_hexdump(MSG_DEBUG, "EAP-PSK: MAC_S", hdr3->mac_s, EAP_PSK_MAC_LEN); wpa_hexdump(MSG_DEBUG, "EAP-PSK: PCHANNEL", pchannel, left); if (left < 4 + 16 + 1) { wpa_printf(MSG_INFO, "EAP-PSK: Too short PCHANNEL data in " "third message (len=%lu, expected 21)", (unsigned long) left); ret->ignore = TRUE; return NULL; } /* MAC_S = OMAC1-AES-128(AK, ID_S||RAND_P) */ buflen = data->id_s_len + EAP_PSK_RAND_LEN; buf = os_malloc(buflen); if (buf == NULL) return NULL; os_memcpy(buf, data->id_s, data->id_s_len); os_memcpy(buf + data->id_s_len, data->rand_p, EAP_PSK_RAND_LEN); if (omac1_aes_128(data->ak, buf, buflen, mac)) { os_free(buf); return NULL; } os_free(buf); if (os_memcmp(mac, hdr3->mac_s, EAP_PSK_MAC_LEN) != 0) { wpa_printf(MSG_WARNING, "EAP-PSK: Invalid MAC_S in third " "message"); ret->methodState = METHOD_DONE; ret->decision = DECISION_FAIL; return NULL; } wpa_printf(MSG_DEBUG, "EAP-PSK: MAC_S verified successfully"); if (eap_psk_derive_keys(data->kdk, data->rand_p, data->tek, data->msk, data->emsk)) { ret->methodState = METHOD_DONE; ret->decision = DECISION_FAIL; return NULL; } wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: TEK", data->tek, EAP_PSK_TEK_LEN); wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: MSK", data->msk, EAP_MSK_LEN); wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: EMSK", data->emsk, EAP_EMSK_LEN); os_memset(nonce, 0, 12); os_memcpy(nonce + 12, pchannel, 4); pchannel += 4; left -= 4; tag = pchannel; pchannel += 16; left -= 16; msg = pchannel; wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: PCHANNEL - nonce", nonce, sizeof(nonce)); wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: PCHANNEL - hdr", wpabuf_head(reqData), 5); wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: PCHANNEL - cipher msg", msg, left); decrypted = os_malloc(left); if (decrypted == NULL) { ret->methodState = METHOD_DONE; ret->decision = DECISION_FAIL; return NULL; } os_memcpy(decrypted, msg, left); if (aes_128_eax_decrypt(data->tek, nonce, sizeof(nonce), wpabuf_head(reqData), sizeof(struct eap_hdr) + 1 + sizeof(*hdr3) - EAP_PSK_MAC_LEN, decrypted, left, tag)) { wpa_printf(MSG_WARNING, "EAP-PSK: PCHANNEL decryption failed"); os_free(decrypted); return NULL; } wpa_hexdump(MSG_DEBUG, "EAP-PSK: Decrypted PCHANNEL message", decrypted, left); /* Verify R flag */ switch (decrypted[0] >> 6) { case EAP_PSK_R_FLAG_CONT: wpa_printf(MSG_DEBUG, "EAP-PSK: R flag - CONT - unsupported"); failed = 1; break; case EAP_PSK_R_FLAG_DONE_SUCCESS: wpa_printf(MSG_DEBUG, "EAP-PSK: R flag - DONE_SUCCESS"); break; case EAP_PSK_R_FLAG_DONE_FAILURE: wpa_printf(MSG_DEBUG, "EAP-PSK: R flag - DONE_FAILURE"); wpa_printf(MSG_INFO, "EAP-PSK: Authentication server rejected " "authentication"); failed = 1; break; } data_len = 1; if ((decrypted[0] & EAP_PSK_E_FLAG) && left > 1) data_len++; plen = sizeof(*hdr4) + 4 + 16 + data_len; resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PSK, plen, EAP_CODE_RESPONSE, eap_get_id(reqData)); if (resp == NULL) { os_free(decrypted); return NULL; } hdr4 = wpabuf_put(resp, sizeof(*hdr4)); hdr4->flags = EAP_PSK_FLAGS_SET_T(3); /* T=3 */ os_memcpy(hdr4->rand_s, hdr3->rand_s, EAP_PSK_RAND_LEN); rpchannel = wpabuf_put(resp, 4 + 16 + data_len); /* nonce++ */ inc_byte_array(nonce, sizeof(nonce)); os_memcpy(rpchannel, nonce + 12, 4); if (decrypted[0] & EAP_PSK_E_FLAG) { wpa_printf(MSG_DEBUG, "EAP-PSK: Unsupported E (Ext) flag"); failed = 1; rpchannel[4 + 16] = (EAP_PSK_R_FLAG_DONE_FAILURE << 6) | EAP_PSK_E_FLAG; if (left > 1) { /* Add empty EXT_Payload with same EXT_Type */ rpchannel[4 + 16 + 1] = decrypted[1]; } } else if (failed) rpchannel[4 + 16] = EAP_PSK_R_FLAG_DONE_FAILURE << 6; else rpchannel[4 + 16] = EAP_PSK_R_FLAG_DONE_SUCCESS << 6; wpa_hexdump(MSG_DEBUG, "EAP-PSK: reply message (plaintext)", rpchannel + 4 + 16, data_len); if (aes_128_eax_encrypt(data->tek, nonce, sizeof(nonce), wpabuf_head(resp), sizeof(struct eap_hdr) + 1 + sizeof(*hdr4), rpchannel + 4 + 16, data_len, rpchannel + 4)) { os_free(decrypted); wpabuf_free(resp); return NULL; } wpa_hexdump(MSG_DEBUG, "EAP-PSK: reply message (PCHANNEL)", rpchannel, 4 + 16 + data_len); wpa_printf(MSG_DEBUG, "EAP-PSK: Completed %ssuccessfully", failed ? "un" : ""); data->state = PSK_DONE; ret->methodState = METHOD_DONE; ret->decision = failed ? DECISION_FAIL : DECISION_UNCOND_SUCC; os_free(decrypted); return resp; } static struct wpabuf * eap_psk_process(struct eap_sm *sm, void *priv, struct eap_method_ret *ret, const struct wpabuf *reqData) { struct eap_psk_data *data = priv; const u8 *pos; struct wpabuf *resp = NULL; size_t len; pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PSK, reqData, &len); if (pos == NULL) { ret->ignore = TRUE; return NULL; } ret->ignore = FALSE; ret->methodState = METHOD_MAY_CONT; ret->decision = DECISION_FAIL; ret->allowNotifications = TRUE; switch (data->state) { case PSK_INIT: resp = eap_psk_process_1(data, ret, reqData); break; case PSK_MAC_SENT: resp = eap_psk_process_3(data, ret, reqData); break; case PSK_DONE: wpa_printf(MSG_DEBUG, "EAP-PSK: in DONE state - ignore " "unexpected message"); ret->ignore = TRUE; return NULL; } if (ret->methodState == METHOD_DONE) { ret->allowNotifications = FALSE; } return resp; } static Boolean eap_psk_isKeyAvailable(struct eap_sm *sm, void *priv) { struct eap_psk_data *data = priv; return data->state == PSK_DONE; } static u8 * eap_psk_getKey(struct eap_sm *sm, void *priv, size_t *len) { struct eap_psk_data *data = priv; u8 *key; if (data->state != PSK_DONE) return NULL; key = os_malloc(EAP_MSK_LEN); if (key == NULL) return NULL; *len = EAP_MSK_LEN; os_memcpy(key, data->msk, EAP_MSK_LEN); return key; } static u8 * eap_psk_get_session_id(struct eap_sm *sm, void *priv, size_t *len) { struct eap_psk_data *data = priv; u8 *id; if (data->state != PSK_DONE) return NULL; *len = 1 + 2 * EAP_PSK_RAND_LEN; id = os_malloc(*len); if (id == NULL) return NULL; id[0] = EAP_TYPE_PSK; os_memcpy(id + 1, data->rand_p, EAP_PSK_RAND_LEN); os_memcpy(id + 1 + EAP_PSK_RAND_LEN, data->rand_s, EAP_PSK_RAND_LEN); wpa_hexdump(MSG_DEBUG, "EAP-PSK: Derived Session-Id", id, *len); return id; } static u8 * eap_psk_get_emsk(struct eap_sm *sm, void *priv, size_t *len) { struct eap_psk_data *data = priv; u8 *key; if (data->state != PSK_DONE) return NULL; key = os_malloc(EAP_EMSK_LEN); if (key == NULL) return NULL; *len = EAP_EMSK_LEN; os_memcpy(key, data->emsk, EAP_EMSK_LEN); return key; } int eap_peer_psk_register(void) { struct eap_method *eap; int ret; eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_PSK, "PSK"); if (eap == NULL) return -1; eap->init = eap_psk_init; eap->deinit = eap_psk_deinit; eap->process = eap_psk_process; eap->isKeyAvailable = eap_psk_isKeyAvailable; eap->getKey = eap_psk_getKey; eap->getSessionId = eap_psk_get_session_id; eap->get_emsk = eap_psk_get_emsk; ret = eap_peer_method_register(eap); if (ret) eap_peer_method_free(eap); return ret; } wpa-2.1/src/eap_peer/eap_pwd.c000066400000000000000000000623361227414666700163260ustar00rootroot00000000000000/* * EAP peer method: EAP-pwd (RFC 5931) * Copyright (c) 2010, Dan Harkins * * 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/sha256.h" #include "eap_peer/eap_i.h" #include "eap_common/eap_pwd_common.h" struct eap_pwd_data { enum { PWD_ID_Req, PWD_Commit_Req, PWD_Confirm_Req, SUCCESS, FAILURE } state; u8 *id_peer; size_t id_peer_len; u8 *id_server; size_t id_server_len; u8 *password; size_t password_len; u16 group_num; EAP_PWD_group *grp; struct wpabuf *inbuf; size_t in_frag_pos; struct wpabuf *outbuf; size_t out_frag_pos; size_t mtu; BIGNUM *k; BIGNUM *private_value; BIGNUM *server_scalar; BIGNUM *my_scalar; EC_POINT *my_element; EC_POINT *server_element; u8 msk[EAP_MSK_LEN]; u8 emsk[EAP_EMSK_LEN]; BN_CTX *bnctx; }; #ifndef CONFIG_NO_STDOUT_DEBUG static const char * eap_pwd_state_txt(int state) { switch (state) { case PWD_ID_Req: return "PWD-ID-Req"; case PWD_Commit_Req: return "PWD-Commit-Req"; case PWD_Confirm_Req: return "PWD-Confirm-Req"; case SUCCESS: return "SUCCESS"; case FAILURE: return "FAILURE"; default: return "PWD-UNK"; } } #endif /* CONFIG_NO_STDOUT_DEBUG */ static void eap_pwd_state(struct eap_pwd_data *data, int state) { wpa_printf(MSG_DEBUG, "EAP-PWD: %s -> %s", eap_pwd_state_txt(data->state), eap_pwd_state_txt(state)); data->state = state; } static void * eap_pwd_init(struct eap_sm *sm) { struct eap_pwd_data *data; const u8 *identity, *password; size_t identity_len, password_len; int fragment_size; password = eap_get_config_password(sm, &password_len); if (password == NULL) { wpa_printf(MSG_INFO, "EAP-PWD: No password configured!"); return NULL; } identity = eap_get_config_identity(sm, &identity_len); if (identity == NULL) { wpa_printf(MSG_INFO, "EAP-PWD: No identity configured!"); return NULL; } if ((data = os_zalloc(sizeof(*data))) == NULL) { wpa_printf(MSG_INFO, "EAP-PWD: memory allocation data fail"); return NULL; } if ((data->bnctx = BN_CTX_new()) == NULL) { wpa_printf(MSG_INFO, "EAP-PWD: bn context allocation fail"); os_free(data); return NULL; } if ((data->id_peer = os_malloc(identity_len)) == NULL) { wpa_printf(MSG_INFO, "EAP-PWD: memory allocation id fail"); BN_CTX_free(data->bnctx); os_free(data); return NULL; } os_memcpy(data->id_peer, identity, identity_len); data->id_peer_len = identity_len; if ((data->password = os_malloc(password_len)) == NULL) { wpa_printf(MSG_INFO, "EAP-PWD: memory allocation psk fail"); BN_CTX_free(data->bnctx); os_free(data->id_peer); os_free(data); return NULL; } os_memcpy(data->password, password, password_len); data->password_len = password_len; data->out_frag_pos = data->in_frag_pos = 0; data->inbuf = data->outbuf = NULL; fragment_size = eap_get_config_fragment_size(sm); if (fragment_size <= 0) data->mtu = 1020; /* default from RFC 5931 */ else data->mtu = fragment_size; data->state = PWD_ID_Req; return data; } static void eap_pwd_deinit(struct eap_sm *sm, void *priv) { struct eap_pwd_data *data = priv; BN_free(data->private_value); BN_free(data->server_scalar); BN_free(data->my_scalar); BN_free(data->k); BN_CTX_free(data->bnctx); EC_POINT_free(data->my_element); EC_POINT_free(data->server_element); os_free(data->id_peer); os_free(data->id_server); os_free(data->password); if (data->grp) { EC_GROUP_free(data->grp->group); EC_POINT_free(data->grp->pwe); BN_free(data->grp->order); BN_free(data->grp->prime); os_free(data->grp); } os_free(data); } static u8 * eap_pwd_getkey(struct eap_sm *sm, void *priv, size_t *len) { struct eap_pwd_data *data = priv; u8 *key; if (data->state != SUCCESS) return NULL; key = os_malloc(EAP_MSK_LEN); if (key == NULL) return NULL; os_memcpy(key, data->msk, EAP_MSK_LEN); *len = EAP_MSK_LEN; return key; } static void eap_pwd_perform_id_exchange(struct eap_sm *sm, struct eap_pwd_data *data, struct eap_method_ret *ret, const struct wpabuf *reqData, const u8 *payload, size_t payload_len) { struct eap_pwd_id *id; if (data->state != PWD_ID_Req) { ret->ignore = TRUE; eap_pwd_state(data, FAILURE); return; } if (payload_len < sizeof(struct eap_pwd_id)) { ret->ignore = TRUE; eap_pwd_state(data, FAILURE); return; } id = (struct eap_pwd_id *) payload; data->group_num = be_to_host16(id->group_num); if ((id->random_function != EAP_PWD_DEFAULT_RAND_FUNC) || (id->prf != EAP_PWD_DEFAULT_PRF)) { ret->ignore = TRUE; eap_pwd_state(data, FAILURE); return; } wpa_printf(MSG_DEBUG, "EAP-PWD (peer): using group %d", data->group_num); data->id_server = os_malloc(payload_len - sizeof(struct eap_pwd_id)); if (data->id_server == NULL) { wpa_printf(MSG_INFO, "EAP-PWD: memory allocation id fail"); eap_pwd_state(data, FAILURE); return; } data->id_server_len = payload_len - sizeof(struct eap_pwd_id); os_memcpy(data->id_server, id->identity, data->id_server_len); wpa_hexdump_ascii(MSG_INFO, "EAP-PWD (peer): server sent id of", data->id_server, data->id_server_len); if ((data->grp = (EAP_PWD_group *) os_malloc(sizeof(EAP_PWD_group))) == NULL) { wpa_printf(MSG_INFO, "EAP-PWD: failed to allocate memory for " "group"); eap_pwd_state(data, FAILURE); return; } /* compute PWE */ if (compute_password_element(data->grp, data->group_num, data->password, data->password_len, data->id_server, data->id_server_len, data->id_peer, data->id_peer_len, id->token)) { wpa_printf(MSG_INFO, "EAP-PWD (peer): unable to compute PWE"); eap_pwd_state(data, FAILURE); return; } wpa_printf(MSG_DEBUG, "EAP-PWD (peer): computed %d bit PWE...", BN_num_bits(data->grp->prime)); data->outbuf = wpabuf_alloc(sizeof(struct eap_pwd_id) + data->id_peer_len); if (data->outbuf == NULL) { eap_pwd_state(data, FAILURE); return; } wpabuf_put_be16(data->outbuf, data->group_num); wpabuf_put_u8(data->outbuf, EAP_PWD_DEFAULT_RAND_FUNC); wpabuf_put_u8(data->outbuf, EAP_PWD_DEFAULT_PRF); wpabuf_put_data(data->outbuf, id->token, sizeof(id->token)); wpabuf_put_u8(data->outbuf, EAP_PWD_PREP_NONE); wpabuf_put_data(data->outbuf, data->id_peer, data->id_peer_len); eap_pwd_state(data, PWD_Commit_Req); } static void eap_pwd_perform_commit_exchange(struct eap_sm *sm, struct eap_pwd_data *data, struct eap_method_ret *ret, const struct wpabuf *reqData, const u8 *payload, size_t payload_len) { EC_POINT *K = NULL, *point = NULL; BIGNUM *mask = NULL, *x = NULL, *y = NULL, *cofactor = NULL; u16 offset; u8 *ptr, *scalar = NULL, *element = NULL; if (((data->private_value = BN_new()) == NULL) || ((data->my_element = EC_POINT_new(data->grp->group)) == NULL) || ((cofactor = BN_new()) == NULL) || ((data->my_scalar = BN_new()) == NULL) || ((mask = BN_new()) == NULL)) { wpa_printf(MSG_INFO, "EAP-PWD (peer): scalar allocation fail"); goto fin; } if (!EC_GROUP_get_cofactor(data->grp->group, cofactor, NULL)) { wpa_printf(MSG_INFO, "EAP-pwd (peer): unable to get cofactor " "for curve"); goto fin; } BN_rand_range(data->private_value, data->grp->order); BN_rand_range(mask, data->grp->order); BN_add(data->my_scalar, data->private_value, mask); BN_mod(data->my_scalar, data->my_scalar, data->grp->order, data->bnctx); if (!EC_POINT_mul(data->grp->group, data->my_element, NULL, data->grp->pwe, mask, data->bnctx)) { wpa_printf(MSG_INFO, "EAP-PWD (peer): element allocation " "fail"); eap_pwd_state(data, FAILURE); goto fin; } if (!EC_POINT_invert(data->grp->group, data->my_element, data->bnctx)) { wpa_printf(MSG_INFO, "EAP-PWD (peer): element inversion fail"); goto fin; } BN_free(mask); if (((x = BN_new()) == NULL) || ((y = BN_new()) == NULL)) { wpa_printf(MSG_INFO, "EAP-PWD (peer): point allocation fail"); goto fin; } /* process the request */ if (((data->server_scalar = BN_new()) == NULL) || ((data->k = BN_new()) == NULL) || ((K = EC_POINT_new(data->grp->group)) == NULL) || ((point = EC_POINT_new(data->grp->group)) == NULL) || ((data->server_element = EC_POINT_new(data->grp->group)) == NULL)) { wpa_printf(MSG_INFO, "EAP-PWD (peer): peer data allocation " "fail"); goto fin; } /* element, x then y, followed by scalar */ ptr = (u8 *) payload; BN_bin2bn(ptr, BN_num_bytes(data->grp->prime), x); ptr += BN_num_bytes(data->grp->prime); BN_bin2bn(ptr, BN_num_bytes(data->grp->prime), y); ptr += BN_num_bytes(data->grp->prime); BN_bin2bn(ptr, BN_num_bytes(data->grp->order), data->server_scalar); if (!EC_POINT_set_affine_coordinates_GFp(data->grp->group, data->server_element, x, y, data->bnctx)) { wpa_printf(MSG_INFO, "EAP-PWD (peer): setting peer element " "fail"); goto fin; } /* check to ensure server's element is not in a small sub-group */ if (BN_cmp(cofactor, BN_value_one())) { if (!EC_POINT_mul(data->grp->group, point, NULL, data->server_element, cofactor, NULL)) { wpa_printf(MSG_INFO, "EAP-PWD (peer): cannot multiply " "server element by order!\n"); goto fin; } if (EC_POINT_is_at_infinity(data->grp->group, point)) { wpa_printf(MSG_INFO, "EAP-PWD (peer): server element " "is at infinity!\n"); goto fin; } } /* compute the shared key, k */ if ((!EC_POINT_mul(data->grp->group, K, NULL, data->grp->pwe, data->server_scalar, data->bnctx)) || (!EC_POINT_add(data->grp->group, K, K, data->server_element, data->bnctx)) || (!EC_POINT_mul(data->grp->group, K, NULL, K, data->private_value, data->bnctx))) { wpa_printf(MSG_INFO, "EAP-PWD (peer): computing shared key " "fail"); goto fin; } /* ensure that the shared key isn't in a small sub-group */ if (BN_cmp(cofactor, BN_value_one())) { if (!EC_POINT_mul(data->grp->group, K, NULL, K, cofactor, NULL)) { wpa_printf(MSG_INFO, "EAP-PWD (peer): cannot multiply " "shared key point by order"); goto fin; } } /* * This check is strictly speaking just for the case above where * co-factor > 1 but it was suggested that even though this is probably * never going to happen it is a simple and safe check "just to be * sure" so let's be safe. */ if (EC_POINT_is_at_infinity(data->grp->group, K)) { wpa_printf(MSG_INFO, "EAP-PWD (peer): shared key point is at " "infinity!\n"); goto fin; } if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, K, data->k, NULL, data->bnctx)) { wpa_printf(MSG_INFO, "EAP-PWD (peer): unable to extract " "shared secret from point"); goto fin; } /* now do the response */ if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, data->my_element, x, y, data->bnctx)) { wpa_printf(MSG_INFO, "EAP-PWD (peer): point assignment fail"); goto fin; } if (((scalar = os_malloc(BN_num_bytes(data->grp->order))) == NULL) || ((element = os_malloc(BN_num_bytes(data->grp->prime) * 2)) == NULL)) { wpa_printf(MSG_INFO, "EAP-PWD (peer): data allocation fail"); goto fin; } /* * bignums occupy as little memory as possible so one that is * sufficiently smaller than the prime or order might need pre-pending * with zeros. */ os_memset(scalar, 0, BN_num_bytes(data->grp->order)); os_memset(element, 0, BN_num_bytes(data->grp->prime) * 2); offset = BN_num_bytes(data->grp->order) - BN_num_bytes(data->my_scalar); BN_bn2bin(data->my_scalar, scalar + offset); offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); BN_bn2bin(x, element + offset); offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y); BN_bn2bin(y, element + BN_num_bytes(data->grp->prime) + offset); data->outbuf = wpabuf_alloc(BN_num_bytes(data->grp->order) + 2 * BN_num_bytes(data->grp->prime)); if (data->outbuf == NULL) goto fin; /* we send the element as (x,y) follwed by the scalar */ wpabuf_put_data(data->outbuf, element, 2 * BN_num_bytes(data->grp->prime)); wpabuf_put_data(data->outbuf, scalar, BN_num_bytes(data->grp->order)); fin: os_free(scalar); os_free(element); BN_free(x); BN_free(y); BN_free(cofactor); EC_POINT_free(K); EC_POINT_free(point); if (data->outbuf == NULL) eap_pwd_state(data, FAILURE); else eap_pwd_state(data, PWD_Confirm_Req); } static void eap_pwd_perform_confirm_exchange(struct eap_sm *sm, struct eap_pwd_data *data, struct eap_method_ret *ret, const struct wpabuf *reqData, const u8 *payload, size_t payload_len) { BIGNUM *x = NULL, *y = NULL; struct crypto_hash *hash; u32 cs; u16 grp; u8 conf[SHA256_MAC_LEN], *cruft = NULL, *ptr; int offset; /* * first build up the ciphersuite which is group | random_function | * prf */ grp = htons(data->group_num); ptr = (u8 *) &cs; os_memcpy(ptr, &grp, sizeof(u16)); ptr += sizeof(u16); *ptr = EAP_PWD_DEFAULT_RAND_FUNC; ptr += sizeof(u8); *ptr = EAP_PWD_DEFAULT_PRF; /* each component of the cruft will be at most as big as the prime */ if (((cruft = os_malloc(BN_num_bytes(data->grp->prime))) == NULL) || ((x = BN_new()) == NULL) || ((y = BN_new()) == NULL)) { wpa_printf(MSG_INFO, "EAP-PWD (server): confirm allocation " "fail"); goto fin; } /* * server's commit is H(k | server_element | server_scalar | * peer_element | peer_scalar | ciphersuite) */ hash = eap_pwd_h_init(); if (hash == NULL) goto fin; /* * zero the memory each time because this is mod prime math and some * value may start with a few zeros and the previous one did not. */ os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(data->k); BN_bn2bin(data->k, cruft + offset); eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); /* server element: x, y */ if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, data->server_element, x, y, data->bnctx)) { wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point " "assignment fail"); goto fin; } os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); BN_bn2bin(x, cruft + offset); eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y); BN_bn2bin(y, cruft + offset); eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); /* server scalar */ os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->order) - BN_num_bytes(data->server_scalar); BN_bn2bin(data->server_scalar, cruft + offset); eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order)); /* my element: x, y */ if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, data->my_element, x, y, data->bnctx)) { wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point " "assignment fail"); goto fin; } os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); BN_bn2bin(x, cruft + offset); eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y); BN_bn2bin(y, cruft + offset); eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); /* my scalar */ os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->order) - BN_num_bytes(data->my_scalar); BN_bn2bin(data->my_scalar, cruft + offset); eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order)); /* the ciphersuite */ eap_pwd_h_update(hash, (u8 *) &cs, sizeof(u32)); /* random function fin */ eap_pwd_h_final(hash, conf); ptr = (u8 *) payload; if (os_memcmp(conf, ptr, SHA256_MAC_LEN)) { wpa_printf(MSG_INFO, "EAP-PWD (peer): confirm did not verify"); goto fin; } wpa_printf(MSG_DEBUG, "EAP-pwd (peer): confirm verified"); /* * compute confirm: * H(k | peer_element | peer_scalar | server_element | server_scalar | * ciphersuite) */ hash = eap_pwd_h_init(); if (hash == NULL) goto fin; /* k */ os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(data->k); BN_bn2bin(data->k, cruft + offset); eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); /* my element */ if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, data->my_element, x, y, data->bnctx)) { wpa_printf(MSG_INFO, "EAP-PWD (peer): confirm point " "assignment fail"); goto fin; } os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); BN_bn2bin(x, cruft + offset); eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y); BN_bn2bin(y, cruft + offset); eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); /* my scalar */ os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->order) - BN_num_bytes(data->my_scalar); BN_bn2bin(data->my_scalar, cruft + offset); eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order)); /* server element: x, y */ if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, data->server_element, x, y, data->bnctx)) { wpa_printf(MSG_INFO, "EAP-PWD (peer): confirm point " "assignment fail"); goto fin; } os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); BN_bn2bin(x, cruft + offset); eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y); BN_bn2bin(y, cruft + offset); eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); /* server scalar */ os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->order) - BN_num_bytes(data->server_scalar); BN_bn2bin(data->server_scalar, cruft + offset); eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order)); /* the ciphersuite */ eap_pwd_h_update(hash, (u8 *) &cs, sizeof(u32)); /* all done */ eap_pwd_h_final(hash, conf); if (compute_keys(data->grp, data->bnctx, data->k, data->my_scalar, data->server_scalar, conf, ptr, &cs, data->msk, data->emsk) < 0) { wpa_printf(MSG_INFO, "EAP-PWD (peer): unable to compute MSK | " "EMSK"); goto fin; } data->outbuf = wpabuf_alloc(SHA256_MAC_LEN); if (data->outbuf == NULL) goto fin; wpabuf_put_data(data->outbuf, conf, SHA256_MAC_LEN); fin: os_free(cruft); BN_free(x); BN_free(y); ret->methodState = METHOD_DONE; if (data->outbuf == NULL) { ret->decision = DECISION_FAIL; eap_pwd_state(data, FAILURE); } else { ret->decision = DECISION_UNCOND_SUCC; eap_pwd_state(data, SUCCESS); } } static struct wpabuf * eap_pwd_process(struct eap_sm *sm, void *priv, struct eap_method_ret *ret, const struct wpabuf *reqData) { struct eap_pwd_data *data = priv; struct wpabuf *resp = NULL; const u8 *pos, *buf; size_t len; u16 tot_len = 0; u8 lm_exch; pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PWD, reqData, &len); if ((pos == NULL) || (len < 1)) { wpa_printf(MSG_DEBUG, "EAP-pwd: Got a frame but pos is %s and " "len is %d", pos == NULL ? "NULL" : "not NULL", (int) len); ret->ignore = TRUE; return NULL; } ret->ignore = FALSE; ret->methodState = METHOD_MAY_CONT; ret->decision = DECISION_FAIL; ret->allowNotifications = FALSE; lm_exch = *pos; pos++; /* skip over the bits and the exch */ len--; /* * we're fragmenting so send out the next fragment */ if (data->out_frag_pos) { /* * this should be an ACK */ if (len) wpa_printf(MSG_INFO, "Bad Response! Fragmenting but " "not an ACK"); wpa_printf(MSG_DEBUG, "EAP-pwd: Got an ACK for a fragment"); /* * check if there are going to be more fragments */ len = wpabuf_len(data->outbuf) - data->out_frag_pos; if ((len + EAP_PWD_HDR_SIZE) > data->mtu) { len = data->mtu - EAP_PWD_HDR_SIZE; EAP_PWD_SET_MORE_BIT(lm_exch); } resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD, EAP_PWD_HDR_SIZE + len, EAP_CODE_RESPONSE, eap_get_id(reqData)); if (resp == NULL) { wpa_printf(MSG_INFO, "Unable to allocate memory for " "next fragment!"); return NULL; } wpabuf_put_u8(resp, lm_exch); buf = wpabuf_head_u8(data->outbuf); wpabuf_put_data(resp, buf + data->out_frag_pos, len); data->out_frag_pos += len; /* * this is the last fragment so get rid of the out buffer */ if (data->out_frag_pos >= wpabuf_len(data->outbuf)) { wpabuf_free(data->outbuf); data->outbuf = NULL; data->out_frag_pos = 0; } wpa_printf(MSG_DEBUG, "EAP-pwd: Send %s fragment of %d bytes", data->out_frag_pos == 0 ? "last" : "next", (int) len); return resp; } /* * see if this is a fragment that needs buffering * * if it's the first fragment there'll be a length field */ if (EAP_PWD_GET_LENGTH_BIT(lm_exch)) { tot_len = WPA_GET_BE16(pos); wpa_printf(MSG_DEBUG, "EAP-pwd: Incoming fragments whose " "total length = %d", tot_len); data->inbuf = wpabuf_alloc(tot_len); if (data->inbuf == NULL) { wpa_printf(MSG_INFO, "Out of memory to buffer " "fragments!"); return NULL; } pos += sizeof(u16); len -= sizeof(u16); } /* * buffer and ACK the fragment */ if (EAP_PWD_GET_MORE_BIT(lm_exch)) { data->in_frag_pos += len; if (data->in_frag_pos > wpabuf_size(data->inbuf)) { wpa_printf(MSG_INFO, "EAP-pwd: Buffer overflow attack " "detected (%d vs. %d)!", (int) data->in_frag_pos, (int) wpabuf_len(data->inbuf)); wpabuf_free(data->inbuf); data->in_frag_pos = 0; return NULL; } wpabuf_put_data(data->inbuf, pos, len); resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD, EAP_PWD_HDR_SIZE, EAP_CODE_RESPONSE, eap_get_id(reqData)); if (resp != NULL) wpabuf_put_u8(resp, (EAP_PWD_GET_EXCHANGE(lm_exch))); wpa_printf(MSG_DEBUG, "EAP-pwd: ACKing a %d byte fragment", (int) len); return resp; } /* * we're buffering and this is the last fragment */ if (data->in_frag_pos) { wpabuf_put_data(data->inbuf, pos, len); wpa_printf(MSG_DEBUG, "EAP-pwd: Last fragment, %d bytes", (int) len); data->in_frag_pos += len; pos = wpabuf_head_u8(data->inbuf); len = data->in_frag_pos; } wpa_printf(MSG_DEBUG, "EAP-pwd: processing frame: exch %d, len %d", EAP_PWD_GET_EXCHANGE(lm_exch), (int) len); switch (EAP_PWD_GET_EXCHANGE(lm_exch)) { case EAP_PWD_OPCODE_ID_EXCH: eap_pwd_perform_id_exchange(sm, data, ret, reqData, pos, len); break; case EAP_PWD_OPCODE_COMMIT_EXCH: eap_pwd_perform_commit_exchange(sm, data, ret, reqData, pos, len); break; case EAP_PWD_OPCODE_CONFIRM_EXCH: eap_pwd_perform_confirm_exchange(sm, data, ret, reqData, pos, len); break; default: wpa_printf(MSG_INFO, "EAP-pwd: Ignoring message with unknown " "opcode %d", lm_exch); break; } /* * if we buffered the just processed input now's the time to free it */ if (data->in_frag_pos) { wpabuf_free(data->inbuf); data->in_frag_pos = 0; } if (data->outbuf == NULL) return NULL; /* generic failure */ /* * we have output! Do we need to fragment it? */ len = wpabuf_len(data->outbuf); if ((len + EAP_PWD_HDR_SIZE) > data->mtu) { resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD, data->mtu, EAP_CODE_RESPONSE, eap_get_id(reqData)); /* * if so it's the first so include a length field */ EAP_PWD_SET_LENGTH_BIT(lm_exch); EAP_PWD_SET_MORE_BIT(lm_exch); tot_len = len; /* * keep the packet at the MTU */ len = data->mtu - EAP_PWD_HDR_SIZE - sizeof(u16); wpa_printf(MSG_DEBUG, "EAP-pwd: Fragmenting output, total " "length = %d", tot_len); } else { resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD, EAP_PWD_HDR_SIZE + len, EAP_CODE_RESPONSE, eap_get_id(reqData)); } if (resp == NULL) return NULL; wpabuf_put_u8(resp, lm_exch); if (EAP_PWD_GET_LENGTH_BIT(lm_exch)) { wpabuf_put_be16(resp, tot_len); data->out_frag_pos += len; } buf = wpabuf_head_u8(data->outbuf); wpabuf_put_data(resp, buf, len); /* * if we're not fragmenting then there's no need to carry this around */ if (data->out_frag_pos == 0) { wpabuf_free(data->outbuf); data->outbuf = NULL; data->out_frag_pos = 0; } return resp; } static Boolean eap_pwd_key_available(struct eap_sm *sm, void *priv) { struct eap_pwd_data *data = priv; return data->state == SUCCESS; } static u8 * eap_pwd_get_emsk(struct eap_sm *sm, void *priv, size_t *len) { struct eap_pwd_data *data = priv; u8 *key; if (data->state != SUCCESS) return NULL; if ((key = os_malloc(EAP_EMSK_LEN)) == NULL) return NULL; os_memcpy(key, data->emsk, EAP_EMSK_LEN); *len = EAP_EMSK_LEN; return key; } int eap_peer_pwd_register(void) { struct eap_method *eap; int ret; EVP_add_digest(EVP_sha256()); eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_PWD, "PWD"); if (eap == NULL) return -1; eap->init = eap_pwd_init; eap->deinit = eap_pwd_deinit; eap->process = eap_pwd_process; eap->isKeyAvailable = eap_pwd_key_available; eap->getKey = eap_pwd_getkey; eap->get_emsk = eap_pwd_get_emsk; ret = eap_peer_method_register(eap); if (ret) eap_peer_method_free(eap); return ret; } wpa-2.1/src/eap_peer/eap_sake.c000066400000000000000000000312111227414666700164430ustar00rootroot00000000000000/* * EAP peer method: EAP-SAKE (RFC 4763) * Copyright (c) 2006-2008, 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/random.h" #include "eap_peer/eap_i.h" #include "eap_common/eap_sake_common.h" struct eap_sake_data { enum { IDENTITY, CHALLENGE, CONFIRM, SUCCESS, FAILURE } state; u8 root_secret_a[EAP_SAKE_ROOT_SECRET_LEN]; u8 root_secret_b[EAP_SAKE_ROOT_SECRET_LEN]; u8 rand_s[EAP_SAKE_RAND_LEN]; u8 rand_p[EAP_SAKE_RAND_LEN]; struct { u8 auth[EAP_SAKE_TEK_AUTH_LEN]; u8 cipher[EAP_SAKE_TEK_CIPHER_LEN]; } tek; u8 msk[EAP_MSK_LEN]; u8 emsk[EAP_EMSK_LEN]; u8 session_id; int session_id_set; u8 *peerid; size_t peerid_len; u8 *serverid; size_t serverid_len; }; static const char * eap_sake_state_txt(int state) { switch (state) { case IDENTITY: return "IDENTITY"; case CHALLENGE: return "CHALLENGE"; case CONFIRM: return "CONFIRM"; case SUCCESS: return "SUCCESS"; case FAILURE: return "FAILURE"; default: return "?"; } } static void eap_sake_state(struct eap_sake_data *data, int state) { wpa_printf(MSG_DEBUG, "EAP-SAKE: %s -> %s", eap_sake_state_txt(data->state), eap_sake_state_txt(state)); data->state = state; } static void eap_sake_deinit(struct eap_sm *sm, void *priv); static void * eap_sake_init(struct eap_sm *sm) { struct eap_sake_data *data; const u8 *identity, *password; size_t identity_len, password_len; password = eap_get_config_password(sm, &password_len); if (!password || password_len != 2 * EAP_SAKE_ROOT_SECRET_LEN) { wpa_printf(MSG_INFO, "EAP-SAKE: No key of correct length " "configured"); return NULL; } data = os_zalloc(sizeof(*data)); if (data == NULL) return NULL; data->state = IDENTITY; identity = eap_get_config_identity(sm, &identity_len); if (identity) { data->peerid = os_malloc(identity_len); if (data->peerid == NULL) { eap_sake_deinit(sm, data); return NULL; } os_memcpy(data->peerid, identity, identity_len); data->peerid_len = identity_len; } os_memcpy(data->root_secret_a, password, EAP_SAKE_ROOT_SECRET_LEN); os_memcpy(data->root_secret_b, password + EAP_SAKE_ROOT_SECRET_LEN, EAP_SAKE_ROOT_SECRET_LEN); return data; } static void eap_sake_deinit(struct eap_sm *sm, void *priv) { struct eap_sake_data *data = priv; os_free(data->serverid); os_free(data->peerid); os_free(data); } static struct wpabuf * eap_sake_build_msg(struct eap_sake_data *data, int id, size_t length, u8 subtype) { struct eap_sake_hdr *sake; struct wpabuf *msg; size_t plen; plen = length + sizeof(struct eap_sake_hdr); msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_SAKE, plen, EAP_CODE_RESPONSE, id); if (msg == NULL) { wpa_printf(MSG_ERROR, "EAP-SAKE: Failed to allocate memory " "request"); return NULL; } sake = wpabuf_put(msg, sizeof(*sake)); sake->version = EAP_SAKE_VERSION; sake->session_id = data->session_id; sake->subtype = subtype; return msg; } static struct wpabuf * eap_sake_process_identity(struct eap_sm *sm, struct eap_sake_data *data, struct eap_method_ret *ret, const struct wpabuf *reqData, const u8 *payload, size_t payload_len) { struct eap_sake_parse_attr attr; struct wpabuf *resp; if (data->state != IDENTITY) { ret->ignore = TRUE; return NULL; } wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Request/Identity"); if (eap_sake_parse_attributes(payload, payload_len, &attr)) return NULL; if (!attr.perm_id_req && !attr.any_id_req) { wpa_printf(MSG_INFO, "EAP-SAKE: No AT_PERM_ID_REQ or " "AT_ANY_ID_REQ in Request/Identity"); return NULL; } wpa_printf(MSG_DEBUG, "EAP-SAKE: Sending Response/Identity"); resp = eap_sake_build_msg(data, eap_get_id(reqData), 2 + data->peerid_len, EAP_SAKE_SUBTYPE_IDENTITY); if (resp == NULL) return NULL; wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_PEERID"); eap_sake_add_attr(resp, EAP_SAKE_AT_PEERID, data->peerid, data->peerid_len); eap_sake_state(data, CHALLENGE); return resp; } static struct wpabuf * eap_sake_process_challenge(struct eap_sm *sm, struct eap_sake_data *data, struct eap_method_ret *ret, const struct wpabuf *reqData, const u8 *payload, size_t payload_len) { struct eap_sake_parse_attr attr; struct wpabuf *resp; u8 *rpos; size_t rlen; if (data->state != IDENTITY && data->state != CHALLENGE) { wpa_printf(MSG_DEBUG, "EAP-SAKE: Request/Challenge received " "in unexpected state (%d)", data->state); ret->ignore = TRUE; return NULL; } if (data->state == IDENTITY) eap_sake_state(data, CHALLENGE); wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Request/Challenge"); if (eap_sake_parse_attributes(payload, payload_len, &attr)) return NULL; if (!attr.rand_s) { wpa_printf(MSG_INFO, "EAP-SAKE: Request/Challenge did not " "include AT_RAND_S"); return NULL; } os_memcpy(data->rand_s, attr.rand_s, EAP_SAKE_RAND_LEN); wpa_hexdump(MSG_MSGDUMP, "EAP-SAKE: RAND_S (server rand)", data->rand_s, EAP_SAKE_RAND_LEN); if (random_get_bytes(data->rand_p, EAP_SAKE_RAND_LEN)) { wpa_printf(MSG_ERROR, "EAP-SAKE: Failed to get random data"); return NULL; } wpa_hexdump(MSG_MSGDUMP, "EAP-SAKE: RAND_P (peer rand)", data->rand_p, EAP_SAKE_RAND_LEN); os_free(data->serverid); data->serverid = NULL; data->serverid_len = 0; if (attr.serverid) { wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-SAKE: SERVERID", attr.serverid, attr.serverid_len); data->serverid = os_malloc(attr.serverid_len); if (data->serverid == NULL) return NULL; os_memcpy(data->serverid, attr.serverid, attr.serverid_len); data->serverid_len = attr.serverid_len; } eap_sake_derive_keys(data->root_secret_a, data->root_secret_b, data->rand_s, data->rand_p, (u8 *) &data->tek, data->msk, data->emsk); wpa_printf(MSG_DEBUG, "EAP-SAKE: Sending Response/Challenge"); rlen = 2 + EAP_SAKE_RAND_LEN + 2 + EAP_SAKE_MIC_LEN; if (data->peerid) rlen += 2 + data->peerid_len; resp = eap_sake_build_msg(data, eap_get_id(reqData), rlen, EAP_SAKE_SUBTYPE_CHALLENGE); if (resp == NULL) return NULL; wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_RAND_P"); eap_sake_add_attr(resp, EAP_SAKE_AT_RAND_P, data->rand_p, EAP_SAKE_RAND_LEN); if (data->peerid) { wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_PEERID"); eap_sake_add_attr(resp, EAP_SAKE_AT_PEERID, data->peerid, data->peerid_len); } wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_MIC_P"); wpabuf_put_u8(resp, EAP_SAKE_AT_MIC_P); wpabuf_put_u8(resp, 2 + EAP_SAKE_MIC_LEN); rpos = wpabuf_put(resp, EAP_SAKE_MIC_LEN); if (eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p, data->serverid, data->serverid_len, data->peerid, data->peerid_len, 1, wpabuf_head(resp), wpabuf_len(resp), rpos, rpos)) { wpa_printf(MSG_INFO, "EAP-SAKE: Failed to compute MIC"); wpabuf_free(resp); return NULL; } eap_sake_state(data, CONFIRM); return resp; } static struct wpabuf * eap_sake_process_confirm(struct eap_sm *sm, struct eap_sake_data *data, struct eap_method_ret *ret, const struct wpabuf *reqData, const u8 *payload, size_t payload_len) { struct eap_sake_parse_attr attr; u8 mic_s[EAP_SAKE_MIC_LEN]; struct wpabuf *resp; u8 *rpos; if (data->state != CONFIRM) { ret->ignore = TRUE; return NULL; } wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Request/Confirm"); if (eap_sake_parse_attributes(payload, payload_len, &attr)) return NULL; if (!attr.mic_s) { wpa_printf(MSG_INFO, "EAP-SAKE: Request/Confirm did not " "include AT_MIC_S"); return NULL; } eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p, data->serverid, data->serverid_len, data->peerid, data->peerid_len, 0, wpabuf_head(reqData), wpabuf_len(reqData), attr.mic_s, mic_s); if (os_memcmp(attr.mic_s, mic_s, EAP_SAKE_MIC_LEN) != 0) { wpa_printf(MSG_INFO, "EAP-SAKE: Incorrect AT_MIC_S"); eap_sake_state(data, FAILURE); ret->methodState = METHOD_DONE; ret->decision = DECISION_FAIL; ret->allowNotifications = FALSE; wpa_printf(MSG_DEBUG, "EAP-SAKE: Sending " "Response/Auth-Reject"); return eap_sake_build_msg(data, eap_get_id(reqData), 0, EAP_SAKE_SUBTYPE_AUTH_REJECT); } wpa_printf(MSG_DEBUG, "EAP-SAKE: Sending Response/Confirm"); resp = eap_sake_build_msg(data, eap_get_id(reqData), 2 + EAP_SAKE_MIC_LEN, EAP_SAKE_SUBTYPE_CONFIRM); if (resp == NULL) return NULL; wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_MIC_P"); wpabuf_put_u8(resp, EAP_SAKE_AT_MIC_P); wpabuf_put_u8(resp, 2 + EAP_SAKE_MIC_LEN); rpos = wpabuf_put(resp, EAP_SAKE_MIC_LEN); if (eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p, data->serverid, data->serverid_len, data->peerid, data->peerid_len, 1, wpabuf_head(resp), wpabuf_len(resp), rpos, rpos)) { wpa_printf(MSG_INFO, "EAP-SAKE: Failed to compute MIC"); wpabuf_free(resp); return NULL; } eap_sake_state(data, SUCCESS); ret->methodState = METHOD_DONE; ret->decision = DECISION_UNCOND_SUCC; ret->allowNotifications = FALSE; return resp; } static struct wpabuf * eap_sake_process(struct eap_sm *sm, void *priv, struct eap_method_ret *ret, const struct wpabuf *reqData) { struct eap_sake_data *data = priv; const struct eap_sake_hdr *req; struct wpabuf *resp; const u8 *pos, *end; size_t len; u8 subtype, session_id; pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SAKE, reqData, &len); if (pos == NULL || len < sizeof(struct eap_sake_hdr)) { ret->ignore = TRUE; return NULL; } req = (const struct eap_sake_hdr *) pos; end = pos + len; subtype = req->subtype; session_id = req->session_id; pos = (const u8 *) (req + 1); wpa_printf(MSG_DEBUG, "EAP-SAKE: Received frame: subtype %d " "session_id %d", subtype, session_id); wpa_hexdump(MSG_DEBUG, "EAP-SAKE: Received attributes", pos, end - pos); if (data->session_id_set && data->session_id != session_id) { wpa_printf(MSG_INFO, "EAP-SAKE: Session ID mismatch (%d,%d)", session_id, data->session_id); ret->ignore = TRUE; return NULL; } data->session_id = session_id; data->session_id_set = 1; ret->ignore = FALSE; ret->methodState = METHOD_MAY_CONT; ret->decision = DECISION_FAIL; ret->allowNotifications = TRUE; switch (subtype) { case EAP_SAKE_SUBTYPE_IDENTITY: resp = eap_sake_process_identity(sm, data, ret, reqData, pos, end - pos); break; case EAP_SAKE_SUBTYPE_CHALLENGE: resp = eap_sake_process_challenge(sm, data, ret, reqData, pos, end - pos); break; case EAP_SAKE_SUBTYPE_CONFIRM: resp = eap_sake_process_confirm(sm, data, ret, reqData, pos, end - pos); break; default: wpa_printf(MSG_DEBUG, "EAP-SAKE: Ignoring message with " "unknown subtype %d", subtype); ret->ignore = TRUE; return NULL; } if (ret->methodState == METHOD_DONE) ret->allowNotifications = FALSE; return resp; } static Boolean eap_sake_isKeyAvailable(struct eap_sm *sm, void *priv) { struct eap_sake_data *data = priv; return data->state == SUCCESS; } static u8 * eap_sake_getKey(struct eap_sm *sm, void *priv, size_t *len) { struct eap_sake_data *data = priv; u8 *key; if (data->state != SUCCESS) return NULL; key = os_malloc(EAP_MSK_LEN); if (key == NULL) return NULL; os_memcpy(key, data->msk, EAP_MSK_LEN); *len = EAP_MSK_LEN; return key; } static u8 * eap_sake_get_session_id(struct eap_sm *sm, void *priv, size_t *len) { struct eap_sake_data *data = priv; u8 *id; if (data->state != SUCCESS) return NULL; *len = 1 + 2 * EAP_SAKE_RAND_LEN; id = os_malloc(*len); if (id == NULL) return NULL; id[0] = EAP_TYPE_SAKE; os_memcpy(id + 1, data->rand_s, EAP_SAKE_RAND_LEN); os_memcpy(id + 1 + EAP_SAKE_RAND_LEN, data->rand_s, EAP_SAKE_RAND_LEN); wpa_hexdump(MSG_DEBUG, "EAP-SAKE: Derived Session-Id", id, *len); return id; } static u8 * eap_sake_get_emsk(struct eap_sm *sm, void *priv, size_t *len) { struct eap_sake_data *data = priv; u8 *key; if (data->state != SUCCESS) return NULL; key = os_malloc(EAP_EMSK_LEN); if (key == NULL) return NULL; os_memcpy(key, data->emsk, EAP_EMSK_LEN); *len = EAP_EMSK_LEN; return key; } int eap_peer_sake_register(void) { struct eap_method *eap; int ret; eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_SAKE, "SAKE"); if (eap == NULL) return -1; eap->init = eap_sake_init; eap->deinit = eap_sake_deinit; eap->process = eap_sake_process; eap->isKeyAvailable = eap_sake_isKeyAvailable; eap->getKey = eap_sake_getKey; eap->getSessionId = eap_sake_get_session_id; eap->get_emsk = eap_sake_get_emsk; ret = eap_peer_method_register(eap); if (ret) eap_peer_method_free(eap); return ret; } wpa-2.1/src/eap_peer/eap_sim.c000066400000000000000000001031301227414666700163100ustar00rootroot00000000000000/* * EAP peer method: EAP-SIM (RFC 4186) * Copyright (c) 2004-2012, 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 "pcsc_funcs.h" #include "crypto/milenage.h" #include "crypto/random.h" #include "eap_peer/eap_i.h" #include "eap_config.h" #include "eap_common/eap_sim_common.h" struct eap_sim_data { u8 *ver_list; size_t ver_list_len; int selected_version; size_t min_num_chal, num_chal; u8 kc[3][EAP_SIM_KC_LEN]; u8 sres[3][EAP_SIM_SRES_LEN]; u8 nonce_mt[EAP_SIM_NONCE_MT_LEN], nonce_s[EAP_SIM_NONCE_S_LEN]; u8 mk[EAP_SIM_MK_LEN]; u8 k_aut[EAP_SIM_K_AUT_LEN]; u8 k_encr[EAP_SIM_K_ENCR_LEN]; u8 msk[EAP_SIM_KEYING_DATA_LEN]; u8 emsk[EAP_EMSK_LEN]; u8 rand[3][GSM_RAND_LEN]; int num_id_req, num_notification; u8 *pseudonym; size_t pseudonym_len; u8 *reauth_id; size_t reauth_id_len; int reauth; unsigned int counter, counter_too_small; u8 *last_eap_identity; size_t last_eap_identity_len; enum { CONTINUE, RESULT_SUCCESS, RESULT_FAILURE, SUCCESS, FAILURE } state; int result_ind, use_result_ind; }; #ifndef CONFIG_NO_STDOUT_DEBUG static const char * eap_sim_state_txt(int state) { switch (state) { case CONTINUE: return "CONTINUE"; case RESULT_SUCCESS: return "RESULT_SUCCESS"; case RESULT_FAILURE: return "RESULT_FAILURE"; case SUCCESS: return "SUCCESS"; case FAILURE: return "FAILURE"; default: return "?"; } } #endif /* CONFIG_NO_STDOUT_DEBUG */ static void eap_sim_state(struct eap_sim_data *data, int state) { wpa_printf(MSG_DEBUG, "EAP-SIM: %s -> %s", eap_sim_state_txt(data->state), eap_sim_state_txt(state)); data->state = state; } static void * eap_sim_init(struct eap_sm *sm) { struct eap_sim_data *data; struct eap_peer_config *config = eap_get_config(sm); data = os_zalloc(sizeof(*data)); if (data == NULL) return NULL; if (random_get_bytes(data->nonce_mt, EAP_SIM_NONCE_MT_LEN)) { wpa_printf(MSG_WARNING, "EAP-SIM: Failed to get random data " "for NONCE_MT"); os_free(data); return NULL; } data->min_num_chal = 2; if (config && config->phase1) { char *pos = os_strstr(config->phase1, "sim_min_num_chal="); if (pos) { data->min_num_chal = atoi(pos + 17); if (data->min_num_chal < 2 || data->min_num_chal > 3) { wpa_printf(MSG_WARNING, "EAP-SIM: Invalid " "sim_min_num_chal configuration " "(%lu, expected 2 or 3)", (unsigned long) data->min_num_chal); os_free(data); return NULL; } wpa_printf(MSG_DEBUG, "EAP-SIM: Set minimum number of " "challenges to %lu", (unsigned long) data->min_num_chal); } data->result_ind = os_strstr(config->phase1, "result_ind=1") != NULL; } if (config && config->anonymous_identity) { data->pseudonym = os_malloc(config->anonymous_identity_len); if (data->pseudonym) { os_memcpy(data->pseudonym, config->anonymous_identity, config->anonymous_identity_len); data->pseudonym_len = config->anonymous_identity_len; } } eap_sim_state(data, CONTINUE); return data; } static void eap_sim_deinit(struct eap_sm *sm, void *priv) { struct eap_sim_data *data = priv; if (data) { os_free(data->ver_list); os_free(data->pseudonym); os_free(data->reauth_id); os_free(data->last_eap_identity); os_free(data); } } static int eap_sim_ext_sim_req(struct eap_sm *sm, struct eap_sim_data *data) { char req[200], *pos, *end; size_t i; wpa_printf(MSG_DEBUG, "EAP-SIM: Use external SIM processing"); pos = req; end = pos + sizeof(req); pos += os_snprintf(pos, end - pos, "GSM-AUTH"); for (i = 0; i < data->num_chal; i++) { pos += os_snprintf(pos, end - pos, ":"); pos += wpa_snprintf_hex(pos, end - pos, data->rand[i], GSM_RAND_LEN); } eap_sm_request_sim(sm, req); return 1; } static int eap_sim_ext_sim_result(struct eap_sm *sm, struct eap_sim_data *data, struct eap_peer_config *conf) { char *resp, *pos; size_t i; wpa_printf(MSG_DEBUG, "EAP-SIM: Use result from external SIM processing"); resp = conf->external_sim_resp; conf->external_sim_resp = NULL; if (os_strncmp(resp, "GSM-AUTH:", 9) != 0) { wpa_printf(MSG_DEBUG, "EAP-SIM: Unrecognized external SIM processing response"); os_free(resp); return -1; } pos = resp + 9; for (i = 0; i < data->num_chal; i++) { wpa_hexdump(MSG_DEBUG, "EAP-SIM: RAND", data->rand[i], GSM_RAND_LEN); if (hexstr2bin(pos, data->kc[i], EAP_SIM_KC_LEN) < 0) goto invalid; wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: Kc", data->kc[i], EAP_SIM_KC_LEN); pos += EAP_SIM_KC_LEN * 2; if (*pos != ':') goto invalid; pos++; if (hexstr2bin(pos, data->sres[i], EAP_SIM_SRES_LEN) < 0) goto invalid; wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: SRES", data->sres[i], EAP_SIM_SRES_LEN); pos += EAP_SIM_SRES_LEN * 2; if (i + 1 < data->num_chal) { if (*pos != ':') goto invalid; pos++; } } os_free(resp); return 0; invalid: wpa_printf(MSG_DEBUG, "EAP-SIM: Invalid external SIM processing GSM-AUTH response"); os_free(resp); return -1; } static int eap_sim_gsm_auth(struct eap_sm *sm, struct eap_sim_data *data) { struct eap_peer_config *conf; wpa_printf(MSG_DEBUG, "EAP-SIM: GSM authentication algorithm"); conf = eap_get_config(sm); if (conf == NULL) return -1; if (sm->external_sim) { if (conf->external_sim_resp) return eap_sim_ext_sim_result(sm, data, conf); else return eap_sim_ext_sim_req(sm, data); } if (conf->pcsc) { if (scard_gsm_auth(sm->scard_ctx, data->rand[0], data->sres[0], data->kc[0]) || scard_gsm_auth(sm->scard_ctx, data->rand[1], data->sres[1], data->kc[1]) || (data->num_chal > 2 && scard_gsm_auth(sm->scard_ctx, data->rand[2], data->sres[2], data->kc[2]))) { wpa_printf(MSG_DEBUG, "EAP-SIM: GSM SIM " "authentication could not be completed"); return -1; } return 0; } #ifdef CONFIG_SIM_SIMULATOR if (conf->password) { u8 opc[16], k[16]; const char *pos; size_t i; wpa_printf(MSG_DEBUG, "EAP-SIM: Use internal GSM-Milenage " "implementation for authentication"); if (conf->password_len < 65) { wpa_printf(MSG_DEBUG, "EAP-SIM: invalid GSM-Milenage " "password"); return -1; } pos = (const char *) conf->password; if (hexstr2bin(pos, k, 16)) return -1; pos += 32; if (*pos != ':') return -1; pos++; if (hexstr2bin(pos, opc, 16)) return -1; for (i = 0; i < data->num_chal; i++) { if (gsm_milenage(opc, k, data->rand[i], data->sres[i], data->kc[i])) { wpa_printf(MSG_DEBUG, "EAP-SIM: " "GSM-Milenage authentication " "could not be completed"); return -1; } wpa_hexdump(MSG_DEBUG, "EAP-SIM: RAND", data->rand[i], GSM_RAND_LEN); wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: SRES", data->sres[i], EAP_SIM_SRES_LEN); wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: Kc", data->kc[i], EAP_SIM_KC_LEN); } return 0; } #endif /* CONFIG_SIM_SIMULATOR */ #ifdef CONFIG_SIM_HARDCODED /* These hardcoded Kc and SRES values are used for testing. RAND to * KC/SREC mapping is very bogus as far as real authentication is * concerned, but it is quite useful for cases where the AS is rotating * the order of pre-configured values. */ { size_t i; wpa_printf(MSG_DEBUG, "EAP-SIM: Use hardcoded Kc and SRES " "values for testing"); for (i = 0; i < data->num_chal; i++) { if (data->rand[i][0] == 0xaa) { os_memcpy(data->kc[i], "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7", EAP_SIM_KC_LEN); os_memcpy(data->sres[i], "\xd1\xd2\xd3\xd4", EAP_SIM_SRES_LEN); } else if (data->rand[i][0] == 0xbb) { os_memcpy(data->kc[i], "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7", EAP_SIM_KC_LEN); os_memcpy(data->sres[i], "\xe1\xe2\xe3\xe4", EAP_SIM_SRES_LEN); } else { os_memcpy(data->kc[i], "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7", EAP_SIM_KC_LEN); os_memcpy(data->sres[i], "\xf1\xf2\xf3\xf4", EAP_SIM_SRES_LEN); } } } return 0; #else /* CONFIG_SIM_HARDCODED */ wpa_printf(MSG_DEBUG, "EAP-SIM: No GSM authentication algorithm " "enabled"); return -1; #endif /* CONFIG_SIM_HARDCODED */ } static int eap_sim_supported_ver(int version) { return version == EAP_SIM_VERSION; } #define CLEAR_PSEUDONYM 0x01 #define CLEAR_REAUTH_ID 0x02 #define CLEAR_EAP_ID 0x04 static void eap_sim_clear_identities(struct eap_sm *sm, struct eap_sim_data *data, int id) { if ((id & CLEAR_PSEUDONYM) && data->pseudonym) { wpa_printf(MSG_DEBUG, "EAP-SIM: forgetting old pseudonym"); os_free(data->pseudonym); data->pseudonym = NULL; data->pseudonym_len = 0; eap_set_anon_id(sm, NULL, 0); } if ((id & CLEAR_REAUTH_ID) && data->reauth_id) { wpa_printf(MSG_DEBUG, "EAP-SIM: forgetting old reauth_id"); os_free(data->reauth_id); data->reauth_id = NULL; data->reauth_id_len = 0; } if ((id & CLEAR_EAP_ID) && data->last_eap_identity) { wpa_printf(MSG_DEBUG, "EAP-SIM: forgetting old eap_id"); os_free(data->last_eap_identity); data->last_eap_identity = NULL; data->last_eap_identity_len = 0; } } static int eap_sim_learn_ids(struct eap_sm *sm, struct eap_sim_data *data, struct eap_sim_attrs *attr) { if (attr->next_pseudonym) { const u8 *identity = NULL; size_t identity_len = 0; const u8 *realm = NULL; size_t realm_len = 0; wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: (encr) AT_NEXT_PSEUDONYM", attr->next_pseudonym, attr->next_pseudonym_len); os_free(data->pseudonym); /* Look for the realm of the permanent identity */ identity = eap_get_config_identity(sm, &identity_len); if (identity) { for (realm = identity, realm_len = identity_len; realm_len > 0; realm_len--, realm++) { if (*realm == '@') break; } } data->pseudonym = os_malloc(attr->next_pseudonym_len + realm_len); if (data->pseudonym == NULL) { wpa_printf(MSG_INFO, "EAP-SIM: (encr) No memory for " "next pseudonym"); data->pseudonym_len = 0; return -1; } os_memcpy(data->pseudonym, attr->next_pseudonym, attr->next_pseudonym_len); if (realm_len) { os_memcpy(data->pseudonym + attr->next_pseudonym_len, realm, realm_len); } data->pseudonym_len = attr->next_pseudonym_len + realm_len; eap_set_anon_id(sm, data->pseudonym, data->pseudonym_len); } if (attr->next_reauth_id) { os_free(data->reauth_id); data->reauth_id = os_malloc(attr->next_reauth_id_len); if (data->reauth_id == NULL) { wpa_printf(MSG_INFO, "EAP-SIM: (encr) No memory for " "next reauth_id"); data->reauth_id_len = 0; return -1; } os_memcpy(data->reauth_id, attr->next_reauth_id, attr->next_reauth_id_len); data->reauth_id_len = attr->next_reauth_id_len; wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: (encr) AT_NEXT_REAUTH_ID", data->reauth_id, data->reauth_id_len); } return 0; } static struct wpabuf * eap_sim_client_error(struct eap_sim_data *data, u8 id, int err) { struct eap_sim_msg *msg; eap_sim_state(data, FAILURE); data->num_id_req = 0; data->num_notification = 0; wpa_printf(MSG_DEBUG, "EAP-SIM: Send Client-Error (error code %d)", err); msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, EAP_TYPE_SIM, EAP_SIM_SUBTYPE_CLIENT_ERROR); eap_sim_msg_add(msg, EAP_SIM_AT_CLIENT_ERROR_CODE, err, NULL, 0); return eap_sim_msg_finish(msg, NULL, NULL, 0); } static struct wpabuf * eap_sim_response_start(struct eap_sm *sm, struct eap_sim_data *data, u8 id, enum eap_sim_id_req id_req) { const u8 *identity = NULL; size_t identity_len = 0; struct eap_sim_msg *msg; data->reauth = 0; if (id_req == ANY_ID && data->reauth_id) { identity = data->reauth_id; identity_len = data->reauth_id_len; data->reauth = 1; } else if ((id_req == ANY_ID || id_req == FULLAUTH_ID) && data->pseudonym) { identity = data->pseudonym; identity_len = data->pseudonym_len; eap_sim_clear_identities(sm, data, CLEAR_REAUTH_ID); } else if (id_req != NO_ID_REQ) { identity = eap_get_config_identity(sm, &identity_len); if (identity) { eap_sim_clear_identities(sm, data, CLEAR_PSEUDONYM | CLEAR_REAUTH_ID); } } if (id_req != NO_ID_REQ) eap_sim_clear_identities(sm, data, CLEAR_EAP_ID); wpa_printf(MSG_DEBUG, "Generating EAP-SIM Start (id=%d)", id); msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, EAP_TYPE_SIM, EAP_SIM_SUBTYPE_START); if (!data->reauth) { wpa_hexdump(MSG_DEBUG, " AT_NONCE_MT", data->nonce_mt, EAP_SIM_NONCE_MT_LEN); eap_sim_msg_add(msg, EAP_SIM_AT_NONCE_MT, 0, data->nonce_mt, EAP_SIM_NONCE_MT_LEN); wpa_printf(MSG_DEBUG, " AT_SELECTED_VERSION %d", data->selected_version); eap_sim_msg_add(msg, EAP_SIM_AT_SELECTED_VERSION, data->selected_version, NULL, 0); } if (identity) { wpa_hexdump_ascii(MSG_DEBUG, " AT_IDENTITY", identity, identity_len); eap_sim_msg_add(msg, EAP_SIM_AT_IDENTITY, identity_len, identity, identity_len); } return eap_sim_msg_finish(msg, NULL, NULL, 0); } static struct wpabuf * eap_sim_response_challenge(struct eap_sim_data *data, u8 id) { struct eap_sim_msg *msg; wpa_printf(MSG_DEBUG, "Generating EAP-SIM Challenge (id=%d)", id); msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, EAP_TYPE_SIM, EAP_SIM_SUBTYPE_CHALLENGE); if (data->use_result_ind) { wpa_printf(MSG_DEBUG, " AT_RESULT_IND"); eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0); } wpa_printf(MSG_DEBUG, " AT_MAC"); eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); return eap_sim_msg_finish(msg, data->k_aut, (u8 *) data->sres, data->num_chal * EAP_SIM_SRES_LEN); } static struct wpabuf * eap_sim_response_reauth(struct eap_sim_data *data, u8 id, int counter_too_small, const u8 *nonce_s) { struct eap_sim_msg *msg; unsigned int counter; wpa_printf(MSG_DEBUG, "Generating EAP-SIM Reauthentication (id=%d)", id); msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, EAP_TYPE_SIM, EAP_SIM_SUBTYPE_REAUTHENTICATION); wpa_printf(MSG_DEBUG, " AT_IV"); wpa_printf(MSG_DEBUG, " AT_ENCR_DATA"); eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV, EAP_SIM_AT_ENCR_DATA); if (counter_too_small) { wpa_printf(MSG_DEBUG, " *AT_COUNTER_TOO_SMALL"); eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER_TOO_SMALL, 0, NULL, 0); counter = data->counter_too_small; } else counter = data->counter; wpa_printf(MSG_DEBUG, " *AT_COUNTER %d", counter); eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, counter, NULL, 0); if (eap_sim_msg_add_encr_end(msg, data->k_encr, EAP_SIM_AT_PADDING)) { wpa_printf(MSG_WARNING, "EAP-SIM: Failed to encrypt " "AT_ENCR_DATA"); eap_sim_msg_free(msg); return NULL; } if (data->use_result_ind) { wpa_printf(MSG_DEBUG, " AT_RESULT_IND"); eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0); } wpa_printf(MSG_DEBUG, " AT_MAC"); eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); return eap_sim_msg_finish(msg, data->k_aut, nonce_s, EAP_SIM_NONCE_S_LEN); } static struct wpabuf * eap_sim_response_notification(struct eap_sim_data *data, u8 id, u16 notification) { struct eap_sim_msg *msg; u8 *k_aut = (notification & 0x4000) == 0 ? data->k_aut : NULL; wpa_printf(MSG_DEBUG, "Generating EAP-SIM Notification (id=%d)", id); msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, EAP_TYPE_SIM, EAP_SIM_SUBTYPE_NOTIFICATION); if (k_aut && data->reauth) { wpa_printf(MSG_DEBUG, " AT_IV"); wpa_printf(MSG_DEBUG, " AT_ENCR_DATA"); eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV, EAP_SIM_AT_ENCR_DATA); wpa_printf(MSG_DEBUG, " *AT_COUNTER %d", data->counter); eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, data->counter, NULL, 0); if (eap_sim_msg_add_encr_end(msg, data->k_encr, EAP_SIM_AT_PADDING)) { wpa_printf(MSG_WARNING, "EAP-SIM: Failed to encrypt " "AT_ENCR_DATA"); eap_sim_msg_free(msg); return NULL; } } if (k_aut) { wpa_printf(MSG_DEBUG, " AT_MAC"); eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); } return eap_sim_msg_finish(msg, k_aut, (u8 *) "", 0); } static struct wpabuf * eap_sim_process_start(struct eap_sm *sm, struct eap_sim_data *data, u8 id, struct eap_sim_attrs *attr) { int selected_version = -1, id_error; size_t i; u8 *pos; wpa_printf(MSG_DEBUG, "EAP-SIM: subtype Start"); if (attr->version_list == NULL) { wpa_printf(MSG_INFO, "EAP-SIM: No AT_VERSION_LIST in " "SIM/Start"); return eap_sim_client_error(data, id, EAP_SIM_UNSUPPORTED_VERSION); } os_free(data->ver_list); data->ver_list = os_malloc(attr->version_list_len); if (data->ver_list == NULL) { wpa_printf(MSG_DEBUG, "EAP-SIM: Failed to allocate " "memory for version list"); return eap_sim_client_error(data, id, EAP_SIM_UNABLE_TO_PROCESS_PACKET); } os_memcpy(data->ver_list, attr->version_list, attr->version_list_len); data->ver_list_len = attr->version_list_len; pos = data->ver_list; for (i = 0; i < data->ver_list_len / 2; i++) { int ver = pos[0] * 256 + pos[1]; pos += 2; if (eap_sim_supported_ver(ver)) { selected_version = ver; break; } } if (selected_version < 0) { wpa_printf(MSG_INFO, "EAP-SIM: Could not find a supported " "version"); return eap_sim_client_error(data, id, EAP_SIM_UNSUPPORTED_VERSION); } wpa_printf(MSG_DEBUG, "EAP-SIM: Selected Version %d", selected_version); data->selected_version = selected_version; id_error = 0; switch (attr->id_req) { case NO_ID_REQ: break; case ANY_ID: if (data->num_id_req > 0) id_error++; data->num_id_req++; break; case FULLAUTH_ID: if (data->num_id_req > 1) id_error++; data->num_id_req++; break; case PERMANENT_ID: if (data->num_id_req > 2) id_error++; data->num_id_req++; break; } if (id_error) { wpa_printf(MSG_INFO, "EAP-SIM: Too many ID requests " "used within one authentication"); return eap_sim_client_error(data, id, EAP_SIM_UNABLE_TO_PROCESS_PACKET); } return eap_sim_response_start(sm, data, id, attr->id_req); } static struct wpabuf * eap_sim_process_challenge(struct eap_sm *sm, struct eap_sim_data *data, u8 id, const struct wpabuf *reqData, struct eap_sim_attrs *attr) { const u8 *identity; size_t identity_len; struct eap_sim_attrs eattr; int res; wpa_printf(MSG_DEBUG, "EAP-SIM: subtype Challenge"); data->reauth = 0; if (!attr->mac || !attr->rand) { wpa_printf(MSG_WARNING, "EAP-SIM: Challenge message " "did not include%s%s", !attr->mac ? " AT_MAC" : "", !attr->rand ? " AT_RAND" : ""); return eap_sim_client_error(data, id, EAP_SIM_UNABLE_TO_PROCESS_PACKET); } wpa_printf(MSG_DEBUG, "EAP-SIM: %lu challenges", (unsigned long) attr->num_chal); if (attr->num_chal < data->min_num_chal) { wpa_printf(MSG_INFO, "EAP-SIM: Insufficient number of " "challenges (%lu)", (unsigned long) attr->num_chal); return eap_sim_client_error(data, id, EAP_SIM_INSUFFICIENT_NUM_OF_CHAL); } if (attr->num_chal > 3) { wpa_printf(MSG_INFO, "EAP-SIM: Too many challenges " "(%lu)", (unsigned long) attr->num_chal); return eap_sim_client_error(data, id, EAP_SIM_UNABLE_TO_PROCESS_PACKET); } /* Verify that RANDs are different */ if (os_memcmp(attr->rand, attr->rand + GSM_RAND_LEN, GSM_RAND_LEN) == 0 || (attr->num_chal > 2 && (os_memcmp(attr->rand, attr->rand + 2 * GSM_RAND_LEN, GSM_RAND_LEN) == 0 || os_memcmp(attr->rand + GSM_RAND_LEN, attr->rand + 2 * GSM_RAND_LEN, GSM_RAND_LEN) == 0))) { wpa_printf(MSG_INFO, "EAP-SIM: Same RAND used multiple times"); return eap_sim_client_error(data, id, EAP_SIM_RAND_NOT_FRESH); } os_memcpy(data->rand, attr->rand, attr->num_chal * GSM_RAND_LEN); data->num_chal = attr->num_chal; res = eap_sim_gsm_auth(sm, data); if (res > 0) { wpa_printf(MSG_DEBUG, "EAP-SIM: Wait for external SIM processing"); return NULL; } if (res) { wpa_printf(MSG_WARNING, "EAP-SIM: GSM authentication failed"); return eap_sim_client_error(data, id, EAP_SIM_UNABLE_TO_PROCESS_PACKET); } if (data->last_eap_identity) { identity = data->last_eap_identity; identity_len = data->last_eap_identity_len; } else if (data->pseudonym) { identity = data->pseudonym; identity_len = data->pseudonym_len; } else identity = eap_get_config_identity(sm, &identity_len); wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Selected identity for MK " "derivation", identity, identity_len); eap_sim_derive_mk(identity, identity_len, data->nonce_mt, data->selected_version, data->ver_list, data->ver_list_len, data->num_chal, (const u8 *) data->kc, data->mk); eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut, data->msk, data->emsk); if (eap_sim_verify_mac(data->k_aut, reqData, attr->mac, data->nonce_mt, EAP_SIM_NONCE_MT_LEN)) { wpa_printf(MSG_WARNING, "EAP-SIM: Challenge message " "used invalid AT_MAC"); return eap_sim_client_error(data, id, EAP_SIM_UNABLE_TO_PROCESS_PACKET); } /* Old reauthentication identity must not be used anymore. In * other words, if no new reauth identity is received, full * authentication will be used on next reauthentication (using * pseudonym identity or permanent identity). */ eap_sim_clear_identities(sm, data, CLEAR_REAUTH_ID | CLEAR_EAP_ID); if (attr->encr_data) { u8 *decrypted; decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data, attr->encr_data_len, attr->iv, &eattr, 0); if (decrypted == NULL) { return eap_sim_client_error( data, id, EAP_SIM_UNABLE_TO_PROCESS_PACKET); } eap_sim_learn_ids(sm, data, &eattr); os_free(decrypted); } if (data->result_ind && attr->result_ind) data->use_result_ind = 1; if (data->state != FAILURE && data->state != RESULT_FAILURE) { eap_sim_state(data, data->use_result_ind ? RESULT_SUCCESS : SUCCESS); } data->num_id_req = 0; data->num_notification = 0; /* RFC 4186 specifies that counter is initialized to one after * fullauth, but initializing it to zero makes it easier to implement * reauth verification. */ data->counter = 0; return eap_sim_response_challenge(data, id); } static int eap_sim_process_notification_reauth(struct eap_sim_data *data, struct eap_sim_attrs *attr) { struct eap_sim_attrs eattr; u8 *decrypted; if (attr->encr_data == NULL || attr->iv == NULL) { wpa_printf(MSG_WARNING, "EAP-SIM: Notification message after " "reauth did not include encrypted data"); return -1; } decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data, attr->encr_data_len, attr->iv, &eattr, 0); if (decrypted == NULL) { wpa_printf(MSG_WARNING, "EAP-SIM: Failed to parse encrypted " "data from notification message"); return -1; } if (eattr.counter < 0 || (size_t) eattr.counter != data->counter) { wpa_printf(MSG_WARNING, "EAP-SIM: Counter in notification " "message does not match with counter in reauth " "message"); os_free(decrypted); return -1; } os_free(decrypted); return 0; } static int eap_sim_process_notification_auth(struct eap_sim_data *data, const struct wpabuf *reqData, struct eap_sim_attrs *attr) { if (attr->mac == NULL) { wpa_printf(MSG_INFO, "EAP-SIM: no AT_MAC in after_auth " "Notification message"); return -1; } if (eap_sim_verify_mac(data->k_aut, reqData, attr->mac, (u8 *) "", 0)) { wpa_printf(MSG_WARNING, "EAP-SIM: Notification message " "used invalid AT_MAC"); return -1; } if (data->reauth && eap_sim_process_notification_reauth(data, attr)) { wpa_printf(MSG_WARNING, "EAP-SIM: Invalid notification " "message after reauth"); return -1; } return 0; } static struct wpabuf * eap_sim_process_notification( struct eap_sm *sm, struct eap_sim_data *data, u8 id, const struct wpabuf *reqData, struct eap_sim_attrs *attr) { wpa_printf(MSG_DEBUG, "EAP-SIM: subtype Notification"); if (data->num_notification > 0) { wpa_printf(MSG_INFO, "EAP-SIM: too many notification " "rounds (only one allowed)"); return eap_sim_client_error(data, id, EAP_SIM_UNABLE_TO_PROCESS_PACKET); } data->num_notification++; if (attr->notification == -1) { wpa_printf(MSG_INFO, "EAP-SIM: no AT_NOTIFICATION in " "Notification message"); return eap_sim_client_error(data, id, EAP_SIM_UNABLE_TO_PROCESS_PACKET); } if ((attr->notification & 0x4000) == 0 && eap_sim_process_notification_auth(data, reqData, attr)) { return eap_sim_client_error(data, id, EAP_SIM_UNABLE_TO_PROCESS_PACKET); } eap_sim_report_notification(sm->msg_ctx, attr->notification, 0); if (attr->notification >= 0 && attr->notification < 32768) { eap_sim_state(data, FAILURE); } else if (attr->notification == EAP_SIM_SUCCESS && data->state == RESULT_SUCCESS) eap_sim_state(data, SUCCESS); return eap_sim_response_notification(data, id, attr->notification); } static struct wpabuf * eap_sim_process_reauthentication( struct eap_sm *sm, struct eap_sim_data *data, u8 id, const struct wpabuf *reqData, struct eap_sim_attrs *attr) { struct eap_sim_attrs eattr; u8 *decrypted; wpa_printf(MSG_DEBUG, "EAP-SIM: subtype Reauthentication"); if (data->reauth_id == NULL) { wpa_printf(MSG_WARNING, "EAP-SIM: Server is trying " "reauthentication, but no reauth_id available"); return eap_sim_client_error(data, id, EAP_SIM_UNABLE_TO_PROCESS_PACKET); } data->reauth = 1; if (eap_sim_verify_mac(data->k_aut, reqData, attr->mac, (u8 *) "", 0)) { wpa_printf(MSG_WARNING, "EAP-SIM: Reauthentication " "did not have valid AT_MAC"); return eap_sim_client_error(data, id, EAP_SIM_UNABLE_TO_PROCESS_PACKET); } if (attr->encr_data == NULL || attr->iv == NULL) { wpa_printf(MSG_WARNING, "EAP-SIM: Reauthentication " "message did not include encrypted data"); return eap_sim_client_error(data, id, EAP_SIM_UNABLE_TO_PROCESS_PACKET); } decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data, attr->encr_data_len, attr->iv, &eattr, 0); if (decrypted == NULL) { wpa_printf(MSG_WARNING, "EAP-SIM: Failed to parse encrypted " "data from reauthentication message"); return eap_sim_client_error(data, id, EAP_SIM_UNABLE_TO_PROCESS_PACKET); } if (eattr.nonce_s == NULL || eattr.counter < 0) { wpa_printf(MSG_INFO, "EAP-SIM: (encr) No%s%s in reauth packet", !eattr.nonce_s ? " AT_NONCE_S" : "", eattr.counter < 0 ? " AT_COUNTER" : ""); os_free(decrypted); return eap_sim_client_error(data, id, EAP_SIM_UNABLE_TO_PROCESS_PACKET); } if (eattr.counter < 0 || (size_t) eattr.counter <= data->counter) { wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid counter " "(%d <= %d)", eattr.counter, data->counter); data->counter_too_small = eattr.counter; /* Reply using Re-auth w/ AT_COUNTER_TOO_SMALL. The current * reauth_id must not be used to start a new reauthentication. * However, since it was used in the last EAP-Response-Identity * packet, it has to saved for the following fullauth to be * used in MK derivation. */ os_free(data->last_eap_identity); data->last_eap_identity = data->reauth_id; data->last_eap_identity_len = data->reauth_id_len; data->reauth_id = NULL; data->reauth_id_len = 0; os_free(decrypted); return eap_sim_response_reauth(data, id, 1, eattr.nonce_s); } data->counter = eattr.counter; os_memcpy(data->nonce_s, eattr.nonce_s, EAP_SIM_NONCE_S_LEN); wpa_hexdump(MSG_DEBUG, "EAP-SIM: (encr) AT_NONCE_S", data->nonce_s, EAP_SIM_NONCE_S_LEN); eap_sim_derive_keys_reauth(data->counter, data->reauth_id, data->reauth_id_len, data->nonce_s, data->mk, data->msk, data->emsk); eap_sim_clear_identities(sm, data, CLEAR_REAUTH_ID | CLEAR_EAP_ID); eap_sim_learn_ids(sm, data, &eattr); if (data->result_ind && attr->result_ind) data->use_result_ind = 1; if (data->state != FAILURE && data->state != RESULT_FAILURE) { eap_sim_state(data, data->use_result_ind ? RESULT_SUCCESS : SUCCESS); } data->num_id_req = 0; data->num_notification = 0; if (data->counter > EAP_SIM_MAX_FAST_REAUTHS) { wpa_printf(MSG_DEBUG, "EAP-SIM: Maximum number of " "fast reauths performed - force fullauth"); eap_sim_clear_identities(sm, data, CLEAR_REAUTH_ID | CLEAR_EAP_ID); } os_free(decrypted); return eap_sim_response_reauth(data, id, 0, data->nonce_s); } static struct wpabuf * eap_sim_process(struct eap_sm *sm, void *priv, struct eap_method_ret *ret, const struct wpabuf *reqData) { struct eap_sim_data *data = priv; const struct eap_hdr *req; u8 subtype, id; struct wpabuf *res; const u8 *pos; struct eap_sim_attrs attr; size_t len; wpa_hexdump_buf(MSG_DEBUG, "EAP-SIM: EAP data", reqData); if (eap_get_config_identity(sm, &len) == NULL) { wpa_printf(MSG_INFO, "EAP-SIM: Identity not configured"); eap_sm_request_identity(sm); ret->ignore = TRUE; return NULL; } pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SIM, reqData, &len); if (pos == NULL || len < 1) { ret->ignore = TRUE; return NULL; } req = wpabuf_head(reqData); id = req->identifier; len = be_to_host16(req->length); ret->ignore = FALSE; ret->methodState = METHOD_MAY_CONT; ret->decision = DECISION_FAIL; ret->allowNotifications = TRUE; subtype = *pos++; wpa_printf(MSG_DEBUG, "EAP-SIM: Subtype=%d", subtype); pos += 2; /* Reserved */ if (eap_sim_parse_attr(pos, wpabuf_head_u8(reqData) + len, &attr, 0, 0)) { res = eap_sim_client_error(data, id, EAP_SIM_UNABLE_TO_PROCESS_PACKET); goto done; } switch (subtype) { case EAP_SIM_SUBTYPE_START: res = eap_sim_process_start(sm, data, id, &attr); break; case EAP_SIM_SUBTYPE_CHALLENGE: res = eap_sim_process_challenge(sm, data, id, reqData, &attr); break; case EAP_SIM_SUBTYPE_NOTIFICATION: res = eap_sim_process_notification(sm, data, id, reqData, &attr); break; case EAP_SIM_SUBTYPE_REAUTHENTICATION: res = eap_sim_process_reauthentication(sm, data, id, reqData, &attr); break; case EAP_SIM_SUBTYPE_CLIENT_ERROR: wpa_printf(MSG_DEBUG, "EAP-SIM: subtype Client-Error"); res = eap_sim_client_error(data, id, EAP_SIM_UNABLE_TO_PROCESS_PACKET); break; default: wpa_printf(MSG_DEBUG, "EAP-SIM: Unknown subtype=%d", subtype); res = eap_sim_client_error(data, id, EAP_SIM_UNABLE_TO_PROCESS_PACKET); break; } done: if (data->state == FAILURE) { ret->decision = DECISION_FAIL; ret->methodState = METHOD_DONE; } else if (data->state == SUCCESS) { ret->decision = data->use_result_ind ? DECISION_UNCOND_SUCC : DECISION_COND_SUCC; ret->methodState = data->use_result_ind ? METHOD_DONE : METHOD_MAY_CONT; } else if (data->state == RESULT_FAILURE) ret->methodState = METHOD_CONT; else if (data->state == RESULT_SUCCESS) ret->methodState = METHOD_CONT; if (ret->methodState == METHOD_DONE) { ret->allowNotifications = FALSE; } return res; } static Boolean eap_sim_has_reauth_data(struct eap_sm *sm, void *priv) { struct eap_sim_data *data = priv; return data->pseudonym || data->reauth_id; } static void eap_sim_deinit_for_reauth(struct eap_sm *sm, void *priv) { struct eap_sim_data *data = priv; eap_sim_clear_identities(sm, data, CLEAR_EAP_ID); data->use_result_ind = 0; } static void * eap_sim_init_for_reauth(struct eap_sm *sm, void *priv) { struct eap_sim_data *data = priv; if (random_get_bytes(data->nonce_mt, EAP_SIM_NONCE_MT_LEN)) { wpa_printf(MSG_WARNING, "EAP-SIM: Failed to get random data " "for NONCE_MT"); os_free(data); return NULL; } data->num_id_req = 0; data->num_notification = 0; eap_sim_state(data, CONTINUE); return priv; } static const u8 * eap_sim_get_identity(struct eap_sm *sm, void *priv, size_t *len) { struct eap_sim_data *data = priv; if (data->reauth_id) { *len = data->reauth_id_len; return data->reauth_id; } if (data->pseudonym) { *len = data->pseudonym_len; return data->pseudonym; } return NULL; } static Boolean eap_sim_isKeyAvailable(struct eap_sm *sm, void *priv) { struct eap_sim_data *data = priv; return data->state == SUCCESS; } static u8 * eap_sim_getKey(struct eap_sm *sm, void *priv, size_t *len) { struct eap_sim_data *data = priv; u8 *key; if (data->state != SUCCESS) return NULL; key = os_malloc(EAP_SIM_KEYING_DATA_LEN); if (key == NULL) return NULL; *len = EAP_SIM_KEYING_DATA_LEN; os_memcpy(key, data->msk, EAP_SIM_KEYING_DATA_LEN); return key; } static u8 * eap_sim_get_session_id(struct eap_sm *sm, void *priv, size_t *len) { struct eap_sim_data *data = priv; u8 *id; if (data->state != SUCCESS) return NULL; *len = 1 + data->num_chal * GSM_RAND_LEN + EAP_SIM_NONCE_MT_LEN; id = os_malloc(*len); if (id == NULL) return NULL; id[0] = EAP_TYPE_SIM; os_memcpy(id + 1, data->rand, data->num_chal * GSM_RAND_LEN); os_memcpy(id + 1 + data->num_chal * GSM_RAND_LEN, data->nonce_mt, EAP_SIM_NONCE_MT_LEN); wpa_hexdump(MSG_DEBUG, "EAP-SIM: Derived Session-Id", id, *len); return id; } static u8 * eap_sim_get_emsk(struct eap_sm *sm, void *priv, size_t *len) { struct eap_sim_data *data = priv; u8 *key; if (data->state != SUCCESS) return NULL; key = os_malloc(EAP_EMSK_LEN); if (key == NULL) return NULL; *len = EAP_EMSK_LEN; os_memcpy(key, data->emsk, EAP_EMSK_LEN); return key; } int eap_peer_sim_register(void) { struct eap_method *eap; int ret; eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_SIM, "SIM"); if (eap == NULL) return -1; eap->init = eap_sim_init; eap->deinit = eap_sim_deinit; eap->process = eap_sim_process; eap->isKeyAvailable = eap_sim_isKeyAvailable; eap->getKey = eap_sim_getKey; eap->getSessionId = eap_sim_get_session_id; eap->has_reauth_data = eap_sim_has_reauth_data; eap->deinit_for_reauth = eap_sim_deinit_for_reauth; eap->init_for_reauth = eap_sim_init_for_reauth; eap->get_identity = eap_sim_get_identity; eap->get_emsk = eap_sim_get_emsk; ret = eap_peer_method_register(eap); if (ret) eap_peer_method_free(eap); return ret; } wpa-2.1/src/eap_peer/eap_tls.c000066400000000000000000000212731227414666700163310ustar00rootroot00000000000000/* * EAP peer method: EAP-TLS (RFC 2716) * Copyright (c) 2004-2008, 2012, 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/tls.h" #include "eap_i.h" #include "eap_tls_common.h" #include "eap_config.h" static void eap_tls_deinit(struct eap_sm *sm, void *priv); struct eap_tls_data { struct eap_ssl_data ssl; u8 *key_data; u8 *session_id; size_t id_len; void *ssl_ctx; u8 eap_type; }; static void * eap_tls_init(struct eap_sm *sm) { struct eap_tls_data *data; struct eap_peer_config *config = eap_get_config(sm); if (config == NULL || ((sm->init_phase2 ? config->private_key2 : config->private_key) == NULL && (sm->init_phase2 ? config->engine2 : config->engine) == 0)) { wpa_printf(MSG_INFO, "EAP-TLS: Private key not configured"); return NULL; } data = os_zalloc(sizeof(*data)); if (data == NULL) return NULL; data->ssl_ctx = sm->init_phase2 && sm->ssl_ctx2 ? sm->ssl_ctx2 : sm->ssl_ctx; if (eap_peer_tls_ssl_init(sm, &data->ssl, config, EAP_TYPE_TLS)) { wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL."); eap_tls_deinit(sm, data); if (config->engine) { wpa_printf(MSG_DEBUG, "EAP-TLS: Requesting Smartcard " "PIN"); eap_sm_request_pin(sm); sm->ignore = TRUE; } else if (config->private_key && !config->private_key_passwd) { wpa_printf(MSG_DEBUG, "EAP-TLS: Requesting private " "key passphrase"); eap_sm_request_passphrase(sm); sm->ignore = TRUE; } return NULL; } data->eap_type = EAP_TYPE_TLS; return data; } #ifdef EAP_UNAUTH_TLS static void * eap_unauth_tls_init(struct eap_sm *sm) { struct eap_tls_data *data; struct eap_peer_config *config = eap_get_config(sm); data = os_zalloc(sizeof(*data)); if (data == NULL) return NULL; data->ssl_ctx = sm->init_phase2 && sm->ssl_ctx2 ? sm->ssl_ctx2 : sm->ssl_ctx; if (eap_peer_tls_ssl_init(sm, &data->ssl, config, EAP_UNAUTH_TLS_TYPE)) { wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL."); eap_tls_deinit(sm, data); return NULL; } data->eap_type = EAP_UNAUTH_TLS_TYPE; return data; } #endif /* EAP_UNAUTH_TLS */ static void eap_tls_deinit(struct eap_sm *sm, void *priv) { struct eap_tls_data *data = priv; if (data == NULL) return; eap_peer_tls_ssl_deinit(sm, &data->ssl); os_free(data->key_data); os_free(data->session_id); os_free(data); } static struct wpabuf * eap_tls_failure(struct eap_sm *sm, struct eap_tls_data *data, struct eap_method_ret *ret, int res, struct wpabuf *resp, u8 id) { wpa_printf(MSG_DEBUG, "EAP-TLS: TLS processing failed"); ret->methodState = METHOD_DONE; ret->decision = DECISION_FAIL; if (res == -1) { struct eap_peer_config *config = eap_get_config(sm); if (config) { /* * The TLS handshake failed. So better forget the old * PIN. It may be wrong, we cannot be sure but trying * the wrong one again might block it on the card--so * better ask the user again. */ os_free(config->pin); config->pin = NULL; } } if (resp) { /* * This is likely an alert message, so send it instead of just * ACKing the error. */ return resp; } return eap_peer_tls_build_ack(id, data->eap_type, 0); } static void eap_tls_success(struct eap_sm *sm, struct eap_tls_data *data, struct eap_method_ret *ret) { wpa_printf(MSG_DEBUG, "EAP-TLS: Done"); ret->methodState = METHOD_DONE; ret->decision = DECISION_UNCOND_SUCC; os_free(data->key_data); data->key_data = eap_peer_tls_derive_key(sm, &data->ssl, "client EAP encryption", EAP_TLS_KEY_LEN + EAP_EMSK_LEN); if (data->key_data) { wpa_hexdump_key(MSG_DEBUG, "EAP-TLS: Derived key", data->key_data, EAP_TLS_KEY_LEN); wpa_hexdump_key(MSG_DEBUG, "EAP-TLS: Derived EMSK", data->key_data + EAP_TLS_KEY_LEN, EAP_EMSK_LEN); } else { wpa_printf(MSG_INFO, "EAP-TLS: Failed to derive key"); } os_free(data->session_id); data->session_id = eap_peer_tls_derive_session_id(sm, &data->ssl, EAP_TYPE_TLS, &data->id_len); if (data->session_id) { wpa_hexdump(MSG_DEBUG, "EAP-TLS: Derived Session-Id", data->session_id, data->id_len); } else { wpa_printf(MSG_ERROR, "EAP-TLS: Failed to derive Session-Id"); } } static struct wpabuf * eap_tls_process(struct eap_sm *sm, void *priv, struct eap_method_ret *ret, const struct wpabuf *reqData) { size_t left; int res; struct wpabuf *resp; u8 flags, id; const u8 *pos; struct eap_tls_data *data = priv; pos = eap_peer_tls_process_init(sm, &data->ssl, data->eap_type, ret, reqData, &left, &flags); if (pos == NULL) return NULL; id = eap_get_id(reqData); if (flags & EAP_TLS_FLAGS_START) { wpa_printf(MSG_DEBUG, "EAP-TLS: Start"); left = 0; /* make sure that this frame is empty, even though it * should always be, anyway */ } resp = NULL; res = eap_peer_tls_process_helper(sm, &data->ssl, data->eap_type, 0, id, pos, left, &resp); if (res < 0) { return eap_tls_failure(sm, data, ret, res, resp, id); } if (tls_connection_established(data->ssl_ctx, data->ssl.conn)) eap_tls_success(sm, data, ret); if (res == 1) { wpabuf_free(resp); return eap_peer_tls_build_ack(id, data->eap_type, 0); } return resp; } static Boolean eap_tls_has_reauth_data(struct eap_sm *sm, void *priv) { struct eap_tls_data *data = priv; return tls_connection_established(data->ssl_ctx, data->ssl.conn); } static void eap_tls_deinit_for_reauth(struct eap_sm *sm, void *priv) { } static void * eap_tls_init_for_reauth(struct eap_sm *sm, void *priv) { struct eap_tls_data *data = priv; os_free(data->key_data); data->key_data = NULL; os_free(data->session_id); data->session_id = NULL; if (eap_peer_tls_reauth_init(sm, &data->ssl)) { os_free(data); return NULL; } return priv; } static int eap_tls_get_status(struct eap_sm *sm, void *priv, char *buf, size_t buflen, int verbose) { struct eap_tls_data *data = priv; return eap_peer_tls_status(sm, &data->ssl, buf, buflen, verbose); } static Boolean eap_tls_isKeyAvailable(struct eap_sm *sm, void *priv) { struct eap_tls_data *data = priv; return data->key_data != NULL; } static u8 * eap_tls_getKey(struct eap_sm *sm, void *priv, size_t *len) { struct eap_tls_data *data = priv; u8 *key; if (data->key_data == NULL) return NULL; key = os_malloc(EAP_TLS_KEY_LEN); if (key == NULL) return NULL; *len = EAP_TLS_KEY_LEN; os_memcpy(key, data->key_data, EAP_TLS_KEY_LEN); return key; } static u8 * eap_tls_get_emsk(struct eap_sm *sm, void *priv, size_t *len) { struct eap_tls_data *data = priv; u8 *key; if (data->key_data == NULL) return NULL; key = os_malloc(EAP_EMSK_LEN); if (key == NULL) return NULL; *len = EAP_EMSK_LEN; os_memcpy(key, data->key_data + EAP_TLS_KEY_LEN, EAP_EMSK_LEN); return key; } static u8 * eap_tls_get_session_id(struct eap_sm *sm, void *priv, size_t *len) { struct eap_tls_data *data = priv; u8 *id; if (data->session_id == NULL) return NULL; id = os_malloc(data->id_len); if (id == NULL) return NULL; *len = data->id_len; os_memcpy(id, data->session_id, data->id_len); return id; } int eap_peer_tls_register(void) { struct eap_method *eap; int ret; eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_TLS, "TLS"); if (eap == NULL) return -1; eap->init = eap_tls_init; eap->deinit = eap_tls_deinit; eap->process = eap_tls_process; eap->isKeyAvailable = eap_tls_isKeyAvailable; eap->getKey = eap_tls_getKey; eap->getSessionId = eap_tls_get_session_id; eap->get_status = eap_tls_get_status; eap->has_reauth_data = eap_tls_has_reauth_data; eap->deinit_for_reauth = eap_tls_deinit_for_reauth; eap->init_for_reauth = eap_tls_init_for_reauth; eap->get_emsk = eap_tls_get_emsk; ret = eap_peer_method_register(eap); if (ret) eap_peer_method_free(eap); return ret; } #ifdef EAP_UNAUTH_TLS int eap_peer_unauth_tls_register(void) { struct eap_method *eap; int ret; eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, EAP_VENDOR_UNAUTH_TLS, EAP_VENDOR_TYPE_UNAUTH_TLS, "UNAUTH-TLS"); if (eap == NULL) return -1; eap->init = eap_unauth_tls_init; eap->deinit = eap_tls_deinit; eap->process = eap_tls_process; eap->isKeyAvailable = eap_tls_isKeyAvailable; eap->getKey = eap_tls_getKey; eap->get_status = eap_tls_get_status; eap->has_reauth_data = eap_tls_has_reauth_data; eap->deinit_for_reauth = eap_tls_deinit_for_reauth; eap->init_for_reauth = eap_tls_init_for_reauth; eap->get_emsk = eap_tls_get_emsk; ret = eap_peer_method_register(eap); if (ret) eap_peer_method_free(eap); return ret; } #endif /* EAP_UNAUTH_TLS */ wpa-2.1/src/eap_peer/eap_tls_common.c000066400000000000000000001013641227414666700177010ustar00rootroot00000000000000/* * EAP peer: EAP-TLS/PEAP/TTLS/FAST common functions * Copyright (c) 2004-2013, 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/sha1.h" #include "crypto/tls.h" #include "eap_i.h" #include "eap_tls_common.h" #include "eap_config.h" static struct wpabuf * eap_tls_msg_alloc(EapType type, size_t payload_len, u8 code, u8 identifier) { if (type == EAP_UNAUTH_TLS_TYPE) return eap_msg_alloc(EAP_VENDOR_UNAUTH_TLS, EAP_VENDOR_TYPE_UNAUTH_TLS, payload_len, code, identifier); return eap_msg_alloc(EAP_VENDOR_IETF, type, payload_len, code, identifier); } static int eap_tls_check_blob(struct eap_sm *sm, const char **name, const u8 **data, size_t *data_len) { const struct wpa_config_blob *blob; if (*name == NULL || os_strncmp(*name, "blob://", 7) != 0) return 0; blob = eap_get_config_blob(sm, *name + 7); if (blob == NULL) { wpa_printf(MSG_ERROR, "%s: Named configuration blob '%s' not " "found", __func__, *name + 7); return -1; } *name = NULL; *data = blob->data; *data_len = blob->len; return 0; } static void eap_tls_params_flags(struct tls_connection_params *params, const char *txt) { if (txt == NULL) return; if (os_strstr(txt, "tls_allow_md5=1")) params->flags |= TLS_CONN_ALLOW_SIGN_RSA_MD5; if (os_strstr(txt, "tls_disable_time_checks=1")) params->flags |= TLS_CONN_DISABLE_TIME_CHECKS; if (os_strstr(txt, "tls_disable_session_ticket=1")) params->flags |= TLS_CONN_DISABLE_SESSION_TICKET; if (os_strstr(txt, "tls_disable_session_ticket=0")) params->flags &= ~TLS_CONN_DISABLE_SESSION_TICKET; } static void eap_tls_params_from_conf1(struct tls_connection_params *params, struct eap_peer_config *config) { params->ca_cert = (char *) config->ca_cert; params->ca_path = (char *) config->ca_path; params->client_cert = (char *) config->client_cert; params->private_key = (char *) config->private_key; params->private_key_passwd = (char *) config->private_key_passwd; params->dh_file = (char *) config->dh_file; params->subject_match = (char *) config->subject_match; params->altsubject_match = (char *) config->altsubject_match; params->suffix_match = config->domain_suffix_match; params->engine = config->engine; params->engine_id = config->engine_id; params->pin = config->pin; params->key_id = config->key_id; params->cert_id = config->cert_id; params->ca_cert_id = config->ca_cert_id; eap_tls_params_flags(params, config->phase1); } static void eap_tls_params_from_conf2(struct tls_connection_params *params, struct eap_peer_config *config) { params->ca_cert = (char *) config->ca_cert2; params->ca_path = (char *) config->ca_path2; params->client_cert = (char *) config->client_cert2; params->private_key = (char *) config->private_key2; params->private_key_passwd = (char *) config->private_key2_passwd; params->dh_file = (char *) config->dh_file2; params->subject_match = (char *) config->subject_match2; params->altsubject_match = (char *) config->altsubject_match2; params->suffix_match = config->domain_suffix_match2; params->engine = config->engine2; params->engine_id = config->engine2_id; params->pin = config->pin2; params->key_id = config->key2_id; params->cert_id = config->cert2_id; params->ca_cert_id = config->ca_cert2_id; eap_tls_params_flags(params, config->phase2); } static int eap_tls_params_from_conf(struct eap_sm *sm, struct eap_ssl_data *data, struct tls_connection_params *params, struct eap_peer_config *config, int phase2) { os_memset(params, 0, sizeof(*params)); if (sm->workaround && data->eap_type != EAP_TYPE_FAST) { /* * Some deployed authentication servers seem to be unable to * handle the TLS Session Ticket extension (they are supposed * to ignore unrecognized TLS extensions, but end up rejecting * the ClientHello instead). As a workaround, disable use of * TLS Sesson Ticket extension for EAP-TLS, EAP-PEAP, and * EAP-TTLS (EAP-FAST uses session ticket, so any server that * supports EAP-FAST does not need this workaround). */ params->flags |= TLS_CONN_DISABLE_SESSION_TICKET; } if (phase2) { wpa_printf(MSG_DEBUG, "TLS: using phase2 config options"); eap_tls_params_from_conf2(params, config); } else { wpa_printf(MSG_DEBUG, "TLS: using phase1 config options"); eap_tls_params_from_conf1(params, config); } /* * Use blob data, if available. Otherwise, leave reference to external * file as-is. */ if (eap_tls_check_blob(sm, ¶ms->ca_cert, ¶ms->ca_cert_blob, ¶ms->ca_cert_blob_len) || eap_tls_check_blob(sm, ¶ms->client_cert, ¶ms->client_cert_blob, ¶ms->client_cert_blob_len) || eap_tls_check_blob(sm, ¶ms->private_key, ¶ms->private_key_blob, ¶ms->private_key_blob_len) || eap_tls_check_blob(sm, ¶ms->dh_file, ¶ms->dh_blob, ¶ms->dh_blob_len)) { wpa_printf(MSG_INFO, "SSL: Failed to get configuration blobs"); return -1; } return 0; } static int eap_tls_init_connection(struct eap_sm *sm, struct eap_ssl_data *data, struct eap_peer_config *config, struct tls_connection_params *params) { int res; if (config->ocsp) params->flags |= TLS_CONN_REQUEST_OCSP; if (config->ocsp == 2) params->flags |= TLS_CONN_REQUIRE_OCSP; data->conn = tls_connection_init(data->ssl_ctx); if (data->conn == NULL) { wpa_printf(MSG_INFO, "SSL: Failed to initialize new TLS " "connection"); return -1; } res = tls_connection_set_params(data->ssl_ctx, data->conn, params); if (res == TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED) { /* * At this point with the pkcs11 engine the PIN might be wrong. * We reset the PIN in the configuration to be sure to not use * it again and the calling function must request a new one. */ os_free(config->pin); config->pin = NULL; } else if (res == TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED) { wpa_printf(MSG_INFO, "TLS: Failed to load private key"); /* * We do not know exactly but maybe the PIN was wrong, * so ask for a new one. */ os_free(config->pin); config->pin = NULL; eap_sm_request_pin(sm); sm->ignore = TRUE; tls_connection_deinit(data->ssl_ctx, data->conn); data->conn = NULL; return -1; } else if (res) { wpa_printf(MSG_INFO, "TLS: Failed to set TLS connection " "parameters"); tls_connection_deinit(data->ssl_ctx, data->conn); data->conn = NULL; return -1; } return 0; } /** * eap_peer_tls_ssl_init - Initialize shared TLS functionality * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * @data: Data for TLS processing * @config: Pointer to the network configuration * @eap_type: EAP method used in Phase 1 (EAP_TYPE_TLS/PEAP/TTLS/FAST) * Returns: 0 on success, -1 on failure * * This function is used to initialize shared TLS functionality for EAP-TLS, * EAP-PEAP, EAP-TTLS, and EAP-FAST. */ int eap_peer_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, struct eap_peer_config *config, u8 eap_type) { struct tls_connection_params params; if (config == NULL) return -1; data->eap = sm; data->eap_type = eap_type; data->phase2 = sm->init_phase2; data->ssl_ctx = sm->init_phase2 && sm->ssl_ctx2 ? sm->ssl_ctx2 : sm->ssl_ctx; if (eap_tls_params_from_conf(sm, data, ¶ms, config, data->phase2) < 0) return -1; if (eap_tls_init_connection(sm, data, config, ¶ms) < 0) return -1; data->tls_out_limit = config->fragment_size; if (data->phase2) { /* Limit the fragment size in the inner TLS authentication * since the outer authentication with EAP-PEAP does not yet * support fragmentation */ if (data->tls_out_limit > 100) data->tls_out_limit -= 100; } if (config->phase1 && os_strstr(config->phase1, "include_tls_length=1")) { wpa_printf(MSG_DEBUG, "TLS: Include TLS Message Length in " "unfragmented packets"); data->include_tls_length = 1; } return 0; } /** * eap_peer_tls_ssl_deinit - Deinitialize shared TLS functionality * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * @data: Data for TLS processing * * This function deinitializes shared TLS functionality that was initialized * with eap_peer_tls_ssl_init(). */ void eap_peer_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data) { tls_connection_deinit(data->ssl_ctx, data->conn); eap_peer_tls_reset_input(data); eap_peer_tls_reset_output(data); } /** * eap_peer_tls_derive_key - Derive a key based on TLS session data * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * @data: Data for TLS processing * @label: Label string for deriving the keys, e.g., "client EAP encryption" * @len: Length of the key material to generate (usually 64 for MSK) * Returns: Pointer to allocated key on success or %NULL on failure * * This function uses TLS-PRF to generate pseudo-random data based on the TLS * session data (client/server random and master key). Each key type may use a * different label to bind the key usage into the generated material. * * The caller is responsible for freeing the returned buffer. */ u8 * eap_peer_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, const char *label, size_t len) { #ifndef CONFIG_FIPS struct tls_keys keys; #endif /* CONFIG_FIPS */ u8 *rnd = NULL, *out; out = os_malloc(len); if (out == NULL) return NULL; /* First, try to use TLS library function for PRF, if available. */ if (tls_connection_prf(data->ssl_ctx, data->conn, label, 0, out, len) == 0) return out; #ifndef CONFIG_FIPS /* * TLS library did not support key generation, so get the needed TLS * session parameters and use an internal implementation of TLS PRF to * derive the key. */ if (tls_connection_get_keys(data->ssl_ctx, data->conn, &keys)) goto fail; if (keys.client_random == NULL || keys.server_random == NULL || keys.master_key == NULL) goto fail; rnd = os_malloc(keys.client_random_len + keys.server_random_len); if (rnd == NULL) goto fail; os_memcpy(rnd, keys.client_random, keys.client_random_len); os_memcpy(rnd + keys.client_random_len, keys.server_random, keys.server_random_len); if (tls_prf_sha1_md5(keys.master_key, keys.master_key_len, label, rnd, keys.client_random_len + keys.server_random_len, out, len)) goto fail; os_free(rnd); return out; fail: #endif /* CONFIG_FIPS */ os_free(out); os_free(rnd); return NULL; } /** * eap_peer_tls_derive_session_id - Derive a Session-Id based on TLS data * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * @data: Data for TLS processing * @eap_type: EAP method used in Phase 1 (EAP_TYPE_TLS/PEAP/TTLS/FAST) * @len: Pointer to length of the session ID generated * Returns: Pointer to allocated Session-Id on success or %NULL on failure * * This function derive the Session-Id based on the TLS session data * (client/server random and method type). * * The caller is responsible for freeing the returned buffer. */ u8 * eap_peer_tls_derive_session_id(struct eap_sm *sm, struct eap_ssl_data *data, u8 eap_type, size_t *len) { struct tls_keys keys; u8 *out; /* * TLS library did not support session ID generation, * so get the needed TLS session parameters */ if (tls_connection_get_keys(sm->ssl_ctx, data->conn, &keys)) return NULL; if (keys.client_random == NULL || keys.server_random == NULL || keys.master_key == NULL) return NULL; *len = 1 + keys.client_random_len + keys.server_random_len; out = os_malloc(*len); if (out == NULL) return NULL; /* Session-Id = EAP type || client.random || server.random */ out[0] = eap_type; os_memcpy(out + 1, keys.client_random, keys.client_random_len); os_memcpy(out + 1 + keys.client_random_len, keys.server_random, keys.server_random_len); return out; } /** * eap_peer_tls_reassemble_fragment - Reassemble a received fragment * @data: Data for TLS processing * @in_data: Next incoming TLS segment * Returns: 0 on success, 1 if more data is needed for the full message, or * -1 on error */ static int eap_peer_tls_reassemble_fragment(struct eap_ssl_data *data, const struct wpabuf *in_data) { size_t tls_in_len, in_len; tls_in_len = data->tls_in ? wpabuf_len(data->tls_in) : 0; in_len = in_data ? wpabuf_len(in_data) : 0; if (tls_in_len + in_len == 0) { /* No message data received?! */ wpa_printf(MSG_WARNING, "SSL: Invalid reassembly state: " "tls_in_left=%lu tls_in_len=%lu in_len=%lu", (unsigned long) data->tls_in_left, (unsigned long) tls_in_len, (unsigned long) in_len); eap_peer_tls_reset_input(data); return -1; } if (tls_in_len + in_len > 65536) { /* * Limit length to avoid rogue servers from causing large * memory allocations. */ wpa_printf(MSG_INFO, "SSL: Too long TLS fragment (size over " "64 kB)"); eap_peer_tls_reset_input(data); return -1; } if (in_len > data->tls_in_left) { /* Sender is doing something odd - reject message */ wpa_printf(MSG_INFO, "SSL: more data than TLS message length " "indicated"); eap_peer_tls_reset_input(data); return -1; } if (wpabuf_resize(&data->tls_in, in_len) < 0) { wpa_printf(MSG_INFO, "SSL: Could not allocate memory for TLS " "data"); eap_peer_tls_reset_input(data); return -1; } if (in_data) wpabuf_put_buf(data->tls_in, in_data); data->tls_in_left -= in_len; if (data->tls_in_left > 0) { wpa_printf(MSG_DEBUG, "SSL: Need %lu bytes more input " "data", (unsigned long) data->tls_in_left); return 1; } return 0; } /** * eap_peer_tls_data_reassemble - Reassemble TLS data * @data: Data for TLS processing * @in_data: Next incoming TLS segment * @need_more_input: Variable for returning whether more input data is needed * to reassemble this TLS packet * Returns: Pointer to output data, %NULL on error or when more data is needed * for the full message (in which case, *need_more_input is also set to 1). * * This function reassembles TLS fragments. Caller must not free the returned * data buffer since an internal pointer to it is maintained. */ static const struct wpabuf * eap_peer_tls_data_reassemble( struct eap_ssl_data *data, const struct wpabuf *in_data, int *need_more_input) { *need_more_input = 0; if (data->tls_in_left > wpabuf_len(in_data) || data->tls_in) { /* Message has fragments */ int res = eap_peer_tls_reassemble_fragment(data, in_data); if (res) { if (res == 1) *need_more_input = 1; return NULL; } /* Message is now fully reassembled. */ } else { /* No fragments in this message, so just make a copy of it. */ data->tls_in_left = 0; data->tls_in = wpabuf_dup(in_data); if (data->tls_in == NULL) return NULL; } return data->tls_in; } /** * eap_tls_process_input - Process incoming TLS message * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * @data: Data for TLS processing * @in_data: Message received from the server * @in_len: Length of in_data * @out_data: Buffer for returning a pointer to application data (if available) * Returns: 0 on success, 1 if more input data is needed, 2 if application data * is available, -1 on failure */ static int eap_tls_process_input(struct eap_sm *sm, struct eap_ssl_data *data, const u8 *in_data, size_t in_len, struct wpabuf **out_data) { const struct wpabuf *msg; int need_more_input; struct wpabuf *appl_data; struct wpabuf buf; wpabuf_set(&buf, in_data, in_len); msg = eap_peer_tls_data_reassemble(data, &buf, &need_more_input); if (msg == NULL) return need_more_input ? 1 : -1; /* Full TLS message reassembled - continue handshake processing */ if (data->tls_out) { /* This should not happen.. */ wpa_printf(MSG_INFO, "SSL: eap_tls_process_input - pending " "tls_out data even though tls_out_len = 0"); wpabuf_free(data->tls_out); WPA_ASSERT(data->tls_out == NULL); } appl_data = NULL; data->tls_out = tls_connection_handshake(data->ssl_ctx, data->conn, msg, &appl_data); eap_peer_tls_reset_input(data); if (appl_data && tls_connection_established(data->ssl_ctx, data->conn) && !tls_connection_get_failed(data->ssl_ctx, data->conn)) { wpa_hexdump_buf_key(MSG_MSGDUMP, "SSL: Application data", appl_data); *out_data = appl_data; return 2; } wpabuf_free(appl_data); return 0; } /** * eap_tls_process_output - Process outgoing TLS message * @data: Data for TLS processing * @eap_type: EAP type (EAP_TYPE_TLS, EAP_TYPE_PEAP, ...) * @peap_version: Version number for EAP-PEAP/TTLS * @id: EAP identifier for the response * @ret: Return value to use on success * @out_data: Buffer for returning the allocated output buffer * Returns: ret (0 or 1) on success, -1 on failure */ static int eap_tls_process_output(struct eap_ssl_data *data, EapType eap_type, int peap_version, u8 id, int ret, struct wpabuf **out_data) { size_t len; u8 *flags; int more_fragments, length_included; if (data->tls_out == NULL) return -1; len = wpabuf_len(data->tls_out) - data->tls_out_pos; wpa_printf(MSG_DEBUG, "SSL: %lu bytes left to be sent out (of total " "%lu bytes)", (unsigned long) len, (unsigned long) wpabuf_len(data->tls_out)); /* * Limit outgoing message to the configured maximum size. Fragment * message if needed. */ if (len > data->tls_out_limit) { more_fragments = 1; len = data->tls_out_limit; wpa_printf(MSG_DEBUG, "SSL: sending %lu bytes, more fragments " "will follow", (unsigned long) len); } else more_fragments = 0; length_included = data->tls_out_pos == 0 && (wpabuf_len(data->tls_out) > data->tls_out_limit || data->include_tls_length); if (!length_included && eap_type == EAP_TYPE_PEAP && peap_version == 0 && !tls_connection_established(data->eap->ssl_ctx, data->conn)) { /* * Windows Server 2008 NPS really wants to have the TLS Message * length included in phase 0 even for unfragmented frames or * it will get very confused with Compound MAC calculation and * Outer TLVs. */ length_included = 1; } *out_data = eap_tls_msg_alloc(eap_type, 1 + length_included * 4 + len, EAP_CODE_RESPONSE, id); if (*out_data == NULL) return -1; flags = wpabuf_put(*out_data, 1); *flags = peap_version; if (more_fragments) *flags |= EAP_TLS_FLAGS_MORE_FRAGMENTS; if (length_included) { *flags |= EAP_TLS_FLAGS_LENGTH_INCLUDED; wpabuf_put_be32(*out_data, wpabuf_len(data->tls_out)); } wpabuf_put_data(*out_data, wpabuf_head_u8(data->tls_out) + data->tls_out_pos, len); data->tls_out_pos += len; if (!more_fragments) eap_peer_tls_reset_output(data); return ret; } /** * eap_peer_tls_process_helper - Process TLS handshake message * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * @data: Data for TLS processing * @eap_type: EAP type (EAP_TYPE_TLS, EAP_TYPE_PEAP, ...) * @peap_version: Version number for EAP-PEAP/TTLS * @id: EAP identifier for the response * @in_data: Message received from the server * @in_len: Length of in_data * @out_data: Buffer for returning a pointer to the response message * Returns: 0 on success, 1 if more input data is needed, 2 if application data * is available, or -1 on failure * * This function can be used to process TLS handshake messages. It reassembles * the received fragments and uses a TLS library to process the messages. The * response data from the TLS library is fragmented to suitable output messages * that the caller can send out. * * out_data is used to return the response message if the return value of this * function is 0, 2, or -1. In case of failure, the message is likely a TLS * alarm message. The caller is responsible for freeing the allocated buffer if * *out_data is not %NULL. * * This function is called for each received TLS message during the TLS * handshake after eap_peer_tls_process_init() call and possible processing of * TLS Flags field. Once the handshake has been completed, i.e., when * tls_connection_established() returns 1, EAP method specific decrypting of * the tunneled data is used. */ int eap_peer_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data, EapType eap_type, int peap_version, u8 id, const u8 *in_data, size_t in_len, struct wpabuf **out_data) { int ret = 0; *out_data = NULL; if (data->tls_out && wpabuf_len(data->tls_out) > 0 && in_len > 0) { wpa_printf(MSG_DEBUG, "SSL: Received non-ACK when output " "fragments are waiting to be sent out"); return -1; } if (data->tls_out == NULL || wpabuf_len(data->tls_out) == 0) { /* * No more data to send out - expect to receive more data from * the AS. */ int res = eap_tls_process_input(sm, data, in_data, in_len, out_data); if (res) { /* * Input processing failed (res = -1) or more data is * needed (res = 1). */ return res; } /* * The incoming message has been reassembled and processed. The * response was allocated into data->tls_out buffer. */ } if (data->tls_out == NULL) { /* * No outgoing fragments remaining from the previous message * and no new message generated. This indicates an error in TLS * processing. */ eap_peer_tls_reset_output(data); return -1; } if (tls_connection_get_failed(data->ssl_ctx, data->conn)) { /* TLS processing has failed - return error */ wpa_printf(MSG_DEBUG, "SSL: Failed - tls_out available to " "report error"); ret = -1; /* TODO: clean pin if engine used? */ } if (data->tls_out == NULL || wpabuf_len(data->tls_out) == 0) { /* * TLS negotiation should now be complete since all other cases * needing more data should have been caught above based on * the TLS Message Length field. */ wpa_printf(MSG_DEBUG, "SSL: No data to be sent out"); wpabuf_free(data->tls_out); data->tls_out = NULL; return 1; } /* Send the pending message (in fragments, if needed). */ return eap_tls_process_output(data, eap_type, peap_version, id, ret, out_data); } /** * eap_peer_tls_build_ack - Build a TLS ACK frame * @id: EAP identifier for the response * @eap_type: EAP type (EAP_TYPE_TLS, EAP_TYPE_PEAP, ...) * @peap_version: Version number for EAP-PEAP/TTLS * Returns: Pointer to the allocated ACK frame or %NULL on failure */ struct wpabuf * eap_peer_tls_build_ack(u8 id, EapType eap_type, int peap_version) { struct wpabuf *resp; resp = eap_tls_msg_alloc(eap_type, 1, EAP_CODE_RESPONSE, id); if (resp == NULL) return NULL; wpa_printf(MSG_DEBUG, "SSL: Building ACK (type=%d id=%d ver=%d)", (int) eap_type, id, peap_version); wpabuf_put_u8(resp, peap_version); /* Flags */ return resp; } /** * eap_peer_tls_reauth_init - Re-initialize shared TLS for session resumption * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * @data: Data for TLS processing * Returns: 0 on success, -1 on failure */ int eap_peer_tls_reauth_init(struct eap_sm *sm, struct eap_ssl_data *data) { eap_peer_tls_reset_input(data); eap_peer_tls_reset_output(data); return tls_connection_shutdown(data->ssl_ctx, data->conn); } /** * eap_peer_tls_status - Get TLS status * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * @data: Data for TLS processing * @buf: Buffer for status information * @buflen: Maximum buffer length * @verbose: Whether to include verbose status information * Returns: Number of bytes written to buf. */ int eap_peer_tls_status(struct eap_sm *sm, struct eap_ssl_data *data, char *buf, size_t buflen, int verbose) { char name[128]; int len = 0, ret; if (tls_get_cipher(data->ssl_ctx, data->conn, name, sizeof(name)) == 0) { ret = os_snprintf(buf + len, buflen - len, "EAP TLS cipher=%s\n", name); if (ret < 0 || (size_t) ret >= buflen - len) return len; len += ret; } return len; } /** * eap_peer_tls_process_init - Initial validation/processing of EAP requests * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * @data: Data for TLS processing * @eap_type: EAP type (EAP_TYPE_TLS, EAP_TYPE_PEAP, ...) * @ret: Return values from EAP request validation and processing * @reqData: EAP request to be processed (eapReqData) * @len: Buffer for returning length of the remaining payload * @flags: Buffer for returning TLS flags * Returns: Pointer to payload after TLS flags and length or %NULL on failure * * This function validates the EAP header and processes the optional TLS * Message Length field. If this is the first fragment of a TLS message, the * TLS reassembly code is initialized to receive the indicated number of bytes. * * EAP-TLS, EAP-PEAP, EAP-TTLS, and EAP-FAST methods are expected to use this * function as the first step in processing received messages. They will need * to process the flags (apart from Message Length Included) that are returned * through the flags pointer and the message payload that will be returned (and * the length is returned through the len pointer). Return values (ret) are set * for continuation of EAP method processing. The caller is responsible for * setting these to indicate completion (either success or failure) based on * the authentication result. */ const u8 * eap_peer_tls_process_init(struct eap_sm *sm, struct eap_ssl_data *data, EapType eap_type, struct eap_method_ret *ret, const struct wpabuf *reqData, size_t *len, u8 *flags) { const u8 *pos; size_t left; unsigned int tls_msg_len; if (tls_get_errors(data->ssl_ctx)) { wpa_printf(MSG_INFO, "SSL: TLS errors detected"); ret->ignore = TRUE; return NULL; } if (eap_type == EAP_UNAUTH_TLS_TYPE) pos = eap_hdr_validate(EAP_VENDOR_UNAUTH_TLS, EAP_VENDOR_TYPE_UNAUTH_TLS, reqData, &left); else pos = eap_hdr_validate(EAP_VENDOR_IETF, eap_type, reqData, &left); if (pos == NULL) { ret->ignore = TRUE; return NULL; } if (left == 0) { wpa_printf(MSG_DEBUG, "SSL: Invalid TLS message: no Flags " "octet included"); if (!sm->workaround) { ret->ignore = TRUE; return NULL; } wpa_printf(MSG_DEBUG, "SSL: Workaround - assume no Flags " "indicates ACK frame"); *flags = 0; } else { *flags = *pos++; left--; } wpa_printf(MSG_DEBUG, "SSL: Received packet(len=%lu) - " "Flags 0x%02x", (unsigned long) wpabuf_len(reqData), *flags); if (*flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) { if (left < 4) { wpa_printf(MSG_INFO, "SSL: Short frame with TLS " "length"); ret->ignore = TRUE; return NULL; } tls_msg_len = WPA_GET_BE32(pos); wpa_printf(MSG_DEBUG, "SSL: TLS Message Length: %d", tls_msg_len); if (data->tls_in_left == 0) { data->tls_in_total = tls_msg_len; data->tls_in_left = tls_msg_len; wpabuf_free(data->tls_in); data->tls_in = NULL; } pos += 4; left -= 4; if (left > tls_msg_len) { wpa_printf(MSG_INFO, "SSL: TLS Message Length (%d " "bytes) smaller than this fragment (%d " "bytes)", (int) tls_msg_len, (int) left); ret->ignore = TRUE; return NULL; } } ret->ignore = FALSE; ret->methodState = METHOD_MAY_CONT; ret->decision = DECISION_FAIL; ret->allowNotifications = TRUE; *len = left; return pos; } /** * eap_peer_tls_reset_input - Reset input buffers * @data: Data for TLS processing * * This function frees any allocated memory for input buffers and resets input * state. */ void eap_peer_tls_reset_input(struct eap_ssl_data *data) { data->tls_in_left = data->tls_in_total = 0; wpabuf_free(data->tls_in); data->tls_in = NULL; } /** * eap_peer_tls_reset_output - Reset output buffers * @data: Data for TLS processing * * This function frees any allocated memory for output buffers and resets * output state. */ void eap_peer_tls_reset_output(struct eap_ssl_data *data) { data->tls_out_pos = 0; wpabuf_free(data->tls_out); data->tls_out = NULL; } /** * eap_peer_tls_decrypt - Decrypt received phase 2 TLS message * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * @data: Data for TLS processing * @in_data: Message received from the server * @in_decrypted: Buffer for returning a pointer to the decrypted message * Returns: 0 on success, 1 if more input data is needed, or -1 on failure */ int eap_peer_tls_decrypt(struct eap_sm *sm, struct eap_ssl_data *data, const struct wpabuf *in_data, struct wpabuf **in_decrypted) { const struct wpabuf *msg; int need_more_input; msg = eap_peer_tls_data_reassemble(data, in_data, &need_more_input); if (msg == NULL) return need_more_input ? 1 : -1; *in_decrypted = tls_connection_decrypt(data->ssl_ctx, data->conn, msg); eap_peer_tls_reset_input(data); if (*in_decrypted == NULL) { wpa_printf(MSG_INFO, "SSL: Failed to decrypt Phase 2 data"); return -1; } return 0; } /** * eap_peer_tls_encrypt - Encrypt phase 2 TLS message * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * @data: Data for TLS processing * @eap_type: EAP type (EAP_TYPE_TLS, EAP_TYPE_PEAP, ...) * @peap_version: Version number for EAP-PEAP/TTLS * @id: EAP identifier for the response * @in_data: Plaintext phase 2 data to encrypt or %NULL to continue fragments * @out_data: Buffer for returning a pointer to the encrypted response message * Returns: 0 on success, -1 on failure */ int eap_peer_tls_encrypt(struct eap_sm *sm, struct eap_ssl_data *data, EapType eap_type, int peap_version, u8 id, const struct wpabuf *in_data, struct wpabuf **out_data) { if (in_data) { eap_peer_tls_reset_output(data); data->tls_out = tls_connection_encrypt(data->ssl_ctx, data->conn, in_data); if (data->tls_out == NULL) { wpa_printf(MSG_INFO, "SSL: Failed to encrypt Phase 2 " "data (in_len=%lu)", (unsigned long) wpabuf_len(in_data)); eap_peer_tls_reset_output(data); return -1; } } return eap_tls_process_output(data, eap_type, peap_version, id, 0, out_data); } /** * eap_peer_select_phase2_methods - Select phase 2 EAP method * @config: Pointer to the network configuration * @prefix: 'phase2' configuration prefix, e.g., "auth=" * @types: Buffer for returning allocated list of allowed EAP methods * @num_types: Buffer for returning number of allocated EAP methods * Returns: 0 on success, -1 on failure * * This function is used to parse EAP method list and select allowed methods * for Phase2 authentication. */ int eap_peer_select_phase2_methods(struct eap_peer_config *config, const char *prefix, struct eap_method_type **types, size_t *num_types) { char *start, *pos, *buf; struct eap_method_type *methods = NULL, *_methods; u8 method; size_t num_methods = 0, prefix_len; if (config == NULL || config->phase2 == NULL) goto get_defaults; start = buf = os_strdup(config->phase2); if (buf == NULL) return -1; prefix_len = os_strlen(prefix); while (start && *start != '\0') { int vendor; pos = os_strstr(start, prefix); if (pos == NULL) break; if (start != pos && *(pos - 1) != ' ') { start = pos + prefix_len; continue; } start = pos + prefix_len; pos = os_strchr(start, ' '); if (pos) *pos++ = '\0'; method = eap_get_phase2_type(start, &vendor); if (vendor == EAP_VENDOR_IETF && method == EAP_TYPE_NONE) { wpa_printf(MSG_ERROR, "TLS: Unsupported Phase2 EAP " "method '%s'", start); } else { num_methods++; _methods = os_realloc_array(methods, num_methods, sizeof(*methods)); if (_methods == NULL) { os_free(methods); os_free(buf); return -1; } methods = _methods; methods[num_methods - 1].vendor = vendor; methods[num_methods - 1].method = method; } start = pos; } os_free(buf); get_defaults: if (methods == NULL) methods = eap_get_phase2_types(config, &num_methods); if (methods == NULL) { wpa_printf(MSG_ERROR, "TLS: No Phase2 EAP methods available"); return -1; } wpa_hexdump(MSG_DEBUG, "TLS: Phase2 EAP types", (u8 *) methods, num_methods * sizeof(struct eap_method_type)); *types = methods; *num_types = num_methods; return 0; } /** * eap_peer_tls_phase2_nak - Generate EAP-Nak for Phase 2 * @types: Buffer for returning allocated list of allowed EAP methods * @num_types: Buffer for returning number of allocated EAP methods * @hdr: EAP-Request header (and the following EAP type octet) * @resp: Buffer for returning the EAP-Nak message * Returns: 0 on success, -1 on failure */ int eap_peer_tls_phase2_nak(struct eap_method_type *types, size_t num_types, struct eap_hdr *hdr, struct wpabuf **resp) { u8 *pos = (u8 *) (hdr + 1); size_t i; /* TODO: add support for expanded Nak */ wpa_printf(MSG_DEBUG, "TLS: Phase 2 Request: Nak type=%d", *pos); wpa_hexdump(MSG_DEBUG, "TLS: Allowed Phase2 EAP types", (u8 *) types, num_types * sizeof(struct eap_method_type)); *resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_NAK, num_types, EAP_CODE_RESPONSE, hdr->identifier); if (*resp == NULL) return -1; for (i = 0; i < num_types; i++) { if (types[i].vendor == EAP_VENDOR_IETF && types[i].method < 256) wpabuf_put_u8(*resp, types[i].method); } eap_update_len(*resp); return 0; } wpa-2.1/src/eap_peer/eap_tls_common.h000066400000000000000000000073711227414666700177110ustar00rootroot00000000000000/* * EAP peer: EAP-TLS/PEAP/TTLS/FAST common functions * Copyright (c) 2004-2009, 2012, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef EAP_TLS_COMMON_H #define EAP_TLS_COMMON_H /** * struct eap_ssl_data - TLS data for EAP methods */ struct eap_ssl_data { /** * conn - TLS connection context data from tls_connection_init() */ struct tls_connection *conn; /** * tls_out - TLS message to be sent out in fragments */ struct wpabuf *tls_out; /** * tls_out_pos - The current position in the outgoing TLS message */ size_t tls_out_pos; /** * tls_out_limit - Maximum fragment size for outgoing TLS messages */ size_t tls_out_limit; /** * tls_in - Received TLS message buffer for re-assembly */ struct wpabuf *tls_in; /** * tls_in_left - Number of remaining bytes in the incoming TLS message */ size_t tls_in_left; /** * tls_in_total - Total number of bytes in the incoming TLS message */ size_t tls_in_total; /** * phase2 - Whether this TLS connection is used in EAP phase 2 (tunnel) */ int phase2; /** * include_tls_length - Whether the TLS length field is included even * if the TLS data is not fragmented */ int include_tls_length; /** * eap - EAP state machine allocated with eap_peer_sm_init() */ struct eap_sm *eap; /** * ssl_ctx - TLS library context to use for the connection */ void *ssl_ctx; /** * eap_type - EAP method used in Phase 1 (EAP_TYPE_TLS/PEAP/TTLS/FAST) */ u8 eap_type; }; /* EAP TLS Flags */ #define EAP_TLS_FLAGS_LENGTH_INCLUDED 0x80 #define EAP_TLS_FLAGS_MORE_FRAGMENTS 0x40 #define EAP_TLS_FLAGS_START 0x20 #define EAP_TLS_VERSION_MASK 0x07 /* could be up to 128 bytes, but only the first 64 bytes are used */ #define EAP_TLS_KEY_LEN 64 /* dummy type used as a flag for UNAUTH-TLS */ #define EAP_UNAUTH_TLS_TYPE 255 int eap_peer_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, struct eap_peer_config *config, u8 eap_type); void eap_peer_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data); u8 * eap_peer_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, const char *label, size_t len); u8 * eap_peer_tls_derive_session_id(struct eap_sm *sm, struct eap_ssl_data *data, u8 eap_type, size_t *len); int eap_peer_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data, EapType eap_type, int peap_version, u8 id, const u8 *in_data, size_t in_len, struct wpabuf **out_data); struct wpabuf * eap_peer_tls_build_ack(u8 id, EapType eap_type, int peap_version); int eap_peer_tls_reauth_init(struct eap_sm *sm, struct eap_ssl_data *data); int eap_peer_tls_status(struct eap_sm *sm, struct eap_ssl_data *data, char *buf, size_t buflen, int verbose); const u8 * eap_peer_tls_process_init(struct eap_sm *sm, struct eap_ssl_data *data, EapType eap_type, struct eap_method_ret *ret, const struct wpabuf *reqData, size_t *len, u8 *flags); void eap_peer_tls_reset_input(struct eap_ssl_data *data); void eap_peer_tls_reset_output(struct eap_ssl_data *data); int eap_peer_tls_decrypt(struct eap_sm *sm, struct eap_ssl_data *data, const struct wpabuf *in_data, struct wpabuf **in_decrypted); int eap_peer_tls_encrypt(struct eap_sm *sm, struct eap_ssl_data *data, EapType eap_type, int peap_version, u8 id, const struct wpabuf *in_data, struct wpabuf **out_data); int eap_peer_select_phase2_methods(struct eap_peer_config *config, const char *prefix, struct eap_method_type **types, size_t *num_types); int eap_peer_tls_phase2_nak(struct eap_method_type *types, size_t num_types, struct eap_hdr *hdr, struct wpabuf **resp); #endif /* EAP_TLS_COMMON_H */ wpa-2.1/src/eap_peer/eap_tnc.c000066400000000000000000000240241227414666700163100ustar00rootroot00000000000000/* * EAP peer method: EAP-TNC (Trusted Network Connect) * Copyright (c) 2007, 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_i.h" #include "tncc.h" struct eap_tnc_data { enum { WAIT_START, PROC_MSG, WAIT_FRAG_ACK, DONE, FAIL } state; struct tncc_data *tncc; struct wpabuf *in_buf; struct wpabuf *out_buf; size_t out_used; size_t fragment_size; }; /* EAP-TNC Flags */ #define EAP_TNC_FLAGS_LENGTH_INCLUDED 0x80 #define EAP_TNC_FLAGS_MORE_FRAGMENTS 0x40 #define EAP_TNC_FLAGS_START 0x20 #define EAP_TNC_VERSION_MASK 0x07 #define EAP_TNC_VERSION 1 static void * eap_tnc_init(struct eap_sm *sm) { struct eap_tnc_data *data; data = os_zalloc(sizeof(*data)); if (data == NULL) return NULL; data->state = WAIT_START; data->fragment_size = 1300; data->tncc = tncc_init(); if (data->tncc == NULL) { os_free(data); return NULL; } return data; } static void eap_tnc_deinit(struct eap_sm *sm, void *priv) { struct eap_tnc_data *data = priv; wpabuf_free(data->in_buf); wpabuf_free(data->out_buf); tncc_deinit(data->tncc); os_free(data); } static struct wpabuf * eap_tnc_build_frag_ack(u8 id, u8 code) { struct wpabuf *msg; msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TNC, 1, code, id); if (msg == NULL) { wpa_printf(MSG_ERROR, "EAP-TNC: Failed to allocate memory " "for fragment ack"); return NULL; } wpabuf_put_u8(msg, EAP_TNC_VERSION); /* Flags */ wpa_printf(MSG_DEBUG, "EAP-TNC: Send fragment ack"); return msg; } static struct wpabuf * eap_tnc_build_msg(struct eap_tnc_data *data, struct eap_method_ret *ret, u8 id) { struct wpabuf *resp; u8 flags; size_t send_len, plen; ret->ignore = FALSE; wpa_printf(MSG_DEBUG, "EAP-TNC: Generating Response"); ret->allowNotifications = TRUE; flags = EAP_TNC_VERSION; send_len = wpabuf_len(data->out_buf) - data->out_used; if (1 + send_len > data->fragment_size) { send_len = data->fragment_size - 1; flags |= EAP_TNC_FLAGS_MORE_FRAGMENTS; if (data->out_used == 0) { flags |= EAP_TNC_FLAGS_LENGTH_INCLUDED; send_len -= 4; } } plen = 1 + send_len; if (flags & EAP_TNC_FLAGS_LENGTH_INCLUDED) plen += 4; resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TNC, plen, EAP_CODE_RESPONSE, id); if (resp == NULL) return NULL; wpabuf_put_u8(resp, flags); /* Flags */ if (flags & EAP_TNC_FLAGS_LENGTH_INCLUDED) wpabuf_put_be32(resp, wpabuf_len(data->out_buf)); wpabuf_put_data(resp, wpabuf_head_u8(data->out_buf) + data->out_used, send_len); data->out_used += send_len; ret->methodState = METHOD_MAY_CONT; ret->decision = DECISION_FAIL; if (data->out_used == wpabuf_len(data->out_buf)) { wpa_printf(MSG_DEBUG, "EAP-TNC: Sending out %lu bytes " "(message sent completely)", (unsigned long) send_len); wpabuf_free(data->out_buf); data->out_buf = NULL; data->out_used = 0; } else { wpa_printf(MSG_DEBUG, "EAP-TNC: Sending out %lu bytes " "(%lu more to send)", (unsigned long) send_len, (unsigned long) wpabuf_len(data->out_buf) - data->out_used); data->state = WAIT_FRAG_ACK; } return resp; } static int eap_tnc_process_cont(struct eap_tnc_data *data, const u8 *buf, size_t len) { /* Process continuation of a pending message */ if (len > wpabuf_tailroom(data->in_buf)) { wpa_printf(MSG_DEBUG, "EAP-TNC: Fragment overflow"); data->state = FAIL; return -1; } wpabuf_put_data(data->in_buf, buf, len); wpa_printf(MSG_DEBUG, "EAP-TNC: Received %lu bytes, waiting for " "%lu bytes more", (unsigned long) len, (unsigned long) wpabuf_tailroom(data->in_buf)); return 0; } static struct wpabuf * eap_tnc_process_fragment(struct eap_tnc_data *data, struct eap_method_ret *ret, u8 id, u8 flags, u32 message_length, const u8 *buf, size_t len) { /* Process a fragment that is not the last one of the message */ if (data->in_buf == NULL && !(flags & EAP_TNC_FLAGS_LENGTH_INCLUDED)) { wpa_printf(MSG_DEBUG, "EAP-TNC: No Message Length field in a " "fragmented packet"); ret->ignore = TRUE; return NULL; } if (data->in_buf == NULL) { /* First fragment of the message */ data->in_buf = wpabuf_alloc(message_length); if (data->in_buf == NULL) { wpa_printf(MSG_DEBUG, "EAP-TNC: No memory for " "message"); ret->ignore = TRUE; return NULL; } wpabuf_put_data(data->in_buf, buf, len); wpa_printf(MSG_DEBUG, "EAP-TNC: Received %lu bytes in first " "fragment, waiting for %lu bytes more", (unsigned long) len, (unsigned long) wpabuf_tailroom(data->in_buf)); } return eap_tnc_build_frag_ack(id, EAP_CODE_RESPONSE); } static struct wpabuf * eap_tnc_process(struct eap_sm *sm, void *priv, struct eap_method_ret *ret, const struct wpabuf *reqData) { struct eap_tnc_data *data = priv; struct wpabuf *resp; const u8 *pos, *end; u8 *rpos, *rpos1; size_t len, rlen; size_t imc_len; char *start_buf, *end_buf; size_t start_len, end_len; int tncs_done = 0; u8 flags, id; u32 message_length = 0; struct wpabuf tmpbuf; pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TNC, reqData, &len); if (pos == NULL) { wpa_printf(MSG_INFO, "EAP-TNC: Invalid frame (pos=%p len=%lu)", pos, (unsigned long) len); ret->ignore = TRUE; return NULL; } id = eap_get_id(reqData); end = pos + len; if (len == 0) flags = 0; /* fragment ack */ else flags = *pos++; if (len > 0 && (flags & EAP_TNC_VERSION_MASK) != EAP_TNC_VERSION) { wpa_printf(MSG_DEBUG, "EAP-TNC: Unsupported version %d", flags & EAP_TNC_VERSION_MASK); ret->ignore = TRUE; return NULL; } if (flags & EAP_TNC_FLAGS_LENGTH_INCLUDED) { if (end - pos < 4) { wpa_printf(MSG_DEBUG, "EAP-TNC: Message underflow"); ret->ignore = TRUE; return NULL; } message_length = WPA_GET_BE32(pos); pos += 4; if (message_length < (u32) (end - pos)) { wpa_printf(MSG_DEBUG, "EAP-TNC: Invalid Message " "Length (%d; %ld remaining in this msg)", message_length, (long) (end - pos)); ret->ignore = TRUE; return NULL; } } wpa_printf(MSG_DEBUG, "EAP-TNC: Received packet: Flags 0x%x " "Message Length %u", flags, message_length); if (data->state == WAIT_FRAG_ACK) { if (len > 1) { wpa_printf(MSG_DEBUG, "EAP-TNC: Unexpected payload in " "WAIT_FRAG_ACK state"); ret->ignore = TRUE; return NULL; } wpa_printf(MSG_DEBUG, "EAP-TNC: Fragment acknowledged"); data->state = PROC_MSG; return eap_tnc_build_msg(data, ret, id); } if (data->in_buf && eap_tnc_process_cont(data, pos, end - pos) < 0) { ret->ignore = TRUE; return NULL; } if (flags & EAP_TNC_FLAGS_MORE_FRAGMENTS) { return eap_tnc_process_fragment(data, ret, id, flags, message_length, pos, end - pos); } if (data->in_buf == NULL) { /* Wrap unfragmented messages as wpabuf without extra copy */ wpabuf_set(&tmpbuf, pos, end - pos); data->in_buf = &tmpbuf; } if (data->state == WAIT_START) { if (!(flags & EAP_TNC_FLAGS_START)) { wpa_printf(MSG_DEBUG, "EAP-TNC: Server did not use " "start flag in the first message"); ret->ignore = TRUE; goto fail; } tncc_init_connection(data->tncc); data->state = PROC_MSG; } else { enum tncc_process_res res; if (flags & EAP_TNC_FLAGS_START) { wpa_printf(MSG_DEBUG, "EAP-TNC: Server used start " "flag again"); ret->ignore = TRUE; goto fail; } res = tncc_process_if_tnccs(data->tncc, wpabuf_head(data->in_buf), wpabuf_len(data->in_buf)); switch (res) { case TNCCS_PROCESS_ERROR: ret->ignore = TRUE; goto fail; case TNCCS_PROCESS_OK_NO_RECOMMENDATION: case TNCCS_RECOMMENDATION_ERROR: wpa_printf(MSG_DEBUG, "EAP-TNC: No " "TNCCS-Recommendation received"); break; case TNCCS_RECOMMENDATION_ALLOW: wpa_msg(sm->msg_ctx, MSG_INFO, "TNC: Recommendation = allow"); tncs_done = 1; break; case TNCCS_RECOMMENDATION_NONE: wpa_msg(sm->msg_ctx, MSG_INFO, "TNC: Recommendation = none"); tncs_done = 1; break; case TNCCS_RECOMMENDATION_ISOLATE: wpa_msg(sm->msg_ctx, MSG_INFO, "TNC: Recommendation = isolate"); tncs_done = 1; break; } } if (data->in_buf != &tmpbuf) wpabuf_free(data->in_buf); data->in_buf = NULL; ret->ignore = FALSE; ret->methodState = METHOD_MAY_CONT; ret->decision = DECISION_UNCOND_SUCC; ret->allowNotifications = TRUE; if (data->out_buf) { data->state = PROC_MSG; return eap_tnc_build_msg(data, ret, id); } if (tncs_done) { resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TNC, 1, EAP_CODE_RESPONSE, eap_get_id(reqData)); if (resp == NULL) return NULL; wpabuf_put_u8(resp, EAP_TNC_VERSION); wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS done - reply with an " "empty ACK message"); return resp; } imc_len = tncc_total_send_len(data->tncc); start_buf = tncc_if_tnccs_start(data->tncc); if (start_buf == NULL) return NULL; start_len = os_strlen(start_buf); end_buf = tncc_if_tnccs_end(); if (end_buf == NULL) { os_free(start_buf); return NULL; } end_len = os_strlen(end_buf); rlen = start_len + imc_len + end_len; resp = wpabuf_alloc(rlen); if (resp == NULL) { os_free(start_buf); os_free(end_buf); return NULL; } wpabuf_put_data(resp, start_buf, start_len); os_free(start_buf); rpos1 = wpabuf_put(resp, 0); rpos = tncc_copy_send_buf(data->tncc, rpos1); wpabuf_put(resp, rpos - rpos1); wpabuf_put_data(resp, end_buf, end_len); os_free(end_buf); wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-TNC: Response", wpabuf_head(resp), wpabuf_len(resp)); data->out_buf = resp; data->state = PROC_MSG; return eap_tnc_build_msg(data, ret, id); fail: if (data->in_buf == &tmpbuf) data->in_buf = NULL; return NULL; } int eap_peer_tnc_register(void) { struct eap_method *eap; int ret; eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_TNC, "TNC"); if (eap == NULL) return -1; eap->init = eap_tnc_init; eap->deinit = eap_tnc_deinit; eap->process = eap_tnc_process; ret = eap_peer_method_register(eap); if (ret) eap_peer_method_free(eap); return ret; } wpa-2.1/src/eap_peer/eap_ttls.c000066400000000000000000001270741227414666700165230ustar00rootroot00000000000000/* * EAP peer method: EAP-TTLS (RFC 5281) * Copyright (c) 2004-2011, 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" #include "crypto/sha1.h" #include "crypto/tls.h" #include "eap_common/chap.h" #include "eap_common/eap_ttls.h" #include "mschapv2.h" #include "eap_i.h" #include "eap_tls_common.h" #include "eap_config.h" #define EAP_TTLS_VERSION 0 static void eap_ttls_deinit(struct eap_sm *sm, void *priv); struct eap_ttls_data { struct eap_ssl_data ssl; int ttls_version; const struct eap_method *phase2_method; void *phase2_priv; int phase2_success; int phase2_start; enum phase2_types { EAP_TTLS_PHASE2_EAP, EAP_TTLS_PHASE2_MSCHAPV2, EAP_TTLS_PHASE2_MSCHAP, EAP_TTLS_PHASE2_PAP, EAP_TTLS_PHASE2_CHAP } phase2_type; struct eap_method_type phase2_eap_type; struct eap_method_type *phase2_eap_types; size_t num_phase2_eap_types; u8 auth_response[MSCHAPV2_AUTH_RESPONSE_LEN]; int auth_response_valid; u8 master_key[MSCHAPV2_MASTER_KEY_LEN]; /* MSCHAPv2 master key */ u8 ident; int resuming; /* starting a resumed session */ int reauth; /* reauthentication */ u8 *key_data; u8 *session_id; size_t id_len; struct wpabuf *pending_phase2_req; #ifdef EAP_TNC int ready_for_tnc; int tnc_started; #endif /* EAP_TNC */ }; static void * eap_ttls_init(struct eap_sm *sm) { struct eap_ttls_data *data; struct eap_peer_config *config = eap_get_config(sm); char *selected; data = os_zalloc(sizeof(*data)); if (data == NULL) return NULL; data->ttls_version = EAP_TTLS_VERSION; selected = "EAP"; data->phase2_type = EAP_TTLS_PHASE2_EAP; if (config && config->phase2) { if (os_strstr(config->phase2, "autheap=")) { selected = "EAP"; data->phase2_type = EAP_TTLS_PHASE2_EAP; } else if (os_strstr(config->phase2, "auth=MSCHAPV2")) { selected = "MSCHAPV2"; data->phase2_type = EAP_TTLS_PHASE2_MSCHAPV2; } else if (os_strstr(config->phase2, "auth=MSCHAP")) { selected = "MSCHAP"; data->phase2_type = EAP_TTLS_PHASE2_MSCHAP; } else if (os_strstr(config->phase2, "auth=PAP")) { selected = "PAP"; data->phase2_type = EAP_TTLS_PHASE2_PAP; } else if (os_strstr(config->phase2, "auth=CHAP")) { selected = "CHAP"; data->phase2_type = EAP_TTLS_PHASE2_CHAP; } } wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase2 type: %s", selected); if (data->phase2_type == EAP_TTLS_PHASE2_EAP) { if (eap_peer_select_phase2_methods(config, "autheap=", &data->phase2_eap_types, &data->num_phase2_eap_types) < 0) { eap_ttls_deinit(sm, data); return NULL; } data->phase2_eap_type.vendor = EAP_VENDOR_IETF; data->phase2_eap_type.method = EAP_TYPE_NONE; } if (eap_peer_tls_ssl_init(sm, &data->ssl, config, EAP_TYPE_TTLS)) { wpa_printf(MSG_INFO, "EAP-TTLS: Failed to initialize SSL."); eap_ttls_deinit(sm, data); return NULL; } return data; } static void eap_ttls_phase2_eap_deinit(struct eap_sm *sm, struct eap_ttls_data *data) { if (data->phase2_priv && data->phase2_method) { data->phase2_method->deinit(sm, data->phase2_priv); data->phase2_method = NULL; data->phase2_priv = NULL; } } static void eap_ttls_deinit(struct eap_sm *sm, void *priv) { struct eap_ttls_data *data = priv; if (data == NULL) return; eap_ttls_phase2_eap_deinit(sm, data); os_free(data->phase2_eap_types); eap_peer_tls_ssl_deinit(sm, &data->ssl); os_free(data->key_data); os_free(data->session_id); wpabuf_free(data->pending_phase2_req); os_free(data); } static u8 * eap_ttls_avp_hdr(u8 *avphdr, u32 avp_code, u32 vendor_id, int mandatory, size_t len) { struct ttls_avp_vendor *avp; u8 flags; size_t hdrlen; avp = (struct ttls_avp_vendor *) avphdr; flags = mandatory ? AVP_FLAGS_MANDATORY : 0; if (vendor_id) { flags |= AVP_FLAGS_VENDOR; hdrlen = sizeof(*avp); avp->vendor_id = host_to_be32(vendor_id); } else { hdrlen = sizeof(struct ttls_avp); } avp->avp_code = host_to_be32(avp_code); avp->avp_length = host_to_be32((flags << 24) | (u32) (hdrlen + len)); return avphdr + hdrlen; } static u8 * eap_ttls_avp_add(u8 *start, u8 *avphdr, u32 avp_code, u32 vendor_id, int mandatory, const u8 *data, size_t len) { u8 *pos; pos = eap_ttls_avp_hdr(avphdr, avp_code, vendor_id, mandatory, len); os_memcpy(pos, data, len); pos += len; AVP_PAD(start, pos); return pos; } static int eap_ttls_avp_encapsulate(struct wpabuf **resp, u32 avp_code, int mandatory) { struct wpabuf *msg; u8 *avp, *pos; msg = wpabuf_alloc(sizeof(struct ttls_avp) + wpabuf_len(*resp) + 4); if (msg == NULL) { wpabuf_free(*resp); *resp = NULL; return -1; } avp = wpabuf_mhead(msg); pos = eap_ttls_avp_hdr(avp, avp_code, 0, mandatory, wpabuf_len(*resp)); os_memcpy(pos, wpabuf_head(*resp), wpabuf_len(*resp)); pos += wpabuf_len(*resp); AVP_PAD(avp, pos); wpabuf_free(*resp); wpabuf_put(msg, pos - avp); *resp = msg; return 0; } static int eap_ttls_v0_derive_key(struct eap_sm *sm, struct eap_ttls_data *data) { os_free(data->key_data); data->key_data = eap_peer_tls_derive_key(sm, &data->ssl, "ttls keying material", EAP_TLS_KEY_LEN); if (!data->key_data) { wpa_printf(MSG_INFO, "EAP-TTLS: Failed to derive key"); return -1; } wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Derived key", data->key_data, EAP_TLS_KEY_LEN); os_free(data->session_id); data->session_id = eap_peer_tls_derive_session_id(sm, &data->ssl, EAP_TYPE_TTLS, &data->id_len); if (data->session_id) { wpa_hexdump(MSG_DEBUG, "EAP-TTLS: Derived Session-Id", data->session_id, data->id_len); } else { wpa_printf(MSG_ERROR, "EAP-TTLS: Failed to derive Session-Id"); } return 0; } static u8 * eap_ttls_implicit_challenge(struct eap_sm *sm, struct eap_ttls_data *data, size_t len) { return eap_peer_tls_derive_key(sm, &data->ssl, "ttls challenge", len); } static void eap_ttls_phase2_select_eap_method(struct eap_ttls_data *data, u8 method) { size_t i; for (i = 0; i < data->num_phase2_eap_types; i++) { if (data->phase2_eap_types[i].vendor != EAP_VENDOR_IETF || data->phase2_eap_types[i].method != method) continue; data->phase2_eap_type.vendor = data->phase2_eap_types[i].vendor; data->phase2_eap_type.method = data->phase2_eap_types[i].method; wpa_printf(MSG_DEBUG, "EAP-TTLS: Selected " "Phase 2 EAP vendor %d method %d", data->phase2_eap_type.vendor, data->phase2_eap_type.method); break; } } static int eap_ttls_phase2_eap_process(struct eap_sm *sm, struct eap_ttls_data *data, struct eap_method_ret *ret, struct eap_hdr *hdr, size_t len, struct wpabuf **resp) { struct wpabuf msg; struct eap_method_ret iret; os_memset(&iret, 0, sizeof(iret)); wpabuf_set(&msg, hdr, len); *resp = data->phase2_method->process(sm, data->phase2_priv, &iret, &msg); if ((iret.methodState == METHOD_DONE || iret.methodState == METHOD_MAY_CONT) && (iret.decision == DECISION_UNCOND_SUCC || iret.decision == DECISION_COND_SUCC || iret.decision == DECISION_FAIL)) { ret->methodState = iret.methodState; ret->decision = iret.decision; } return 0; } static int eap_ttls_phase2_request_eap_method(struct eap_sm *sm, struct eap_ttls_data *data, struct eap_method_ret *ret, struct eap_hdr *hdr, size_t len, u8 method, struct wpabuf **resp) { #ifdef EAP_TNC if (data->tnc_started && data->phase2_method && data->phase2_priv && method == EAP_TYPE_TNC && data->phase2_eap_type.method == EAP_TYPE_TNC) return eap_ttls_phase2_eap_process(sm, data, ret, hdr, len, resp); if (data->ready_for_tnc && !data->tnc_started && method == EAP_TYPE_TNC) { wpa_printf(MSG_DEBUG, "EAP-TTLS: Start TNC after completed " "EAP method"); data->tnc_started = 1; } if (data->tnc_started) { if (data->phase2_eap_type.vendor != EAP_VENDOR_IETF || data->phase2_eap_type.method == EAP_TYPE_TNC) { wpa_printf(MSG_DEBUG, "EAP-TTLS: Unexpected EAP " "type %d for TNC", method); return -1; } data->phase2_eap_type.vendor = EAP_VENDOR_IETF; data->phase2_eap_type.method = method; wpa_printf(MSG_DEBUG, "EAP-TTLS: Selected " "Phase 2 EAP vendor %d method %d (TNC)", data->phase2_eap_type.vendor, data->phase2_eap_type.method); if (data->phase2_type == EAP_TTLS_PHASE2_EAP) eap_ttls_phase2_eap_deinit(sm, data); } #endif /* EAP_TNC */ if (data->phase2_eap_type.vendor == EAP_VENDOR_IETF && data->phase2_eap_type.method == EAP_TYPE_NONE) eap_ttls_phase2_select_eap_method(data, method); if (method != data->phase2_eap_type.method || method == EAP_TYPE_NONE) { if (eap_peer_tls_phase2_nak(data->phase2_eap_types, data->num_phase2_eap_types, hdr, resp)) return -1; return 0; } if (data->phase2_priv == NULL) { data->phase2_method = eap_peer_get_eap_method( EAP_VENDOR_IETF, method); if (data->phase2_method) { sm->init_phase2 = 1; data->phase2_priv = data->phase2_method->init(sm); sm->init_phase2 = 0; } } if (data->phase2_priv == NULL || data->phase2_method == NULL) { wpa_printf(MSG_INFO, "EAP-TTLS: failed to initialize " "Phase 2 EAP method %d", method); return -1; } return eap_ttls_phase2_eap_process(sm, data, ret, hdr, len, resp); } static int eap_ttls_phase2_request_eap(struct eap_sm *sm, struct eap_ttls_data *data, struct eap_method_ret *ret, struct eap_hdr *hdr, struct wpabuf **resp) { size_t len = be_to_host16(hdr->length); u8 *pos; struct eap_peer_config *config = eap_get_config(sm); if (len <= sizeof(struct eap_hdr)) { wpa_printf(MSG_INFO, "EAP-TTLS: too short " "Phase 2 request (len=%lu)", (unsigned long) len); return -1; } pos = (u8 *) (hdr + 1); wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 EAP Request: type=%d", *pos); switch (*pos) { case EAP_TYPE_IDENTITY: *resp = eap_sm_buildIdentity(sm, hdr->identifier, 1); break; default: if (eap_ttls_phase2_request_eap_method(sm, data, ret, hdr, len, *pos, resp) < 0) return -1; break; } if (*resp == NULL && (config->pending_req_identity || config->pending_req_password || config->pending_req_otp)) { return 0; } if (*resp == NULL) return -1; wpa_hexdump_buf(MSG_DEBUG, "EAP-TTLS: AVP encapsulate EAP Response", *resp); return eap_ttls_avp_encapsulate(resp, RADIUS_ATTR_EAP_MESSAGE, 1); } static int eap_ttls_phase2_request_mschapv2(struct eap_sm *sm, struct eap_ttls_data *data, struct eap_method_ret *ret, struct wpabuf **resp) { #ifdef EAP_MSCHAPv2 struct wpabuf *msg; u8 *buf, *pos, *challenge, *peer_challenge; const u8 *identity, *password; size_t identity_len, password_len; int pwhash; wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 MSCHAPV2 Request"); identity = eap_get_config_identity(sm, &identity_len); password = eap_get_config_password2(sm, &password_len, &pwhash); if (identity == NULL || password == NULL) return -1; msg = wpabuf_alloc(identity_len + 1000); if (msg == NULL) { wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAPV2: Failed to allocate memory"); return -1; } pos = buf = wpabuf_mhead(msg); /* User-Name */ pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1, identity, identity_len); /* MS-CHAP-Challenge */ challenge = eap_ttls_implicit_challenge( sm, data, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN + 1); if (challenge == NULL) { wpabuf_free(msg); wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAPV2: Failed to derive " "implicit challenge"); return -1; } pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_MS_CHAP_CHALLENGE, RADIUS_VENDOR_ID_MICROSOFT, 1, challenge, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN); /* MS-CHAP2-Response */ pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_MS_CHAP2_RESPONSE, RADIUS_VENDOR_ID_MICROSOFT, 1, EAP_TTLS_MSCHAPV2_RESPONSE_LEN); data->ident = challenge[EAP_TTLS_MSCHAPV2_CHALLENGE_LEN]; *pos++ = data->ident; *pos++ = 0; /* Flags */ if (os_get_random(pos, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN) < 0) { os_free(challenge); wpabuf_free(msg); wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAPV2: Failed to get " "random data for peer challenge"); return -1; } peer_challenge = pos; pos += EAP_TTLS_MSCHAPV2_CHALLENGE_LEN; os_memset(pos, 0, 8); /* Reserved, must be zero */ pos += 8; if (mschapv2_derive_response(identity, identity_len, password, password_len, pwhash, challenge, peer_challenge, pos, data->auth_response, data->master_key)) { os_free(challenge); wpabuf_free(msg); wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAPV2: Failed to derive " "response"); return -1; } data->auth_response_valid = 1; pos += 24; os_free(challenge); AVP_PAD(buf, pos); wpabuf_put(msg, pos - buf); *resp = msg; if (sm->workaround) { /* At least FreeRADIUS seems to be terminating * EAP-TTLS/MSHCAPV2 without the expected MS-CHAP-v2 Success * packet. */ wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: EAP workaround - " "allow success without tunneled response"); ret->methodState = METHOD_MAY_CONT; ret->decision = DECISION_COND_SUCC; } return 0; #else /* EAP_MSCHAPv2 */ wpa_printf(MSG_ERROR, "EAP-TTLS: MSCHAPv2 not included in the build"); return -1; #endif /* EAP_MSCHAPv2 */ } static int eap_ttls_phase2_request_mschap(struct eap_sm *sm, struct eap_ttls_data *data, struct eap_method_ret *ret, struct wpabuf **resp) { struct wpabuf *msg; u8 *buf, *pos, *challenge; const u8 *identity, *password; size_t identity_len, password_len; int pwhash; wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 MSCHAP Request"); identity = eap_get_config_identity(sm, &identity_len); password = eap_get_config_password2(sm, &password_len, &pwhash); if (identity == NULL || password == NULL) return -1; msg = wpabuf_alloc(identity_len + 1000); if (msg == NULL) { wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAP: Failed to allocate memory"); return -1; } pos = buf = wpabuf_mhead(msg); /* User-Name */ pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1, identity, identity_len); /* MS-CHAP-Challenge */ challenge = eap_ttls_implicit_challenge( sm, data, EAP_TTLS_MSCHAP_CHALLENGE_LEN + 1); if (challenge == NULL) { wpabuf_free(msg); wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAP: Failed to derive " "implicit challenge"); return -1; } pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_MS_CHAP_CHALLENGE, RADIUS_VENDOR_ID_MICROSOFT, 1, challenge, EAP_TTLS_MSCHAP_CHALLENGE_LEN); /* MS-CHAP-Response */ pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_MS_CHAP_RESPONSE, RADIUS_VENDOR_ID_MICROSOFT, 1, EAP_TTLS_MSCHAP_RESPONSE_LEN); data->ident = challenge[EAP_TTLS_MSCHAP_CHALLENGE_LEN]; *pos++ = data->ident; *pos++ = 1; /* Flags: Use NT style passwords */ os_memset(pos, 0, 24); /* LM-Response */ pos += 24; if (pwhash) { challenge_response(challenge, password, pos); /* NT-Response */ wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: MSCHAP password hash", password, 16); } else { nt_challenge_response(challenge, password, password_len, pos); /* NT-Response */ wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-TTLS: MSCHAP password", password, password_len); } wpa_hexdump(MSG_DEBUG, "EAP-TTLS: MSCHAP implicit challenge", challenge, EAP_TTLS_MSCHAP_CHALLENGE_LEN); wpa_hexdump(MSG_DEBUG, "EAP-TTLS: MSCHAP response", pos, 24); pos += 24; os_free(challenge); AVP_PAD(buf, pos); wpabuf_put(msg, pos - buf); *resp = msg; /* EAP-TTLS/MSCHAP does not provide tunneled success * notification, so assume that Phase2 succeeds. */ ret->methodState = METHOD_DONE; ret->decision = DECISION_COND_SUCC; return 0; } static int eap_ttls_phase2_request_pap(struct eap_sm *sm, struct eap_ttls_data *data, struct eap_method_ret *ret, struct wpabuf **resp) { struct wpabuf *msg; u8 *buf, *pos; size_t pad; const u8 *identity, *password; size_t identity_len, password_len; wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 PAP Request"); identity = eap_get_config_identity(sm, &identity_len); password = eap_get_config_password(sm, &password_len); if (identity == NULL || password == NULL) return -1; msg = wpabuf_alloc(identity_len + password_len + 100); if (msg == NULL) { wpa_printf(MSG_ERROR, "EAP-TTLS/PAP: Failed to allocate memory"); return -1; } pos = buf = wpabuf_mhead(msg); /* User-Name */ pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1, identity, identity_len); /* User-Password; in RADIUS, this is encrypted, but EAP-TTLS encrypts * the data, so no separate encryption is used in the AVP itself. * However, the password is padded to obfuscate its length. */ pad = password_len == 0 ? 16 : (16 - (password_len & 15)) & 15; pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_USER_PASSWORD, 0, 1, password_len + pad); os_memcpy(pos, password, password_len); pos += password_len; os_memset(pos, 0, pad); pos += pad; AVP_PAD(buf, pos); wpabuf_put(msg, pos - buf); *resp = msg; /* EAP-TTLS/PAP does not provide tunneled success notification, * so assume that Phase2 succeeds. */ ret->methodState = METHOD_DONE; ret->decision = DECISION_COND_SUCC; return 0; } static int eap_ttls_phase2_request_chap(struct eap_sm *sm, struct eap_ttls_data *data, struct eap_method_ret *ret, struct wpabuf **resp) { struct wpabuf *msg; u8 *buf, *pos, *challenge; const u8 *identity, *password; size_t identity_len, password_len; wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 CHAP Request"); identity = eap_get_config_identity(sm, &identity_len); password = eap_get_config_password(sm, &password_len); if (identity == NULL || password == NULL) return -1; msg = wpabuf_alloc(identity_len + 1000); if (msg == NULL) { wpa_printf(MSG_ERROR, "EAP-TTLS/CHAP: Failed to allocate memory"); return -1; } pos = buf = wpabuf_mhead(msg); /* User-Name */ pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1, identity, identity_len); /* CHAP-Challenge */ challenge = eap_ttls_implicit_challenge( sm, data, EAP_TTLS_CHAP_CHALLENGE_LEN + 1); if (challenge == NULL) { wpabuf_free(msg); wpa_printf(MSG_ERROR, "EAP-TTLS/CHAP: Failed to derive " "implicit challenge"); return -1; } pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_CHAP_CHALLENGE, 0, 1, challenge, EAP_TTLS_CHAP_CHALLENGE_LEN); /* CHAP-Password */ pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_CHAP_PASSWORD, 0, 1, 1 + EAP_TTLS_CHAP_PASSWORD_LEN); data->ident = challenge[EAP_TTLS_CHAP_CHALLENGE_LEN]; *pos++ = data->ident; /* MD5(Ident + Password + Challenge) */ chap_md5(data->ident, password, password_len, challenge, EAP_TTLS_CHAP_CHALLENGE_LEN, pos); wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: CHAP username", identity, identity_len); wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-TTLS: CHAP password", password, password_len); wpa_hexdump(MSG_DEBUG, "EAP-TTLS: CHAP implicit challenge", challenge, EAP_TTLS_CHAP_CHALLENGE_LEN); wpa_hexdump(MSG_DEBUG, "EAP-TTLS: CHAP password", pos, EAP_TTLS_CHAP_PASSWORD_LEN); pos += EAP_TTLS_CHAP_PASSWORD_LEN; os_free(challenge); AVP_PAD(buf, pos); wpabuf_put(msg, pos - buf); *resp = msg; /* EAP-TTLS/CHAP does not provide tunneled success * notification, so assume that Phase2 succeeds. */ ret->methodState = METHOD_DONE; ret->decision = DECISION_COND_SUCC; return 0; } static int eap_ttls_phase2_request(struct eap_sm *sm, struct eap_ttls_data *data, struct eap_method_ret *ret, struct eap_hdr *hdr, struct wpabuf **resp) { int res = 0; size_t len; enum phase2_types phase2_type = data->phase2_type; #ifdef EAP_TNC if (data->tnc_started) { wpa_printf(MSG_DEBUG, "EAP-TTLS: Processing TNC"); phase2_type = EAP_TTLS_PHASE2_EAP; } #endif /* EAP_TNC */ if (phase2_type == EAP_TTLS_PHASE2_MSCHAPV2 || phase2_type == EAP_TTLS_PHASE2_MSCHAP || phase2_type == EAP_TTLS_PHASE2_PAP || phase2_type == EAP_TTLS_PHASE2_CHAP) { if (eap_get_config_identity(sm, &len) == NULL) { wpa_printf(MSG_INFO, "EAP-TTLS: Identity not configured"); eap_sm_request_identity(sm); if (eap_get_config_password(sm, &len) == NULL) eap_sm_request_password(sm); return 0; } if (eap_get_config_password(sm, &len) == NULL) { wpa_printf(MSG_INFO, "EAP-TTLS: Password not configured"); eap_sm_request_password(sm); return 0; } } switch (phase2_type) { case EAP_TTLS_PHASE2_EAP: res = eap_ttls_phase2_request_eap(sm, data, ret, hdr, resp); break; case EAP_TTLS_PHASE2_MSCHAPV2: res = eap_ttls_phase2_request_mschapv2(sm, data, ret, resp); break; case EAP_TTLS_PHASE2_MSCHAP: res = eap_ttls_phase2_request_mschap(sm, data, ret, resp); break; case EAP_TTLS_PHASE2_PAP: res = eap_ttls_phase2_request_pap(sm, data, ret, resp); break; case EAP_TTLS_PHASE2_CHAP: res = eap_ttls_phase2_request_chap(sm, data, ret, resp); break; default: wpa_printf(MSG_ERROR, "EAP-TTLS: Phase 2 - Unknown"); res = -1; break; } if (res < 0) { ret->methodState = METHOD_DONE; ret->decision = DECISION_FAIL; } return res; } struct ttls_parse_avp { u8 *mschapv2; u8 *eapdata; size_t eap_len; int mschapv2_error; }; static int eap_ttls_parse_attr_eap(const u8 *dpos, size_t dlen, struct ttls_parse_avp *parse) { wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP - EAP Message"); if (parse->eapdata == NULL) { parse->eapdata = os_malloc(dlen); if (parse->eapdata == NULL) { wpa_printf(MSG_WARNING, "EAP-TTLS: Failed to allocate " "memory for Phase 2 EAP data"); return -1; } os_memcpy(parse->eapdata, dpos, dlen); parse->eap_len = dlen; } else { u8 *neweap = os_realloc(parse->eapdata, parse->eap_len + dlen); if (neweap == NULL) { wpa_printf(MSG_WARNING, "EAP-TTLS: Failed to allocate " "memory for Phase 2 EAP data"); return -1; } os_memcpy(neweap + parse->eap_len, dpos, dlen); parse->eapdata = neweap; parse->eap_len += dlen; } return 0; } static int eap_ttls_parse_avp(u8 *pos, size_t left, struct ttls_parse_avp *parse) { struct ttls_avp *avp; u32 avp_code, avp_length, vendor_id = 0; u8 avp_flags, *dpos; size_t dlen; avp = (struct ttls_avp *) pos; avp_code = be_to_host32(avp->avp_code); avp_length = be_to_host32(avp->avp_length); avp_flags = (avp_length >> 24) & 0xff; avp_length &= 0xffffff; wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP: code=%d flags=0x%02x " "length=%d", (int) avp_code, avp_flags, (int) avp_length); if (avp_length > left) { wpa_printf(MSG_WARNING, "EAP-TTLS: AVP overflow " "(len=%d, left=%lu) - dropped", (int) avp_length, (unsigned long) left); return -1; } if (avp_length < sizeof(*avp)) { wpa_printf(MSG_WARNING, "EAP-TTLS: Invalid AVP length %d", avp_length); return -1; } dpos = (u8 *) (avp + 1); dlen = avp_length - sizeof(*avp); if (avp_flags & AVP_FLAGS_VENDOR) { if (dlen < 4) { wpa_printf(MSG_WARNING, "EAP-TTLS: Vendor AVP " "underflow"); return -1; } vendor_id = WPA_GET_BE32(dpos); wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP vendor_id %d", (int) vendor_id); dpos += 4; dlen -= 4; } wpa_hexdump(MSG_DEBUG, "EAP-TTLS: AVP data", dpos, dlen); if (vendor_id == 0 && avp_code == RADIUS_ATTR_EAP_MESSAGE) { if (eap_ttls_parse_attr_eap(dpos, dlen, parse) < 0) return -1; } else if (vendor_id == 0 && avp_code == RADIUS_ATTR_REPLY_MESSAGE) { /* This is an optional message that can be displayed to * the user. */ wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: AVP - Reply-Message", dpos, dlen); } else if (vendor_id == RADIUS_VENDOR_ID_MICROSOFT && avp_code == RADIUS_ATTR_MS_CHAP2_SUCCESS) { wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: MS-CHAP2-Success", dpos, dlen); if (dlen != 43) { wpa_printf(MSG_WARNING, "EAP-TTLS: Unexpected " "MS-CHAP2-Success length " "(len=%lu, expected 43)", (unsigned long) dlen); return -1; } parse->mschapv2 = dpos; } else if (vendor_id == RADIUS_VENDOR_ID_MICROSOFT && avp_code == RADIUS_ATTR_MS_CHAP_ERROR) { wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: MS-CHAP-Error", dpos, dlen); parse->mschapv2_error = 1; } else if (avp_flags & AVP_FLAGS_MANDATORY) { wpa_printf(MSG_WARNING, "EAP-TTLS: Unsupported mandatory AVP " "code %d vendor_id %d - dropped", (int) avp_code, (int) vendor_id); return -1; } else { wpa_printf(MSG_DEBUG, "EAP-TTLS: Ignoring unsupported AVP " "code %d vendor_id %d", (int) avp_code, (int) vendor_id); } return avp_length; } static int eap_ttls_parse_avps(struct wpabuf *in_decrypted, struct ttls_parse_avp *parse) { u8 *pos; size_t left, pad; int avp_length; pos = wpabuf_mhead(in_decrypted); left = wpabuf_len(in_decrypted); wpa_hexdump(MSG_DEBUG, "EAP-TTLS: Decrypted Phase 2 AVPs", pos, left); if (left < sizeof(struct ttls_avp)) { wpa_printf(MSG_WARNING, "EAP-TTLS: Too short Phase 2 AVP frame" " len=%lu expected %lu or more - dropped", (unsigned long) left, (unsigned long) sizeof(struct ttls_avp)); return -1; } /* Parse AVPs */ os_memset(parse, 0, sizeof(*parse)); while (left > 0) { avp_length = eap_ttls_parse_avp(pos, left, parse); if (avp_length < 0) return -1; pad = (4 - (avp_length & 3)) & 3; pos += avp_length + pad; if (left < avp_length + pad) left = 0; else left -= avp_length + pad; } return 0; } static u8 * eap_ttls_fake_identity_request(void) { struct eap_hdr *hdr; u8 *buf; wpa_printf(MSG_DEBUG, "EAP-TTLS: empty data in beginning of " "Phase 2 - use fake EAP-Request Identity"); buf = os_malloc(sizeof(*hdr) + 1); if (buf == NULL) { wpa_printf(MSG_WARNING, "EAP-TTLS: failed to allocate " "memory for fake EAP-Identity Request"); return NULL; } hdr = (struct eap_hdr *) buf; hdr->code = EAP_CODE_REQUEST; hdr->identifier = 0; hdr->length = host_to_be16(sizeof(*hdr) + 1); buf[sizeof(*hdr)] = EAP_TYPE_IDENTITY; return buf; } static int eap_ttls_encrypt_response(struct eap_sm *sm, struct eap_ttls_data *data, struct wpabuf *resp, u8 identifier, struct wpabuf **out_data) { if (resp == NULL) return 0; wpa_hexdump_buf_key(MSG_DEBUG, "EAP-TTLS: Encrypting Phase 2 data", resp); if (eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_TTLS, data->ttls_version, identifier, resp, out_data)) { wpa_printf(MSG_INFO, "EAP-TTLS: Failed to encrypt a Phase 2 " "frame"); return -1; } wpabuf_free(resp); return 0; } static int eap_ttls_process_phase2_eap(struct eap_sm *sm, struct eap_ttls_data *data, struct eap_method_ret *ret, struct ttls_parse_avp *parse, struct wpabuf **resp) { struct eap_hdr *hdr; size_t len; if (parse->eapdata == NULL) { wpa_printf(MSG_WARNING, "EAP-TTLS: No EAP Message in the " "packet - dropped"); return -1; } wpa_hexdump(MSG_DEBUG, "EAP-TTLS: Phase 2 EAP", parse->eapdata, parse->eap_len); hdr = (struct eap_hdr *) parse->eapdata; if (parse->eap_len < sizeof(*hdr)) { wpa_printf(MSG_WARNING, "EAP-TTLS: Too short Phase 2 EAP " "frame (len=%lu, expected %lu or more) - dropped", (unsigned long) parse->eap_len, (unsigned long) sizeof(*hdr)); return -1; } len = be_to_host16(hdr->length); if (len > parse->eap_len) { wpa_printf(MSG_INFO, "EAP-TTLS: Length mismatch in Phase 2 " "EAP frame (EAP hdr len=%lu, EAP data len in " "AVP=%lu)", (unsigned long) len, (unsigned long) parse->eap_len); return -1; } wpa_printf(MSG_DEBUG, "EAP-TTLS: received Phase 2: code=%d " "identifier=%d length=%lu", hdr->code, hdr->identifier, (unsigned long) len); switch (hdr->code) { case EAP_CODE_REQUEST: if (eap_ttls_phase2_request(sm, data, ret, hdr, resp)) { wpa_printf(MSG_INFO, "EAP-TTLS: Phase2 Request " "processing failed"); return -1; } break; default: wpa_printf(MSG_INFO, "EAP-TTLS: Unexpected code=%d in " "Phase 2 EAP header", hdr->code); return -1; } return 0; } static int eap_ttls_process_phase2_mschapv2(struct eap_sm *sm, struct eap_ttls_data *data, struct eap_method_ret *ret, struct ttls_parse_avp *parse) { #ifdef EAP_MSCHAPv2 if (parse->mschapv2_error) { wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Received " "MS-CHAP-Error - failed"); ret->methodState = METHOD_DONE; ret->decision = DECISION_FAIL; /* Reply with empty data to ACK error */ return 1; } if (parse->mschapv2 == NULL) { #ifdef EAP_TNC if (data->phase2_success && parse->eapdata) { /* * Allow EAP-TNC to be started after successfully * completed MSCHAPV2. */ return 1; } #endif /* EAP_TNC */ wpa_printf(MSG_WARNING, "EAP-TTLS: no MS-CHAP2-Success AVP " "received for Phase2 MSCHAPV2"); return -1; } if (parse->mschapv2[0] != data->ident) { wpa_printf(MSG_WARNING, "EAP-TTLS: Ident mismatch for Phase 2 " "MSCHAPV2 (received Ident 0x%02x, expected 0x%02x)", parse->mschapv2[0], data->ident); return -1; } if (!data->auth_response_valid || mschapv2_verify_auth_response(data->auth_response, parse->mschapv2 + 1, 42)) { wpa_printf(MSG_WARNING, "EAP-TTLS: Invalid authenticator " "response in Phase 2 MSCHAPV2 success request"); return -1; } wpa_printf(MSG_INFO, "EAP-TTLS: Phase 2 MSCHAPV2 " "authentication succeeded"); ret->methodState = METHOD_DONE; ret->decision = DECISION_UNCOND_SUCC; data->phase2_success = 1; /* * Reply with empty data; authentication server will reply * with EAP-Success after this. */ return 1; #else /* EAP_MSCHAPv2 */ wpa_printf(MSG_ERROR, "EAP-TTLS: MSCHAPv2 not included in the build"); return -1; #endif /* EAP_MSCHAPv2 */ } #ifdef EAP_TNC static int eap_ttls_process_tnc_start(struct eap_sm *sm, struct eap_ttls_data *data, struct eap_method_ret *ret, struct ttls_parse_avp *parse, struct wpabuf **resp) { /* TNC uses inner EAP method after non-EAP TTLS phase 2. */ if (parse->eapdata == NULL) { wpa_printf(MSG_INFO, "EAP-TTLS: Phase 2 received " "unexpected tunneled data (no EAP)"); return -1; } if (!data->ready_for_tnc) { wpa_printf(MSG_INFO, "EAP-TTLS: Phase 2 received " "EAP after non-EAP, but not ready for TNC"); return -1; } wpa_printf(MSG_DEBUG, "EAP-TTLS: Start TNC after completed " "non-EAP method"); data->tnc_started = 1; if (eap_ttls_process_phase2_eap(sm, data, ret, parse, resp) < 0) return -1; return 0; } #endif /* EAP_TNC */ static int eap_ttls_process_decrypted(struct eap_sm *sm, struct eap_ttls_data *data, struct eap_method_ret *ret, u8 identifier, struct ttls_parse_avp *parse, struct wpabuf *in_decrypted, struct wpabuf **out_data) { struct wpabuf *resp = NULL; struct eap_peer_config *config = eap_get_config(sm); int res; enum phase2_types phase2_type = data->phase2_type; #ifdef EAP_TNC if (data->tnc_started) phase2_type = EAP_TTLS_PHASE2_EAP; #endif /* EAP_TNC */ switch (phase2_type) { case EAP_TTLS_PHASE2_EAP: if (eap_ttls_process_phase2_eap(sm, data, ret, parse, &resp) < 0) return -1; break; case EAP_TTLS_PHASE2_MSCHAPV2: res = eap_ttls_process_phase2_mschapv2(sm, data, ret, parse); #ifdef EAP_TNC if (res == 1 && parse->eapdata && data->phase2_success) { /* * TNC may be required as the next * authentication method within the tunnel. */ ret->methodState = METHOD_MAY_CONT; data->ready_for_tnc = 1; if (eap_ttls_process_tnc_start(sm, data, ret, parse, &resp) == 0) break; } #endif /* EAP_TNC */ return res; case EAP_TTLS_PHASE2_MSCHAP: case EAP_TTLS_PHASE2_PAP: case EAP_TTLS_PHASE2_CHAP: #ifdef EAP_TNC if (eap_ttls_process_tnc_start(sm, data, ret, parse, &resp) < 0) return -1; break; #else /* EAP_TNC */ /* EAP-TTLS/{MSCHAP,PAP,CHAP} should not send any TLS tunneled * requests to the supplicant */ wpa_printf(MSG_INFO, "EAP-TTLS: Phase 2 received unexpected " "tunneled data"); return -1; #endif /* EAP_TNC */ } if (resp) { if (eap_ttls_encrypt_response(sm, data, resp, identifier, out_data) < 0) return -1; } else if (config->pending_req_identity || config->pending_req_password || config->pending_req_otp || config->pending_req_new_password) { wpabuf_free(data->pending_phase2_req); data->pending_phase2_req = wpabuf_dup(in_decrypted); } return 0; } static int eap_ttls_implicit_identity_request(struct eap_sm *sm, struct eap_ttls_data *data, struct eap_method_ret *ret, u8 identifier, struct wpabuf **out_data) { int retval = 0; struct eap_hdr *hdr; struct wpabuf *resp; hdr = (struct eap_hdr *) eap_ttls_fake_identity_request(); if (hdr == NULL) { ret->methodState = METHOD_DONE; ret->decision = DECISION_FAIL; return -1; } resp = NULL; if (eap_ttls_phase2_request(sm, data, ret, hdr, &resp)) { wpa_printf(MSG_INFO, "EAP-TTLS: Phase2 Request " "processing failed"); retval = -1; } else { struct eap_peer_config *config = eap_get_config(sm); if (resp == NULL && (config->pending_req_identity || config->pending_req_password || config->pending_req_otp || config->pending_req_new_password)) { /* * Use empty buffer to force implicit request * processing when EAP request is re-processed after * user input. */ wpabuf_free(data->pending_phase2_req); data->pending_phase2_req = wpabuf_alloc(0); } retval = eap_ttls_encrypt_response(sm, data, resp, identifier, out_data); } os_free(hdr); if (retval < 0) { ret->methodState = METHOD_DONE; ret->decision = DECISION_FAIL; } return retval; } static int eap_ttls_phase2_start(struct eap_sm *sm, struct eap_ttls_data *data, struct eap_method_ret *ret, u8 identifier, struct wpabuf **out_data) { data->phase2_start = 0; /* * EAP-TTLS does not use Phase2 on fast re-auth; this must be done only * if TLS part was indeed resuming a previous session. Most * Authentication Servers terminate EAP-TTLS before reaching this * point, but some do not. Make wpa_supplicant stop phase 2 here, if * needed. */ if (data->reauth && tls_connection_resumed(sm->ssl_ctx, data->ssl.conn)) { wpa_printf(MSG_DEBUG, "EAP-TTLS: Session resumption - " "skip phase 2"); *out_data = eap_peer_tls_build_ack(identifier, EAP_TYPE_TTLS, data->ttls_version); ret->methodState = METHOD_DONE; ret->decision = DECISION_UNCOND_SUCC; data->phase2_success = 1; return 0; } return eap_ttls_implicit_identity_request(sm, data, ret, identifier, out_data); } static int eap_ttls_decrypt(struct eap_sm *sm, struct eap_ttls_data *data, struct eap_method_ret *ret, u8 identifier, const struct wpabuf *in_data, struct wpabuf **out_data) { struct wpabuf *in_decrypted = NULL; int retval = 0; struct ttls_parse_avp parse; os_memset(&parse, 0, sizeof(parse)); wpa_printf(MSG_DEBUG, "EAP-TTLS: received %lu bytes encrypted data for" " Phase 2", in_data ? (unsigned long) wpabuf_len(in_data) : 0); if (data->pending_phase2_req) { wpa_printf(MSG_DEBUG, "EAP-TTLS: Pending Phase 2 request - " "skip decryption and use old data"); /* Clear TLS reassembly state. */ eap_peer_tls_reset_input(&data->ssl); in_decrypted = data->pending_phase2_req; data->pending_phase2_req = NULL; if (wpabuf_len(in_decrypted) == 0) { wpabuf_free(in_decrypted); return eap_ttls_implicit_identity_request( sm, data, ret, identifier, out_data); } goto continue_req; } if ((in_data == NULL || wpabuf_len(in_data) == 0) && data->phase2_start) { return eap_ttls_phase2_start(sm, data, ret, identifier, out_data); } if (in_data == NULL || wpabuf_len(in_data) == 0) { /* Received TLS ACK - requesting more fragments */ return eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_TTLS, data->ttls_version, identifier, NULL, out_data); } retval = eap_peer_tls_decrypt(sm, &data->ssl, in_data, &in_decrypted); if (retval) goto done; continue_req: data->phase2_start = 0; if (eap_ttls_parse_avps(in_decrypted, &parse) < 0) { retval = -1; goto done; } retval = eap_ttls_process_decrypted(sm, data, ret, identifier, &parse, in_decrypted, out_data); done: wpabuf_free(in_decrypted); os_free(parse.eapdata); if (retval < 0) { ret->methodState = METHOD_DONE; ret->decision = DECISION_FAIL; } return retval; } static int eap_ttls_process_handshake(struct eap_sm *sm, struct eap_ttls_data *data, struct eap_method_ret *ret, u8 identifier, const u8 *in_data, size_t in_len, struct wpabuf **out_data) { int res; res = eap_peer_tls_process_helper(sm, &data->ssl, EAP_TYPE_TTLS, data->ttls_version, identifier, in_data, in_len, out_data); if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) { wpa_printf(MSG_DEBUG, "EAP-TTLS: TLS done, proceed to " "Phase 2"); if (data->resuming) { wpa_printf(MSG_DEBUG, "EAP-TTLS: fast reauth - may " "skip Phase 2"); ret->decision = DECISION_COND_SUCC; ret->methodState = METHOD_MAY_CONT; } data->phase2_start = 1; eap_ttls_v0_derive_key(sm, data); if (*out_data == NULL || wpabuf_len(*out_data) == 0) { if (eap_ttls_decrypt(sm, data, ret, identifier, NULL, out_data)) { wpa_printf(MSG_WARNING, "EAP-TTLS: " "failed to process early " "start for Phase 2"); } res = 0; } data->resuming = 0; } if (res == 2) { struct wpabuf msg; /* * Application data included in the handshake message. */ wpabuf_free(data->pending_phase2_req); data->pending_phase2_req = *out_data; *out_data = NULL; wpabuf_set(&msg, in_data, in_len); res = eap_ttls_decrypt(sm, data, ret, identifier, &msg, out_data); } return res; } static void eap_ttls_check_auth_status(struct eap_sm *sm, struct eap_ttls_data *data, struct eap_method_ret *ret) { if (ret->methodState == METHOD_DONE) { ret->allowNotifications = FALSE; if (ret->decision == DECISION_UNCOND_SUCC || ret->decision == DECISION_COND_SUCC) { wpa_printf(MSG_DEBUG, "EAP-TTLS: Authentication " "completed successfully"); data->phase2_success = 1; #ifdef EAP_TNC if (!data->ready_for_tnc && !data->tnc_started) { /* * TNC may be required as the next * authentication method within the tunnel. */ ret->methodState = METHOD_MAY_CONT; data->ready_for_tnc = 1; } #endif /* EAP_TNC */ } } else if (ret->methodState == METHOD_MAY_CONT && (ret->decision == DECISION_UNCOND_SUCC || ret->decision == DECISION_COND_SUCC)) { wpa_printf(MSG_DEBUG, "EAP-TTLS: Authentication " "completed successfully (MAY_CONT)"); data->phase2_success = 1; } } static struct wpabuf * eap_ttls_process(struct eap_sm *sm, void *priv, struct eap_method_ret *ret, const struct wpabuf *reqData) { size_t left; int res; u8 flags, id; struct wpabuf *resp; const u8 *pos; struct eap_ttls_data *data = priv; pos = eap_peer_tls_process_init(sm, &data->ssl, EAP_TYPE_TTLS, ret, reqData, &left, &flags); if (pos == NULL) return NULL; id = eap_get_id(reqData); if (flags & EAP_TLS_FLAGS_START) { wpa_printf(MSG_DEBUG, "EAP-TTLS: Start (server ver=%d, own " "ver=%d)", flags & EAP_TLS_VERSION_MASK, data->ttls_version); /* RFC 5281, Ch. 9.2: * "This packet MAY contain additional information in the form * of AVPs, which may provide useful hints to the client" * For now, ignore any potential extra data. */ left = 0; } resp = NULL; if (tls_connection_established(sm->ssl_ctx, data->ssl.conn) && !data->resuming) { struct wpabuf msg; wpabuf_set(&msg, pos, left); res = eap_ttls_decrypt(sm, data, ret, id, &msg, &resp); } else { res = eap_ttls_process_handshake(sm, data, ret, id, pos, left, &resp); } eap_ttls_check_auth_status(sm, data, ret); /* FIX: what about res == -1? Could just move all error processing into * the other functions and get rid of this res==1 case here. */ if (res == 1) { wpabuf_free(resp); return eap_peer_tls_build_ack(id, EAP_TYPE_TTLS, data->ttls_version); } return resp; } static Boolean eap_ttls_has_reauth_data(struct eap_sm *sm, void *priv) { struct eap_ttls_data *data = priv; return tls_connection_established(sm->ssl_ctx, data->ssl.conn) && data->phase2_success; } static void eap_ttls_deinit_for_reauth(struct eap_sm *sm, void *priv) { struct eap_ttls_data *data = priv; wpabuf_free(data->pending_phase2_req); data->pending_phase2_req = NULL; #ifdef EAP_TNC data->ready_for_tnc = 0; data->tnc_started = 0; #endif /* EAP_TNC */ } static void * eap_ttls_init_for_reauth(struct eap_sm *sm, void *priv) { struct eap_ttls_data *data = priv; os_free(data->key_data); data->key_data = NULL; os_free(data->session_id); data->session_id = NULL; if (eap_peer_tls_reauth_init(sm, &data->ssl)) { os_free(data); return NULL; } if (data->phase2_priv && data->phase2_method && data->phase2_method->init_for_reauth) data->phase2_method->init_for_reauth(sm, data->phase2_priv); data->phase2_start = 0; data->phase2_success = 0; data->resuming = 1; data->reauth = 1; return priv; } static int eap_ttls_get_status(struct eap_sm *sm, void *priv, char *buf, size_t buflen, int verbose) { struct eap_ttls_data *data = priv; int len, ret; len = eap_peer_tls_status(sm, &data->ssl, buf, buflen, verbose); ret = os_snprintf(buf + len, buflen - len, "EAP-TTLSv%d Phase2 method=", data->ttls_version); if (ret < 0 || (size_t) ret >= buflen - len) return len; len += ret; switch (data->phase2_type) { case EAP_TTLS_PHASE2_EAP: ret = os_snprintf(buf + len, buflen - len, "EAP-%s\n", data->phase2_method ? data->phase2_method->name : "?"); break; case EAP_TTLS_PHASE2_MSCHAPV2: ret = os_snprintf(buf + len, buflen - len, "MSCHAPV2\n"); break; case EAP_TTLS_PHASE2_MSCHAP: ret = os_snprintf(buf + len, buflen - len, "MSCHAP\n"); break; case EAP_TTLS_PHASE2_PAP: ret = os_snprintf(buf + len, buflen - len, "PAP\n"); break; case EAP_TTLS_PHASE2_CHAP: ret = os_snprintf(buf + len, buflen - len, "CHAP\n"); break; default: ret = 0; break; } if (ret < 0 || (size_t) ret >= buflen - len) return len; len += ret; return len; } static Boolean eap_ttls_isKeyAvailable(struct eap_sm *sm, void *priv) { struct eap_ttls_data *data = priv; return data->key_data != NULL && data->phase2_success; } static u8 * eap_ttls_getKey(struct eap_sm *sm, void *priv, size_t *len) { struct eap_ttls_data *data = priv; u8 *key; if (data->key_data == NULL || !data->phase2_success) return NULL; key = os_malloc(EAP_TLS_KEY_LEN); if (key == NULL) return NULL; *len = EAP_TLS_KEY_LEN; os_memcpy(key, data->key_data, EAP_TLS_KEY_LEN); return key; } static u8 * eap_ttls_get_session_id(struct eap_sm *sm, void *priv, size_t *len) { struct eap_ttls_data *data = priv; u8 *id; if (data->session_id == NULL || !data->phase2_success) return NULL; id = os_malloc(data->id_len); if (id == NULL) return NULL; *len = data->id_len; os_memcpy(id, data->session_id, data->id_len); return id; } int eap_peer_ttls_register(void) { struct eap_method *eap; int ret; eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_TTLS, "TTLS"); if (eap == NULL) return -1; eap->init = eap_ttls_init; eap->deinit = eap_ttls_deinit; eap->process = eap_ttls_process; eap->isKeyAvailable = eap_ttls_isKeyAvailable; eap->getKey = eap_ttls_getKey; eap->getSessionId = eap_ttls_get_session_id; eap->get_status = eap_ttls_get_status; eap->has_reauth_data = eap_ttls_has_reauth_data; eap->deinit_for_reauth = eap_ttls_deinit_for_reauth; eap->init_for_reauth = eap_ttls_init_for_reauth; ret = eap_peer_method_register(eap); if (ret) eap_peer_method_free(eap); return ret; } wpa-2.1/src/eap_peer/eap_vendor_test.c000066400000000000000000000103411227414666700200550ustar00rootroot00000000000000/* * EAP peer method: Test method for vendor specific (expanded) EAP type * Copyright (c) 2005-2006, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. * * This file implements a vendor specific test method using EAP expanded types. * This is only for test use and must not be used for authentication since no * security is provided. */ #include "includes.h" #include "common.h" #include "eap_i.h" #ifdef TEST_PENDING_REQUEST #include "eloop.h" #endif /* TEST_PENDING_REQUEST */ #define EAP_VENDOR_ID EAP_VENDOR_HOSTAP #define EAP_VENDOR_TYPE 0xfcfbfaf9 /* #define TEST_PENDING_REQUEST */ struct eap_vendor_test_data { enum { INIT, CONFIRM, SUCCESS } state; int first_try; }; static void * eap_vendor_test_init(struct eap_sm *sm) { struct eap_vendor_test_data *data; data = os_zalloc(sizeof(*data)); if (data == NULL) return NULL; data->state = INIT; data->first_try = 1; return data; } static void eap_vendor_test_deinit(struct eap_sm *sm, void *priv) { struct eap_vendor_test_data *data = priv; os_free(data); } #ifdef TEST_PENDING_REQUEST static void eap_vendor_ready(void *eloop_ctx, void *timeout_ctx) { struct eap_sm *sm = eloop_ctx; wpa_printf(MSG_DEBUG, "EAP-VENDOR-TEST: Ready to re-process pending " "request"); eap_notify_pending(sm); } #endif /* TEST_PENDING_REQUEST */ static struct wpabuf * eap_vendor_test_process(struct eap_sm *sm, void *priv, struct eap_method_ret *ret, const struct wpabuf *reqData) { struct eap_vendor_test_data *data = priv; struct wpabuf *resp; const u8 *pos; size_t len; pos = eap_hdr_validate(EAP_VENDOR_ID, EAP_VENDOR_TYPE, reqData, &len); if (pos == NULL || len < 1) { ret->ignore = TRUE; return NULL; } if (data->state == INIT && *pos != 1) { wpa_printf(MSG_DEBUG, "EAP-VENDOR-TEST: Unexpected message " "%d in INIT state", *pos); ret->ignore = TRUE; return NULL; } if (data->state == CONFIRM && *pos != 3) { wpa_printf(MSG_DEBUG, "EAP-VENDOR-TEST: Unexpected message " "%d in CONFIRM state", *pos); ret->ignore = TRUE; return NULL; } if (data->state == SUCCESS) { wpa_printf(MSG_DEBUG, "EAP-VENDOR-TEST: Unexpected message " "in SUCCESS state"); ret->ignore = TRUE; return NULL; } if (data->state == CONFIRM) { #ifdef TEST_PENDING_REQUEST if (data->first_try) { data->first_try = 0; wpa_printf(MSG_DEBUG, "EAP-VENDOR-TEST: Testing " "pending request"); ret->ignore = TRUE; eloop_register_timeout(1, 0, eap_vendor_ready, sm, NULL); return NULL; } #endif /* TEST_PENDING_REQUEST */ } ret->ignore = FALSE; wpa_printf(MSG_DEBUG, "EAP-VENDOR-TEST: Generating Response"); ret->allowNotifications = TRUE; resp = eap_msg_alloc(EAP_VENDOR_ID, EAP_VENDOR_TYPE, 1, EAP_CODE_RESPONSE, eap_get_id(reqData)); if (resp == NULL) return NULL; if (data->state == INIT) { wpabuf_put_u8(resp, 2); data->state = CONFIRM; ret->methodState = METHOD_CONT; ret->decision = DECISION_FAIL; } else { wpabuf_put_u8(resp, 4); data->state = SUCCESS; ret->methodState = METHOD_DONE; ret->decision = DECISION_UNCOND_SUCC; } return resp; } static Boolean eap_vendor_test_isKeyAvailable(struct eap_sm *sm, void *priv) { struct eap_vendor_test_data *data = priv; return data->state == SUCCESS; } static u8 * eap_vendor_test_getKey(struct eap_sm *sm, void *priv, size_t *len) { struct eap_vendor_test_data *data = priv; u8 *key; const int key_len = 64; if (data->state != SUCCESS) return NULL; key = os_malloc(key_len); if (key == NULL) return NULL; os_memset(key, 0x11, key_len / 2); os_memset(key + key_len / 2, 0x22, key_len / 2); *len = key_len; return key; } int eap_peer_vendor_test_register(void) { struct eap_method *eap; int ret; eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, EAP_VENDOR_ID, EAP_VENDOR_TYPE, "VENDOR-TEST"); if (eap == NULL) return -1; eap->init = eap_vendor_test_init; eap->deinit = eap_vendor_test_deinit; eap->process = eap_vendor_test_process; eap->isKeyAvailable = eap_vendor_test_isKeyAvailable; eap->getKey = eap_vendor_test_getKey; ret = eap_peer_method_register(eap); if (ret) eap_peer_method_free(eap); return ret; } wpa-2.1/src/eap_peer/eap_wsc.c000066400000000000000000000344151227414666700163250ustar00rootroot00000000000000/* * EAP-WSC peer for Wi-Fi Protected Setup * Copyright (c) 2007-2009, 2012, 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 "uuid.h" #include "eap_i.h" #include "eap_common/eap_wsc_common.h" #include "wps/wps.h" #include "wps/wps_defs.h" struct eap_wsc_data { enum { WAIT_START, MESG, FRAG_ACK, WAIT_FRAG_ACK, DONE, FAIL } state; int registrar; struct wpabuf *in_buf; struct wpabuf *out_buf; enum wsc_op_code in_op_code, out_op_code; size_t out_used; size_t fragment_size; struct wps_data *wps; struct wps_context *wps_ctx; }; static const char * eap_wsc_state_txt(int state) { switch (state) { case WAIT_START: return "WAIT_START"; case MESG: return "MESG"; case FRAG_ACK: return "FRAG_ACK"; case WAIT_FRAG_ACK: return "WAIT_FRAG_ACK"; case DONE: return "DONE"; case FAIL: return "FAIL"; default: return "?"; } } static void eap_wsc_state(struct eap_wsc_data *data, int state) { wpa_printf(MSG_DEBUG, "EAP-WSC: %s -> %s", eap_wsc_state_txt(data->state), eap_wsc_state_txt(state)); data->state = state; } static int eap_wsc_new_ap_settings(struct wps_credential *cred, const char *params) { const char *pos, *end; size_t len; os_memset(cred, 0, sizeof(*cred)); pos = os_strstr(params, "new_ssid="); if (pos == NULL) return 0; pos += 9; end = os_strchr(pos, ' '); if (end == NULL) len = os_strlen(pos); else len = end - pos; if ((len & 1) || len > 2 * sizeof(cred->ssid) || hexstr2bin(pos, cred->ssid, len / 2)) { wpa_printf(MSG_DEBUG, "EAP-WSC: Invalid new_ssid"); return -1; } cred->ssid_len = len / 2; pos = os_strstr(params, "new_auth="); if (pos == NULL) { wpa_printf(MSG_DEBUG, "EAP-WSC: Missing new_auth"); return -1; } if (os_strncmp(pos + 9, "OPEN", 4) == 0) cred->auth_type = WPS_AUTH_OPEN; else if (os_strncmp(pos + 9, "WPAPSK", 6) == 0) cred->auth_type = WPS_AUTH_WPAPSK; else if (os_strncmp(pos + 9, "WPA2PSK", 7) == 0) cred->auth_type = WPS_AUTH_WPA2PSK; else { wpa_printf(MSG_DEBUG, "EAP-WSC: Unknown new_auth"); return -1; } pos = os_strstr(params, "new_encr="); if (pos == NULL) { wpa_printf(MSG_DEBUG, "EAP-WSC: Missing new_encr"); return -1; } if (os_strncmp(pos + 9, "NONE", 4) == 0) cred->encr_type = WPS_ENCR_NONE; else if (os_strncmp(pos + 9, "WEP", 3) == 0) cred->encr_type = WPS_ENCR_WEP; else if (os_strncmp(pos + 9, "TKIP", 4) == 0) cred->encr_type = WPS_ENCR_TKIP; else if (os_strncmp(pos + 9, "CCMP", 4) == 0) cred->encr_type = WPS_ENCR_AES; else { wpa_printf(MSG_DEBUG, "EAP-WSC: Unknown new_encr"); return -1; } pos = os_strstr(params, "new_key="); if (pos == NULL) return 0; pos += 8; end = os_strchr(pos, ' '); if (end == NULL) len = os_strlen(pos); else len = end - pos; if ((len & 1) || len > 2 * sizeof(cred->key) || hexstr2bin(pos, cred->key, len / 2)) { wpa_printf(MSG_DEBUG, "EAP-WSC: Invalid new_key"); return -1; } cred->key_len = len / 2; return 1; } static void * eap_wsc_init(struct eap_sm *sm) { struct eap_wsc_data *data; const u8 *identity; size_t identity_len; int registrar; struct wps_config cfg; const char *pos, *end; const char *phase1; struct wps_context *wps; struct wps_credential new_ap_settings; int res; int nfc = 0; u8 pkhash[WPS_OOB_PUBKEY_HASH_LEN]; wps = sm->wps; if (wps == NULL) { wpa_printf(MSG_ERROR, "EAP-WSC: WPS context not available"); return NULL; } identity = eap_get_config_identity(sm, &identity_len); if (identity && identity_len == WSC_ID_REGISTRAR_LEN && os_memcmp(identity, WSC_ID_REGISTRAR, WSC_ID_REGISTRAR_LEN) == 0) registrar = 1; /* Supplicant is Registrar */ else if (identity && identity_len == WSC_ID_ENROLLEE_LEN && os_memcmp(identity, WSC_ID_ENROLLEE, WSC_ID_ENROLLEE_LEN) == 0) registrar = 0; /* Supplicant is Enrollee */ else { wpa_hexdump_ascii(MSG_INFO, "EAP-WSC: Unexpected identity", identity, identity_len); return NULL; } data = os_zalloc(sizeof(*data)); if (data == NULL) return NULL; data->state = registrar ? MESG : WAIT_START; data->registrar = registrar; data->wps_ctx = wps; os_memset(&cfg, 0, sizeof(cfg)); cfg.wps = wps; cfg.registrar = registrar; phase1 = eap_get_config_phase1(sm); if (phase1 == NULL) { wpa_printf(MSG_INFO, "EAP-WSC: phase1 configuration data not " "set"); os_free(data); return NULL; } pos = os_strstr(phase1, "pin="); if (pos) { pos += 4; cfg.pin = (const u8 *) pos; while (*pos != '\0' && *pos != ' ') pos++; cfg.pin_len = pos - (const char *) cfg.pin; if (cfg.pin_len == 6 && os_strncmp((const char *) cfg.pin, "nfc-pw", 6) == 0) { cfg.pin = NULL; cfg.pin_len = 0; nfc = 1; } } else { pos = os_strstr(phase1, "pbc=1"); if (pos) cfg.pbc = 1; } pos = os_strstr(phase1, "dev_pw_id="); if (pos) { u16 id = atoi(pos + 10); if (id == DEV_PW_NFC_CONNECTION_HANDOVER) nfc = 1; if (cfg.pin || id == DEV_PW_NFC_CONNECTION_HANDOVER) cfg.dev_pw_id = id; } if (cfg.pin == NULL && !cfg.pbc && !nfc) { wpa_printf(MSG_INFO, "EAP-WSC: PIN or PBC not set in phase1 " "configuration data"); os_free(data); return NULL; } pos = os_strstr(phase1, " pkhash="); if (pos) { size_t len; pos += 8; end = os_strchr(pos, ' '); if (end) len = end - pos; else len = os_strlen(pos); if (len != 2 * WPS_OOB_PUBKEY_HASH_LEN || hexstr2bin(pos, pkhash, WPS_OOB_PUBKEY_HASH_LEN)) { wpa_printf(MSG_INFO, "EAP-WSC: Invalid pkhash"); os_free(data); return NULL; } cfg.peer_pubkey_hash = pkhash; } res = eap_wsc_new_ap_settings(&new_ap_settings, phase1); if (res < 0) { os_free(data); wpa_printf(MSG_DEBUG, "EAP-WSC: Failed to parse new AP " "settings"); return NULL; } if (res == 1) { wpa_printf(MSG_DEBUG, "EAP-WSC: Provide new AP settings for " "WPS"); cfg.new_ap_settings = &new_ap_settings; } data->wps = wps_init(&cfg); if (data->wps == NULL) { os_free(data); wpa_printf(MSG_DEBUG, "EAP-WSC: wps_init failed"); return NULL; } res = eap_get_config_fragment_size(sm); if (res > 0) data->fragment_size = res; else data->fragment_size = WSC_FRAGMENT_SIZE; wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment size limit %u", (unsigned int) data->fragment_size); if (registrar && cfg.pin) { wps_registrar_add_pin(data->wps_ctx->registrar, NULL, NULL, cfg.pin, cfg.pin_len, 0); } /* Use reduced client timeout for WPS to avoid long wait */ if (sm->ClientTimeout > 30) sm->ClientTimeout = 30; return data; } static void eap_wsc_deinit(struct eap_sm *sm, void *priv) { struct eap_wsc_data *data = priv; wpabuf_free(data->in_buf); wpabuf_free(data->out_buf); wps_deinit(data->wps); os_free(data->wps_ctx->network_key); data->wps_ctx->network_key = NULL; os_free(data); } static struct wpabuf * eap_wsc_build_msg(struct eap_wsc_data *data, struct eap_method_ret *ret, u8 id) { struct wpabuf *resp; u8 flags; size_t send_len, plen; ret->ignore = FALSE; wpa_printf(MSG_DEBUG, "EAP-WSC: Generating Response"); ret->allowNotifications = TRUE; flags = 0; send_len = wpabuf_len(data->out_buf) - data->out_used; if (2 + send_len > data->fragment_size) { send_len = data->fragment_size - 2; flags |= WSC_FLAGS_MF; if (data->out_used == 0) { flags |= WSC_FLAGS_LF; send_len -= 2; } } plen = 2 + send_len; if (flags & WSC_FLAGS_LF) plen += 2; resp = eap_msg_alloc(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, plen, EAP_CODE_RESPONSE, id); if (resp == NULL) return NULL; wpabuf_put_u8(resp, data->out_op_code); /* Op-Code */ wpabuf_put_u8(resp, flags); /* Flags */ if (flags & WSC_FLAGS_LF) wpabuf_put_be16(resp, wpabuf_len(data->out_buf)); wpabuf_put_data(resp, wpabuf_head_u8(data->out_buf) + data->out_used, send_len); data->out_used += send_len; ret->methodState = METHOD_MAY_CONT; ret->decision = DECISION_FAIL; if (data->out_used == wpabuf_len(data->out_buf)) { wpa_printf(MSG_DEBUG, "EAP-WSC: Sending out %lu bytes " "(message sent completely)", (unsigned long) send_len); wpabuf_free(data->out_buf); data->out_buf = NULL; data->out_used = 0; if ((data->state == FAIL && data->out_op_code == WSC_ACK) || data->out_op_code == WSC_NACK || data->out_op_code == WSC_Done) { eap_wsc_state(data, FAIL); ret->methodState = METHOD_DONE; } else eap_wsc_state(data, MESG); } else { wpa_printf(MSG_DEBUG, "EAP-WSC: Sending out %lu bytes " "(%lu more to send)", (unsigned long) send_len, (unsigned long) wpabuf_len(data->out_buf) - data->out_used); eap_wsc_state(data, WAIT_FRAG_ACK); } return resp; } static int eap_wsc_process_cont(struct eap_wsc_data *data, const u8 *buf, size_t len, u8 op_code) { /* Process continuation of a pending message */ if (op_code != data->in_op_code) { wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d in " "fragment (expected %d)", op_code, data->in_op_code); return -1; } if (len > wpabuf_tailroom(data->in_buf)) { wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment overflow"); eap_wsc_state(data, FAIL); return -1; } wpabuf_put_data(data->in_buf, buf, len); wpa_printf(MSG_DEBUG, "EAP-WSC: Received %lu bytes, waiting " "for %lu bytes more", (unsigned long) len, (unsigned long) wpabuf_tailroom(data->in_buf)); return 0; } static struct wpabuf * eap_wsc_process_fragment(struct eap_wsc_data *data, struct eap_method_ret *ret, u8 id, u8 flags, u8 op_code, u16 message_length, const u8 *buf, size_t len) { /* Process a fragment that is not the last one of the message */ if (data->in_buf == NULL && !(flags & WSC_FLAGS_LF)) { wpa_printf(MSG_DEBUG, "EAP-WSC: No Message Length field in a " "fragmented packet"); ret->ignore = TRUE; return NULL; } if (data->in_buf == NULL) { /* First fragment of the message */ data->in_buf = wpabuf_alloc(message_length); if (data->in_buf == NULL) { wpa_printf(MSG_DEBUG, "EAP-WSC: No memory for " "message"); ret->ignore = TRUE; return NULL; } data->in_op_code = op_code; wpabuf_put_data(data->in_buf, buf, len); wpa_printf(MSG_DEBUG, "EAP-WSC: Received %lu bytes in first " "fragment, waiting for %lu bytes more", (unsigned long) len, (unsigned long) wpabuf_tailroom(data->in_buf)); } return eap_wsc_build_frag_ack(id, EAP_CODE_RESPONSE); } static struct wpabuf * eap_wsc_process(struct eap_sm *sm, void *priv, struct eap_method_ret *ret, const struct wpabuf *reqData) { struct eap_wsc_data *data = priv; const u8 *start, *pos, *end; size_t len; u8 op_code, flags, id; u16 message_length = 0; enum wps_process_res res; struct wpabuf tmpbuf; struct wpabuf *r; pos = eap_hdr_validate(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, reqData, &len); if (pos == NULL || len < 2) { ret->ignore = TRUE; return NULL; } id = eap_get_id(reqData); start = pos; end = start + len; op_code = *pos++; flags = *pos++; if (flags & WSC_FLAGS_LF) { if (end - pos < 2) { wpa_printf(MSG_DEBUG, "EAP-WSC: Message underflow"); ret->ignore = TRUE; return NULL; } message_length = WPA_GET_BE16(pos); pos += 2; if (message_length < end - pos) { wpa_printf(MSG_DEBUG, "EAP-WSC: Invalid Message " "Length"); ret->ignore = TRUE; return NULL; } } wpa_printf(MSG_DEBUG, "EAP-WSC: Received packet: Op-Code %d " "Flags 0x%x Message Length %d", op_code, flags, message_length); if (data->state == WAIT_FRAG_ACK) { if (op_code != WSC_FRAG_ACK) { wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d " "in WAIT_FRAG_ACK state", op_code); ret->ignore = TRUE; return NULL; } wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment acknowledged"); eap_wsc_state(data, MESG); return eap_wsc_build_msg(data, ret, id); } if (op_code != WSC_ACK && op_code != WSC_NACK && op_code != WSC_MSG && op_code != WSC_Done && op_code != WSC_Start) { wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d", op_code); ret->ignore = TRUE; return NULL; } if (data->state == WAIT_START) { if (op_code != WSC_Start) { wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d " "in WAIT_START state", op_code); ret->ignore = TRUE; return NULL; } wpa_printf(MSG_DEBUG, "EAP-WSC: Received start"); eap_wsc_state(data, MESG); /* Start message has empty payload, skip processing */ goto send_msg; } else if (op_code == WSC_Start) { wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d", op_code); ret->ignore = TRUE; return NULL; } if (data->in_buf && eap_wsc_process_cont(data, pos, end - pos, op_code) < 0) { ret->ignore = TRUE; return NULL; } if (flags & WSC_FLAGS_MF) { return eap_wsc_process_fragment(data, ret, id, flags, op_code, message_length, pos, end - pos); } if (data->in_buf == NULL) { /* Wrap unfragmented messages as wpabuf without extra copy */ wpabuf_set(&tmpbuf, pos, end - pos); data->in_buf = &tmpbuf; } res = wps_process_msg(data->wps, op_code, data->in_buf); switch (res) { case WPS_DONE: wpa_printf(MSG_DEBUG, "EAP-WSC: WPS processing completed " "successfully - wait for EAP failure"); eap_wsc_state(data, FAIL); break; case WPS_CONTINUE: eap_wsc_state(data, MESG); break; case WPS_FAILURE: case WPS_PENDING: wpa_printf(MSG_DEBUG, "EAP-WSC: WPS processing failed"); eap_wsc_state(data, FAIL); break; } if (data->in_buf != &tmpbuf) wpabuf_free(data->in_buf); data->in_buf = NULL; send_msg: if (data->out_buf == NULL) { data->out_buf = wps_get_msg(data->wps, &data->out_op_code); if (data->out_buf == NULL) { wpa_printf(MSG_DEBUG, "EAP-WSC: Failed to receive " "message from WPS"); return NULL; } data->out_used = 0; } eap_wsc_state(data, MESG); r = eap_wsc_build_msg(data, ret, id); if (data->state == FAIL && ret->methodState == METHOD_DONE) { /* Use reduced client timeout for WPS to avoid long wait */ if (sm->ClientTimeout > 2) sm->ClientTimeout = 2; } return r; } int eap_peer_wsc_register(void) { struct eap_method *eap; int ret; eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, "WSC"); if (eap == NULL) return -1; eap->init = eap_wsc_init; eap->deinit = eap_wsc_deinit; eap->process = eap_wsc_process; ret = eap_peer_method_register(eap); if (ret) eap_peer_method_free(eap); return ret; } wpa-2.1/src/eap_peer/ikev2.c000066400000000000000000001000341227414666700157130ustar00rootroot00000000000000/* * IKEv2 responder (RFC 4306) for EAP-IKEV2 * Copyright (c) 2007, 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/dh_groups.h" #include "crypto/random.h" #include "ikev2.h" void ikev2_responder_deinit(struct ikev2_responder_data *data) { ikev2_free_keys(&data->keys); wpabuf_free(data->i_dh_public); wpabuf_free(data->r_dh_private); os_free(data->IDi); os_free(data->IDr); os_free(data->shared_secret); wpabuf_free(data->i_sign_msg); wpabuf_free(data->r_sign_msg); os_free(data->key_pad); } static int ikev2_derive_keys(struct ikev2_responder_data *data) { u8 *buf, *pos, *pad, skeyseed[IKEV2_MAX_HASH_LEN]; size_t buf_len, pad_len; struct wpabuf *shared; const struct ikev2_integ_alg *integ; const struct ikev2_prf_alg *prf; const struct ikev2_encr_alg *encr; int ret; const u8 *addr[2]; size_t len[2]; /* RFC 4306, Sect. 2.14 */ integ = ikev2_get_integ(data->proposal.integ); prf = ikev2_get_prf(data->proposal.prf); encr = ikev2_get_encr(data->proposal.encr); if (integ == NULL || prf == NULL || encr == NULL) { wpa_printf(MSG_INFO, "IKEV2: Unsupported proposal"); return -1; } shared = dh_derive_shared(data->i_dh_public, data->r_dh_private, data->dh); if (shared == NULL) return -1; /* Construct Ni | Nr | SPIi | SPIr */ buf_len = data->i_nonce_len + data->r_nonce_len + 2 * IKEV2_SPI_LEN; buf = os_malloc(buf_len); if (buf == NULL) { wpabuf_free(shared); return -1; } pos = buf; os_memcpy(pos, data->i_nonce, data->i_nonce_len); pos += data->i_nonce_len; os_memcpy(pos, data->r_nonce, data->r_nonce_len); pos += data->r_nonce_len; os_memcpy(pos, data->i_spi, IKEV2_SPI_LEN); pos += IKEV2_SPI_LEN; os_memcpy(pos, data->r_spi, IKEV2_SPI_LEN); #ifdef CCNS_PL #if __BYTE_ORDER == __LITTLE_ENDIAN { int i; u8 *tmp = pos - IKEV2_SPI_LEN; /* Incorrect byte re-ordering on little endian hosts.. */ for (i = 0; i < IKEV2_SPI_LEN; i++) *tmp++ = data->i_spi[IKEV2_SPI_LEN - 1 - i]; for (i = 0; i < IKEV2_SPI_LEN; i++) *tmp++ = data->r_spi[IKEV2_SPI_LEN - 1 - i]; } #endif #endif /* CCNS_PL */ /* SKEYSEED = prf(Ni | Nr, g^ir) */ /* Use zero-padding per RFC 4306, Sect. 2.14 */ pad_len = data->dh->prime_len - wpabuf_len(shared); #ifdef CCNS_PL /* Shared secret is not zero-padded correctly */ pad_len = 0; #endif /* CCNS_PL */ pad = os_zalloc(pad_len ? pad_len : 1); if (pad == NULL) { wpabuf_free(shared); os_free(buf); return -1; } addr[0] = pad; len[0] = pad_len; addr[1] = wpabuf_head(shared); len[1] = wpabuf_len(shared); if (ikev2_prf_hash(prf->id, buf, data->i_nonce_len + data->r_nonce_len, 2, addr, len, skeyseed) < 0) { wpabuf_free(shared); os_free(buf); os_free(pad); return -1; } os_free(pad); wpabuf_free(shared); /* DH parameters are not needed anymore, so free them */ wpabuf_free(data->i_dh_public); data->i_dh_public = NULL; wpabuf_free(data->r_dh_private); data->r_dh_private = NULL; wpa_hexdump_key(MSG_DEBUG, "IKEV2: SKEYSEED", skeyseed, prf->hash_len); ret = ikev2_derive_sk_keys(prf, integ, encr, skeyseed, buf, buf_len, &data->keys); os_free(buf); return ret; } static int ikev2_parse_transform(struct ikev2_proposal_data *prop, const u8 *pos, const u8 *end) { int transform_len; const struct ikev2_transform *t; u16 transform_id; const u8 *tend; if (end - pos < (int) sizeof(*t)) { wpa_printf(MSG_INFO, "IKEV2: Too short transform"); return -1; } t = (const struct ikev2_transform *) pos; transform_len = WPA_GET_BE16(t->transform_length); if (transform_len < (int) sizeof(*t) || pos + transform_len > end) { wpa_printf(MSG_INFO, "IKEV2: Invalid transform length %d", transform_len); return -1; } tend = pos + transform_len; transform_id = WPA_GET_BE16(t->transform_id); wpa_printf(MSG_DEBUG, "IKEV2: Transform:"); wpa_printf(MSG_DEBUG, "IKEV2: Type: %d Transform Length: %d " "Transform Type: %d Transform ID: %d", t->type, transform_len, t->transform_type, transform_id); if (t->type != 0 && t->type != 3) { wpa_printf(MSG_INFO, "IKEV2: Unexpected Transform type"); return -1; } pos = (const u8 *) (t + 1); if (pos < tend) { wpa_hexdump(MSG_DEBUG, "IKEV2: Transform Attributes", pos, tend - pos); } switch (t->transform_type) { case IKEV2_TRANSFORM_ENCR: if (ikev2_get_encr(transform_id)) { if (transform_id == ENCR_AES_CBC) { if (tend - pos != 4) { wpa_printf(MSG_DEBUG, "IKEV2: No " "Transform Attr for AES"); break; } #ifdef CCNS_PL if (WPA_GET_BE16(pos) != 0x001d /* ?? */) { wpa_printf(MSG_DEBUG, "IKEV2: Not a " "Key Size attribute for " "AES"); break; } #else /* CCNS_PL */ if (WPA_GET_BE16(pos) != 0x800e) { wpa_printf(MSG_DEBUG, "IKEV2: Not a " "Key Size attribute for " "AES"); break; } #endif /* CCNS_PL */ if (WPA_GET_BE16(pos + 2) != 128) { wpa_printf(MSG_DEBUG, "IKEV2: " "Unsupported AES key size " "%d bits", WPA_GET_BE16(pos + 2)); break; } } prop->encr = transform_id; } break; case IKEV2_TRANSFORM_PRF: if (ikev2_get_prf(transform_id)) prop->prf = transform_id; break; case IKEV2_TRANSFORM_INTEG: if (ikev2_get_integ(transform_id)) prop->integ = transform_id; break; case IKEV2_TRANSFORM_DH: if (dh_groups_get(transform_id)) prop->dh = transform_id; break; } return transform_len; } static int ikev2_parse_proposal(struct ikev2_proposal_data *prop, const u8 *pos, const u8 *end) { const u8 *pend, *ppos; int proposal_len, i; const struct ikev2_proposal *p; if (end - pos < (int) sizeof(*p)) { wpa_printf(MSG_INFO, "IKEV2: Too short proposal"); return -1; } /* FIX: AND processing if multiple proposals use the same # */ p = (const struct ikev2_proposal *) pos; proposal_len = WPA_GET_BE16(p->proposal_length); if (proposal_len < (int) sizeof(*p) || pos + proposal_len > end) { wpa_printf(MSG_INFO, "IKEV2: Invalid proposal length %d", proposal_len); return -1; } wpa_printf(MSG_DEBUG, "IKEV2: SAi1 Proposal # %d", p->proposal_num); wpa_printf(MSG_DEBUG, "IKEV2: Type: %d Proposal Length: %d " " Protocol ID: %d", p->type, proposal_len, p->protocol_id); wpa_printf(MSG_DEBUG, "IKEV2: SPI Size: %d Transforms: %d", p->spi_size, p->num_transforms); if (p->type != 0 && p->type != 2) { wpa_printf(MSG_INFO, "IKEV2: Unexpected Proposal type"); return -1; } if (p->protocol_id != IKEV2_PROTOCOL_IKE) { wpa_printf(MSG_DEBUG, "IKEV2: Unexpected Protocol ID " "(only IKE allowed for EAP-IKEv2)"); return -1; } if (p->proposal_num != prop->proposal_num) { if (p->proposal_num == prop->proposal_num + 1) prop->proposal_num = p->proposal_num; else { wpa_printf(MSG_INFO, "IKEV2: Unexpected Proposal #"); return -1; } } ppos = (const u8 *) (p + 1); pend = pos + proposal_len; if (ppos + p->spi_size > pend) { wpa_printf(MSG_INFO, "IKEV2: Not enough room for SPI " "in proposal"); return -1; } if (p->spi_size) { wpa_hexdump(MSG_DEBUG, "IKEV2: SPI", ppos, p->spi_size); ppos += p->spi_size; } /* * For initial IKE_SA negotiation, SPI Size MUST be zero; for * subsequent negotiations, it must be 8 for IKE. We only support * initial case for now. */ if (p->spi_size != 0) { wpa_printf(MSG_INFO, "IKEV2: Unexpected SPI Size"); return -1; } if (p->num_transforms == 0) { wpa_printf(MSG_INFO, "IKEV2: At least one transform required"); return -1; } for (i = 0; i < (int) p->num_transforms; i++) { int tlen = ikev2_parse_transform(prop, ppos, pend); if (tlen < 0) return -1; ppos += tlen; } if (ppos != pend) { wpa_printf(MSG_INFO, "IKEV2: Unexpected data after " "transforms"); return -1; } return proposal_len; } static int ikev2_process_sai1(struct ikev2_responder_data *data, const u8 *sai1, size_t sai1_len) { struct ikev2_proposal_data prop; const u8 *pos, *end; int found = 0; /* Security Association Payloads: */ if (sai1 == NULL) { wpa_printf(MSG_INFO, "IKEV2: SAi1 not received"); return -1; } os_memset(&prop, 0, sizeof(prop)); prop.proposal_num = 1; pos = sai1; end = sai1 + sai1_len; while (pos < end) { int plen; prop.integ = -1; prop.prf = -1; prop.encr = -1; prop.dh = -1; plen = ikev2_parse_proposal(&prop, pos, end); if (plen < 0) return -1; if (!found && prop.integ != -1 && prop.prf != -1 && prop.encr != -1 && prop.dh != -1) { os_memcpy(&data->proposal, &prop, sizeof(prop)); data->dh = dh_groups_get(prop.dh); found = 1; } pos += plen; } if (pos != end) { wpa_printf(MSG_INFO, "IKEV2: Unexpected data after proposals"); return -1; } if (!found) { wpa_printf(MSG_INFO, "IKEV2: No acceptable proposal found"); return -1; } wpa_printf(MSG_DEBUG, "IKEV2: Accepted proposal #%d: ENCR:%d PRF:%d " "INTEG:%d D-H:%d", data->proposal.proposal_num, data->proposal.encr, data->proposal.prf, data->proposal.integ, data->proposal.dh); return 0; } static int ikev2_process_kei(struct ikev2_responder_data *data, const u8 *kei, size_t kei_len) { u16 group; /* * Key Exchange Payload: * DH Group # (16 bits) * RESERVED (16 bits) * Key Exchange Data (Diffie-Hellman public value) */ if (kei == NULL) { wpa_printf(MSG_INFO, "IKEV2: KEi not received"); return -1; } if (kei_len < 4 + 96) { wpa_printf(MSG_INFO, "IKEV2: Too show Key Exchange Payload"); return -1; } group = WPA_GET_BE16(kei); wpa_printf(MSG_DEBUG, "IKEV2: KEi DH Group #%u", group); if (group != data->proposal.dh) { wpa_printf(MSG_DEBUG, "IKEV2: KEi DH Group #%u does not match " "with the selected proposal (%u)", group, data->proposal.dh); /* Reject message with Notify payload of type * INVALID_KE_PAYLOAD (RFC 4306, Sect. 3.4) */ data->error_type = INVALID_KE_PAYLOAD; data->state = NOTIFY; return -1; } if (data->dh == NULL) { wpa_printf(MSG_INFO, "IKEV2: Unsupported DH group"); return -1; } /* RFC 4306, Section 3.4: * The length of DH public value MUST be equal to the length of the * prime modulus. */ if (kei_len - 4 != data->dh->prime_len) { wpa_printf(MSG_INFO, "IKEV2: Invalid DH public value length " "%ld (expected %ld)", (long) (kei_len - 4), (long) data->dh->prime_len); return -1; } wpabuf_free(data->i_dh_public); data->i_dh_public = wpabuf_alloc(kei_len - 4); if (data->i_dh_public == NULL) return -1; wpabuf_put_data(data->i_dh_public, kei + 4, kei_len - 4); wpa_hexdump_buf(MSG_DEBUG, "IKEV2: KEi Diffie-Hellman Public Value", data->i_dh_public); return 0; } static int ikev2_process_ni(struct ikev2_responder_data *data, const u8 *ni, size_t ni_len) { if (ni == NULL) { wpa_printf(MSG_INFO, "IKEV2: Ni not received"); return -1; } if (ni_len < IKEV2_NONCE_MIN_LEN || ni_len > IKEV2_NONCE_MAX_LEN) { wpa_printf(MSG_INFO, "IKEV2: Invalid Ni length %ld", (long) ni_len); return -1; } #ifdef CCNS_PL /* Zeros are removed incorrectly from the beginning of the nonces */ while (ni_len > 1 && *ni == 0) { ni_len--; ni++; } #endif /* CCNS_PL */ data->i_nonce_len = ni_len; os_memcpy(data->i_nonce, ni, ni_len); wpa_hexdump(MSG_MSGDUMP, "IKEV2: Ni", data->i_nonce, data->i_nonce_len); return 0; } static int ikev2_process_sa_init(struct ikev2_responder_data *data, const struct ikev2_hdr *hdr, struct ikev2_payloads *pl) { if (ikev2_process_sai1(data, pl->sa, pl->sa_len) < 0 || ikev2_process_kei(data, pl->ke, pl->ke_len) < 0 || ikev2_process_ni(data, pl->nonce, pl->nonce_len) < 0) return -1; os_memcpy(data->i_spi, hdr->i_spi, IKEV2_SPI_LEN); return 0; } static int ikev2_process_idi(struct ikev2_responder_data *data, const u8 *idi, size_t idi_len) { u8 id_type; if (idi == NULL) { wpa_printf(MSG_INFO, "IKEV2: No IDi received"); return -1; } if (idi_len < 4) { wpa_printf(MSG_INFO, "IKEV2: Too short IDi payload"); return -1; } id_type = idi[0]; idi += 4; idi_len -= 4; wpa_printf(MSG_DEBUG, "IKEV2: IDi ID Type %d", id_type); wpa_hexdump_ascii(MSG_DEBUG, "IKEV2: IDi", idi, idi_len); os_free(data->IDi); data->IDi = os_malloc(idi_len); if (data->IDi == NULL) return -1; os_memcpy(data->IDi, idi, idi_len); data->IDi_len = idi_len; data->IDi_type = id_type; return 0; } static int ikev2_process_cert(struct ikev2_responder_data *data, const u8 *cert, size_t cert_len) { u8 cert_encoding; if (cert == NULL) { if (data->peer_auth == PEER_AUTH_CERT) { wpa_printf(MSG_INFO, "IKEV2: No Certificate received"); return -1; } return 0; } if (cert_len < 1) { wpa_printf(MSG_INFO, "IKEV2: No Cert Encoding field"); return -1; } cert_encoding = cert[0]; cert++; cert_len--; wpa_printf(MSG_DEBUG, "IKEV2: Cert Encoding %d", cert_encoding); wpa_hexdump(MSG_MSGDUMP, "IKEV2: Certificate Data", cert, cert_len); /* TODO: validate certificate */ return 0; } static int ikev2_process_auth_cert(struct ikev2_responder_data *data, u8 method, const u8 *auth, size_t auth_len) { if (method != AUTH_RSA_SIGN) { wpa_printf(MSG_INFO, "IKEV2: Unsupported authentication " "method %d", method); return -1; } /* TODO: validate AUTH */ return 0; } static int ikev2_process_auth_secret(struct ikev2_responder_data *data, u8 method, const u8 *auth, size_t auth_len) { u8 auth_data[IKEV2_MAX_HASH_LEN]; const struct ikev2_prf_alg *prf; if (method != AUTH_SHARED_KEY_MIC) { wpa_printf(MSG_INFO, "IKEV2: Unsupported authentication " "method %d", method); return -1; } /* msg | Nr | prf(SK_pi,IDi') */ if (ikev2_derive_auth_data(data->proposal.prf, data->i_sign_msg, data->IDi, data->IDi_len, data->IDi_type, &data->keys, 1, data->shared_secret, data->shared_secret_len, data->r_nonce, data->r_nonce_len, data->key_pad, data->key_pad_len, auth_data) < 0) { wpa_printf(MSG_INFO, "IKEV2: Could not derive AUTH data"); return -1; } wpabuf_free(data->i_sign_msg); data->i_sign_msg = NULL; prf = ikev2_get_prf(data->proposal.prf); if (prf == NULL) return -1; if (auth_len != prf->hash_len || os_memcmp(auth, auth_data, auth_len) != 0) { wpa_printf(MSG_INFO, "IKEV2: Invalid Authentication Data"); wpa_hexdump(MSG_DEBUG, "IKEV2: Received Authentication Data", auth, auth_len); wpa_hexdump(MSG_DEBUG, "IKEV2: Expected Authentication Data", auth_data, prf->hash_len); data->error_type = AUTHENTICATION_FAILED; data->state = NOTIFY; return -1; } wpa_printf(MSG_DEBUG, "IKEV2: Server authenticated successfully " "using shared keys"); return 0; } static int ikev2_process_auth(struct ikev2_responder_data *data, const u8 *auth, size_t auth_len) { u8 auth_method; if (auth == NULL) { wpa_printf(MSG_INFO, "IKEV2: No Authentication Payload"); return -1; } if (auth_len < 4) { wpa_printf(MSG_INFO, "IKEV2: Too short Authentication " "Payload"); return -1; } auth_method = auth[0]; auth += 4; auth_len -= 4; wpa_printf(MSG_DEBUG, "IKEV2: Auth Method %d", auth_method); wpa_hexdump(MSG_MSGDUMP, "IKEV2: Authentication Data", auth, auth_len); switch (data->peer_auth) { case PEER_AUTH_CERT: return ikev2_process_auth_cert(data, auth_method, auth, auth_len); case PEER_AUTH_SECRET: return ikev2_process_auth_secret(data, auth_method, auth, auth_len); } return -1; } static int ikev2_process_sa_auth_decrypted(struct ikev2_responder_data *data, u8 next_payload, u8 *payload, size_t payload_len) { struct ikev2_payloads pl; wpa_printf(MSG_DEBUG, "IKEV2: Processing decrypted payloads"); if (ikev2_parse_payloads(&pl, next_payload, payload, payload + payload_len) < 0) { wpa_printf(MSG_INFO, "IKEV2: Failed to parse decrypted " "payloads"); return -1; } if (ikev2_process_idi(data, pl.idi, pl.idi_len) < 0 || ikev2_process_cert(data, pl.cert, pl.cert_len) < 0 || ikev2_process_auth(data, pl.auth, pl.auth_len) < 0) return -1; return 0; } static int ikev2_process_sa_auth(struct ikev2_responder_data *data, const struct ikev2_hdr *hdr, struct ikev2_payloads *pl) { u8 *decrypted; size_t decrypted_len; int ret; decrypted = ikev2_decrypt_payload(data->proposal.encr, data->proposal.integ, &data->keys, 1, hdr, pl->encrypted, pl->encrypted_len, &decrypted_len); if (decrypted == NULL) return -1; ret = ikev2_process_sa_auth_decrypted(data, pl->encr_next_payload, decrypted, decrypted_len); os_free(decrypted); return ret; } static int ikev2_validate_rx_state(struct ikev2_responder_data *data, u8 exchange_type, u32 message_id) { switch (data->state) { case SA_INIT: /* Expect to receive IKE_SA_INIT: HDR, SAi1, KEi, Ni */ if (exchange_type != IKE_SA_INIT) { wpa_printf(MSG_INFO, "IKEV2: Unexpected Exchange Type " "%u in SA_INIT state", exchange_type); return -1; } if (message_id != 0) { wpa_printf(MSG_INFO, "IKEV2: Unexpected Message ID %u " "in SA_INIT state", message_id); return -1; } break; case SA_AUTH: /* Expect to receive IKE_SA_AUTH: * HDR, SK {IDi, [CERT,] [CERTREQ,] [IDr,] * AUTH, SAi2, TSi, TSr} */ if (exchange_type != IKE_SA_AUTH) { wpa_printf(MSG_INFO, "IKEV2: Unexpected Exchange Type " "%u in SA_AUTH state", exchange_type); return -1; } if (message_id != 1) { wpa_printf(MSG_INFO, "IKEV2: Unexpected Message ID %u " "in SA_AUTH state", message_id); return -1; } break; case CHILD_SA: if (exchange_type != CREATE_CHILD_SA) { wpa_printf(MSG_INFO, "IKEV2: Unexpected Exchange Type " "%u in CHILD_SA state", exchange_type); return -1; } if (message_id != 2) { wpa_printf(MSG_INFO, "IKEV2: Unexpected Message ID %u " "in CHILD_SA state", message_id); return -1; } break; case NOTIFY: case IKEV2_DONE: case IKEV2_FAILED: return -1; } return 0; } int ikev2_responder_process(struct ikev2_responder_data *data, const struct wpabuf *buf) { const struct ikev2_hdr *hdr; u32 length, message_id; const u8 *pos, *end; struct ikev2_payloads pl; wpa_printf(MSG_MSGDUMP, "IKEV2: Received message (len %lu)", (unsigned long) wpabuf_len(buf)); if (wpabuf_len(buf) < sizeof(*hdr)) { wpa_printf(MSG_INFO, "IKEV2: Too short frame to include HDR"); return -1; } data->error_type = 0; hdr = (const struct ikev2_hdr *) wpabuf_head(buf); end = wpabuf_head_u8(buf) + wpabuf_len(buf); message_id = WPA_GET_BE32(hdr->message_id); length = WPA_GET_BE32(hdr->length); wpa_hexdump(MSG_DEBUG, "IKEV2: IKE_SA Initiator's SPI", hdr->i_spi, IKEV2_SPI_LEN); wpa_hexdump(MSG_DEBUG, "IKEV2: IKE_SA Responder's SPI", hdr->r_spi, IKEV2_SPI_LEN); wpa_printf(MSG_DEBUG, "IKEV2: Next Payload: %u Version: 0x%x " "Exchange Type: %u", hdr->next_payload, hdr->version, hdr->exchange_type); wpa_printf(MSG_DEBUG, "IKEV2: Message ID: %u Length: %u", message_id, length); if (hdr->version != IKEV2_VERSION) { wpa_printf(MSG_INFO, "IKEV2: Unsupported HDR version 0x%x " "(expected 0x%x)", hdr->version, IKEV2_VERSION); return -1; } if (length != wpabuf_len(buf)) { wpa_printf(MSG_INFO, "IKEV2: Invalid length (HDR: %lu != " "RX: %lu)", (unsigned long) length, (unsigned long) wpabuf_len(buf)); return -1; } if (ikev2_validate_rx_state(data, hdr->exchange_type, message_id) < 0) return -1; if ((hdr->flags & (IKEV2_HDR_INITIATOR | IKEV2_HDR_RESPONSE)) != IKEV2_HDR_INITIATOR) { wpa_printf(MSG_INFO, "IKEV2: Unexpected Flags value 0x%x", hdr->flags); return -1; } if (data->state != SA_INIT) { if (os_memcmp(data->i_spi, hdr->i_spi, IKEV2_SPI_LEN) != 0) { wpa_printf(MSG_INFO, "IKEV2: Unexpected IKE_SA " "Initiator's SPI"); return -1; } if (os_memcmp(data->r_spi, hdr->r_spi, IKEV2_SPI_LEN) != 0) { wpa_printf(MSG_INFO, "IKEV2: Unexpected IKE_SA " "Responder's SPI"); return -1; } } pos = (const u8 *) (hdr + 1); if (ikev2_parse_payloads(&pl, hdr->next_payload, pos, end) < 0) return -1; if (data->state == SA_INIT) { data->last_msg = LAST_MSG_SA_INIT; if (ikev2_process_sa_init(data, hdr, &pl) < 0) { if (data->state == NOTIFY) return 0; return -1; } wpabuf_free(data->i_sign_msg); data->i_sign_msg = wpabuf_dup(buf); } if (data->state == SA_AUTH) { data->last_msg = LAST_MSG_SA_AUTH; if (ikev2_process_sa_auth(data, hdr, &pl) < 0) { if (data->state == NOTIFY) return 0; return -1; } } return 0; } static void ikev2_build_hdr(struct ikev2_responder_data *data, struct wpabuf *msg, u8 exchange_type, u8 next_payload, u32 message_id) { struct ikev2_hdr *hdr; wpa_printf(MSG_DEBUG, "IKEV2: Adding HDR"); /* HDR - RFC 4306, Sect. 3.1 */ hdr = wpabuf_put(msg, sizeof(*hdr)); os_memcpy(hdr->i_spi, data->i_spi, IKEV2_SPI_LEN); os_memcpy(hdr->r_spi, data->r_spi, IKEV2_SPI_LEN); hdr->next_payload = next_payload; hdr->version = IKEV2_VERSION; hdr->exchange_type = exchange_type; hdr->flags = IKEV2_HDR_RESPONSE; WPA_PUT_BE32(hdr->message_id, message_id); } static int ikev2_build_sar1(struct ikev2_responder_data *data, struct wpabuf *msg, u8 next_payload) { struct ikev2_payload_hdr *phdr; size_t plen; struct ikev2_proposal *p; struct ikev2_transform *t; wpa_printf(MSG_DEBUG, "IKEV2: Adding SAr1 payload"); /* SAr1 - RFC 4306, Sect. 2.7 and 3.3 */ phdr = wpabuf_put(msg, sizeof(*phdr)); phdr->next_payload = next_payload; phdr->flags = 0; p = wpabuf_put(msg, sizeof(*p)); #ifdef CCNS_PL /* Seems to require that the Proposal # is 1 even though RFC 4306 * Sect 3.3.1 has following requirement "When a proposal is accepted, * all of the proposal numbers in the SA payload MUST be the same and * MUST match the number on the proposal sent that was accepted.". */ p->proposal_num = 1; #else /* CCNS_PL */ p->proposal_num = data->proposal.proposal_num; #endif /* CCNS_PL */ p->protocol_id = IKEV2_PROTOCOL_IKE; p->num_transforms = 4; t = wpabuf_put(msg, sizeof(*t)); t->type = 3; t->transform_type = IKEV2_TRANSFORM_ENCR; WPA_PUT_BE16(t->transform_id, data->proposal.encr); if (data->proposal.encr == ENCR_AES_CBC) { /* Transform Attribute: Key Len = 128 bits */ #ifdef CCNS_PL wpabuf_put_be16(msg, 0x001d); /* ?? */ #else /* CCNS_PL */ wpabuf_put_be16(msg, 0x800e); /* AF=1, AttrType=14 */ #endif /* CCNS_PL */ wpabuf_put_be16(msg, 128); /* 128-bit key */ } plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) t; WPA_PUT_BE16(t->transform_length, plen); t = wpabuf_put(msg, sizeof(*t)); t->type = 3; WPA_PUT_BE16(t->transform_length, sizeof(*t)); t->transform_type = IKEV2_TRANSFORM_PRF; WPA_PUT_BE16(t->transform_id, data->proposal.prf); t = wpabuf_put(msg, sizeof(*t)); t->type = 3; WPA_PUT_BE16(t->transform_length, sizeof(*t)); t->transform_type = IKEV2_TRANSFORM_INTEG; WPA_PUT_BE16(t->transform_id, data->proposal.integ); t = wpabuf_put(msg, sizeof(*t)); WPA_PUT_BE16(t->transform_length, sizeof(*t)); t->transform_type = IKEV2_TRANSFORM_DH; WPA_PUT_BE16(t->transform_id, data->proposal.dh); plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) p; WPA_PUT_BE16(p->proposal_length, plen); plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr; WPA_PUT_BE16(phdr->payload_length, plen); return 0; } static int ikev2_build_ker(struct ikev2_responder_data *data, struct wpabuf *msg, u8 next_payload) { struct ikev2_payload_hdr *phdr; size_t plen; struct wpabuf *pv; wpa_printf(MSG_DEBUG, "IKEV2: Adding KEr payload"); pv = dh_init(data->dh, &data->r_dh_private); if (pv == NULL) { wpa_printf(MSG_DEBUG, "IKEV2: Failed to initialize DH"); return -1; } /* KEr - RFC 4306, Sect. 3.4 */ phdr = wpabuf_put(msg, sizeof(*phdr)); phdr->next_payload = next_payload; phdr->flags = 0; wpabuf_put_be16(msg, data->proposal.dh); /* DH Group # */ wpabuf_put(msg, 2); /* RESERVED */ /* * RFC 4306, Sect. 3.4: possible zero padding for public value to * match the length of the prime. */ wpabuf_put(msg, data->dh->prime_len - wpabuf_len(pv)); wpabuf_put_buf(msg, pv); wpabuf_free(pv); plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr; WPA_PUT_BE16(phdr->payload_length, plen); return 0; } static int ikev2_build_nr(struct ikev2_responder_data *data, struct wpabuf *msg, u8 next_payload) { struct ikev2_payload_hdr *phdr; size_t plen; wpa_printf(MSG_DEBUG, "IKEV2: Adding Nr payload"); /* Nr - RFC 4306, Sect. 3.9 */ phdr = wpabuf_put(msg, sizeof(*phdr)); phdr->next_payload = next_payload; phdr->flags = 0; wpabuf_put_data(msg, data->r_nonce, data->r_nonce_len); plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr; WPA_PUT_BE16(phdr->payload_length, plen); return 0; } static int ikev2_build_idr(struct ikev2_responder_data *data, struct wpabuf *msg, u8 next_payload) { struct ikev2_payload_hdr *phdr; size_t plen; wpa_printf(MSG_DEBUG, "IKEV2: Adding IDr payload"); if (data->IDr == NULL) { wpa_printf(MSG_INFO, "IKEV2: No IDr available"); return -1; } /* IDr - RFC 4306, Sect. 3.5 */ phdr = wpabuf_put(msg, sizeof(*phdr)); phdr->next_payload = next_payload; phdr->flags = 0; wpabuf_put_u8(msg, ID_KEY_ID); wpabuf_put(msg, 3); /* RESERVED */ wpabuf_put_data(msg, data->IDr, data->IDr_len); plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr; WPA_PUT_BE16(phdr->payload_length, plen); return 0; } static int ikev2_build_auth(struct ikev2_responder_data *data, struct wpabuf *msg, u8 next_payload) { struct ikev2_payload_hdr *phdr; size_t plen; const struct ikev2_prf_alg *prf; wpa_printf(MSG_DEBUG, "IKEV2: Adding AUTH payload"); prf = ikev2_get_prf(data->proposal.prf); if (prf == NULL) return -1; /* Authentication - RFC 4306, Sect. 3.8 */ phdr = wpabuf_put(msg, sizeof(*phdr)); phdr->next_payload = next_payload; phdr->flags = 0; wpabuf_put_u8(msg, AUTH_SHARED_KEY_MIC); wpabuf_put(msg, 3); /* RESERVED */ /* msg | Ni | prf(SK_pr,IDr') */ if (ikev2_derive_auth_data(data->proposal.prf, data->r_sign_msg, data->IDr, data->IDr_len, ID_KEY_ID, &data->keys, 0, data->shared_secret, data->shared_secret_len, data->i_nonce, data->i_nonce_len, data->key_pad, data->key_pad_len, wpabuf_put(msg, prf->hash_len)) < 0) { wpa_printf(MSG_INFO, "IKEV2: Could not derive AUTH data"); return -1; } wpabuf_free(data->r_sign_msg); data->r_sign_msg = NULL; plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr; WPA_PUT_BE16(phdr->payload_length, plen); return 0; } static int ikev2_build_notification(struct ikev2_responder_data *data, struct wpabuf *msg, u8 next_payload) { struct ikev2_payload_hdr *phdr; size_t plen; wpa_printf(MSG_DEBUG, "IKEV2: Adding Notification payload"); if (data->error_type == 0) { wpa_printf(MSG_INFO, "IKEV2: No Notify Message Type " "available"); return -1; } /* Notify - RFC 4306, Sect. 3.10 */ phdr = wpabuf_put(msg, sizeof(*phdr)); phdr->next_payload = next_payload; phdr->flags = 0; #ifdef CCNS_PL wpabuf_put_u8(msg, 1); /* Protocol ID: IKE_SA notification */ #else /* CCNS_PL */ wpabuf_put_u8(msg, 0); /* Protocol ID: no existing SA */ #endif /* CCNS_PL */ wpabuf_put_u8(msg, 0); /* SPI Size */ wpabuf_put_be16(msg, data->error_type); switch (data->error_type) { case INVALID_KE_PAYLOAD: if (data->proposal.dh == -1) { wpa_printf(MSG_INFO, "IKEV2: No DH Group selected for " "INVALID_KE_PAYLOAD notifications"); return -1; } wpabuf_put_be16(msg, data->proposal.dh); wpa_printf(MSG_DEBUG, "IKEV2: INVALID_KE_PAYLOAD - request " "DH Group #%d", data->proposal.dh); break; case AUTHENTICATION_FAILED: /* no associated data */ break; default: wpa_printf(MSG_INFO, "IKEV2: Unsupported Notify Message Type " "%d", data->error_type); return -1; } plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr; WPA_PUT_BE16(phdr->payload_length, plen); return 0; } static struct wpabuf * ikev2_build_sa_init(struct ikev2_responder_data *data) { struct wpabuf *msg; /* build IKE_SA_INIT: HDR, SAr1, KEr, Nr, [CERTREQ], [SK{IDr}] */ if (os_get_random(data->r_spi, IKEV2_SPI_LEN)) return NULL; wpa_hexdump(MSG_DEBUG, "IKEV2: IKE_SA Responder's SPI", data->r_spi, IKEV2_SPI_LEN); data->r_nonce_len = IKEV2_NONCE_MIN_LEN; if (random_get_bytes(data->r_nonce, data->r_nonce_len)) return NULL; #ifdef CCNS_PL /* Zeros are removed incorrectly from the beginning of the nonces in * key derivation; as a workaround, make sure Nr does not start with * zero.. */ if (data->r_nonce[0] == 0) data->r_nonce[0] = 1; #endif /* CCNS_PL */ wpa_hexdump(MSG_DEBUG, "IKEV2: Nr", data->r_nonce, data->r_nonce_len); msg = wpabuf_alloc(sizeof(struct ikev2_hdr) + data->IDr_len + 1500); if (msg == NULL) return NULL; ikev2_build_hdr(data, msg, IKE_SA_INIT, IKEV2_PAYLOAD_SA, 0); if (ikev2_build_sar1(data, msg, IKEV2_PAYLOAD_KEY_EXCHANGE) || ikev2_build_ker(data, msg, IKEV2_PAYLOAD_NONCE) || ikev2_build_nr(data, msg, data->peer_auth == PEER_AUTH_SECRET ? IKEV2_PAYLOAD_ENCRYPTED : IKEV2_PAYLOAD_NO_NEXT_PAYLOAD)) { wpabuf_free(msg); return NULL; } if (ikev2_derive_keys(data)) { wpabuf_free(msg); return NULL; } if (data->peer_auth == PEER_AUTH_CERT) { /* TODO: CERTREQ with SHA-1 hashes of Subject Public Key Info * for trust agents */ } if (data->peer_auth == PEER_AUTH_SECRET) { struct wpabuf *plain = wpabuf_alloc(data->IDr_len + 1000); if (plain == NULL) { wpabuf_free(msg); return NULL; } if (ikev2_build_idr(data, plain, IKEV2_PAYLOAD_NO_NEXT_PAYLOAD) || ikev2_build_encrypted(data->proposal.encr, data->proposal.integ, &data->keys, 0, msg, plain, IKEV2_PAYLOAD_IDr)) { wpabuf_free(plain); wpabuf_free(msg); return NULL; } wpabuf_free(plain); } ikev2_update_hdr(msg); wpa_hexdump_buf(MSG_MSGDUMP, "IKEV2: Sending message (SA_INIT)", msg); data->state = SA_AUTH; wpabuf_free(data->r_sign_msg); data->r_sign_msg = wpabuf_dup(msg); return msg; } static struct wpabuf * ikev2_build_sa_auth(struct ikev2_responder_data *data) { struct wpabuf *msg, *plain; /* build IKE_SA_AUTH: HDR, SK {IDr, [CERT,] AUTH} */ msg = wpabuf_alloc(sizeof(struct ikev2_hdr) + data->IDr_len + 1000); if (msg == NULL) return NULL; ikev2_build_hdr(data, msg, IKE_SA_AUTH, IKEV2_PAYLOAD_ENCRYPTED, 1); plain = wpabuf_alloc(data->IDr_len + 1000); if (plain == NULL) { wpabuf_free(msg); return NULL; } if (ikev2_build_idr(data, plain, IKEV2_PAYLOAD_AUTHENTICATION) || ikev2_build_auth(data, plain, IKEV2_PAYLOAD_NO_NEXT_PAYLOAD) || ikev2_build_encrypted(data->proposal.encr, data->proposal.integ, &data->keys, 0, msg, plain, IKEV2_PAYLOAD_IDr)) { wpabuf_free(plain); wpabuf_free(msg); return NULL; } wpabuf_free(plain); wpa_hexdump_buf(MSG_MSGDUMP, "IKEV2: Sending message (SA_AUTH)", msg); data->state = IKEV2_DONE; return msg; } static struct wpabuf * ikev2_build_notify(struct ikev2_responder_data *data) { struct wpabuf *msg; msg = wpabuf_alloc(sizeof(struct ikev2_hdr) + 1000); if (msg == NULL) return NULL; if (data->last_msg == LAST_MSG_SA_AUTH) { /* HDR, SK{N} */ struct wpabuf *plain = wpabuf_alloc(100); if (plain == NULL) { wpabuf_free(msg); return NULL; } ikev2_build_hdr(data, msg, IKE_SA_AUTH, IKEV2_PAYLOAD_ENCRYPTED, 1); if (ikev2_build_notification(data, plain, IKEV2_PAYLOAD_NO_NEXT_PAYLOAD) || ikev2_build_encrypted(data->proposal.encr, data->proposal.integ, &data->keys, 0, msg, plain, IKEV2_PAYLOAD_NOTIFICATION)) { wpabuf_free(plain); wpabuf_free(msg); return NULL; } wpabuf_free(plain); data->state = IKEV2_FAILED; } else { /* HDR, N */ ikev2_build_hdr(data, msg, IKE_SA_INIT, IKEV2_PAYLOAD_NOTIFICATION, 0); if (ikev2_build_notification(data, msg, IKEV2_PAYLOAD_NO_NEXT_PAYLOAD)) { wpabuf_free(msg); return NULL; } data->state = SA_INIT; } ikev2_update_hdr(msg); wpa_hexdump_buf(MSG_MSGDUMP, "IKEV2: Sending message (Notification)", msg); return msg; } struct wpabuf * ikev2_responder_build(struct ikev2_responder_data *data) { switch (data->state) { case SA_INIT: return ikev2_build_sa_init(data); case SA_AUTH: return ikev2_build_sa_auth(data); case CHILD_SA: return NULL; case NOTIFY: return ikev2_build_notify(data); case IKEV2_DONE: case IKEV2_FAILED: return NULL; } return NULL; } wpa-2.1/src/eap_peer/ikev2.h000066400000000000000000000025531227414666700157270ustar00rootroot00000000000000/* * IKEv2 responder (RFC 4306) for EAP-IKEV2 * Copyright (c) 2007, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef IKEV2_H #define IKEV2_H #include "eap_common/ikev2_common.h" struct ikev2_proposal_data { u8 proposal_num; int integ; int prf; int encr; int dh; }; struct ikev2_responder_data { enum { SA_INIT, SA_AUTH, CHILD_SA, NOTIFY, IKEV2_DONE, IKEV2_FAILED } state; u8 i_spi[IKEV2_SPI_LEN]; u8 r_spi[IKEV2_SPI_LEN]; u8 i_nonce[IKEV2_NONCE_MAX_LEN]; size_t i_nonce_len; u8 r_nonce[IKEV2_NONCE_MAX_LEN]; size_t r_nonce_len; struct wpabuf *i_dh_public; struct wpabuf *r_dh_private; struct ikev2_proposal_data proposal; const struct dh_group *dh; struct ikev2_keys keys; u8 *IDi; size_t IDi_len; u8 IDi_type; u8 *IDr; size_t IDr_len; struct wpabuf *r_sign_msg; struct wpabuf *i_sign_msg; u8 *shared_secret; size_t shared_secret_len; enum { PEER_AUTH_CERT, PEER_AUTH_SECRET } peer_auth; u8 *key_pad; size_t key_pad_len; u16 error_type; enum { LAST_MSG_SA_INIT, LAST_MSG_SA_AUTH } last_msg; }; void ikev2_responder_deinit(struct ikev2_responder_data *data); int ikev2_responder_process(struct ikev2_responder_data *data, const struct wpabuf *buf); struct wpabuf * ikev2_responder_build(struct ikev2_responder_data *data); #endif /* IKEV2_H */ wpa-2.1/src/eap_peer/mschapv2.c000066400000000000000000000071211227414666700164210ustar00rootroot00000000000000/* * MSCHAPV2 (RFC 2759) * Copyright (c) 2004-2008, 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" #include "mschapv2.h" const u8 * mschapv2_remove_domain(const u8 *username, size_t *len) { size_t i; /* * MSCHAPv2 does not include optional domain name in the * challenge-response calculation, so remove domain prefix * (if present). */ for (i = 0; i < *len; i++) { if (username[i] == '\\') { *len -= i + 1; return username + i + 1; } } return username; } int mschapv2_derive_response(const u8 *identity, size_t identity_len, const u8 *password, size_t password_len, int pwhash, const u8 *auth_challenge, const u8 *peer_challenge, u8 *nt_response, u8 *auth_response, u8 *master_key) { const u8 *username; size_t username_len; u8 password_hash[16], password_hash_hash[16]; wpa_hexdump_ascii(MSG_DEBUG, "MSCHAPV2: Identity", identity, identity_len); username_len = identity_len; username = mschapv2_remove_domain(identity, &username_len); wpa_hexdump_ascii(MSG_DEBUG, "MSCHAPV2: Username", username, username_len); wpa_hexdump(MSG_DEBUG, "MSCHAPV2: auth_challenge", auth_challenge, MSCHAPV2_CHAL_LEN); wpa_hexdump(MSG_DEBUG, "MSCHAPV2: peer_challenge", peer_challenge, MSCHAPV2_CHAL_LEN); wpa_hexdump_ascii(MSG_DEBUG, "MSCHAPV2: username", username, username_len); /* Authenticator response is not really needed yet, but calculate it * here so that challenges need not be saved. */ if (pwhash) { wpa_hexdump_key(MSG_DEBUG, "MSCHAPV2: password hash", password, password_len); if (generate_nt_response_pwhash(auth_challenge, peer_challenge, username, username_len, password, nt_response) || generate_authenticator_response_pwhash( password, peer_challenge, auth_challenge, username, username_len, nt_response, auth_response)) return -1; } else { wpa_hexdump_ascii_key(MSG_DEBUG, "MSCHAPV2: password", password, password_len); if (generate_nt_response(auth_challenge, peer_challenge, username, username_len, password, password_len, nt_response) || generate_authenticator_response(password, password_len, peer_challenge, auth_challenge, username, username_len, nt_response, auth_response)) return -1; } wpa_hexdump(MSG_DEBUG, "MSCHAPV2: NT Response", nt_response, MSCHAPV2_NT_RESPONSE_LEN); wpa_hexdump(MSG_DEBUG, "MSCHAPV2: Auth Response", auth_response, MSCHAPV2_AUTH_RESPONSE_LEN); /* Generate master_key here since we have the needed data available. */ if (pwhash) { if (hash_nt_password_hash(password, password_hash_hash)) return -1; } else { if (nt_password_hash(password, password_len, password_hash) || hash_nt_password_hash(password_hash, password_hash_hash)) return -1; } if (get_master_key(password_hash_hash, nt_response, master_key)) return -1; wpa_hexdump_key(MSG_DEBUG, "MSCHAPV2: Master Key", master_key, MSCHAPV2_MASTER_KEY_LEN); return 0; } int mschapv2_verify_auth_response(const u8 *auth_response, const u8 *buf, size_t buf_len) { u8 recv_response[MSCHAPV2_AUTH_RESPONSE_LEN]; if (buf_len < 2 + 2 * MSCHAPV2_AUTH_RESPONSE_LEN || buf[0] != 'S' || buf[1] != '=' || hexstr2bin((char *) (buf + 2), recv_response, MSCHAPV2_AUTH_RESPONSE_LEN) || os_memcmp(auth_response, recv_response, MSCHAPV2_AUTH_RESPONSE_LEN) != 0) return -1; return 0; } wpa-2.1/src/eap_peer/mschapv2.h000066400000000000000000000015021227414666700164230ustar00rootroot00000000000000/* * MSCHAPV2 (RFC 2759) * Copyright (c) 2004-2008, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef MSCHAPV2_H #define MSCHAPV2_H #define MSCHAPV2_CHAL_LEN 16 #define MSCHAPV2_NT_RESPONSE_LEN 24 #define MSCHAPV2_AUTH_RESPONSE_LEN 20 #define MSCHAPV2_MASTER_KEY_LEN 16 const u8 * mschapv2_remove_domain(const u8 *username, size_t *len); int mschapv2_derive_response(const u8 *username, size_t username_len, const u8 *password, size_t password_len, int pwhash, const u8 *auth_challenge, const u8 *peer_challenge, u8 *nt_response, u8 *auth_response, u8 *master_key); int mschapv2_verify_auth_response(const u8 *auth_response, const u8 *buf, size_t buf_len); #endif /* MSCHAPV2_H */ wpa-2.1/src/eap_peer/tncc.c000066400000000000000000000764001227414666700156330ustar00rootroot00000000000000/* * EAP-TNC - TNCC (IF-IMC and IF-TNCCS) * Copyright (c) 2007, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "includes.h" #ifndef CONFIG_NATIVE_WINDOWS #include #endif /* CONFIG_NATIVE_WINDOWS */ #include "common.h" #include "base64.h" #include "tncc.h" #include "eap_common/eap_tlv_common.h" #include "eap_common/eap_defs.h" #ifdef UNICODE #define TSTR "%S" #else /* UNICODE */ #define TSTR "%s" #endif /* UNICODE */ #define TNC_CONFIG_FILE "/etc/tnc_config" #define TNC_WINREG_PATH TEXT("SOFTWARE\\Trusted Computing Group\\TNC\\IMCs") #define IF_TNCCS_START \ "\n" \ "\n" #define IF_TNCCS_END "\n" /* TNC IF-IMC */ typedef unsigned long TNC_UInt32; typedef unsigned char *TNC_BufferReference; typedef TNC_UInt32 TNC_IMCID; typedef TNC_UInt32 TNC_ConnectionID; typedef TNC_UInt32 TNC_ConnectionState; typedef TNC_UInt32 TNC_RetryReason; typedef TNC_UInt32 TNC_MessageType; typedef TNC_MessageType *TNC_MessageTypeList; typedef TNC_UInt32 TNC_VendorID; typedef TNC_UInt32 TNC_MessageSubtype; typedef TNC_UInt32 TNC_Version; typedef TNC_UInt32 TNC_Result; typedef TNC_Result (*TNC_TNCC_BindFunctionPointer)( TNC_IMCID imcID, char *functionName, void **pOutfunctionPointer); #define TNC_RESULT_SUCCESS 0 #define TNC_RESULT_NOT_INITIALIZED 1 #define TNC_RESULT_ALREADY_INITIALIZED 2 #define TNC_RESULT_NO_COMMON_VERSION 3 #define TNC_RESULT_CANT_RETRY 4 #define TNC_RESULT_WONT_RETRY 5 #define TNC_RESULT_INVALID_PARAMETER 6 #define TNC_RESULT_CANT_RESPOND 7 #define TNC_RESULT_ILLEGAL_OPERATION 8 #define TNC_RESULT_OTHER 9 #define TNC_RESULT_FATAL 10 #define TNC_CONNECTION_STATE_CREATE 0 #define TNC_CONNECTION_STATE_HANDSHAKE 1 #define TNC_CONNECTION_STATE_ACCESS_ALLOWED 2 #define TNC_CONNECTION_STATE_ACCESS_ISOLATED 3 #define TNC_CONNECTION_STATE_ACCESS_NONE 4 #define TNC_CONNECTION_STATE_DELETE 5 #define TNC_IFIMC_VERSION_1 1 #define TNC_VENDORID_ANY ((TNC_VendorID) 0xffffff) #define TNC_SUBTYPE_ANY ((TNC_MessageSubtype) 0xff) /* TNCC-TNCS Message Types */ #define TNC_TNCCS_RECOMMENDATION 0x00000001 #define TNC_TNCCS_ERROR 0x00000002 #define TNC_TNCCS_PREFERREDLANGUAGE 0x00000003 #define TNC_TNCCS_REASONSTRINGS 0x00000004 /* IF-TNCCS-SOH - SSoH and SSoHR Attributes */ enum { SSOH_MS_MACHINE_INVENTORY = 1, SSOH_MS_QUARANTINE_STATE = 2, SSOH_MS_PACKET_INFO = 3, SSOH_MS_SYSTEMGENERATED_IDS = 4, SSOH_MS_MACHINENAME = 5, SSOH_MS_CORRELATIONID = 6, SSOH_MS_INSTALLED_SHVS = 7, SSOH_MS_MACHINE_INVENTORY_EX = 8 }; struct tnc_if_imc { struct tnc_if_imc *next; char *name; char *path; void *dlhandle; /* from dlopen() */ TNC_IMCID imcID; TNC_ConnectionID connectionID; TNC_MessageTypeList supported_types; size_t num_supported_types; u8 *imc_send; size_t imc_send_len; /* Functions implemented by IMCs (with TNC_IMC_ prefix) */ TNC_Result (*Initialize)( TNC_IMCID imcID, TNC_Version minVersion, TNC_Version maxVersion, TNC_Version *pOutActualVersion); TNC_Result (*NotifyConnectionChange)( TNC_IMCID imcID, TNC_ConnectionID connectionID, TNC_ConnectionState newState); TNC_Result (*BeginHandshake)( TNC_IMCID imcID, TNC_ConnectionID connectionID); TNC_Result (*ReceiveMessage)( TNC_IMCID imcID, TNC_ConnectionID connectionID, TNC_BufferReference messageBuffer, TNC_UInt32 messageLength, TNC_MessageType messageType); TNC_Result (*BatchEnding)( TNC_IMCID imcID, TNC_ConnectionID connectionID); TNC_Result (*Terminate)(TNC_IMCID imcID); TNC_Result (*ProvideBindFunction)( TNC_IMCID imcID, TNC_TNCC_BindFunctionPointer bindFunction); }; struct tncc_data { struct tnc_if_imc *imc; unsigned int last_batchid; }; #define TNC_MAX_IMC_ID 10 static struct tnc_if_imc *tnc_imc[TNC_MAX_IMC_ID] = { NULL }; /* TNCC functions that IMCs can call */ TNC_Result TNC_TNCC_ReportMessageTypes( TNC_IMCID imcID, TNC_MessageTypeList supportedTypes, TNC_UInt32 typeCount) { TNC_UInt32 i; struct tnc_if_imc *imc; wpa_printf(MSG_DEBUG, "TNC: TNC_TNCC_ReportMessageTypes(imcID=%lu " "typeCount=%lu)", (unsigned long) imcID, (unsigned long) typeCount); for (i = 0; i < typeCount; i++) { wpa_printf(MSG_DEBUG, "TNC: supportedTypes[%lu] = %lu", i, supportedTypes[i]); } if (imcID >= TNC_MAX_IMC_ID || tnc_imc[imcID] == NULL) return TNC_RESULT_INVALID_PARAMETER; imc = tnc_imc[imcID]; os_free(imc->supported_types); imc->supported_types = os_malloc(typeCount * sizeof(TNC_MessageType)); if (imc->supported_types == NULL) return TNC_RESULT_FATAL; os_memcpy(imc->supported_types, supportedTypes, typeCount * sizeof(TNC_MessageType)); imc->num_supported_types = typeCount; return TNC_RESULT_SUCCESS; } TNC_Result TNC_TNCC_SendMessage( TNC_IMCID imcID, TNC_ConnectionID connectionID, TNC_BufferReference message, TNC_UInt32 messageLength, TNC_MessageType messageType) { struct tnc_if_imc *imc; unsigned char *b64; size_t b64len; wpa_printf(MSG_DEBUG, "TNC: TNC_TNCC_SendMessage(imcID=%lu " "connectionID=%lu messageType=%lu)", imcID, connectionID, messageType); wpa_hexdump_ascii(MSG_DEBUG, "TNC: TNC_TNCC_SendMessage", message, messageLength); if (imcID >= TNC_MAX_IMC_ID || tnc_imc[imcID] == NULL) return TNC_RESULT_INVALID_PARAMETER; b64 = base64_encode(message, messageLength, &b64len); if (b64 == NULL) return TNC_RESULT_FATAL; imc = tnc_imc[imcID]; os_free(imc->imc_send); imc->imc_send_len = 0; imc->imc_send = os_zalloc(b64len + 100); if (imc->imc_send == NULL) { os_free(b64); return TNC_RESULT_OTHER; } imc->imc_send_len = os_snprintf((char *) imc->imc_send, b64len + 100, "%08X" "%s", (unsigned int) messageType, b64); os_free(b64); return TNC_RESULT_SUCCESS; } TNC_Result TNC_TNCC_RequestHandshakeRetry( TNC_IMCID imcID, TNC_ConnectionID connectionID, TNC_RetryReason reason) { wpa_printf(MSG_DEBUG, "TNC: TNC_TNCC_RequestHandshakeRetry"); if (imcID >= TNC_MAX_IMC_ID || tnc_imc[imcID] == NULL) return TNC_RESULT_INVALID_PARAMETER; /* * TODO: trigger a call to eapol_sm_request_reauth(). This would * require that the IMC continues to be loaded in memory afer * authentication.. */ return TNC_RESULT_SUCCESS; } TNC_Result TNC_9048_LogMessage(TNC_IMCID imcID, TNC_UInt32 severity, const char *message) { wpa_printf(MSG_DEBUG, "TNC: TNC_9048_LogMessage(imcID=%lu " "severity==%lu message='%s')", imcID, severity, message); return TNC_RESULT_SUCCESS; } TNC_Result TNC_9048_UserMessage(TNC_IMCID imcID, TNC_ConnectionID connectionID, const char *message) { wpa_printf(MSG_DEBUG, "TNC: TNC_9048_UserMessage(imcID=%lu " "connectionID==%lu message='%s')", imcID, connectionID, message); return TNC_RESULT_SUCCESS; } TNC_Result TNC_TNCC_BindFunction( TNC_IMCID imcID, char *functionName, void **pOutfunctionPointer) { wpa_printf(MSG_DEBUG, "TNC: TNC_TNCC_BindFunction(imcID=%lu, " "functionName='%s')", (unsigned long) imcID, functionName); if (imcID >= TNC_MAX_IMC_ID || tnc_imc[imcID] == NULL) return TNC_RESULT_INVALID_PARAMETER; if (pOutfunctionPointer == NULL) return TNC_RESULT_INVALID_PARAMETER; if (os_strcmp(functionName, "TNC_TNCC_ReportMessageTypes") == 0) *pOutfunctionPointer = TNC_TNCC_ReportMessageTypes; else if (os_strcmp(functionName, "TNC_TNCC_SendMessage") == 0) *pOutfunctionPointer = TNC_TNCC_SendMessage; else if (os_strcmp(functionName, "TNC_TNCC_RequestHandshakeRetry") == 0) *pOutfunctionPointer = TNC_TNCC_RequestHandshakeRetry; else if (os_strcmp(functionName, "TNC_9048_LogMessage") == 0) *pOutfunctionPointer = TNC_9048_LogMessage; else if (os_strcmp(functionName, "TNC_9048_UserMessage") == 0) *pOutfunctionPointer = TNC_9048_UserMessage; else *pOutfunctionPointer = NULL; return TNC_RESULT_SUCCESS; } static void * tncc_get_sym(void *handle, char *func) { void *fptr; #ifdef CONFIG_NATIVE_WINDOWS #ifdef _WIN32_WCE fptr = GetProcAddressA(handle, func); #else /* _WIN32_WCE */ fptr = GetProcAddress(handle, func); #endif /* _WIN32_WCE */ #else /* CONFIG_NATIVE_WINDOWS */ fptr = dlsym(handle, func); #endif /* CONFIG_NATIVE_WINDOWS */ return fptr; } static int tncc_imc_resolve_funcs(struct tnc_if_imc *imc) { void *handle = imc->dlhandle; /* Mandatory IMC functions */ imc->Initialize = tncc_get_sym(handle, "TNC_IMC_Initialize"); if (imc->Initialize == NULL) { wpa_printf(MSG_ERROR, "TNC: IMC does not export " "TNC_IMC_Initialize"); return -1; } imc->BeginHandshake = tncc_get_sym(handle, "TNC_IMC_BeginHandshake"); if (imc->BeginHandshake == NULL) { wpa_printf(MSG_ERROR, "TNC: IMC does not export " "TNC_IMC_BeginHandshake"); return -1; } imc->ProvideBindFunction = tncc_get_sym(handle, "TNC_IMC_ProvideBindFunction"); if (imc->ProvideBindFunction == NULL) { wpa_printf(MSG_ERROR, "TNC: IMC does not export " "TNC_IMC_ProvideBindFunction"); return -1; } /* Optional IMC functions */ imc->NotifyConnectionChange = tncc_get_sym(handle, "TNC_IMC_NotifyConnectionChange"); imc->ReceiveMessage = tncc_get_sym(handle, "TNC_IMC_ReceiveMessage"); imc->BatchEnding = tncc_get_sym(handle, "TNC_IMC_BatchEnding"); imc->Terminate = tncc_get_sym(handle, "TNC_IMC_Terminate"); return 0; } static int tncc_imc_initialize(struct tnc_if_imc *imc) { TNC_Result res; TNC_Version imc_ver; wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMC_Initialize for IMC '%s'", imc->name); res = imc->Initialize(imc->imcID, TNC_IFIMC_VERSION_1, TNC_IFIMC_VERSION_1, &imc_ver); wpa_printf(MSG_DEBUG, "TNC: TNC_IMC_Initialize: res=%lu imc_ver=%lu", (unsigned long) res, (unsigned long) imc_ver); return res == TNC_RESULT_SUCCESS ? 0 : -1; } static int tncc_imc_terminate(struct tnc_if_imc *imc) { TNC_Result res; if (imc->Terminate == NULL) return 0; wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMC_Terminate for IMC '%s'", imc->name); res = imc->Terminate(imc->imcID); wpa_printf(MSG_DEBUG, "TNC: TNC_IMC_Terminate: %lu", (unsigned long) res); return res == TNC_RESULT_SUCCESS ? 0 : -1; } static int tncc_imc_provide_bind_function(struct tnc_if_imc *imc) { TNC_Result res; wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMC_ProvideBindFunction for " "IMC '%s'", imc->name); res = imc->ProvideBindFunction(imc->imcID, TNC_TNCC_BindFunction); wpa_printf(MSG_DEBUG, "TNC: TNC_IMC_ProvideBindFunction: res=%lu", (unsigned long) res); return res == TNC_RESULT_SUCCESS ? 0 : -1; } static int tncc_imc_notify_connection_change(struct tnc_if_imc *imc, TNC_ConnectionState state) { TNC_Result res; if (imc->NotifyConnectionChange == NULL) return 0; wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMC_NotifyConnectionChange(%d)" " for IMC '%s'", (int) state, imc->name); res = imc->NotifyConnectionChange(imc->imcID, imc->connectionID, state); wpa_printf(MSG_DEBUG, "TNC: TNC_IMC_NotifyConnectionChange: %lu", (unsigned long) res); return res == TNC_RESULT_SUCCESS ? 0 : -1; } static int tncc_imc_begin_handshake(struct tnc_if_imc *imc) { TNC_Result res; wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMC_BeginHandshake for IMC " "'%s'", imc->name); res = imc->BeginHandshake(imc->imcID, imc->connectionID); wpa_printf(MSG_DEBUG, "TNC: TNC_IMC_BeginHandshake: %lu", (unsigned long) res); return res == TNC_RESULT_SUCCESS ? 0 : -1; } static int tncc_load_imc(struct tnc_if_imc *imc) { if (imc->path == NULL) { wpa_printf(MSG_DEBUG, "TNC: No IMC configured"); return -1; } wpa_printf(MSG_DEBUG, "TNC: Opening IMC: %s (%s)", imc->name, imc->path); #ifdef CONFIG_NATIVE_WINDOWS #ifdef UNICODE { TCHAR *lib = wpa_strdup_tchar(imc->path); if (lib == NULL) return -1; imc->dlhandle = LoadLibrary(lib); os_free(lib); } #else /* UNICODE */ imc->dlhandle = LoadLibrary(imc->path); #endif /* UNICODE */ if (imc->dlhandle == NULL) { wpa_printf(MSG_ERROR, "TNC: Failed to open IMC '%s' (%s): %d", imc->name, imc->path, (int) GetLastError()); return -1; } #else /* CONFIG_NATIVE_WINDOWS */ imc->dlhandle = dlopen(imc->path, RTLD_LAZY); if (imc->dlhandle == NULL) { wpa_printf(MSG_ERROR, "TNC: Failed to open IMC '%s' (%s): %s", imc->name, imc->path, dlerror()); return -1; } #endif /* CONFIG_NATIVE_WINDOWS */ if (tncc_imc_resolve_funcs(imc) < 0) { wpa_printf(MSG_ERROR, "TNC: Failed to resolve IMC functions"); return -1; } if (tncc_imc_initialize(imc) < 0 || tncc_imc_provide_bind_function(imc) < 0) { wpa_printf(MSG_ERROR, "TNC: Failed to initialize IMC"); return -1; } return 0; } static void tncc_unload_imc(struct tnc_if_imc *imc) { tncc_imc_terminate(imc); tnc_imc[imc->imcID] = NULL; if (imc->dlhandle) { #ifdef CONFIG_NATIVE_WINDOWS FreeLibrary(imc->dlhandle); #else /* CONFIG_NATIVE_WINDOWS */ dlclose(imc->dlhandle); #endif /* CONFIG_NATIVE_WINDOWS */ } os_free(imc->name); os_free(imc->path); os_free(imc->supported_types); os_free(imc->imc_send); } static int tncc_supported_type(struct tnc_if_imc *imc, unsigned int type) { size_t i; unsigned int vendor, subtype; if (imc == NULL || imc->supported_types == NULL) return 0; vendor = type >> 8; subtype = type & 0xff; for (i = 0; i < imc->num_supported_types; i++) { unsigned int svendor, ssubtype; svendor = imc->supported_types[i] >> 8; ssubtype = imc->supported_types[i] & 0xff; if ((vendor == svendor || svendor == TNC_VENDORID_ANY) && (subtype == ssubtype || ssubtype == TNC_SUBTYPE_ANY)) return 1; } return 0; } static void tncc_send_to_imcs(struct tncc_data *tncc, unsigned int type, const u8 *msg, size_t len) { struct tnc_if_imc *imc; TNC_Result res; wpa_hexdump_ascii(MSG_MSGDUMP, "TNC: Message to IMC(s)", msg, len); for (imc = tncc->imc; imc; imc = imc->next) { if (imc->ReceiveMessage == NULL || !tncc_supported_type(imc, type)) continue; wpa_printf(MSG_DEBUG, "TNC: Call ReceiveMessage for IMC '%s'", imc->name); res = imc->ReceiveMessage(imc->imcID, imc->connectionID, (TNC_BufferReference) msg, len, type); wpa_printf(MSG_DEBUG, "TNC: ReceiveMessage: %lu", (unsigned long) res); } } void tncc_init_connection(struct tncc_data *tncc) { struct tnc_if_imc *imc; for (imc = tncc->imc; imc; imc = imc->next) { tncc_imc_notify_connection_change( imc, TNC_CONNECTION_STATE_CREATE); tncc_imc_notify_connection_change( imc, TNC_CONNECTION_STATE_HANDSHAKE); os_free(imc->imc_send); imc->imc_send = NULL; imc->imc_send_len = 0; tncc_imc_begin_handshake(imc); } } size_t tncc_total_send_len(struct tncc_data *tncc) { struct tnc_if_imc *imc; size_t len = 0; for (imc = tncc->imc; imc; imc = imc->next) len += imc->imc_send_len; return len; } u8 * tncc_copy_send_buf(struct tncc_data *tncc, u8 *pos) { struct tnc_if_imc *imc; for (imc = tncc->imc; imc; imc = imc->next) { if (imc->imc_send == NULL) continue; os_memcpy(pos, imc->imc_send, imc->imc_send_len); pos += imc->imc_send_len; os_free(imc->imc_send); imc->imc_send = NULL; imc->imc_send_len = 0; } return pos; } char * tncc_if_tnccs_start(struct tncc_data *tncc) { char *buf = os_malloc(1000); if (buf == NULL) return NULL; tncc->last_batchid++; os_snprintf(buf, 1000, IF_TNCCS_START, tncc->last_batchid); return buf; } char * tncc_if_tnccs_end(void) { char *buf = os_malloc(100); if (buf == NULL) return NULL; os_snprintf(buf, 100, IF_TNCCS_END); return buf; } static void tncc_notify_recommendation(struct tncc_data *tncc, enum tncc_process_res res) { TNC_ConnectionState state; struct tnc_if_imc *imc; switch (res) { case TNCCS_RECOMMENDATION_ALLOW: state = TNC_CONNECTION_STATE_ACCESS_ALLOWED; break; case TNCCS_RECOMMENDATION_NONE: state = TNC_CONNECTION_STATE_ACCESS_NONE; break; case TNCCS_RECOMMENDATION_ISOLATE: state = TNC_CONNECTION_STATE_ACCESS_ISOLATED; break; default: state = TNC_CONNECTION_STATE_ACCESS_NONE; break; } for (imc = tncc->imc; imc; imc = imc->next) tncc_imc_notify_connection_change(imc, state); } static int tncc_get_type(char *start, unsigned int *type) { char *pos = os_strstr(start, ""); if (pos == NULL) return -1; pos += 6; *type = strtoul(pos, NULL, 16); return 0; } static unsigned char * tncc_get_base64(char *start, size_t *decoded_len) { char *pos, *pos2; unsigned char *decoded; pos = os_strstr(start, ""); if (pos == NULL) return NULL; pos += 8; pos2 = os_strstr(pos, ""); if (pos2 == NULL) return NULL; *pos2 = '\0'; decoded = base64_decode((unsigned char *) pos, os_strlen(pos), decoded_len); *pos2 = '<'; if (decoded == NULL) { wpa_printf(MSG_DEBUG, "TNC: Failed to decode Base64 data"); } return decoded; } static enum tncc_process_res tncc_get_recommendation(char *start) { char *pos, *pos2, saved; int recom; pos = os_strstr(start, ""); if (start == NULL || end == NULL || start > end) { os_free(buf); return TNCCS_PROCESS_ERROR; } start += 13; while (*start == ' ') start++; *end = '\0'; pos = os_strstr(start, "BatchId="); if (pos == NULL) { os_free(buf); return TNCCS_PROCESS_ERROR; } pos += 8; if (*pos == '"') pos++; batch_id = atoi(pos); wpa_printf(MSG_DEBUG, "TNC: Received IF-TNCCS BatchId=%u", batch_id); if (batch_id != tncc->last_batchid + 1) { wpa_printf(MSG_DEBUG, "TNC: Unexpected IF-TNCCS BatchId " "%u (expected %u)", batch_id, tncc->last_batchid + 1); os_free(buf); return TNCCS_PROCESS_ERROR; } tncc->last_batchid = batch_id; while (*pos != '\0' && *pos != '>') pos++; if (*pos == '\0') { os_free(buf); return TNCCS_PROCESS_ERROR; } pos++; payload = start; /* * * 01234567 * foo== * */ while (*start) { char *endpos; unsigned int type; pos = os_strstr(start, ""); if (pos == NULL) break; start = pos + 17; end = os_strstr(start, ""); if (end == NULL) break; *end = '\0'; endpos = end; end += 18; if (tncc_get_type(start, &type) < 0) { *endpos = '<'; start = end; continue; } wpa_printf(MSG_DEBUG, "TNC: IMC-IMV-Message Type 0x%x", type); decoded = tncc_get_base64(start, &decoded_len); if (decoded == NULL) { *endpos = '<'; start = end; continue; } tncc_send_to_imcs(tncc, type, decoded, decoded_len); os_free(decoded); start = end; } /* * * 01234567 * * foo== * */ start = payload; while (*start) { unsigned int type; char *xml, *xmlend, *endpos; pos = os_strstr(start, ""); if (pos == NULL) break; start = pos + 19; end = os_strstr(start, ""); if (end == NULL) break; *end = '\0'; endpos = end; end += 20; if (tncc_get_type(start, &type) < 0) { *endpos = '<'; start = end; continue; } wpa_printf(MSG_DEBUG, "TNC: TNCC-TNCS-Message Type 0x%x", type); /* Base64 OR XML */ decoded = NULL; xml = NULL; xmlend = NULL; pos = os_strstr(start, ""); if (pos) { pos += 5; pos2 = os_strstr(pos, ""); if (pos2 == NULL) { *endpos = '<'; start = end; continue; } xmlend = pos2; xml = pos; } else { decoded = tncc_get_base64(start, &decoded_len); if (decoded == NULL) { *endpos = '<'; start = end; continue; } } if (decoded) { wpa_hexdump_ascii(MSG_MSGDUMP, "TNC: TNCC-TNCS-Message Base64", decoded, decoded_len); os_free(decoded); } if (xml) { wpa_hexdump_ascii(MSG_MSGDUMP, "TNC: TNCC-TNCS-Message XML", (unsigned char *) xml, xmlend - xml); } if (type == TNC_TNCCS_RECOMMENDATION && xml) { /* * * */ *xmlend = '\0'; res = tncc_get_recommendation(xml); *xmlend = '<'; recommendation_msg = 1; } start = end; } os_free(buf); if (recommendation_msg) tncc_notify_recommendation(tncc, res); return res; } #ifdef CONFIG_NATIVE_WINDOWS static int tncc_read_config_reg(struct tncc_data *tncc, HKEY hive) { HKEY hk, hk2; LONG ret; DWORD i; struct tnc_if_imc *imc, *last; int j; last = tncc->imc; while (last && last->next) last = last->next; ret = RegOpenKeyEx(hive, TNC_WINREG_PATH, 0, KEY_ENUMERATE_SUB_KEYS, &hk); if (ret != ERROR_SUCCESS) return 0; for (i = 0; ; i++) { TCHAR name[255], *val; DWORD namelen, buflen; namelen = 255; ret = RegEnumKeyEx(hk, i, name, &namelen, NULL, NULL, NULL, NULL); if (ret == ERROR_NO_MORE_ITEMS) break; if (ret != ERROR_SUCCESS) { wpa_printf(MSG_DEBUG, "TNC: RegEnumKeyEx failed: 0x%x", (unsigned int) ret); break; } if (namelen >= 255) namelen = 255 - 1; name[namelen] = '\0'; wpa_printf(MSG_DEBUG, "TNC: IMC '" TSTR "'", name); ret = RegOpenKeyEx(hk, name, 0, KEY_QUERY_VALUE, &hk2); if (ret != ERROR_SUCCESS) { wpa_printf(MSG_DEBUG, "Could not open IMC key '" TSTR "'", name); continue; } ret = RegQueryValueEx(hk2, TEXT("Path"), NULL, NULL, NULL, &buflen); if (ret != ERROR_SUCCESS) { wpa_printf(MSG_DEBUG, "TNC: Could not read Path from " "IMC key '" TSTR "'", name); RegCloseKey(hk2); continue; } val = os_malloc(buflen); if (val == NULL) { RegCloseKey(hk2); continue; } ret = RegQueryValueEx(hk2, TEXT("Path"), NULL, NULL, (LPBYTE) val, &buflen); if (ret != ERROR_SUCCESS) { os_free(val); RegCloseKey(hk2); continue; } RegCloseKey(hk2); wpa_unicode2ascii_inplace(val); wpa_printf(MSG_DEBUG, "TNC: IMC Path '%s'", (char *) val); for (j = 0; j < TNC_MAX_IMC_ID; j++) { if (tnc_imc[j] == NULL) break; } if (j >= TNC_MAX_IMC_ID) { wpa_printf(MSG_DEBUG, "TNC: Too many IMCs"); os_free(val); continue; } imc = os_zalloc(sizeof(*imc)); if (imc == NULL) { os_free(val); break; } imc->imcID = j; wpa_unicode2ascii_inplace(name); imc->name = os_strdup((char *) name); imc->path = os_strdup((char *) val); os_free(val); if (last == NULL) tncc->imc = imc; else last->next = imc; last = imc; tnc_imc[imc->imcID] = imc; } RegCloseKey(hk); return 0; } static int tncc_read_config(struct tncc_data *tncc) { if (tncc_read_config_reg(tncc, HKEY_LOCAL_MACHINE) < 0 || tncc_read_config_reg(tncc, HKEY_CURRENT_USER) < 0) return -1; return 0; } #else /* CONFIG_NATIVE_WINDOWS */ static struct tnc_if_imc * tncc_parse_imc(char *start, char *end, int *error) { struct tnc_if_imc *imc; char *pos, *pos2; int i; for (i = 0; i < TNC_MAX_IMC_ID; i++) { if (tnc_imc[i] == NULL) break; } if (i >= TNC_MAX_IMC_ID) { wpa_printf(MSG_DEBUG, "TNC: Too many IMCs"); return NULL; } imc = os_zalloc(sizeof(*imc)); if (imc == NULL) { *error = 1; return NULL; } imc->imcID = i; pos = start; wpa_printf(MSG_DEBUG, "TNC: Configured IMC: %s", pos); if (pos + 1 >= end || *pos != '"') { wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMC line '%s' " "(no starting quotation mark)", start); os_free(imc); return NULL; } pos++; pos2 = pos; while (pos2 < end && *pos2 != '"') pos2++; if (pos2 >= end) { wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMC line '%s' " "(no ending quotation mark)", start); os_free(imc); return NULL; } *pos2 = '\0'; wpa_printf(MSG_DEBUG, "TNC: Name: '%s'", pos); imc->name = os_strdup(pos); pos = pos2 + 1; if (pos >= end || *pos != ' ') { wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMC line '%s' " "(no space after name)", start); os_free(imc->name); os_free(imc); return NULL; } pos++; wpa_printf(MSG_DEBUG, "TNC: IMC file: '%s'", pos); imc->path = os_strdup(pos); tnc_imc[imc->imcID] = imc; return imc; } static int tncc_read_config(struct tncc_data *tncc) { char *config, *end, *pos, *line_end; size_t config_len; struct tnc_if_imc *imc, *last; last = NULL; config = os_readfile(TNC_CONFIG_FILE, &config_len); if (config == NULL) { wpa_printf(MSG_ERROR, "TNC: Could not open TNC configuration " "file '%s'", TNC_CONFIG_FILE); return -1; } end = config + config_len; for (pos = config; pos < end; pos = line_end + 1) { line_end = pos; while (*line_end != '\n' && *line_end != '\r' && line_end < end) line_end++; *line_end = '\0'; if (os_strncmp(pos, "IMC ", 4) == 0) { int error = 0; imc = tncc_parse_imc(pos + 4, line_end, &error); if (error) return -1; if (imc) { if (last == NULL) tncc->imc = imc; else last->next = imc; last = imc; } } } os_free(config); return 0; } #endif /* CONFIG_NATIVE_WINDOWS */ struct tncc_data * tncc_init(void) { struct tncc_data *tncc; struct tnc_if_imc *imc; tncc = os_zalloc(sizeof(*tncc)); if (tncc == NULL) return NULL; /* TODO: * move loading and Initialize() to a location that is not * re-initialized for every EAP-TNC session (?) */ if (tncc_read_config(tncc) < 0) { wpa_printf(MSG_ERROR, "TNC: Failed to read TNC configuration"); goto failed; } for (imc = tncc->imc; imc; imc = imc->next) { if (tncc_load_imc(imc)) { wpa_printf(MSG_ERROR, "TNC: Failed to load IMC '%s'", imc->name); goto failed; } } return tncc; failed: tncc_deinit(tncc); return NULL; } void tncc_deinit(struct tncc_data *tncc) { struct tnc_if_imc *imc, *prev; imc = tncc->imc; while (imc) { tncc_unload_imc(imc); prev = imc; imc = imc->next; os_free(prev); } os_free(tncc); } static struct wpabuf * tncc_build_soh(int ver) { struct wpabuf *buf; u8 *tlv_len, *tlv_len2, *outer_len, *inner_len, *ssoh_len, *end; u8 correlation_id[24]; /* TODO: get correct name */ char *machinename = "wpa_supplicant@w1.fi"; if (os_get_random(correlation_id, sizeof(correlation_id))) return NULL; wpa_hexdump(MSG_DEBUG, "TNC: SoH Correlation ID", correlation_id, sizeof(correlation_id)); buf = wpabuf_alloc(200); if (buf == NULL) return NULL; /* Vendor-Specific TLV (Microsoft) - SoH */ wpabuf_put_be16(buf, EAP_TLV_VENDOR_SPECIFIC_TLV); /* TLV Type */ tlv_len = wpabuf_put(buf, 2); /* Length */ wpabuf_put_be32(buf, EAP_VENDOR_MICROSOFT); /* Vendor_Id */ wpabuf_put_be16(buf, 0x01); /* TLV Type - SoH TLV */ tlv_len2 = wpabuf_put(buf, 2); /* Length */ /* SoH Header */ wpabuf_put_be16(buf, EAP_TLV_VENDOR_SPECIFIC_TLV); /* Outer Type */ outer_len = wpabuf_put(buf, 2); wpabuf_put_be32(buf, EAP_VENDOR_MICROSOFT); /* IANA SMI Code */ wpabuf_put_be16(buf, ver); /* Inner Type */ inner_len = wpabuf_put(buf, 2); if (ver == 2) { /* SoH Mode Sub-Header */ /* Outer Type */ wpabuf_put_be16(buf, EAP_TLV_VENDOR_SPECIFIC_TLV); wpabuf_put_be16(buf, 4 + 24 + 1 + 1); /* Length */ wpabuf_put_be32(buf, EAP_VENDOR_MICROSOFT); /* IANA SMI Code */ /* Value: */ wpabuf_put_data(buf, correlation_id, sizeof(correlation_id)); wpabuf_put_u8(buf, 0x01); /* Intent Flag - Request */ wpabuf_put_u8(buf, 0x00); /* Content-Type Flag */ } /* SSoH TLV */ /* System-Health-Id */ wpabuf_put_be16(buf, 0x0002); /* Type */ wpabuf_put_be16(buf, 4); /* Length */ wpabuf_put_be32(buf, 79616); /* Vendor-Specific Attribute */ wpabuf_put_be16(buf, EAP_TLV_VENDOR_SPECIFIC_TLV); ssoh_len = wpabuf_put(buf, 2); wpabuf_put_be32(buf, EAP_VENDOR_MICROSOFT); /* IANA SMI Code */ /* MS-Packet-Info */ wpabuf_put_u8(buf, SSOH_MS_PACKET_INFO); /* Note: IF-TNCCS-SOH v1.0 r8 claims this field to be: * Reserved(4 bits) r(1 bit) Vers(3 bits), but Windows XP * SP3 seems to be sending 0x11 for SSoH, i.e., r(request/response) bit * would not be in the specified location. * [MS-SOH] 4.0.2: Reserved(3 bits) r(1 bit) Vers(4 bits) */ wpabuf_put_u8(buf, 0x11); /* r=request, vers=1 */ /* MS-Machine-Inventory */ /* TODO: get correct values; 0 = not applicable for OS */ wpabuf_put_u8(buf, SSOH_MS_MACHINE_INVENTORY); wpabuf_put_be32(buf, 0); /* osVersionMajor */ wpabuf_put_be32(buf, 0); /* osVersionMinor */ wpabuf_put_be32(buf, 0); /* osVersionBuild */ wpabuf_put_be16(buf, 0); /* spVersionMajor */ wpabuf_put_be16(buf, 0); /* spVersionMinor */ wpabuf_put_be16(buf, 0); /* procArch */ /* MS-MachineName */ wpabuf_put_u8(buf, SSOH_MS_MACHINENAME); wpabuf_put_be16(buf, os_strlen(machinename) + 1); wpabuf_put_data(buf, machinename, os_strlen(machinename) + 1); /* MS-CorrelationId */ wpabuf_put_u8(buf, SSOH_MS_CORRELATIONID); wpabuf_put_data(buf, correlation_id, sizeof(correlation_id)); /* MS-Quarantine-State */ wpabuf_put_u8(buf, SSOH_MS_QUARANTINE_STATE); wpabuf_put_be16(buf, 1); /* Flags: ExtState=0, f=0, qState=1 */ wpabuf_put_be32(buf, 0xffffffff); /* ProbTime (hi) */ wpabuf_put_be32(buf, 0xffffffff); /* ProbTime (lo) */ wpabuf_put_be16(buf, 1); /* urlLenInBytes */ wpabuf_put_u8(buf, 0); /* null termination for the url */ /* MS-Machine-Inventory-Ex */ wpabuf_put_u8(buf, SSOH_MS_MACHINE_INVENTORY_EX); wpabuf_put_be32(buf, 0); /* Reserved * (note: Windows XP SP3 uses 0xdecafbad) */ wpabuf_put_u8(buf, 1); /* ProductType: Client */ /* Update SSoH Length */ end = wpabuf_put(buf, 0); WPA_PUT_BE16(ssoh_len, end - ssoh_len - 2); /* TODO: SoHReportEntry TLV (zero or more) */ /* Update length fields */ end = wpabuf_put(buf, 0); WPA_PUT_BE16(tlv_len, end - tlv_len - 2); WPA_PUT_BE16(tlv_len2, end - tlv_len2 - 2); WPA_PUT_BE16(outer_len, end - outer_len - 2); WPA_PUT_BE16(inner_len, end - inner_len - 2); return buf; } struct wpabuf * tncc_process_soh_request(int ver, const u8 *data, size_t len) { const u8 *pos; wpa_hexdump(MSG_DEBUG, "TNC: SoH Request", data, len); if (len < 12) return NULL; /* SoH Request */ pos = data; /* TLV Type */ if (WPA_GET_BE16(pos) != EAP_TLV_VENDOR_SPECIFIC_TLV) return NULL; pos += 2; /* Length */ if (WPA_GET_BE16(pos) < 8) return NULL; pos += 2; /* Vendor_Id */ if (WPA_GET_BE32(pos) != EAP_VENDOR_MICROSOFT) return NULL; pos += 4; /* TLV Type */ if (WPA_GET_BE16(pos) != 0x02 /* SoH request TLV */) return NULL; wpa_printf(MSG_DEBUG, "TNC: SoH Request TLV received"); return tncc_build_soh(2); } wpa-2.1/src/eap_peer/tncc.h000066400000000000000000000017421227414666700156350ustar00rootroot00000000000000/* * EAP-TNC - TNCC (IF-IMC and IF-TNCCS) * Copyright (c) 2007, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef TNCC_H #define TNCC_H struct tncc_data; struct tncc_data * tncc_init(void); void tncc_deinit(struct tncc_data *tncc); void tncc_init_connection(struct tncc_data *tncc); size_t tncc_total_send_len(struct tncc_data *tncc); u8 * tncc_copy_send_buf(struct tncc_data *tncc, u8 *pos); char * tncc_if_tnccs_start(struct tncc_data *tncc); char * tncc_if_tnccs_end(void); enum tncc_process_res { TNCCS_PROCESS_ERROR = -1, TNCCS_PROCESS_OK_NO_RECOMMENDATION = 0, TNCCS_RECOMMENDATION_ERROR, TNCCS_RECOMMENDATION_ALLOW, TNCCS_RECOMMENDATION_NONE, TNCCS_RECOMMENDATION_ISOLATE }; enum tncc_process_res tncc_process_if_tnccs(struct tncc_data *tncc, const u8 *msg, size_t len); struct wpabuf * tncc_process_soh_request(int ver, const u8 *data, size_t len); #endif /* TNCC_H */ wpa-2.1/src/eap_server/000077500000000000000000000000001227414666700151045ustar00rootroot00000000000000wpa-2.1/src/eap_server/Makefile000066400000000000000000000001641227414666700165450ustar00rootroot00000000000000all: @echo Nothing to be made. clean: rm -f *~ *.o *.d *.gcno *.gcda *.gcov install: @echo Nothing to be made. wpa-2.1/src/eap_server/eap.h000066400000000000000000000063071227414666700160300ustar00rootroot00000000000000/* * hostapd / EAP Full Authenticator state machine (RFC 4137) * Copyright (c) 2004-2007, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef EAP_H #define EAP_H #include "common/defs.h" #include "eap_common/eap_defs.h" #include "eap_server/eap_methods.h" #include "wpabuf.h" struct eap_sm; #define EAP_TTLS_AUTH_PAP 1 #define EAP_TTLS_AUTH_CHAP 2 #define EAP_TTLS_AUTH_MSCHAP 4 #define EAP_TTLS_AUTH_MSCHAPV2 8 struct eap_user { struct { int vendor; u32 method; } methods[EAP_MAX_METHODS]; u8 *password; size_t password_len; int password_hash; /* whether password is hashed with * nt_password_hash() */ int phase2; int force_version; int ttls_auth; /* bitfield of * EAP_TTLS_AUTH_{PAP,CHAP,MSCHAP,MSCHAPV2} */ }; struct eap_eapol_interface { /* Lower layer to full authenticator variables */ Boolean eapResp; /* shared with EAPOL Backend Authentication */ struct wpabuf *eapRespData; Boolean portEnabled; int retransWhile; Boolean eapRestart; /* shared with EAPOL Authenticator PAE */ int eapSRTT; int eapRTTVAR; /* Full authenticator to lower layer variables */ Boolean eapReq; /* shared with EAPOL Backend Authentication */ Boolean eapNoReq; /* shared with EAPOL Backend Authentication */ Boolean eapSuccess; Boolean eapFail; Boolean eapTimeout; struct wpabuf *eapReqData; u8 *eapKeyData; size_t eapKeyDataLen; Boolean eapKeyAvailable; /* called keyAvailable in IEEE 802.1X-2004 */ /* AAA interface to full authenticator variables */ Boolean aaaEapReq; Boolean aaaEapNoReq; Boolean aaaSuccess; Boolean aaaFail; struct wpabuf *aaaEapReqData; u8 *aaaEapKeyData; size_t aaaEapKeyDataLen; Boolean aaaEapKeyAvailable; int aaaMethodTimeout; /* Full authenticator to AAA interface variables */ Boolean aaaEapResp; struct wpabuf *aaaEapRespData; /* aaaIdentity -> eap_get_identity() */ Boolean aaaTimeout; }; struct eapol_callbacks { int (*get_eap_user)(void *ctx, const u8 *identity, size_t identity_len, int phase2, struct eap_user *user); const char * (*get_eap_req_id_text)(void *ctx, size_t *len); }; struct eap_config { void *ssl_ctx; void *msg_ctx; void *eap_sim_db_priv; Boolean backend_auth; int eap_server; u16 pwd_group; u8 *pac_opaque_encr_key; u8 *eap_fast_a_id; size_t eap_fast_a_id_len; char *eap_fast_a_id_info; int eap_fast_prov; int pac_key_lifetime; int pac_key_refresh_time; int eap_sim_aka_result_ind; int tnc; struct wps_context *wps; const struct wpabuf *assoc_wps_ie; const struct wpabuf *assoc_p2p_ie; const u8 *peer_addr; int fragment_size; int pbc_in_m1; const u8 *server_id; size_t server_id_len; }; struct eap_sm * eap_server_sm_init(void *eapol_ctx, struct eapol_callbacks *eapol_cb, struct eap_config *eap_conf); void eap_server_sm_deinit(struct eap_sm *sm); int eap_server_sm_step(struct eap_sm *sm); void eap_sm_notify_cached(struct eap_sm *sm); void eap_sm_pending_cb(struct eap_sm *sm); int eap_sm_method_pending(struct eap_sm *sm); const u8 * eap_get_identity(struct eap_sm *sm, size_t *len); struct eap_eapol_interface * eap_get_interface(struct eap_sm *sm); void eap_server_clear_identity(struct eap_sm *sm); #endif /* EAP_H */ wpa-2.1/src/eap_server/eap_i.h000066400000000000000000000135251227414666700163400ustar00rootroot00000000000000/* * hostapd / EAP Authenticator state machine internal structures (RFC 4137) * Copyright (c) 2004-2007, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef EAP_I_H #define EAP_I_H #include "wpabuf.h" #include "eap_server/eap.h" #include "eap_common/eap_common.h" /* RFC 4137 - EAP Standalone Authenticator */ /** * struct eap_method - EAP method interface * This structure defines the EAP method interface. Each method will need to * register its own EAP type, EAP name, and set of function pointers for method * specific operations. This interface is based on section 5.4 of RFC 4137. */ struct eap_method { int vendor; EapType method; const char *name; void * (*init)(struct eap_sm *sm); void * (*initPickUp)(struct eap_sm *sm); void (*reset)(struct eap_sm *sm, void *priv); struct wpabuf * (*buildReq)(struct eap_sm *sm, void *priv, u8 id); int (*getTimeout)(struct eap_sm *sm, void *priv); Boolean (*check)(struct eap_sm *sm, void *priv, struct wpabuf *respData); void (*process)(struct eap_sm *sm, void *priv, struct wpabuf *respData); Boolean (*isDone)(struct eap_sm *sm, void *priv); u8 * (*getKey)(struct eap_sm *sm, void *priv, size_t *len); /* isSuccess is not specified in draft-ietf-eap-statemachine-05.txt, * but it is useful in implementing Policy.getDecision() */ Boolean (*isSuccess)(struct eap_sm *sm, void *priv); /** * free - Free EAP method data * @method: Pointer to the method data registered with * eap_server_method_register(). * * This function will be called when the EAP method is being * unregistered. If the EAP method allocated resources during * registration (e.g., allocated struct eap_method), they should be * freed in this function. No other method functions will be called * after this call. If this function is not defined (i.e., function * pointer is %NULL), a default handler is used to release the method * data with free(method). This is suitable for most cases. */ void (*free)(struct eap_method *method); #define EAP_SERVER_METHOD_INTERFACE_VERSION 1 /** * version - Version of the EAP server method interface * * The EAP server method implementation should set this variable to * EAP_SERVER_METHOD_INTERFACE_VERSION. This is used to verify that the * EAP method is using supported API version when using dynamically * loadable EAP methods. */ int version; /** * next - Pointer to the next EAP method * * This variable is used internally in the EAP method registration code * to create a linked list of registered EAP methods. */ struct eap_method *next; /** * get_emsk - Get EAP method specific keying extended material (EMSK) * @sm: Pointer to EAP state machine allocated with eap_sm_init() * @priv: Pointer to private EAP method data from eap_method::init() * @len: Pointer to a variable to store EMSK length * Returns: EMSK or %NULL if not available * * This function can be used to get the extended keying material from * the EAP method. The key may already be stored in the method-specific * private data or this function may derive the key. */ u8 * (*get_emsk)(struct eap_sm *sm, void *priv, size_t *len); }; /** * struct eap_sm - EAP server state machine data */ struct eap_sm { enum { EAP_DISABLED, EAP_INITIALIZE, EAP_IDLE, EAP_RECEIVED, EAP_INTEGRITY_CHECK, EAP_METHOD_RESPONSE, EAP_METHOD_REQUEST, EAP_PROPOSE_METHOD, EAP_SELECT_ACTION, EAP_SEND_REQUEST, EAP_DISCARD, EAP_NAK, EAP_RETRANSMIT, EAP_SUCCESS, EAP_FAILURE, EAP_TIMEOUT_FAILURE, EAP_PICK_UP_METHOD, EAP_INITIALIZE_PASSTHROUGH, EAP_IDLE2, EAP_RETRANSMIT2, EAP_RECEIVED2, EAP_DISCARD2, EAP_SEND_REQUEST2, EAP_AAA_REQUEST, EAP_AAA_RESPONSE, EAP_AAA_IDLE, EAP_TIMEOUT_FAILURE2, EAP_FAILURE2, EAP_SUCCESS2 } EAP_state; /* Constants */ int MaxRetrans; struct eap_eapol_interface eap_if; /* Full authenticator state machine local variables */ /* Long-term (maintained between packets) */ EapType currentMethod; int currentId; enum { METHOD_PROPOSED, METHOD_CONTINUE, METHOD_END } methodState; int retransCount; struct wpabuf *lastReqData; int methodTimeout; /* Short-term (not maintained between packets) */ Boolean rxResp; int respId; EapType respMethod; int respVendor; u32 respVendorMethod; Boolean ignore; enum { DECISION_SUCCESS, DECISION_FAILURE, DECISION_CONTINUE, DECISION_PASSTHROUGH } decision; /* Miscellaneous variables */ const struct eap_method *m; /* selected EAP method */ /* not defined in RFC 4137 */ Boolean changed; void *eapol_ctx, *msg_ctx; struct eapol_callbacks *eapol_cb; void *eap_method_priv; u8 *identity; size_t identity_len; /* Whether Phase 2 method should validate identity match */ int require_identity_match; int lastId; /* Identifier used in the last EAP-Packet */ struct eap_user *user; int user_eap_method_index; int init_phase2; void *ssl_ctx; struct eap_sim_db_data *eap_sim_db_priv; Boolean backend_auth; Boolean update_user; int eap_server; int num_rounds; enum { METHOD_PENDING_NONE, METHOD_PENDING_WAIT, METHOD_PENDING_CONT } method_pending; u8 *auth_challenge; u8 *peer_challenge; u8 *pac_opaque_encr_key; u8 *eap_fast_a_id; size_t eap_fast_a_id_len; char *eap_fast_a_id_info; enum { NO_PROV, ANON_PROV, AUTH_PROV, BOTH_PROV } eap_fast_prov; int pac_key_lifetime; int pac_key_refresh_time; int eap_sim_aka_result_ind; int tnc; u16 pwd_group; struct wps_context *wps; struct wpabuf *assoc_wps_ie; struct wpabuf *assoc_p2p_ie; Boolean start_reauth; u8 peer_addr[ETH_ALEN]; /* Fragmentation size for EAP method init() handler */ int fragment_size; int pbc_in_m1; const u8 *server_id; size_t server_id_len; }; int eap_user_get(struct eap_sm *sm, const u8 *identity, size_t identity_len, int phase2); void eap_sm_process_nak(struct eap_sm *sm, const u8 *nak_list, size_t len); #endif /* EAP_I_H */ wpa-2.1/src/eap_server/eap_methods.h000066400000000000000000000033031227414666700175440ustar00rootroot00000000000000/* * EAP server 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_SERVER_METHODS_H #define EAP_SERVER_METHODS_H #include "eap_common/eap_defs.h" const struct eap_method * eap_server_get_eap_method(int vendor, EapType method); struct eap_method * eap_server_method_alloc(int version, int vendor, EapType method, const char *name); void eap_server_method_free(struct eap_method *method); int eap_server_method_register(struct eap_method *method); EapType eap_server_get_type(const char *name, int *vendor); void eap_server_unregister_methods(void); const char * eap_server_get_name(int vendor, EapType type); /* EAP server method registration calls for statically linked in methods */ int eap_server_identity_register(void); int eap_server_md5_register(void); int eap_server_tls_register(void); int eap_server_unauth_tls_register(void); int eap_server_mschapv2_register(void); int eap_server_peap_register(void); int eap_server_tlv_register(void); int eap_server_gtc_register(void); int eap_server_ttls_register(void); int eap_server_sim_register(void); int eap_server_aka_register(void); int eap_server_aka_prime_register(void); int eap_server_pax_register(void); int eap_server_psk_register(void); int eap_server_sake_register(void); int eap_server_gpsk_register(void); int eap_server_vendor_test_register(void); int eap_server_fast_register(void); int eap_server_wsc_register(void); int eap_server_ikev2_register(void); int eap_server_tnc_register(void); int eap_server_pwd_register(void); int eap_server_eke_register(void); #endif /* EAP_SERVER_METHODS_H */ wpa-2.1/src/eap_server/eap_server.c000066400000000000000000001071771227414666700174200ustar00rootroot00000000000000/* * hostapd / EAP Full Authenticator state machine (RFC 4137) * Copyright (c) 2004-2007, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. * * This state machine is based on the full authenticator state machine defined * in RFC 4137. However, to support backend authentication in RADIUS * authentication server functionality, parts of backend authenticator (also * from RFC 4137) are mixed in. This functionality is enabled by setting * backend_auth configuration variable to TRUE. */ #include "includes.h" #include "common.h" #include "eap_i.h" #include "state_machine.h" #include "common/wpa_ctrl.h" #define STATE_MACHINE_DATA struct eap_sm #define STATE_MACHINE_DEBUG_PREFIX "EAP" #define EAP_MAX_AUTH_ROUNDS 50 static void eap_user_free(struct eap_user *user); /* EAP state machines are described in RFC 4137 */ static int eap_sm_calculateTimeout(struct eap_sm *sm, int retransCount, int eapSRTT, int eapRTTVAR, int methodTimeout); static void eap_sm_parseEapResp(struct eap_sm *sm, const struct wpabuf *resp); static int eap_sm_getId(const struct wpabuf *data); static struct wpabuf * eap_sm_buildSuccess(struct eap_sm *sm, u8 id); static struct wpabuf * eap_sm_buildFailure(struct eap_sm *sm, u8 id); static int eap_sm_nextId(struct eap_sm *sm, int id); static void eap_sm_Policy_update(struct eap_sm *sm, const u8 *nak_list, size_t len); static EapType eap_sm_Policy_getNextMethod(struct eap_sm *sm, int *vendor); static int eap_sm_Policy_getDecision(struct eap_sm *sm); static Boolean eap_sm_Policy_doPickUp(struct eap_sm *sm, EapType method); static int eap_copy_buf(struct wpabuf **dst, const struct wpabuf *src) { if (src == NULL) return -1; wpabuf_free(*dst); *dst = wpabuf_dup(src); return *dst ? 0 : -1; } static int eap_copy_data(u8 **dst, size_t *dst_len, const u8 *src, size_t src_len) { if (src == NULL) return -1; os_free(*dst); *dst = os_malloc(src_len); if (*dst) { os_memcpy(*dst, src, src_len); *dst_len = src_len; return 0; } else { *dst_len = 0; return -1; } } #define EAP_COPY(dst, src) \ eap_copy_data((dst), (dst ## Len), (src), (src ## Len)) /** * eap_user_get - Fetch user information from the database * @sm: Pointer to EAP state machine allocated with eap_server_sm_init() * @identity: Identity (User-Name) of the user * @identity_len: Length of identity in bytes * @phase2: 0 = EAP phase1 user, 1 = EAP phase2 (tunneled) user * Returns: 0 on success, or -1 on failure * * This function is used to fetch user information for EAP. The user will be * selected based on the specified identity. sm->user and * sm->user_eap_method_index are updated for the new user when a matching user * is found. sm->user can be used to get user information (e.g., password). */ int eap_user_get(struct eap_sm *sm, const u8 *identity, size_t identity_len, int phase2) { struct eap_user *user; if (sm == NULL || sm->eapol_cb == NULL || sm->eapol_cb->get_eap_user == NULL) return -1; eap_user_free(sm->user); sm->user = NULL; user = os_zalloc(sizeof(*user)); if (user == NULL) return -1; if (sm->eapol_cb->get_eap_user(sm->eapol_ctx, identity, identity_len, phase2, user) != 0) { eap_user_free(user); return -1; } sm->user = user; sm->user_eap_method_index = 0; return 0; } SM_STATE(EAP, DISABLED) { SM_ENTRY(EAP, DISABLED); sm->num_rounds = 0; } SM_STATE(EAP, INITIALIZE) { SM_ENTRY(EAP, INITIALIZE); if (sm->eap_if.eapRestart && !sm->eap_server && sm->identity) { /* * Need to allow internal Identity method to be used instead * of passthrough at the beginning of reauthentication. */ eap_server_clear_identity(sm); } sm->currentId = -1; sm->eap_if.eapSuccess = FALSE; sm->eap_if.eapFail = FALSE; sm->eap_if.eapTimeout = FALSE; os_free(sm->eap_if.eapKeyData); sm->eap_if.eapKeyData = NULL; sm->eap_if.eapKeyDataLen = 0; sm->eap_if.eapKeyAvailable = FALSE; sm->eap_if.eapRestart = FALSE; /* * This is not defined in RFC 4137, but method state needs to be * reseted here so that it does not remain in success state when * re-authentication starts. */ if (sm->m && sm->eap_method_priv) { sm->m->reset(sm, sm->eap_method_priv); sm->eap_method_priv = NULL; } sm->m = NULL; sm->user_eap_method_index = 0; if (sm->backend_auth) { sm->currentMethod = EAP_TYPE_NONE; /* parse rxResp, respId, respMethod */ eap_sm_parseEapResp(sm, sm->eap_if.eapRespData); if (sm->rxResp) { sm->currentId = sm->respId; } } sm->num_rounds = 0; sm->method_pending = METHOD_PENDING_NONE; wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_STARTED MACSTR, MAC2STR(sm->peer_addr)); } SM_STATE(EAP, PICK_UP_METHOD) { SM_ENTRY(EAP, PICK_UP_METHOD); if (eap_sm_Policy_doPickUp(sm, sm->respMethod)) { sm->currentMethod = sm->respMethod; if (sm->m && sm->eap_method_priv) { sm->m->reset(sm, sm->eap_method_priv); sm->eap_method_priv = NULL; } sm->m = eap_server_get_eap_method(EAP_VENDOR_IETF, sm->currentMethod); if (sm->m && sm->m->initPickUp) { sm->eap_method_priv = sm->m->initPickUp(sm); if (sm->eap_method_priv == NULL) { wpa_printf(MSG_DEBUG, "EAP: Failed to " "initialize EAP method %d", sm->currentMethod); sm->m = NULL; sm->currentMethod = EAP_TYPE_NONE; } } else { sm->m = NULL; sm->currentMethod = EAP_TYPE_NONE; } } wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_PROPOSED_METHOD "method=%u", sm->currentMethod); } SM_STATE(EAP, IDLE) { SM_ENTRY(EAP, IDLE); sm->eap_if.retransWhile = eap_sm_calculateTimeout( sm, sm->retransCount, sm->eap_if.eapSRTT, sm->eap_if.eapRTTVAR, sm->methodTimeout); } SM_STATE(EAP, RETRANSMIT) { SM_ENTRY(EAP, RETRANSMIT); sm->retransCount++; if (sm->retransCount <= sm->MaxRetrans && sm->lastReqData) { if (eap_copy_buf(&sm->eap_if.eapReqData, sm->lastReqData) == 0) sm->eap_if.eapReq = TRUE; } } SM_STATE(EAP, RECEIVED) { SM_ENTRY(EAP, RECEIVED); /* parse rxResp, respId, respMethod */ eap_sm_parseEapResp(sm, sm->eap_if.eapRespData); sm->num_rounds++; } SM_STATE(EAP, DISCARD) { SM_ENTRY(EAP, DISCARD); sm->eap_if.eapResp = FALSE; sm->eap_if.eapNoReq = TRUE; } SM_STATE(EAP, SEND_REQUEST) { SM_ENTRY(EAP, SEND_REQUEST); sm->retransCount = 0; if (sm->eap_if.eapReqData) { if (eap_copy_buf(&sm->lastReqData, sm->eap_if.eapReqData) == 0) { sm->eap_if.eapResp = FALSE; sm->eap_if.eapReq = TRUE; } else { sm->eap_if.eapResp = FALSE; sm->eap_if.eapReq = FALSE; } } else { wpa_printf(MSG_INFO, "EAP: SEND_REQUEST - no eapReqData"); sm->eap_if.eapResp = FALSE; sm->eap_if.eapReq = FALSE; sm->eap_if.eapNoReq = TRUE; } } SM_STATE(EAP, INTEGRITY_CHECK) { SM_ENTRY(EAP, INTEGRITY_CHECK); if (!eap_hdr_len_valid(sm->eap_if.eapRespData, 1)) { sm->ignore = TRUE; return; } if (sm->m->check) { sm->ignore = sm->m->check(sm, sm->eap_method_priv, sm->eap_if.eapRespData); } } SM_STATE(EAP, METHOD_REQUEST) { SM_ENTRY(EAP, METHOD_REQUEST); if (sm->m == NULL) { wpa_printf(MSG_DEBUG, "EAP: method not initialized"); return; } sm->currentId = eap_sm_nextId(sm, sm->currentId); wpa_printf(MSG_DEBUG, "EAP: building EAP-Request: Identifier %d", sm->currentId); sm->lastId = sm->currentId; wpabuf_free(sm->eap_if.eapReqData); sm->eap_if.eapReqData = sm->m->buildReq(sm, sm->eap_method_priv, sm->currentId); if (sm->m->getTimeout) sm->methodTimeout = sm->m->getTimeout(sm, sm->eap_method_priv); else sm->methodTimeout = 0; } SM_STATE(EAP, METHOD_RESPONSE) { SM_ENTRY(EAP, METHOD_RESPONSE); if (!eap_hdr_len_valid(sm->eap_if.eapRespData, 1)) return; sm->m->process(sm, sm->eap_method_priv, sm->eap_if.eapRespData); if (sm->m->isDone(sm, sm->eap_method_priv)) { eap_sm_Policy_update(sm, NULL, 0); os_free(sm->eap_if.eapKeyData); if (sm->m->getKey) { sm->eap_if.eapKeyData = sm->m->getKey( sm, sm->eap_method_priv, &sm->eap_if.eapKeyDataLen); } else { sm->eap_if.eapKeyData = NULL; sm->eap_if.eapKeyDataLen = 0; } sm->methodState = METHOD_END; } else { sm->methodState = METHOD_CONTINUE; } } SM_STATE(EAP, PROPOSE_METHOD) { int vendor; EapType type; SM_ENTRY(EAP, PROPOSE_METHOD); try_another_method: type = eap_sm_Policy_getNextMethod(sm, &vendor); if (vendor == EAP_VENDOR_IETF) sm->currentMethod = type; else sm->currentMethod = EAP_TYPE_EXPANDED; if (sm->m && sm->eap_method_priv) { sm->m->reset(sm, sm->eap_method_priv); sm->eap_method_priv = NULL; } sm->m = eap_server_get_eap_method(vendor, type); if (sm->m) { sm->eap_method_priv = sm->m->init(sm); if (sm->eap_method_priv == NULL) { wpa_printf(MSG_DEBUG, "EAP: Failed to initialize EAP " "method %d", sm->currentMethod); sm->m = NULL; sm->currentMethod = EAP_TYPE_NONE; goto try_another_method; } } if (sm->m == NULL) { wpa_printf(MSG_DEBUG, "EAP: Could not find suitable EAP method"); sm->decision = DECISION_FAILURE; return; } if (sm->currentMethod == EAP_TYPE_IDENTITY || sm->currentMethod == EAP_TYPE_NOTIFICATION) sm->methodState = METHOD_CONTINUE; else sm->methodState = METHOD_PROPOSED; wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_PROPOSED_METHOD "vendor=%u method=%u", vendor, sm->currentMethod); } SM_STATE(EAP, NAK) { const struct eap_hdr *nak; size_t len = 0; const u8 *pos; const u8 *nak_list = NULL; SM_ENTRY(EAP, NAK); if (sm->eap_method_priv) { sm->m->reset(sm, sm->eap_method_priv); sm->eap_method_priv = NULL; } sm->m = NULL; if (!eap_hdr_len_valid(sm->eap_if.eapRespData, 1)) return; nak = wpabuf_head(sm->eap_if.eapRespData); if (nak && wpabuf_len(sm->eap_if.eapRespData) > sizeof(*nak)) { len = be_to_host16(nak->length); if (len > wpabuf_len(sm->eap_if.eapRespData)) len = wpabuf_len(sm->eap_if.eapRespData); pos = (const u8 *) (nak + 1); len -= sizeof(*nak); if (*pos == EAP_TYPE_NAK) { pos++; len--; nak_list = pos; } } eap_sm_Policy_update(sm, nak_list, len); } SM_STATE(EAP, SELECT_ACTION) { SM_ENTRY(EAP, SELECT_ACTION); sm->decision = eap_sm_Policy_getDecision(sm); } SM_STATE(EAP, TIMEOUT_FAILURE) { SM_ENTRY(EAP, TIMEOUT_FAILURE); sm->eap_if.eapTimeout = TRUE; } SM_STATE(EAP, FAILURE) { SM_ENTRY(EAP, FAILURE); wpabuf_free(sm->eap_if.eapReqData); sm->eap_if.eapReqData = eap_sm_buildFailure(sm, sm->currentId); wpabuf_free(sm->lastReqData); sm->lastReqData = NULL; sm->eap_if.eapFail = TRUE; wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_FAILURE MACSTR, MAC2STR(sm->peer_addr)); } SM_STATE(EAP, SUCCESS) { SM_ENTRY(EAP, SUCCESS); wpabuf_free(sm->eap_if.eapReqData); sm->eap_if.eapReqData = eap_sm_buildSuccess(sm, sm->currentId); wpabuf_free(sm->lastReqData); sm->lastReqData = NULL; if (sm->eap_if.eapKeyData) sm->eap_if.eapKeyAvailable = TRUE; sm->eap_if.eapSuccess = TRUE; wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_SUCCESS MACSTR, MAC2STR(sm->peer_addr)); } SM_STATE(EAP, INITIALIZE_PASSTHROUGH) { SM_ENTRY(EAP, INITIALIZE_PASSTHROUGH); wpabuf_free(sm->eap_if.aaaEapRespData); sm->eap_if.aaaEapRespData = NULL; } SM_STATE(EAP, IDLE2) { SM_ENTRY(EAP, IDLE2); sm->eap_if.retransWhile = eap_sm_calculateTimeout( sm, sm->retransCount, sm->eap_if.eapSRTT, sm->eap_if.eapRTTVAR, sm->methodTimeout); } SM_STATE(EAP, RETRANSMIT2) { SM_ENTRY(EAP, RETRANSMIT2); sm->retransCount++; if (sm->retransCount <= sm->MaxRetrans && sm->lastReqData) { if (eap_copy_buf(&sm->eap_if.eapReqData, sm->lastReqData) == 0) sm->eap_if.eapReq = TRUE; } } SM_STATE(EAP, RECEIVED2) { SM_ENTRY(EAP, RECEIVED2); /* parse rxResp, respId, respMethod */ eap_sm_parseEapResp(sm, sm->eap_if.eapRespData); } SM_STATE(EAP, DISCARD2) { SM_ENTRY(EAP, DISCARD2); sm->eap_if.eapResp = FALSE; sm->eap_if.eapNoReq = TRUE; } SM_STATE(EAP, SEND_REQUEST2) { SM_ENTRY(EAP, SEND_REQUEST2); sm->retransCount = 0; if (sm->eap_if.eapReqData) { if (eap_copy_buf(&sm->lastReqData, sm->eap_if.eapReqData) == 0) { sm->eap_if.eapResp = FALSE; sm->eap_if.eapReq = TRUE; } else { sm->eap_if.eapResp = FALSE; sm->eap_if.eapReq = FALSE; } } else { wpa_printf(MSG_INFO, "EAP: SEND_REQUEST2 - no eapReqData"); sm->eap_if.eapResp = FALSE; sm->eap_if.eapReq = FALSE; sm->eap_if.eapNoReq = TRUE; } } SM_STATE(EAP, AAA_REQUEST) { SM_ENTRY(EAP, AAA_REQUEST); if (sm->eap_if.eapRespData == NULL) { wpa_printf(MSG_INFO, "EAP: AAA_REQUEST - no eapRespData"); return; } /* * if (respMethod == IDENTITY) * aaaIdentity = eapRespData * This is already taken care of by the EAP-Identity method which * stores the identity into sm->identity. */ eap_copy_buf(&sm->eap_if.aaaEapRespData, sm->eap_if.eapRespData); } SM_STATE(EAP, AAA_RESPONSE) { SM_ENTRY(EAP, AAA_RESPONSE); eap_copy_buf(&sm->eap_if.eapReqData, sm->eap_if.aaaEapReqData); sm->currentId = eap_sm_getId(sm->eap_if.eapReqData); sm->methodTimeout = sm->eap_if.aaaMethodTimeout; } SM_STATE(EAP, AAA_IDLE) { SM_ENTRY(EAP, AAA_IDLE); sm->eap_if.aaaFail = FALSE; sm->eap_if.aaaSuccess = FALSE; sm->eap_if.aaaEapReq = FALSE; sm->eap_if.aaaEapNoReq = FALSE; sm->eap_if.aaaEapResp = TRUE; } SM_STATE(EAP, TIMEOUT_FAILURE2) { SM_ENTRY(EAP, TIMEOUT_FAILURE2); sm->eap_if.eapTimeout = TRUE; } SM_STATE(EAP, FAILURE2) { SM_ENTRY(EAP, FAILURE2); eap_copy_buf(&sm->eap_if.eapReqData, sm->eap_if.aaaEapReqData); sm->eap_if.eapFail = TRUE; } SM_STATE(EAP, SUCCESS2) { SM_ENTRY(EAP, SUCCESS2); eap_copy_buf(&sm->eap_if.eapReqData, sm->eap_if.aaaEapReqData); sm->eap_if.eapKeyAvailable = sm->eap_if.aaaEapKeyAvailable; if (sm->eap_if.aaaEapKeyAvailable) { EAP_COPY(&sm->eap_if.eapKeyData, sm->eap_if.aaaEapKeyData); } else { os_free(sm->eap_if.eapKeyData); sm->eap_if.eapKeyData = NULL; sm->eap_if.eapKeyDataLen = 0; } sm->eap_if.eapSuccess = TRUE; /* * Start reauthentication with identity request even though we know the * previously used identity. This is needed to get reauthentication * started properly. */ sm->start_reauth = TRUE; } SM_STEP(EAP) { if (sm->eap_if.eapRestart && sm->eap_if.portEnabled) SM_ENTER_GLOBAL(EAP, INITIALIZE); else if (!sm->eap_if.portEnabled) SM_ENTER_GLOBAL(EAP, DISABLED); else if (sm->num_rounds > EAP_MAX_AUTH_ROUNDS) { if (sm->num_rounds == EAP_MAX_AUTH_ROUNDS + 1) { wpa_printf(MSG_DEBUG, "EAP: more than %d " "authentication rounds - abort", EAP_MAX_AUTH_ROUNDS); sm->num_rounds++; SM_ENTER_GLOBAL(EAP, FAILURE); } } else switch (sm->EAP_state) { case EAP_INITIALIZE: if (sm->backend_auth) { if (!sm->rxResp) SM_ENTER(EAP, SELECT_ACTION); else if (sm->rxResp && (sm->respMethod == EAP_TYPE_NAK || (sm->respMethod == EAP_TYPE_EXPANDED && sm->respVendor == EAP_VENDOR_IETF && sm->respVendorMethod == EAP_TYPE_NAK))) SM_ENTER(EAP, NAK); else SM_ENTER(EAP, PICK_UP_METHOD); } else { SM_ENTER(EAP, SELECT_ACTION); } break; case EAP_PICK_UP_METHOD: if (sm->currentMethod == EAP_TYPE_NONE) { SM_ENTER(EAP, SELECT_ACTION); } else { SM_ENTER(EAP, METHOD_RESPONSE); } break; case EAP_DISABLED: if (sm->eap_if.portEnabled) SM_ENTER(EAP, INITIALIZE); break; case EAP_IDLE: if (sm->eap_if.retransWhile == 0) SM_ENTER(EAP, RETRANSMIT); else if (sm->eap_if.eapResp) SM_ENTER(EAP, RECEIVED); break; case EAP_RETRANSMIT: if (sm->retransCount > sm->MaxRetrans) SM_ENTER(EAP, TIMEOUT_FAILURE); else SM_ENTER(EAP, IDLE); break; case EAP_RECEIVED: if (sm->rxResp && (sm->respId == sm->currentId) && (sm->respMethod == EAP_TYPE_NAK || (sm->respMethod == EAP_TYPE_EXPANDED && sm->respVendor == EAP_VENDOR_IETF && sm->respVendorMethod == EAP_TYPE_NAK)) && (sm->methodState == METHOD_PROPOSED)) SM_ENTER(EAP, NAK); else if (sm->rxResp && (sm->respId == sm->currentId) && ((sm->respMethod == sm->currentMethod) || (sm->respMethod == EAP_TYPE_EXPANDED && sm->respVendor == EAP_VENDOR_IETF && sm->respVendorMethod == sm->currentMethod))) SM_ENTER(EAP, INTEGRITY_CHECK); else { wpa_printf(MSG_DEBUG, "EAP: RECEIVED->DISCARD: " "rxResp=%d respId=%d currentId=%d " "respMethod=%d currentMethod=%d", sm->rxResp, sm->respId, sm->currentId, sm->respMethod, sm->currentMethod); SM_ENTER(EAP, DISCARD); } break; case EAP_DISCARD: SM_ENTER(EAP, IDLE); break; case EAP_SEND_REQUEST: SM_ENTER(EAP, IDLE); break; case EAP_INTEGRITY_CHECK: if (sm->ignore) SM_ENTER(EAP, DISCARD); else SM_ENTER(EAP, METHOD_RESPONSE); break; case EAP_METHOD_REQUEST: if (sm->m == NULL) { /* * This transition is not mentioned in RFC 4137, but it * is needed to handle cleanly a case where EAP method * initialization fails. */ SM_ENTER(EAP, FAILURE); break; } SM_ENTER(EAP, SEND_REQUEST); break; case EAP_METHOD_RESPONSE: /* * Note: Mechanism to allow EAP methods to wait while going * through pending processing is an extension to RFC 4137 * which only defines the transits to SELECT_ACTION and * METHOD_REQUEST from this METHOD_RESPONSE state. */ if (sm->methodState == METHOD_END) SM_ENTER(EAP, SELECT_ACTION); else if (sm->method_pending == METHOD_PENDING_WAIT) { wpa_printf(MSG_DEBUG, "EAP: Method has pending " "processing - wait before proceeding to " "METHOD_REQUEST state"); } else if (sm->method_pending == METHOD_PENDING_CONT) { wpa_printf(MSG_DEBUG, "EAP: Method has completed " "pending processing - reprocess pending " "EAP message"); sm->method_pending = METHOD_PENDING_NONE; SM_ENTER(EAP, METHOD_RESPONSE); } else SM_ENTER(EAP, METHOD_REQUEST); break; case EAP_PROPOSE_METHOD: /* * Note: Mechanism to allow EAP methods to wait while going * through pending processing is an extension to RFC 4137 * which only defines the transit to METHOD_REQUEST from this * PROPOSE_METHOD state. */ if (sm->method_pending == METHOD_PENDING_WAIT) { wpa_printf(MSG_DEBUG, "EAP: Method has pending " "processing - wait before proceeding to " "METHOD_REQUEST state"); if (sm->user_eap_method_index > 0) sm->user_eap_method_index--; } else if (sm->method_pending == METHOD_PENDING_CONT) { wpa_printf(MSG_DEBUG, "EAP: Method has completed " "pending processing - reprocess pending " "EAP message"); sm->method_pending = METHOD_PENDING_NONE; SM_ENTER(EAP, PROPOSE_METHOD); } else SM_ENTER(EAP, METHOD_REQUEST); break; case EAP_NAK: SM_ENTER(EAP, SELECT_ACTION); break; case EAP_SELECT_ACTION: if (sm->decision == DECISION_FAILURE) SM_ENTER(EAP, FAILURE); else if (sm->decision == DECISION_SUCCESS) SM_ENTER(EAP, SUCCESS); else if (sm->decision == DECISION_PASSTHROUGH) SM_ENTER(EAP, INITIALIZE_PASSTHROUGH); else SM_ENTER(EAP, PROPOSE_METHOD); break; case EAP_TIMEOUT_FAILURE: break; case EAP_FAILURE: break; case EAP_SUCCESS: break; case EAP_INITIALIZE_PASSTHROUGH: if (sm->currentId == -1) SM_ENTER(EAP, AAA_IDLE); else SM_ENTER(EAP, AAA_REQUEST); break; case EAP_IDLE2: if (sm->eap_if.eapResp) SM_ENTER(EAP, RECEIVED2); else if (sm->eap_if.retransWhile == 0) SM_ENTER(EAP, RETRANSMIT2); break; case EAP_RETRANSMIT2: if (sm->retransCount > sm->MaxRetrans) SM_ENTER(EAP, TIMEOUT_FAILURE2); else SM_ENTER(EAP, IDLE2); break; case EAP_RECEIVED2: if (sm->rxResp && (sm->respId == sm->currentId)) SM_ENTER(EAP, AAA_REQUEST); else SM_ENTER(EAP, DISCARD2); break; case EAP_DISCARD2: SM_ENTER(EAP, IDLE2); break; case EAP_SEND_REQUEST2: SM_ENTER(EAP, IDLE2); break; case EAP_AAA_REQUEST: SM_ENTER(EAP, AAA_IDLE); break; case EAP_AAA_RESPONSE: SM_ENTER(EAP, SEND_REQUEST2); break; case EAP_AAA_IDLE: if (sm->eap_if.aaaFail) SM_ENTER(EAP, FAILURE2); else if (sm->eap_if.aaaSuccess) SM_ENTER(EAP, SUCCESS2); else if (sm->eap_if.aaaEapReq) SM_ENTER(EAP, AAA_RESPONSE); else if (sm->eap_if.aaaTimeout) SM_ENTER(EAP, TIMEOUT_FAILURE2); break; case EAP_TIMEOUT_FAILURE2: break; case EAP_FAILURE2: break; case EAP_SUCCESS2: break; } } static int eap_sm_calculateTimeout(struct eap_sm *sm, int retransCount, int eapSRTT, int eapRTTVAR, int methodTimeout) { int rto, i; if (methodTimeout) { /* * EAP method (either internal or through AAA server, provided * timeout hint. Use that as-is as a timeout for retransmitting * the EAP request if no response is received. */ wpa_printf(MSG_DEBUG, "EAP: retransmit timeout %d seconds " "(from EAP method hint)", methodTimeout); return methodTimeout; } /* * RFC 3748 recommends algorithms described in RFC 2988 for estimation * of the retransmission timeout. This should be implemented once * round-trip time measurements are available. For nowm a simple * backoff mechanism is used instead if there are no EAP method * specific hints. * * SRTT = smoothed round-trip time * RTTVAR = round-trip time variation * RTO = retransmission timeout */ /* * RFC 2988, 2.1: before RTT measurement, set RTO to 3 seconds for * initial retransmission and then double the RTO to provide back off * per 5.5. Limit the maximum RTO to 20 seconds per RFC 3748, 4.3 * modified RTOmax. */ rto = 3; for (i = 0; i < retransCount; i++) { rto *= 2; if (rto >= 20) { rto = 20; break; } } wpa_printf(MSG_DEBUG, "EAP: retransmit timeout %d seconds " "(from dynamic back off; retransCount=%d)", rto, retransCount); return rto; } static void eap_sm_parseEapResp(struct eap_sm *sm, const struct wpabuf *resp) { const struct eap_hdr *hdr; size_t plen; /* parse rxResp, respId, respMethod */ sm->rxResp = FALSE; sm->respId = -1; sm->respMethod = EAP_TYPE_NONE; sm->respVendor = EAP_VENDOR_IETF; sm->respVendorMethod = EAP_TYPE_NONE; if (resp == NULL || wpabuf_len(resp) < sizeof(*hdr)) { wpa_printf(MSG_DEBUG, "EAP: parseEapResp: invalid resp=%p " "len=%lu", resp, resp ? (unsigned long) wpabuf_len(resp) : 0); return; } hdr = wpabuf_head(resp); plen = be_to_host16(hdr->length); if (plen > wpabuf_len(resp)) { wpa_printf(MSG_DEBUG, "EAP: Ignored truncated EAP-Packet " "(len=%lu plen=%lu)", (unsigned long) wpabuf_len(resp), (unsigned long) plen); return; } sm->respId = hdr->identifier; if (hdr->code == EAP_CODE_RESPONSE) sm->rxResp = TRUE; if (plen > sizeof(*hdr)) { u8 *pos = (u8 *) (hdr + 1); sm->respMethod = *pos++; if (sm->respMethod == EAP_TYPE_EXPANDED) { if (plen < sizeof(*hdr) + 8) { wpa_printf(MSG_DEBUG, "EAP: Ignored truncated " "expanded EAP-Packet (plen=%lu)", (unsigned long) plen); return; } sm->respVendor = WPA_GET_BE24(pos); pos += 3; sm->respVendorMethod = WPA_GET_BE32(pos); } } wpa_printf(MSG_DEBUG, "EAP: parseEapResp: rxResp=%d respId=%d " "respMethod=%u respVendor=%u respVendorMethod=%u", sm->rxResp, sm->respId, sm->respMethod, sm->respVendor, sm->respVendorMethod); } static int eap_sm_getId(const struct wpabuf *data) { const struct eap_hdr *hdr; if (data == NULL || wpabuf_len(data) < sizeof(*hdr)) return -1; hdr = wpabuf_head(data); wpa_printf(MSG_DEBUG, "EAP: getId: id=%d", hdr->identifier); return hdr->identifier; } static struct wpabuf * eap_sm_buildSuccess(struct eap_sm *sm, u8 id) { struct wpabuf *msg; struct eap_hdr *resp; wpa_printf(MSG_DEBUG, "EAP: Building EAP-Success (id=%d)", id); msg = wpabuf_alloc(sizeof(*resp)); if (msg == NULL) return NULL; resp = wpabuf_put(msg, sizeof(*resp)); resp->code = EAP_CODE_SUCCESS; resp->identifier = id; resp->length = host_to_be16(sizeof(*resp)); return msg; } static struct wpabuf * eap_sm_buildFailure(struct eap_sm *sm, u8 id) { struct wpabuf *msg; struct eap_hdr *resp; wpa_printf(MSG_DEBUG, "EAP: Building EAP-Failure (id=%d)", id); msg = wpabuf_alloc(sizeof(*resp)); if (msg == NULL) return NULL; resp = wpabuf_put(msg, sizeof(*resp)); resp->code = EAP_CODE_FAILURE; resp->identifier = id; resp->length = host_to_be16(sizeof(*resp)); return msg; } static int eap_sm_nextId(struct eap_sm *sm, int id) { if (id < 0) { /* RFC 3748 Ch 4.1: recommended to initialize Identifier with a * random number */ id = rand() & 0xff; if (id != sm->lastId) return id; } return (id + 1) & 0xff; } /** * eap_sm_process_nak - Process EAP-Response/Nak * @sm: Pointer to EAP state machine allocated with eap_server_sm_init() * @nak_list: Nak list (allowed methods) from the supplicant * @len: Length of nak_list in bytes * * This function is called when EAP-Response/Nak is received from the * supplicant. This can happen for both phase 1 and phase 2 authentications. */ void eap_sm_process_nak(struct eap_sm *sm, const u8 *nak_list, size_t len) { int i; size_t j; if (sm->user == NULL) return; wpa_printf(MSG_MSGDUMP, "EAP: processing NAK (current EAP method " "index %d)", sm->user_eap_method_index); wpa_hexdump(MSG_MSGDUMP, "EAP: configured methods", (u8 *) sm->user->methods, EAP_MAX_METHODS * sizeof(sm->user->methods[0])); wpa_hexdump(MSG_MSGDUMP, "EAP: list of methods supported by the peer", nak_list, len); i = sm->user_eap_method_index; while (i < EAP_MAX_METHODS && (sm->user->methods[i].vendor != EAP_VENDOR_IETF || sm->user->methods[i].method != EAP_TYPE_NONE)) { if (sm->user->methods[i].vendor != EAP_VENDOR_IETF) goto not_found; for (j = 0; j < len; j++) { if (nak_list[j] == sm->user->methods[i].method) { break; } } if (j < len) { /* found */ i++; continue; } not_found: /* not found - remove from the list */ if (i + 1 < EAP_MAX_METHODS) { os_memmove(&sm->user->methods[i], &sm->user->methods[i + 1], (EAP_MAX_METHODS - i - 1) * sizeof(sm->user->methods[0])); } sm->user->methods[EAP_MAX_METHODS - 1].vendor = EAP_VENDOR_IETF; sm->user->methods[EAP_MAX_METHODS - 1].method = EAP_TYPE_NONE; } wpa_hexdump(MSG_MSGDUMP, "EAP: new list of configured methods", (u8 *) sm->user->methods, EAP_MAX_METHODS * sizeof(sm->user->methods[0])); } static void eap_sm_Policy_update(struct eap_sm *sm, const u8 *nak_list, size_t len) { if (nak_list == NULL || sm == NULL || sm->user == NULL) return; if (sm->user->phase2) { wpa_printf(MSG_DEBUG, "EAP: EAP-Nak received after Phase2 user" " info was selected - reject"); sm->decision = DECISION_FAILURE; return; } eap_sm_process_nak(sm, nak_list, len); } static EapType eap_sm_Policy_getNextMethod(struct eap_sm *sm, int *vendor) { EapType next; int idx = sm->user_eap_method_index; /* In theory, there should be no problems with starting * re-authentication with something else than EAP-Request/Identity and * this does indeed work with wpa_supplicant. However, at least Funk * Supplicant seemed to ignore re-auth if it skipped * EAP-Request/Identity. * Re-auth sets currentId == -1, so that can be used here to select * whether Identity needs to be requested again. */ if (sm->identity == NULL || sm->currentId == -1) { *vendor = EAP_VENDOR_IETF; next = EAP_TYPE_IDENTITY; sm->update_user = TRUE; } else if (sm->user && idx < EAP_MAX_METHODS && (sm->user->methods[idx].vendor != EAP_VENDOR_IETF || sm->user->methods[idx].method != EAP_TYPE_NONE)) { *vendor = sm->user->methods[idx].vendor; next = sm->user->methods[idx].method; sm->user_eap_method_index++; } else { *vendor = EAP_VENDOR_IETF; next = EAP_TYPE_NONE; } wpa_printf(MSG_DEBUG, "EAP: getNextMethod: vendor %d type %d", *vendor, next); return next; } static int eap_sm_Policy_getDecision(struct eap_sm *sm) { if (!sm->eap_server && sm->identity && !sm->start_reauth) { wpa_printf(MSG_DEBUG, "EAP: getDecision: -> PASSTHROUGH"); return DECISION_PASSTHROUGH; } if (sm->m && sm->currentMethod != EAP_TYPE_IDENTITY && sm->m->isSuccess(sm, sm->eap_method_priv)) { wpa_printf(MSG_DEBUG, "EAP: getDecision: method succeeded -> " "SUCCESS"); sm->update_user = TRUE; return DECISION_SUCCESS; } if (sm->m && sm->m->isDone(sm, sm->eap_method_priv) && !sm->m->isSuccess(sm, sm->eap_method_priv)) { wpa_printf(MSG_DEBUG, "EAP: getDecision: method failed -> " "FAILURE"); sm->update_user = TRUE; return DECISION_FAILURE; } if ((sm->user == NULL || sm->update_user) && sm->identity && !sm->start_reauth) { /* * Allow Identity method to be started once to allow identity * selection hint to be sent from the authentication server, * but prevent a loop of Identity requests by only allowing * this to happen once. */ int id_req = 0; if (sm->user && sm->currentMethod == EAP_TYPE_IDENTITY && sm->user->methods[0].vendor == EAP_VENDOR_IETF && sm->user->methods[0].method == EAP_TYPE_IDENTITY) id_req = 1; if (eap_user_get(sm, sm->identity, sm->identity_len, 0) != 0) { wpa_printf(MSG_DEBUG, "EAP: getDecision: user not " "found from database -> FAILURE"); return DECISION_FAILURE; } if (id_req && sm->user && sm->user->methods[0].vendor == EAP_VENDOR_IETF && sm->user->methods[0].method == EAP_TYPE_IDENTITY) { wpa_printf(MSG_DEBUG, "EAP: getDecision: stop " "identity request loop -> FAILURE"); sm->update_user = TRUE; return DECISION_FAILURE; } sm->update_user = FALSE; } sm->start_reauth = FALSE; if (sm->user && sm->user_eap_method_index < EAP_MAX_METHODS && (sm->user->methods[sm->user_eap_method_index].vendor != EAP_VENDOR_IETF || sm->user->methods[sm->user_eap_method_index].method != EAP_TYPE_NONE)) { wpa_printf(MSG_DEBUG, "EAP: getDecision: another method " "available -> CONTINUE"); return DECISION_CONTINUE; } if (sm->identity == NULL || sm->currentId == -1) { wpa_printf(MSG_DEBUG, "EAP: getDecision: no identity known " "yet -> CONTINUE"); return DECISION_CONTINUE; } wpa_printf(MSG_DEBUG, "EAP: getDecision: no more methods available -> " "FAILURE"); return DECISION_FAILURE; } static Boolean eap_sm_Policy_doPickUp(struct eap_sm *sm, EapType method) { return method == EAP_TYPE_IDENTITY ? TRUE : FALSE; } /** * eap_server_sm_step - Step EAP server state machine * @sm: Pointer to EAP state machine allocated with eap_server_sm_init() * Returns: 1 if EAP state was changed or 0 if not * * This function advances EAP state machine to a new state to match with the * current variables. This should be called whenever variables used by the EAP * state machine have changed. */ int eap_server_sm_step(struct eap_sm *sm) { int res = 0; do { sm->changed = FALSE; SM_STEP_RUN(EAP); if (sm->changed) res = 1; } while (sm->changed); return res; } static void eap_user_free(struct eap_user *user) { if (user == NULL) return; os_free(user->password); user->password = NULL; os_free(user); } /** * eap_server_sm_init - Allocate and initialize EAP server state machine * @eapol_ctx: Context data to be used with eapol_cb calls * @eapol_cb: Pointer to EAPOL callback functions * @conf: EAP configuration * Returns: Pointer to the allocated EAP state machine or %NULL on failure * * This function allocates and initializes an EAP state machine. */ struct eap_sm * eap_server_sm_init(void *eapol_ctx, struct eapol_callbacks *eapol_cb, struct eap_config *conf) { struct eap_sm *sm; sm = os_zalloc(sizeof(*sm)); if (sm == NULL) return NULL; sm->eapol_ctx = eapol_ctx; sm->eapol_cb = eapol_cb; sm->MaxRetrans = 5; /* RFC 3748: max 3-5 retransmissions suggested */ sm->ssl_ctx = conf->ssl_ctx; sm->msg_ctx = conf->msg_ctx; sm->eap_sim_db_priv = conf->eap_sim_db_priv; sm->backend_auth = conf->backend_auth; sm->eap_server = conf->eap_server; if (conf->pac_opaque_encr_key) { sm->pac_opaque_encr_key = os_malloc(16); if (sm->pac_opaque_encr_key) { os_memcpy(sm->pac_opaque_encr_key, conf->pac_opaque_encr_key, 16); } } if (conf->eap_fast_a_id) { sm->eap_fast_a_id = os_malloc(conf->eap_fast_a_id_len); if (sm->eap_fast_a_id) { os_memcpy(sm->eap_fast_a_id, conf->eap_fast_a_id, conf->eap_fast_a_id_len); sm->eap_fast_a_id_len = conf->eap_fast_a_id_len; } } if (conf->eap_fast_a_id_info) sm->eap_fast_a_id_info = os_strdup(conf->eap_fast_a_id_info); sm->eap_fast_prov = conf->eap_fast_prov; sm->pac_key_lifetime = conf->pac_key_lifetime; sm->pac_key_refresh_time = conf->pac_key_refresh_time; sm->eap_sim_aka_result_ind = conf->eap_sim_aka_result_ind; sm->tnc = conf->tnc; sm->wps = conf->wps; if (conf->assoc_wps_ie) sm->assoc_wps_ie = wpabuf_dup(conf->assoc_wps_ie); if (conf->assoc_p2p_ie) sm->assoc_p2p_ie = wpabuf_dup(conf->assoc_p2p_ie); if (conf->peer_addr) os_memcpy(sm->peer_addr, conf->peer_addr, ETH_ALEN); sm->fragment_size = conf->fragment_size; sm->pwd_group = conf->pwd_group; sm->pbc_in_m1 = conf->pbc_in_m1; sm->server_id = conf->server_id; sm->server_id_len = conf->server_id_len; wpa_printf(MSG_DEBUG, "EAP: Server state machine created"); return sm; } /** * eap_server_sm_deinit - Deinitialize and free an EAP server state machine * @sm: Pointer to EAP state machine allocated with eap_server_sm_init() * * This function deinitializes EAP state machine and frees all allocated * resources. */ void eap_server_sm_deinit(struct eap_sm *sm) { if (sm == NULL) return; wpa_printf(MSG_DEBUG, "EAP: Server state machine removed"); if (sm->m && sm->eap_method_priv) sm->m->reset(sm, sm->eap_method_priv); wpabuf_free(sm->eap_if.eapReqData); os_free(sm->eap_if.eapKeyData); wpabuf_free(sm->lastReqData); wpabuf_free(sm->eap_if.eapRespData); os_free(sm->identity); os_free(sm->pac_opaque_encr_key); os_free(sm->eap_fast_a_id); os_free(sm->eap_fast_a_id_info); wpabuf_free(sm->eap_if.aaaEapReqData); wpabuf_free(sm->eap_if.aaaEapRespData); os_free(sm->eap_if.aaaEapKeyData); eap_user_free(sm->user); wpabuf_free(sm->assoc_wps_ie); wpabuf_free(sm->assoc_p2p_ie); os_free(sm); } /** * eap_sm_notify_cached - Notify EAP state machine of cached PMK * @sm: Pointer to EAP state machine allocated with eap_server_sm_init() * * This function is called when PMKSA caching is used to skip EAP * authentication. */ void eap_sm_notify_cached(struct eap_sm *sm) { if (sm == NULL) return; sm->EAP_state = EAP_SUCCESS; } /** * eap_sm_pending_cb - EAP state machine callback for a pending EAP request * @sm: Pointer to EAP state machine allocated with eap_server_sm_init() * * This function is called when data for a pending EAP-Request is received. */ void eap_sm_pending_cb(struct eap_sm *sm) { if (sm == NULL) return; wpa_printf(MSG_DEBUG, "EAP: Callback for pending request received"); if (sm->method_pending == METHOD_PENDING_WAIT) sm->method_pending = METHOD_PENDING_CONT; } /** * eap_sm_method_pending - Query whether EAP method is waiting for pending data * @sm: Pointer to EAP state machine allocated with eap_server_sm_init() * Returns: 1 if method is waiting for pending data or 0 if not */ int eap_sm_method_pending(struct eap_sm *sm) { if (sm == NULL) return 0; return sm->method_pending == METHOD_PENDING_WAIT; } /** * eap_get_identity - Get the user identity (from EAP-Response/Identity) * @sm: Pointer to EAP state machine allocated with eap_server_sm_init() * @len: Buffer for returning identity length * Returns: Pointer to the user identity or %NULL if not available */ const u8 * eap_get_identity(struct eap_sm *sm, size_t *len) { *len = sm->identity_len; return sm->identity; } /** * eap_get_interface - Get pointer to EAP-EAPOL interface data * @sm: Pointer to EAP state machine allocated with eap_server_sm_init() * Returns: Pointer to the EAP-EAPOL interface data */ struct eap_eapol_interface * eap_get_interface(struct eap_sm *sm) { return &sm->eap_if; } /** * eap_server_clear_identity - Clear EAP identity information * @sm: Pointer to EAP state machine allocated with eap_server_sm_init() * * This function can be used to clear the EAP identity information in the EAP * server context. This allows the EAP/Identity method to be used again after * EAPOL-Start or EAPOL-Logoff. */ void eap_server_clear_identity(struct eap_sm *sm) { os_free(sm->identity); sm->identity = NULL; } wpa-2.1/src/eap_server/eap_server_aka.c000066400000000000000000001075031227414666700202250ustar00rootroot00000000000000/* * hostapd / EAP-AKA (RFC 4187) and EAP-AKA' (RFC 5448) * Copyright (c) 2005-2012, 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/sha256.h" #include "crypto/crypto.h" #include "crypto/random.h" #include "eap_common/eap_sim_common.h" #include "eap_server/eap_i.h" #include "eap_server/eap_sim_db.h" struct eap_aka_data { u8 mk[EAP_SIM_MK_LEN]; u8 nonce_s[EAP_SIM_NONCE_S_LEN]; u8 k_aut[EAP_AKA_PRIME_K_AUT_LEN]; u8 k_encr[EAP_SIM_K_ENCR_LEN]; u8 k_re[EAP_AKA_PRIME_K_RE_LEN]; /* EAP-AKA' only */ u8 msk[EAP_SIM_KEYING_DATA_LEN]; u8 emsk[EAP_EMSK_LEN]; u8 rand[EAP_AKA_RAND_LEN]; u8 autn[EAP_AKA_AUTN_LEN]; u8 ck[EAP_AKA_CK_LEN]; u8 ik[EAP_AKA_IK_LEN]; u8 res[EAP_AKA_RES_MAX_LEN]; size_t res_len; enum { IDENTITY, CHALLENGE, REAUTH, NOTIFICATION, SUCCESS, FAILURE } state; char *next_pseudonym; char *next_reauth_id; u16 counter; struct eap_sim_reauth *reauth; int auts_reported; /* whether the current AUTS has been reported to the * eap_sim_db */ u16 notification; int use_result_ind; struct wpabuf *id_msgs; int pending_id; u8 eap_method; u8 *network_name; size_t network_name_len; u16 kdf; int identity_round; char permanent[20]; /* Permanent username */ }; static void eap_aka_fullauth(struct eap_sm *sm, struct eap_aka_data *data); static const char * eap_aka_state_txt(int state) { switch (state) { case IDENTITY: return "IDENTITY"; case CHALLENGE: return "CHALLENGE"; case REAUTH: return "REAUTH"; case SUCCESS: return "SUCCESS"; case FAILURE: return "FAILURE"; case NOTIFICATION: return "NOTIFICATION"; default: return "Unknown?!"; } } static void eap_aka_state(struct eap_aka_data *data, int state) { wpa_printf(MSG_DEBUG, "EAP-AKA: %s -> %s", eap_aka_state_txt(data->state), eap_aka_state_txt(state)); data->state = state; } static int eap_aka_check_identity_reauth(struct eap_sm *sm, struct eap_aka_data *data, const char *username) { if (data->eap_method == EAP_TYPE_AKA_PRIME && username[0] != EAP_AKA_PRIME_REAUTH_ID_PREFIX) return 0; if (data->eap_method == EAP_TYPE_AKA && username[0] != EAP_AKA_REAUTH_ID_PREFIX) return 0; wpa_printf(MSG_DEBUG, "EAP-AKA: Reauth username '%s'", username); data->reauth = eap_sim_db_get_reauth_entry(sm->eap_sim_db_priv, username); if (data->reauth == NULL) { wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown reauth identity - " "request full auth identity"); /* Remain in IDENTITY state for another round */ return 0; } wpa_printf(MSG_DEBUG, "EAP-AKA: Using fast re-authentication"); os_strlcpy(data->permanent, data->reauth->permanent, sizeof(data->permanent)); data->counter = data->reauth->counter; if (data->eap_method == EAP_TYPE_AKA_PRIME) { os_memcpy(data->k_encr, data->reauth->k_encr, EAP_SIM_K_ENCR_LEN); os_memcpy(data->k_aut, data->reauth->k_aut, EAP_AKA_PRIME_K_AUT_LEN); os_memcpy(data->k_re, data->reauth->k_re, EAP_AKA_PRIME_K_RE_LEN); } else { os_memcpy(data->mk, data->reauth->mk, EAP_SIM_MK_LEN); } eap_aka_state(data, REAUTH); return 1; } static void eap_aka_check_identity(struct eap_sm *sm, struct eap_aka_data *data) { char *username; /* Check if we already know the identity from EAP-Response/Identity */ username = sim_get_username(sm->identity, sm->identity_len); if (username == NULL) return; if (eap_aka_check_identity_reauth(sm, data, username) > 0) { os_free(username); /* * Since re-auth username was recognized, skip AKA/Identity * exchange. */ return; } if ((data->eap_method == EAP_TYPE_AKA_PRIME && username[0] == EAP_AKA_PRIME_PSEUDONYM_PREFIX) || (data->eap_method == EAP_TYPE_AKA && username[0] == EAP_AKA_PSEUDONYM_PREFIX)) { const char *permanent; wpa_printf(MSG_DEBUG, "EAP-AKA: Pseudonym username '%s'", username); permanent = eap_sim_db_get_permanent( sm->eap_sim_db_priv, username); if (permanent == NULL) { os_free(username); wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown pseudonym " "identity - request permanent identity"); /* Remain in IDENTITY state for another round */ return; } os_strlcpy(data->permanent, permanent, sizeof(data->permanent)); /* * Since pseudonym username was recognized, skip AKA/Identity * exchange. */ eap_aka_fullauth(sm, data); } os_free(username); } static void * eap_aka_init(struct eap_sm *sm) { struct eap_aka_data *data; if (sm->eap_sim_db_priv == NULL) { wpa_printf(MSG_WARNING, "EAP-AKA: eap_sim_db not configured"); return NULL; } data = os_zalloc(sizeof(*data)); if (data == NULL) return NULL; data->eap_method = EAP_TYPE_AKA; data->state = IDENTITY; data->pending_id = -1; eap_aka_check_identity(sm, data); return data; } #ifdef EAP_SERVER_AKA_PRIME static void * eap_aka_prime_init(struct eap_sm *sm) { struct eap_aka_data *data; /* TODO: make ANID configurable; see 3GPP TS 24.302 */ char *network_name = "WLAN"; if (sm->eap_sim_db_priv == NULL) { wpa_printf(MSG_WARNING, "EAP-AKA: eap_sim_db not configured"); return NULL; } data = os_zalloc(sizeof(*data)); if (data == NULL) return NULL; data->eap_method = EAP_TYPE_AKA_PRIME; data->network_name = (u8 *) os_strdup(network_name); if (data->network_name == NULL) { os_free(data); return NULL; } data->network_name_len = os_strlen(network_name); data->state = IDENTITY; data->pending_id = -1; eap_aka_check_identity(sm, data); return data; } #endif /* EAP_SERVER_AKA_PRIME */ static void eap_aka_reset(struct eap_sm *sm, void *priv) { struct eap_aka_data *data = priv; os_free(data->next_pseudonym); os_free(data->next_reauth_id); wpabuf_free(data->id_msgs); os_free(data->network_name); os_free(data); } static int eap_aka_add_id_msg(struct eap_aka_data *data, const struct wpabuf *msg) { if (msg == NULL) return -1; if (data->id_msgs == NULL) { data->id_msgs = wpabuf_dup(msg); return data->id_msgs == NULL ? -1 : 0; } if (wpabuf_resize(&data->id_msgs, wpabuf_len(msg)) < 0) return -1; wpabuf_put_buf(data->id_msgs, msg); return 0; } static void eap_aka_add_checkcode(struct eap_aka_data *data, struct eap_sim_msg *msg) { const u8 *addr; size_t len; u8 hash[SHA256_MAC_LEN]; wpa_printf(MSG_DEBUG, " AT_CHECKCODE"); if (data->id_msgs == NULL) { /* * No EAP-AKA/Identity packets were exchanged - send empty * checkcode. */ eap_sim_msg_add(msg, EAP_SIM_AT_CHECKCODE, 0, NULL, 0); return; } /* Checkcode is SHA1 hash over all EAP-AKA/Identity packets. */ addr = wpabuf_head(data->id_msgs); len = wpabuf_len(data->id_msgs); wpa_hexdump(MSG_MSGDUMP, "EAP-AKA: AT_CHECKCODE data", addr, len); if (data->eap_method == EAP_TYPE_AKA_PRIME) sha256_vector(1, &addr, &len, hash); else sha1_vector(1, &addr, &len, hash); eap_sim_msg_add(msg, EAP_SIM_AT_CHECKCODE, 0, hash, data->eap_method == EAP_TYPE_AKA_PRIME ? EAP_AKA_PRIME_CHECKCODE_LEN : EAP_AKA_CHECKCODE_LEN); } static int eap_aka_verify_checkcode(struct eap_aka_data *data, const u8 *checkcode, size_t checkcode_len) { const u8 *addr; size_t len; u8 hash[SHA256_MAC_LEN]; size_t hash_len; if (checkcode == NULL) return -1; if (data->id_msgs == NULL) { if (checkcode_len != 0) { wpa_printf(MSG_DEBUG, "EAP-AKA: Checkcode from peer " "indicates that AKA/Identity messages were " "used, but they were not"); return -1; } return 0; } hash_len = data->eap_method == EAP_TYPE_AKA_PRIME ? EAP_AKA_PRIME_CHECKCODE_LEN : EAP_AKA_CHECKCODE_LEN; if (checkcode_len != hash_len) { wpa_printf(MSG_DEBUG, "EAP-AKA: Checkcode from peer indicates " "that AKA/Identity message were not used, but they " "were"); return -1; } /* Checkcode is SHA1 hash over all EAP-AKA/Identity packets. */ addr = wpabuf_head(data->id_msgs); len = wpabuf_len(data->id_msgs); if (data->eap_method == EAP_TYPE_AKA_PRIME) sha256_vector(1, &addr, &len, hash); else sha1_vector(1, &addr, &len, hash); if (os_memcmp(hash, checkcode, hash_len) != 0) { wpa_printf(MSG_DEBUG, "EAP-AKA: Mismatch in AT_CHECKCODE"); return -1; } return 0; } static struct wpabuf * eap_aka_build_identity(struct eap_sm *sm, struct eap_aka_data *data, u8 id) { struct eap_sim_msg *msg; struct wpabuf *buf; wpa_printf(MSG_DEBUG, "EAP-AKA: Generating Identity"); msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, data->eap_method, EAP_AKA_SUBTYPE_IDENTITY); data->identity_round++; if (data->identity_round == 1) { /* * RFC 4187, Chap. 4.1.4 recommends that identity from EAP is * ignored and the AKA/Identity is used to request the * identity. */ wpa_printf(MSG_DEBUG, " AT_ANY_ID_REQ"); eap_sim_msg_add(msg, EAP_SIM_AT_ANY_ID_REQ, 0, NULL, 0); } else if (data->identity_round > 3) { /* Cannot use more than three rounds of Identity messages */ eap_sim_msg_free(msg); return NULL; } else if (sm->identity && sm->identity_len > 0 && (sm->identity[0] == EAP_AKA_REAUTH_ID_PREFIX || sm->identity[0] == EAP_AKA_PRIME_REAUTH_ID_PREFIX)) { /* Reauth id may have expired - try fullauth */ wpa_printf(MSG_DEBUG, " AT_FULLAUTH_ID_REQ"); eap_sim_msg_add(msg, EAP_SIM_AT_FULLAUTH_ID_REQ, 0, NULL, 0); } else { wpa_printf(MSG_DEBUG, " AT_PERMANENT_ID_REQ"); eap_sim_msg_add(msg, EAP_SIM_AT_PERMANENT_ID_REQ, 0, NULL, 0); } buf = eap_sim_msg_finish(msg, NULL, NULL, 0); if (eap_aka_add_id_msg(data, buf) < 0) { wpabuf_free(buf); return NULL; } data->pending_id = id; return buf; } static int eap_aka_build_encr(struct eap_sm *sm, struct eap_aka_data *data, struct eap_sim_msg *msg, u16 counter, const u8 *nonce_s) { os_free(data->next_pseudonym); if (nonce_s == NULL) { data->next_pseudonym = eap_sim_db_get_next_pseudonym( sm->eap_sim_db_priv, data->eap_method == EAP_TYPE_AKA_PRIME ? EAP_SIM_DB_AKA_PRIME : EAP_SIM_DB_AKA); } else { /* Do not update pseudonym during re-authentication */ data->next_pseudonym = NULL; } os_free(data->next_reauth_id); if (data->counter <= EAP_AKA_MAX_FAST_REAUTHS) { data->next_reauth_id = eap_sim_db_get_next_reauth_id( sm->eap_sim_db_priv, data->eap_method == EAP_TYPE_AKA_PRIME ? EAP_SIM_DB_AKA_PRIME : EAP_SIM_DB_AKA); } else { wpa_printf(MSG_DEBUG, "EAP-AKA: Max fast re-authentication " "count exceeded - force full authentication"); data->next_reauth_id = NULL; } if (data->next_pseudonym == NULL && data->next_reauth_id == NULL && counter == 0 && nonce_s == NULL) return 0; wpa_printf(MSG_DEBUG, " AT_IV"); wpa_printf(MSG_DEBUG, " AT_ENCR_DATA"); eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV, EAP_SIM_AT_ENCR_DATA); if (counter > 0) { wpa_printf(MSG_DEBUG, " *AT_COUNTER (%u)", counter); eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, counter, NULL, 0); } if (nonce_s) { wpa_printf(MSG_DEBUG, " *AT_NONCE_S"); eap_sim_msg_add(msg, EAP_SIM_AT_NONCE_S, 0, nonce_s, EAP_SIM_NONCE_S_LEN); } if (data->next_pseudonym) { wpa_printf(MSG_DEBUG, " *AT_NEXT_PSEUDONYM (%s)", data->next_pseudonym); eap_sim_msg_add(msg, EAP_SIM_AT_NEXT_PSEUDONYM, os_strlen(data->next_pseudonym), (u8 *) data->next_pseudonym, os_strlen(data->next_pseudonym)); } if (data->next_reauth_id) { wpa_printf(MSG_DEBUG, " *AT_NEXT_REAUTH_ID (%s)", data->next_reauth_id); eap_sim_msg_add(msg, EAP_SIM_AT_NEXT_REAUTH_ID, os_strlen(data->next_reauth_id), (u8 *) data->next_reauth_id, os_strlen(data->next_reauth_id)); } if (eap_sim_msg_add_encr_end(msg, data->k_encr, EAP_SIM_AT_PADDING)) { wpa_printf(MSG_WARNING, "EAP-AKA: Failed to encrypt " "AT_ENCR_DATA"); return -1; } return 0; } static struct wpabuf * eap_aka_build_challenge(struct eap_sm *sm, struct eap_aka_data *data, u8 id) { struct eap_sim_msg *msg; wpa_printf(MSG_DEBUG, "EAP-AKA: Generating Challenge"); msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, data->eap_method, EAP_AKA_SUBTYPE_CHALLENGE); wpa_printf(MSG_DEBUG, " AT_RAND"); eap_sim_msg_add(msg, EAP_SIM_AT_RAND, 0, data->rand, EAP_AKA_RAND_LEN); wpa_printf(MSG_DEBUG, " AT_AUTN"); eap_sim_msg_add(msg, EAP_SIM_AT_AUTN, 0, data->autn, EAP_AKA_AUTN_LEN); if (data->eap_method == EAP_TYPE_AKA_PRIME) { if (data->kdf) { /* Add the selected KDF into the beginning */ wpa_printf(MSG_DEBUG, " AT_KDF"); eap_sim_msg_add(msg, EAP_SIM_AT_KDF, data->kdf, NULL, 0); } wpa_printf(MSG_DEBUG, " AT_KDF"); eap_sim_msg_add(msg, EAP_SIM_AT_KDF, EAP_AKA_PRIME_KDF, NULL, 0); wpa_printf(MSG_DEBUG, " AT_KDF_INPUT"); eap_sim_msg_add(msg, EAP_SIM_AT_KDF_INPUT, data->network_name_len, data->network_name, data->network_name_len); } if (eap_aka_build_encr(sm, data, msg, 0, NULL)) { eap_sim_msg_free(msg); return NULL; } eap_aka_add_checkcode(data, msg); if (sm->eap_sim_aka_result_ind) { wpa_printf(MSG_DEBUG, " AT_RESULT_IND"); eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0); } #ifdef EAP_SERVER_AKA_PRIME if (data->eap_method == EAP_TYPE_AKA) { u16 flags = 0; int i; int aka_prime_preferred = 0; i = 0; while (sm->user && i < EAP_MAX_METHODS && (sm->user->methods[i].vendor != EAP_VENDOR_IETF || sm->user->methods[i].method != EAP_TYPE_NONE)) { if (sm->user->methods[i].vendor == EAP_VENDOR_IETF) { if (sm->user->methods[i].method == EAP_TYPE_AKA) break; if (sm->user->methods[i].method == EAP_TYPE_AKA_PRIME) { aka_prime_preferred = 1; break; } } i++; } if (aka_prime_preferred) flags |= EAP_AKA_BIDDING_FLAG_D; eap_sim_msg_add(msg, EAP_SIM_AT_BIDDING, flags, NULL, 0); } #endif /* EAP_SERVER_AKA_PRIME */ wpa_printf(MSG_DEBUG, " AT_MAC"); eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); return eap_sim_msg_finish(msg, data->k_aut, NULL, 0); } static struct wpabuf * eap_aka_build_reauth(struct eap_sm *sm, struct eap_aka_data *data, u8 id) { struct eap_sim_msg *msg; wpa_printf(MSG_DEBUG, "EAP-AKA: Generating Re-authentication"); if (random_get_bytes(data->nonce_s, EAP_SIM_NONCE_S_LEN)) return NULL; wpa_hexdump_key(MSG_MSGDUMP, "EAP-AKA: NONCE_S", data->nonce_s, EAP_SIM_NONCE_S_LEN); if (data->eap_method == EAP_TYPE_AKA_PRIME) { eap_aka_prime_derive_keys_reauth(data->k_re, data->counter, sm->identity, sm->identity_len, data->nonce_s, data->msk, data->emsk); } else { eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut, data->msk, data->emsk); eap_sim_derive_keys_reauth(data->counter, sm->identity, sm->identity_len, data->nonce_s, data->mk, data->msk, data->emsk); } msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, data->eap_method, EAP_AKA_SUBTYPE_REAUTHENTICATION); if (eap_aka_build_encr(sm, data, msg, data->counter, data->nonce_s)) { eap_sim_msg_free(msg); return NULL; } eap_aka_add_checkcode(data, msg); if (sm->eap_sim_aka_result_ind) { wpa_printf(MSG_DEBUG, " AT_RESULT_IND"); eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0); } wpa_printf(MSG_DEBUG, " AT_MAC"); eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); return eap_sim_msg_finish(msg, data->k_aut, NULL, 0); } static struct wpabuf * eap_aka_build_notification(struct eap_sm *sm, struct eap_aka_data *data, u8 id) { struct eap_sim_msg *msg; wpa_printf(MSG_DEBUG, "EAP-AKA: Generating Notification"); msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, data->eap_method, EAP_AKA_SUBTYPE_NOTIFICATION); wpa_printf(MSG_DEBUG, " AT_NOTIFICATION (%d)", data->notification); eap_sim_msg_add(msg, EAP_SIM_AT_NOTIFICATION, data->notification, NULL, 0); if (data->use_result_ind) { if (data->reauth) { wpa_printf(MSG_DEBUG, " AT_IV"); wpa_printf(MSG_DEBUG, " AT_ENCR_DATA"); eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV, EAP_SIM_AT_ENCR_DATA); wpa_printf(MSG_DEBUG, " *AT_COUNTER (%u)", data->counter); eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, data->counter, NULL, 0); if (eap_sim_msg_add_encr_end(msg, data->k_encr, EAP_SIM_AT_PADDING)) { wpa_printf(MSG_WARNING, "EAP-AKA: Failed to " "encrypt AT_ENCR_DATA"); eap_sim_msg_free(msg); return NULL; } } wpa_printf(MSG_DEBUG, " AT_MAC"); eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); } return eap_sim_msg_finish(msg, data->k_aut, NULL, 0); } static struct wpabuf * eap_aka_buildReq(struct eap_sm *sm, void *priv, u8 id) { struct eap_aka_data *data = priv; data->auts_reported = 0; switch (data->state) { case IDENTITY: return eap_aka_build_identity(sm, data, id); case CHALLENGE: return eap_aka_build_challenge(sm, data, id); case REAUTH: return eap_aka_build_reauth(sm, data, id); case NOTIFICATION: return eap_aka_build_notification(sm, data, id); default: wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown state %d in " "buildReq", data->state); break; } return NULL; } static Boolean eap_aka_check(struct eap_sm *sm, void *priv, struct wpabuf *respData) { struct eap_aka_data *data = priv; const u8 *pos; size_t len; pos = eap_hdr_validate(EAP_VENDOR_IETF, data->eap_method, respData, &len); if (pos == NULL || len < 3) { wpa_printf(MSG_INFO, "EAP-AKA: Invalid frame"); return TRUE; } return FALSE; } static Boolean eap_aka_subtype_ok(struct eap_aka_data *data, u8 subtype) { if (subtype == EAP_AKA_SUBTYPE_CLIENT_ERROR || subtype == EAP_AKA_SUBTYPE_AUTHENTICATION_REJECT) return FALSE; switch (data->state) { case IDENTITY: if (subtype != EAP_AKA_SUBTYPE_IDENTITY) { wpa_printf(MSG_INFO, "EAP-AKA: Unexpected response " "subtype %d", subtype); return TRUE; } break; case CHALLENGE: if (subtype != EAP_AKA_SUBTYPE_CHALLENGE && subtype != EAP_AKA_SUBTYPE_SYNCHRONIZATION_FAILURE) { wpa_printf(MSG_INFO, "EAP-AKA: Unexpected response " "subtype %d", subtype); return TRUE; } break; case REAUTH: if (subtype != EAP_AKA_SUBTYPE_REAUTHENTICATION) { wpa_printf(MSG_INFO, "EAP-AKA: Unexpected response " "subtype %d", subtype); return TRUE; } break; case NOTIFICATION: if (subtype != EAP_AKA_SUBTYPE_NOTIFICATION) { wpa_printf(MSG_INFO, "EAP-AKA: Unexpected response " "subtype %d", subtype); return TRUE; } break; default: wpa_printf(MSG_INFO, "EAP-AKA: Unexpected state (%d) for " "processing a response", data->state); return TRUE; } return FALSE; } static void eap_aka_determine_identity(struct eap_sm *sm, struct eap_aka_data *data) { char *username; wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA: Identity", sm->identity, sm->identity_len); username = sim_get_username(sm->identity, sm->identity_len); if (username == NULL) { data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; eap_aka_state(data, NOTIFICATION); return; } if (eap_aka_check_identity_reauth(sm, data, username) > 0) { os_free(username); return; } if (((data->eap_method == EAP_TYPE_AKA_PRIME && username[0] == EAP_AKA_PRIME_REAUTH_ID_PREFIX) || (data->eap_method == EAP_TYPE_AKA && username[0] == EAP_AKA_REAUTH_ID_PREFIX)) && data->identity_round == 1) { /* Remain in IDENTITY state for another round to request full * auth identity since we did not recognize reauth id */ os_free(username); return; } if ((data->eap_method == EAP_TYPE_AKA_PRIME && username[0] == EAP_AKA_PRIME_PSEUDONYM_PREFIX) || (data->eap_method == EAP_TYPE_AKA && username[0] == EAP_AKA_PSEUDONYM_PREFIX)) { const char *permanent; wpa_printf(MSG_DEBUG, "EAP-AKA: Pseudonym username '%s'", username); permanent = eap_sim_db_get_permanent( sm->eap_sim_db_priv, username); os_free(username); if (permanent == NULL) { wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown pseudonym " "identity - request permanent identity"); /* Remain in IDENTITY state for another round */ return; } os_strlcpy(data->permanent, permanent, sizeof(data->permanent)); } else if ((data->eap_method == EAP_TYPE_AKA_PRIME && username[0] == EAP_AKA_PRIME_PERMANENT_PREFIX) || (data->eap_method == EAP_TYPE_AKA && username[0] == EAP_AKA_PERMANENT_PREFIX)) { wpa_printf(MSG_DEBUG, "EAP-AKA: Permanent username '%s'", username); os_strlcpy(data->permanent, username, sizeof(data->permanent)); os_free(username); } else { wpa_printf(MSG_DEBUG, "EAP-AKA: Unrecognized username '%s'", username); os_free(username); data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; eap_aka_state(data, NOTIFICATION); return; } eap_aka_fullauth(sm, data); } static void eap_aka_fullauth(struct eap_sm *sm, struct eap_aka_data *data) { size_t identity_len; int res; res = eap_sim_db_get_aka_auth(sm->eap_sim_db_priv, data->permanent, data->rand, data->autn, data->ik, data->ck, data->res, &data->res_len, sm); if (res == EAP_SIM_DB_PENDING) { wpa_printf(MSG_DEBUG, "EAP-AKA: AKA authentication data " "not yet available - pending request"); sm->method_pending = METHOD_PENDING_WAIT; return; } #ifdef EAP_SERVER_AKA_PRIME if (data->eap_method == EAP_TYPE_AKA_PRIME) { /* Note: AUTN = (SQN ^ AK) || AMF || MAC which gives us the * needed 6-octet SQN ^AK for CK',IK' derivation */ eap_aka_prime_derive_ck_ik_prime(data->ck, data->ik, data->autn, data->network_name, data->network_name_len); } #endif /* EAP_SERVER_AKA_PRIME */ data->reauth = NULL; data->counter = 0; /* reset re-auth counter since this is full auth */ if (res != 0) { wpa_printf(MSG_INFO, "EAP-AKA: Failed to get AKA " "authentication data for the peer"); data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; eap_aka_state(data, NOTIFICATION); return; } if (sm->method_pending == METHOD_PENDING_WAIT) { wpa_printf(MSG_DEBUG, "EAP-AKA: AKA authentication data " "available - abort pending wait"); sm->method_pending = METHOD_PENDING_NONE; } identity_len = sm->identity_len; while (identity_len > 0 && sm->identity[identity_len - 1] == '\0') { wpa_printf(MSG_DEBUG, "EAP-AKA: Workaround - drop last null " "character from identity"); identity_len--; } wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA: Identity for MK derivation", sm->identity, identity_len); if (data->eap_method == EAP_TYPE_AKA_PRIME) { eap_aka_prime_derive_keys(sm->identity, identity_len, data->ik, data->ck, data->k_encr, data->k_aut, data->k_re, data->msk, data->emsk); } else { eap_aka_derive_mk(sm->identity, identity_len, data->ik, data->ck, data->mk); eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut, data->msk, data->emsk); } eap_aka_state(data, CHALLENGE); } static void eap_aka_process_identity(struct eap_sm *sm, struct eap_aka_data *data, struct wpabuf *respData, struct eap_sim_attrs *attr) { u8 *new_identity; wpa_printf(MSG_DEBUG, "EAP-AKA: Processing Identity"); if (attr->mac || attr->iv || attr->encr_data) { wpa_printf(MSG_WARNING, "EAP-AKA: Unexpected attribute " "received in EAP-Response/AKA-Identity"); data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; eap_aka_state(data, NOTIFICATION); return; } /* * We always request identity with AKA/Identity, so the peer is * required to have replied with one. */ if (!attr->identity || attr->identity_len == 0) { wpa_printf(MSG_DEBUG, "EAP-AKA: Peer did not provide any " "identity"); data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; eap_aka_state(data, NOTIFICATION); return; } new_identity = os_malloc(attr->identity_len); if (new_identity == NULL) { data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; eap_aka_state(data, NOTIFICATION); return; } os_free(sm->identity); sm->identity = new_identity; os_memcpy(sm->identity, attr->identity, attr->identity_len); sm->identity_len = attr->identity_len; eap_aka_determine_identity(sm, data); if (eap_get_id(respData) == data->pending_id) { data->pending_id = -1; eap_aka_add_id_msg(data, respData); } } static int eap_aka_verify_mac(struct eap_aka_data *data, const struct wpabuf *req, const u8 *mac, const u8 *extra, size_t extra_len) { if (data->eap_method == EAP_TYPE_AKA_PRIME) return eap_sim_verify_mac_sha256(data->k_aut, req, mac, extra, extra_len); return eap_sim_verify_mac(data->k_aut, req, mac, extra, extra_len); } static void eap_aka_process_challenge(struct eap_sm *sm, struct eap_aka_data *data, struct wpabuf *respData, struct eap_sim_attrs *attr) { wpa_printf(MSG_DEBUG, "EAP-AKA: Processing Challenge"); #ifdef EAP_SERVER_AKA_PRIME #if 0 /* KDF negotiation; to be enabled only after more than one KDF is * supported */ if (data->eap_method == EAP_TYPE_AKA_PRIME && attr->kdf_count == 1 && attr->mac == NULL) { if (attr->kdf[0] != EAP_AKA_PRIME_KDF) { wpa_printf(MSG_WARNING, "EAP-AKA': Peer selected " "unknown KDF"); data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; eap_aka_state(data, NOTIFICATION); return; } data->kdf = attr->kdf[0]; /* Allow negotiation to continue with the selected KDF by * sending another Challenge message */ wpa_printf(MSG_DEBUG, "EAP-AKA': KDF %d selected", data->kdf); return; } #endif #endif /* EAP_SERVER_AKA_PRIME */ if (attr->checkcode && eap_aka_verify_checkcode(data, attr->checkcode, attr->checkcode_len)) { wpa_printf(MSG_WARNING, "EAP-AKA: Invalid AT_CHECKCODE in the " "message"); data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; eap_aka_state(data, NOTIFICATION); return; } if (attr->mac == NULL || eap_aka_verify_mac(data, respData, attr->mac, NULL, 0)) { wpa_printf(MSG_WARNING, "EAP-AKA: Challenge message " "did not include valid AT_MAC"); data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; eap_aka_state(data, NOTIFICATION); return; } /* * AT_RES is padded, so verify that there is enough room for RES and * that the RES length in bits matches with the expected RES. */ if (attr->res == NULL || attr->res_len < data->res_len || attr->res_len_bits != data->res_len * 8 || os_memcmp(attr->res, data->res, data->res_len) != 0) { wpa_printf(MSG_WARNING, "EAP-AKA: Challenge message did not " "include valid AT_RES (attr len=%lu, res len=%lu " "bits, expected %lu bits)", (unsigned long) attr->res_len, (unsigned long) attr->res_len_bits, (unsigned long) data->res_len * 8); data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; eap_aka_state(data, NOTIFICATION); return; } wpa_printf(MSG_DEBUG, "EAP-AKA: Challenge response includes the " "correct AT_MAC"); if (sm->eap_sim_aka_result_ind && attr->result_ind) { data->use_result_ind = 1; data->notification = EAP_SIM_SUCCESS; eap_aka_state(data, NOTIFICATION); } else eap_aka_state(data, SUCCESS); if (data->next_pseudonym) { eap_sim_db_add_pseudonym(sm->eap_sim_db_priv, data->permanent, data->next_pseudonym); data->next_pseudonym = NULL; } if (data->next_reauth_id) { if (data->eap_method == EAP_TYPE_AKA_PRIME) { #ifdef EAP_SERVER_AKA_PRIME eap_sim_db_add_reauth_prime(sm->eap_sim_db_priv, data->permanent, data->next_reauth_id, data->counter + 1, data->k_encr, data->k_aut, data->k_re); #endif /* EAP_SERVER_AKA_PRIME */ } else { eap_sim_db_add_reauth(sm->eap_sim_db_priv, data->permanent, data->next_reauth_id, data->counter + 1, data->mk); } data->next_reauth_id = NULL; } } static void eap_aka_process_sync_failure(struct eap_sm *sm, struct eap_aka_data *data, struct wpabuf *respData, struct eap_sim_attrs *attr) { wpa_printf(MSG_DEBUG, "EAP-AKA: Processing Synchronization-Failure"); if (attr->auts == NULL) { wpa_printf(MSG_WARNING, "EAP-AKA: Synchronization-Failure " "message did not include valid AT_AUTS"); data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; eap_aka_state(data, NOTIFICATION); return; } /* Avoid re-reporting AUTS when processing pending EAP packet by * maintaining a local flag stating whether this AUTS has already been * reported. */ if (!data->auts_reported && eap_sim_db_resynchronize(sm->eap_sim_db_priv, data->permanent, attr->auts, data->rand)) { wpa_printf(MSG_WARNING, "EAP-AKA: Resynchronization failed"); data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; eap_aka_state(data, NOTIFICATION); return; } data->auts_reported = 1; /* Remain in CHALLENGE state to re-try after resynchronization */ eap_aka_fullauth(sm, data); } static void eap_aka_process_reauth(struct eap_sm *sm, struct eap_aka_data *data, struct wpabuf *respData, struct eap_sim_attrs *attr) { struct eap_sim_attrs eattr; u8 *decrypted = NULL; wpa_printf(MSG_DEBUG, "EAP-AKA: Processing Reauthentication"); if (attr->mac == NULL || eap_aka_verify_mac(data, respData, attr->mac, data->nonce_s, EAP_SIM_NONCE_S_LEN)) { wpa_printf(MSG_WARNING, "EAP-AKA: Re-authentication message " "did not include valid AT_MAC"); goto fail; } if (attr->encr_data == NULL || attr->iv == NULL) { wpa_printf(MSG_WARNING, "EAP-AKA: Reauthentication " "message did not include encrypted data"); goto fail; } decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data, attr->encr_data_len, attr->iv, &eattr, 0); if (decrypted == NULL) { wpa_printf(MSG_WARNING, "EAP-AKA: Failed to parse encrypted " "data from reauthentication message"); goto fail; } if (eattr.counter != data->counter) { wpa_printf(MSG_WARNING, "EAP-AKA: Re-authentication message " "used incorrect counter %u, expected %u", eattr.counter, data->counter); goto fail; } os_free(decrypted); decrypted = NULL; wpa_printf(MSG_DEBUG, "EAP-AKA: Re-authentication response includes " "the correct AT_MAC"); if (eattr.counter_too_small) { wpa_printf(MSG_DEBUG, "EAP-AKA: Re-authentication response " "included AT_COUNTER_TOO_SMALL - starting full " "authentication"); eap_aka_fullauth(sm, data); return; } if (sm->eap_sim_aka_result_ind && attr->result_ind) { data->use_result_ind = 1; data->notification = EAP_SIM_SUCCESS; eap_aka_state(data, NOTIFICATION); } else eap_aka_state(data, SUCCESS); if (data->next_reauth_id) { if (data->eap_method == EAP_TYPE_AKA_PRIME) { #ifdef EAP_SERVER_AKA_PRIME eap_sim_db_add_reauth_prime(sm->eap_sim_db_priv, data->permanent, data->next_reauth_id, data->counter + 1, data->k_encr, data->k_aut, data->k_re); #endif /* EAP_SERVER_AKA_PRIME */ } else { eap_sim_db_add_reauth(sm->eap_sim_db_priv, data->permanent, data->next_reauth_id, data->counter + 1, data->mk); } data->next_reauth_id = NULL; } else { eap_sim_db_remove_reauth(sm->eap_sim_db_priv, data->reauth); data->reauth = NULL; } return; fail: data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; eap_aka_state(data, NOTIFICATION); eap_sim_db_remove_reauth(sm->eap_sim_db_priv, data->reauth); data->reauth = NULL; os_free(decrypted); } static void eap_aka_process_client_error(struct eap_sm *sm, struct eap_aka_data *data, struct wpabuf *respData, struct eap_sim_attrs *attr) { wpa_printf(MSG_DEBUG, "EAP-AKA: Client reported error %d", attr->client_error_code); if (data->notification == EAP_SIM_SUCCESS && data->use_result_ind) eap_aka_state(data, SUCCESS); else eap_aka_state(data, FAILURE); } static void eap_aka_process_authentication_reject( struct eap_sm *sm, struct eap_aka_data *data, struct wpabuf *respData, struct eap_sim_attrs *attr) { wpa_printf(MSG_DEBUG, "EAP-AKA: Client rejected authentication"); eap_aka_state(data, FAILURE); } static void eap_aka_process_notification(struct eap_sm *sm, struct eap_aka_data *data, struct wpabuf *respData, struct eap_sim_attrs *attr) { wpa_printf(MSG_DEBUG, "EAP-AKA: Client replied to notification"); if (data->notification == EAP_SIM_SUCCESS && data->use_result_ind) eap_aka_state(data, SUCCESS); else eap_aka_state(data, FAILURE); } static void eap_aka_process(struct eap_sm *sm, void *priv, struct wpabuf *respData) { struct eap_aka_data *data = priv; const u8 *pos, *end; u8 subtype; size_t len; struct eap_sim_attrs attr; pos = eap_hdr_validate(EAP_VENDOR_IETF, data->eap_method, respData, &len); if (pos == NULL || len < 3) return; end = pos + len; subtype = *pos; pos += 3; if (eap_aka_subtype_ok(data, subtype)) { wpa_printf(MSG_DEBUG, "EAP-AKA: Unrecognized or unexpected " "EAP-AKA Subtype in EAP Response"); data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; eap_aka_state(data, NOTIFICATION); return; } if (eap_sim_parse_attr(pos, end, &attr, data->eap_method == EAP_TYPE_AKA_PRIME ? 2 : 1, 0)) { wpa_printf(MSG_DEBUG, "EAP-AKA: Failed to parse attributes"); data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; eap_aka_state(data, NOTIFICATION); return; } if (subtype == EAP_AKA_SUBTYPE_CLIENT_ERROR) { eap_aka_process_client_error(sm, data, respData, &attr); return; } if (subtype == EAP_AKA_SUBTYPE_AUTHENTICATION_REJECT) { eap_aka_process_authentication_reject(sm, data, respData, &attr); return; } switch (data->state) { case IDENTITY: eap_aka_process_identity(sm, data, respData, &attr); break; case CHALLENGE: if (subtype == EAP_AKA_SUBTYPE_SYNCHRONIZATION_FAILURE) { eap_aka_process_sync_failure(sm, data, respData, &attr); } else { eap_aka_process_challenge(sm, data, respData, &attr); } break; case REAUTH: eap_aka_process_reauth(sm, data, respData, &attr); break; case NOTIFICATION: eap_aka_process_notification(sm, data, respData, &attr); break; default: wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown state %d in " "process", data->state); break; } } static Boolean eap_aka_isDone(struct eap_sm *sm, void *priv) { struct eap_aka_data *data = priv; return data->state == SUCCESS || data->state == FAILURE; } static u8 * eap_aka_getKey(struct eap_sm *sm, void *priv, size_t *len) { struct eap_aka_data *data = priv; u8 *key; if (data->state != SUCCESS) return NULL; key = os_malloc(EAP_SIM_KEYING_DATA_LEN); if (key == NULL) return NULL; os_memcpy(key, data->msk, EAP_SIM_KEYING_DATA_LEN); *len = EAP_SIM_KEYING_DATA_LEN; return key; } static u8 * eap_aka_get_emsk(struct eap_sm *sm, void *priv, size_t *len) { struct eap_aka_data *data = priv; u8 *key; if (data->state != SUCCESS) return NULL; key = os_malloc(EAP_EMSK_LEN); if (key == NULL) return NULL; os_memcpy(key, data->emsk, EAP_EMSK_LEN); *len = EAP_EMSK_LEN; return key; } static Boolean eap_aka_isSuccess(struct eap_sm *sm, void *priv) { struct eap_aka_data *data = priv; return data->state == SUCCESS; } int eap_server_aka_register(void) { struct eap_method *eap; int ret; eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_AKA, "AKA"); if (eap == NULL) return -1; eap->init = eap_aka_init; eap->reset = eap_aka_reset; eap->buildReq = eap_aka_buildReq; eap->check = eap_aka_check; eap->process = eap_aka_process; eap->isDone = eap_aka_isDone; eap->getKey = eap_aka_getKey; eap->isSuccess = eap_aka_isSuccess; eap->get_emsk = eap_aka_get_emsk; ret = eap_server_method_register(eap); if (ret) eap_server_method_free(eap); return ret; } #ifdef EAP_SERVER_AKA_PRIME int eap_server_aka_prime_register(void) { struct eap_method *eap; int ret; eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_AKA_PRIME, "AKA'"); if (eap == NULL) return -1; eap->init = eap_aka_prime_init; eap->reset = eap_aka_reset; eap->buildReq = eap_aka_buildReq; eap->check = eap_aka_check; eap->process = eap_aka_process; eap->isDone = eap_aka_isDone; eap->getKey = eap_aka_getKey; eap->isSuccess = eap_aka_isSuccess; eap->get_emsk = eap_aka_get_emsk; ret = eap_server_method_register(eap); if (ret) eap_server_method_free(eap); return ret; } #endif /* EAP_SERVER_AKA_PRIME */ wpa-2.1/src/eap_server/eap_server_eke.c000066400000000000000000000511521227414666700202330ustar00rootroot00000000000000/* * hostapd / EAP-EKE (RFC 6124) server * Copyright (c) 2013, 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/random.h" #include "eap_server/eap_i.h" #include "eap_common/eap_eke_common.h" struct eap_eke_data { enum { IDENTITY, COMMIT, CONFIRM, FAILURE_REPORT, SUCCESS, FAILURE } state; u8 msk[EAP_MSK_LEN]; u8 emsk[EAP_EMSK_LEN]; u8 *peerid; size_t peerid_len; u8 peerid_type; u8 serverid_type; u8 dh_priv[EAP_EKE_MAX_DH_LEN]; u8 key[EAP_EKE_MAX_KEY_LEN]; struct eap_eke_session sess; u8 nonce_p[EAP_EKE_MAX_NONCE_LEN]; u8 nonce_s[EAP_EKE_MAX_NONCE_LEN]; struct wpabuf *msgs; int phase2; u32 failure_code; }; static const char * eap_eke_state_txt(int state) { switch (state) { case IDENTITY: return "IDENTITY"; case COMMIT: return "COMMIT"; case CONFIRM: return "CONFIRM"; case FAILURE_REPORT: return "FAILURE_REPORT"; case SUCCESS: return "SUCCESS"; case FAILURE: return "FAILURE"; default: return "?"; } } static void eap_eke_state(struct eap_eke_data *data, int state) { wpa_printf(MSG_DEBUG, "EAP-EKE: %s -> %s", eap_eke_state_txt(data->state), eap_eke_state_txt(state)); data->state = state; } static void eap_eke_fail(struct eap_eke_data *data, u32 code) { wpa_printf(MSG_DEBUG, "EAP-EKE: Failure - code 0x%x", code); data->failure_code = code; eap_eke_state(data, FAILURE_REPORT); } static void * eap_eke_init(struct eap_sm *sm) { struct eap_eke_data *data; size_t i; data = os_zalloc(sizeof(*data)); if (data == NULL) return NULL; eap_eke_state(data, IDENTITY); data->serverid_type = EAP_EKE_ID_OPAQUE; for (i = 0; i < sm->server_id_len; i++) { if (sm->server_id[i] == '.' && data->serverid_type == EAP_EKE_ID_OPAQUE) data->serverid_type = EAP_EKE_ID_FQDN; if (sm->server_id[i] == '@') data->serverid_type = EAP_EKE_ID_NAI; } data->phase2 = sm->init_phase2; return data; } static void eap_eke_reset(struct eap_sm *sm, void *priv) { struct eap_eke_data *data = priv; eap_eke_session_clean(&data->sess); os_free(data->peerid); wpabuf_free(data->msgs); os_free(data); } static struct wpabuf * eap_eke_build_msg(struct eap_eke_data *data, u8 id, size_t length, u8 eke_exch) { struct wpabuf *msg; size_t plen; plen = 1 + length; msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_EKE, plen, EAP_CODE_REQUEST, id); if (msg == NULL) { wpa_printf(MSG_ERROR, "EAP-EKE: Failed to allocate memory"); return NULL; } wpabuf_put_u8(msg, eke_exch); return msg; } static int supported_proposal(const u8 *pos) { if (pos[0] == EAP_EKE_DHGROUP_EKE_16 && pos[1] == EAP_EKE_ENCR_AES128_CBC && pos[2] == EAP_EKE_PRF_HMAC_SHA2_256 && pos[3] == EAP_EKE_MAC_HMAC_SHA2_256) return 1; if (pos[0] == EAP_EKE_DHGROUP_EKE_15 && pos[1] == EAP_EKE_ENCR_AES128_CBC && pos[2] == EAP_EKE_PRF_HMAC_SHA2_256 && pos[3] == EAP_EKE_MAC_HMAC_SHA2_256) return 1; if (pos[0] == EAP_EKE_DHGROUP_EKE_14 && pos[1] == EAP_EKE_ENCR_AES128_CBC && pos[2] == EAP_EKE_PRF_HMAC_SHA2_256 && pos[3] == EAP_EKE_MAC_HMAC_SHA2_256) return 1; if (pos[0] == EAP_EKE_DHGROUP_EKE_14 && pos[1] == EAP_EKE_ENCR_AES128_CBC && pos[2] == EAP_EKE_PRF_HMAC_SHA1 && pos[3] == EAP_EKE_MAC_HMAC_SHA1) return 1; return 0; } static struct wpabuf * eap_eke_build_failure(struct eap_eke_data *data, u8 id) { struct wpabuf *msg; wpa_printf(MSG_DEBUG, "EAP-EKE: Request/Failure: Failure-Code=0x%x", data->failure_code); msg = eap_eke_build_msg(data, id, 4, EAP_EKE_FAILURE); if (msg == NULL) { eap_eke_state(data, FAILURE); return NULL; } wpabuf_put_be32(msg, data->failure_code); return msg; } static struct wpabuf * eap_eke_build_identity(struct eap_sm *sm, struct eap_eke_data *data, u8 id) { struct wpabuf *msg; size_t plen; wpa_printf(MSG_DEBUG, "EAP-EKE: Request/Identity"); plen = 2 + 4 * 4 + 1 + sm->server_id_len; msg = eap_eke_build_msg(data, id, plen, EAP_EKE_ID); if (msg == NULL) return NULL; wpabuf_put_u8(msg, 4); /* NumProposals */ wpabuf_put_u8(msg, 0); /* Reserved */ /* Proposal - DH Group 16 with AES128-CBC and SHA256 */ wpabuf_put_u8(msg, EAP_EKE_DHGROUP_EKE_16); /* Group Description */ wpabuf_put_u8(msg, EAP_EKE_ENCR_AES128_CBC); /* Encryption */ wpabuf_put_u8(msg, EAP_EKE_PRF_HMAC_SHA2_256); /* PRF */ wpabuf_put_u8(msg, EAP_EKE_MAC_HMAC_SHA2_256); /* MAC */ /* Proposal - DH Group 15 with AES128-CBC and SHA256 */ wpabuf_put_u8(msg, EAP_EKE_DHGROUP_EKE_15); /* Group Description */ wpabuf_put_u8(msg, EAP_EKE_ENCR_AES128_CBC); /* Encryption */ wpabuf_put_u8(msg, EAP_EKE_PRF_HMAC_SHA2_256); /* PRF */ wpabuf_put_u8(msg, EAP_EKE_MAC_HMAC_SHA2_256); /* MAC */ /* Proposal - DH Group 14 with AES128-CBC and SHA256 */ wpabuf_put_u8(msg, EAP_EKE_DHGROUP_EKE_14); /* Group Description */ wpabuf_put_u8(msg, EAP_EKE_ENCR_AES128_CBC); /* Encryption */ wpabuf_put_u8(msg, EAP_EKE_PRF_HMAC_SHA2_256); /* PRF */ wpabuf_put_u8(msg, EAP_EKE_MAC_HMAC_SHA2_256); /* MAC */ /* * Proposal - DH Group 14 with AES128-CBC and SHA1 * (mandatory to implement algorithms) */ wpabuf_put_u8(msg, EAP_EKE_DHGROUP_EKE_14); /* Group Description */ wpabuf_put_u8(msg, EAP_EKE_ENCR_AES128_CBC); /* Encryption */ wpabuf_put_u8(msg, EAP_EKE_PRF_HMAC_SHA1); /* PRF */ wpabuf_put_u8(msg, EAP_EKE_MAC_HMAC_SHA1); /* MAC */ /* Server IDType + Identity */ wpabuf_put_u8(msg, data->serverid_type); wpabuf_put_data(msg, sm->server_id, sm->server_id_len); wpabuf_free(data->msgs); data->msgs = wpabuf_dup(msg); if (data->msgs == NULL) { wpabuf_free(msg); return NULL; } return msg; } static struct wpabuf * eap_eke_build_commit(struct eap_sm *sm, struct eap_eke_data *data, u8 id) { struct wpabuf *msg; u8 pub[EAP_EKE_MAX_DH_LEN]; wpa_printf(MSG_DEBUG, "EAP-EKE: Request/Commit"); if (sm->user == NULL || sm->user->password == NULL) { wpa_printf(MSG_INFO, "EAP-EKE: Password with not configured"); eap_eke_fail(data, EAP_EKE_FAIL_PASSWD_NOT_FOUND); return eap_eke_build_failure(data, id); } if (eap_eke_derive_key(&data->sess, sm->user->password, sm->user->password_len, sm->server_id, sm->server_id_len, data->peerid, data->peerid_len, data->key) < 0) { wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive key"); eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); return eap_eke_build_failure(data, id); } msg = eap_eke_build_msg(data, id, data->sess.dhcomp_len, EAP_EKE_COMMIT); if (msg == NULL) { eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); return eap_eke_build_failure(data, id); } /* * y_s = g ^ x_s (mod p) * x_s = random number 2 .. p-1 * temp = prf(0+, password) * key = prf+(temp, ID_S | ID_P) * DHComponent_S = Encr(key, y_s) */ if (eap_eke_dh_init(data->sess.dhgroup, data->dh_priv, pub) < 0) { wpa_printf(MSG_INFO, "EAP-EKE: Failed to initialize DH"); eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); return eap_eke_build_failure(data, id); } if (eap_eke_dhcomp(&data->sess, data->key, pub, wpabuf_put(msg, data->sess.dhcomp_len)) < 0) { wpa_printf(MSG_INFO, "EAP-EKE: Failed to build DHComponent_S"); wpabuf_free(msg); eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); return eap_eke_build_failure(data, id); } if (wpabuf_resize(&data->msgs, wpabuf_len(msg)) < 0) { wpabuf_free(msg); eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); return eap_eke_build_failure(data, id); } wpabuf_put_buf(data->msgs, msg); return msg; } static struct wpabuf * eap_eke_build_confirm(struct eap_sm *sm, struct eap_eke_data *data, u8 id) { struct wpabuf *msg; size_t plen, prot_len; u8 nonces[2 * EAP_EKE_MAX_NONCE_LEN]; u8 *auth; wpa_printf(MSG_DEBUG, "EAP-EKE: Request/Confirm"); plen = data->sess.pnonce_ps_len + data->sess.prf_len; msg = eap_eke_build_msg(data, id, plen, EAP_EKE_CONFIRM); if (msg == NULL) { eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); return eap_eke_build_failure(data, id); } if (random_get_bytes(data->nonce_s, data->sess.nonce_len)) { wpabuf_free(msg); eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); return eap_eke_build_failure(data, id); } wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Nonce_S", data->nonce_s, data->sess.nonce_len); os_memcpy(nonces, data->nonce_p, data->sess.nonce_len); os_memcpy(nonces + data->sess.nonce_len, data->nonce_s, data->sess.nonce_len); prot_len = wpabuf_tailroom(msg); if (eap_eke_prot(&data->sess, nonces, 2 * data->sess.nonce_len, wpabuf_put(msg, 0), &prot_len) < 0) { wpabuf_free(msg); eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); return eap_eke_build_failure(data, id); } wpabuf_put(msg, prot_len); if (eap_eke_derive_ka(&data->sess, sm->server_id, sm->server_id_len, data->peerid, data->peerid_len, data->nonce_p, data->nonce_s) < 0) { wpabuf_free(msg); eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); return eap_eke_build_failure(data, id); } auth = wpabuf_put(msg, data->sess.prf_len); if (eap_eke_auth(&data->sess, "EAP-EKE server", data->msgs, auth) < 0) { wpabuf_free(msg); eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); return eap_eke_build_failure(data, id); } wpa_hexdump(MSG_DEBUG, "EAP-EKE: Auth_S", auth, data->sess.prf_len); return msg; } static struct wpabuf * eap_eke_buildReq(struct eap_sm *sm, void *priv, u8 id) { struct eap_eke_data *data = priv; switch (data->state) { case IDENTITY: return eap_eke_build_identity(sm, data, id); case COMMIT: return eap_eke_build_commit(sm, data, id); case CONFIRM: return eap_eke_build_confirm(sm, data, id); case FAILURE_REPORT: return eap_eke_build_failure(data, id); default: wpa_printf(MSG_DEBUG, "EAP-EKE: Unknown state %d in buildReq", data->state); break; } return NULL; } static Boolean eap_eke_check(struct eap_sm *sm, void *priv, struct wpabuf *respData) { struct eap_eke_data *data = priv; size_t len; const u8 *pos; u8 eke_exch; pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_EKE, respData, &len); if (pos == NULL || len < 1) { wpa_printf(MSG_INFO, "EAP-EKE: Invalid frame"); return TRUE; } eke_exch = *pos; wpa_printf(MSG_DEBUG, "EAP-EKE: Received frame: EKE-Exch=%d", eke_exch); if (data->state == IDENTITY && eke_exch == EAP_EKE_ID) return FALSE; if (data->state == COMMIT && eke_exch == EAP_EKE_COMMIT) return FALSE; if (data->state == CONFIRM && eke_exch == EAP_EKE_CONFIRM) return FALSE; if (eke_exch == EAP_EKE_FAILURE) return FALSE; wpa_printf(MSG_INFO, "EAP-EKE: Unexpected EKE-Exch=%d in state=%d", eke_exch, data->state); return TRUE; } static void eap_eke_process_identity(struct eap_sm *sm, struct eap_eke_data *data, const struct wpabuf *respData, const u8 *payload, size_t payloadlen) { const u8 *pos, *end; int i; wpa_printf(MSG_DEBUG, "EAP-EKE: Received Response/Identity"); if (data->state != IDENTITY) { eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR); return; } pos = payload; end = payload + payloadlen; if (pos + 2 + 4 + 1 > end) { wpa_printf(MSG_INFO, "EAP-EKE: Too short EAP-EKE-ID payload"); eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR); return; } if (*pos != 1) { wpa_printf(MSG_INFO, "EAP-EKE: Unexpected NumProposals %d (expected 1)", *pos); eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR); return; } pos += 2; if (!supported_proposal(pos)) { wpa_printf(MSG_INFO, "EAP-EKE: Unexpected Proposal (%u:%u:%u:%u)", pos[0], pos[1], pos[2], pos[3]); eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR); return; } wpa_printf(MSG_DEBUG, "EAP-EKE: Selected Proposal (%u:%u:%u:%u)", pos[0], pos[1], pos[2], pos[3]); if (eap_eke_session_init(&data->sess, pos[0], pos[1], pos[2], pos[3]) < 0) { eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); return; } pos += 4; data->peerid_type = *pos++; os_free(data->peerid); data->peerid = os_malloc(end - pos); if (data->peerid == NULL) { wpa_printf(MSG_INFO, "EAP-EKE: Failed to allocate memory for peerid"); eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); return; } os_memcpy(data->peerid, pos, end - pos); data->peerid_len = end - pos; wpa_printf(MSG_DEBUG, "EAP-EKE: Peer IDType %u", data->peerid_type); wpa_hexdump_ascii(MSG_DEBUG, "EAP-EKE: Peer Identity", data->peerid, data->peerid_len); if (eap_user_get(sm, data->peerid, data->peerid_len, data->phase2)) { wpa_printf(MSG_INFO, "EAP-EKE: Peer Identity not found from user database"); eap_eke_fail(data, EAP_EKE_FAIL_PASSWD_NOT_FOUND); return; } for (i = 0; i < EAP_MAX_METHODS; i++) { if (sm->user->methods[i].vendor == EAP_VENDOR_IETF && sm->user->methods[i].method == EAP_TYPE_EKE) break; } if (i == EAP_MAX_METHODS) { wpa_printf(MSG_INFO, "EAP-EKE: Matching user entry does not allow EAP-EKE"); eap_eke_fail(data, EAP_EKE_FAIL_PASSWD_NOT_FOUND); return; } if (sm->user->password == NULL || sm->user->password_len == 0) { wpa_printf(MSG_INFO, "EAP-EKE: No password configured for peer"); eap_eke_fail(data, EAP_EKE_FAIL_PASSWD_NOT_FOUND); return; } if (wpabuf_resize(&data->msgs, wpabuf_len(respData)) < 0) { eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); return; } wpabuf_put_buf(data->msgs, respData); eap_eke_state(data, COMMIT); } static void eap_eke_process_commit(struct eap_sm *sm, struct eap_eke_data *data, const struct wpabuf *respData, const u8 *payload, size_t payloadlen) { const u8 *pos, *end, *dhcomp, *pnonce; size_t decrypt_len; wpa_printf(MSG_DEBUG, "EAP-EKE: Received Response/Commit"); if (data->state != COMMIT) { eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR); return; } pos = payload; end = payload + payloadlen; if (pos + data->sess.dhcomp_len + data->sess.pnonce_len > end) { wpa_printf(MSG_DEBUG, "EAP-EKE: Too short EAP-EKE-Commit"); eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR); return; } wpa_hexdump(MSG_DEBUG, "EAP-EKE: DHComponent_P", pos, data->sess.dhcomp_len); dhcomp = pos; pos += data->sess.dhcomp_len; wpa_hexdump(MSG_DEBUG, "EAP-EKE: PNonce_P", pos, data->sess.pnonce_len); pnonce = pos; pos += data->sess.pnonce_len; wpa_hexdump(MSG_DEBUG, "EAP-EKE: CBValue", pos, end - pos); if (eap_eke_shared_secret(&data->sess, data->key, data->dh_priv, dhcomp) < 0) { wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive shared secret"); eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); return; } if (eap_eke_derive_ke_ki(&data->sess, sm->server_id, sm->server_id_len, data->peerid, data->peerid_len) < 0) { wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive Ke/Ki"); eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); return; } decrypt_len = sizeof(data->nonce_p); if (eap_eke_decrypt_prot(&data->sess, pnonce, data->sess.pnonce_len, data->nonce_p, &decrypt_len) < 0) { wpa_printf(MSG_INFO, "EAP-EKE: Failed to decrypt PNonce_P"); eap_eke_fail(data, EAP_EKE_FAIL_AUTHENTICATION_FAIL); return; } if (decrypt_len < (size_t) data->sess.nonce_len) { wpa_printf(MSG_INFO, "EAP-EKE: PNonce_P protected data too short to include Nonce_P"); eap_eke_fail(data, EAP_EKE_FAIL_AUTHENTICATION_FAIL); return; } wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Nonce_P", data->nonce_p, data->sess.nonce_len); if (wpabuf_resize(&data->msgs, wpabuf_len(respData)) < 0) { eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); return; } wpabuf_put_buf(data->msgs, respData); eap_eke_state(data, CONFIRM); } static void eap_eke_process_confirm(struct eap_sm *sm, struct eap_eke_data *data, const struct wpabuf *respData, const u8 *payload, size_t payloadlen) { size_t decrypt_len; u8 nonce[EAP_EKE_MAX_NONCE_LEN]; u8 auth_p[EAP_EKE_MAX_HASH_LEN]; wpa_printf(MSG_DEBUG, "EAP-EKE: Received Response/Confirm"); if (data->state != CONFIRM) { eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR); return; } wpa_printf(MSG_DEBUG, "EAP-EKE: Received Response/Confirm"); if (payloadlen < (size_t) data->sess.pnonce_len + data->sess.prf_len) { wpa_printf(MSG_DEBUG, "EAP-EKE: Too short EAP-EKE-Confirm"); eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR); return; } decrypt_len = sizeof(nonce); if (eap_eke_decrypt_prot(&data->sess, payload, data->sess.pnonce_len, nonce, &decrypt_len) < 0) { wpa_printf(MSG_INFO, "EAP-EKE: Failed to decrypt PNonce_S"); eap_eke_fail(data, EAP_EKE_FAIL_AUTHENTICATION_FAIL); return; } if (decrypt_len < (size_t) data->sess.nonce_len) { wpa_printf(MSG_INFO, "EAP-EKE: PNonce_S protected data too short to include Nonce_S"); eap_eke_fail(data, EAP_EKE_FAIL_AUTHENTICATION_FAIL); return; } wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Received Nonce_S", nonce, data->sess.nonce_len); if (os_memcmp(nonce, data->nonce_s, data->sess.nonce_len) != 0) { wpa_printf(MSG_INFO, "EAP-EKE: Received Nonce_S does not match previously sent Nonce_S"); eap_eke_fail(data, EAP_EKE_FAIL_AUTHENTICATION_FAIL); return; } if (eap_eke_auth(&data->sess, "EAP-EKE peer", data->msgs, auth_p) < 0) { wpa_printf(MSG_INFO, "EAP-EKE: Could not derive Auth_P"); eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); return; } wpa_hexdump(MSG_DEBUG, "EAP-EKE: Auth_P", auth_p, data->sess.prf_len); if (os_memcmp(auth_p, payload + data->sess.pnonce_len, data->sess.prf_len) != 0) { wpa_printf(MSG_INFO, "EAP-EKE: Auth_P does not match"); eap_eke_fail(data, EAP_EKE_FAIL_AUTHENTICATION_FAIL); return; } if (eap_eke_derive_msk(&data->sess, sm->server_id, sm->server_id_len, data->peerid, data->peerid_len, data->nonce_s, data->nonce_p, data->msk, data->emsk) < 0) { wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive MSK/EMSK"); eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); return; } os_memset(data->dh_priv, 0, sizeof(data->dh_priv)); os_memset(data->key, 0, sizeof(data->key)); eap_eke_session_clean(&data->sess); eap_eke_state(data, SUCCESS); } static void eap_eke_process_failure(struct eap_sm *sm, struct eap_eke_data *data, const struct wpabuf *respData, const u8 *payload, size_t payloadlen) { u32 code; wpa_printf(MSG_DEBUG, "EAP-EKE: Received Response/Failure"); if (payloadlen < 4) { wpa_printf(MSG_DEBUG, "EAP-EKE: Too short EAP-EKE-Failure"); eap_eke_state(data, FAILURE); return; } code = WPA_GET_BE32(payload); wpa_printf(MSG_DEBUG, "EAP-EKE: Peer reported failure code 0x%x", code); eap_eke_state(data, FAILURE); } static void eap_eke_process(struct eap_sm *sm, void *priv, struct wpabuf *respData) { struct eap_eke_data *data = priv; u8 eke_exch; size_t len; const u8 *pos, *end; pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_EKE, respData, &len); if (pos == NULL || len < 1) return; eke_exch = *pos; end = pos + len; pos++; wpa_hexdump(MSG_DEBUG, "EAP-EKE: Received payload", pos, end - pos); switch (eke_exch) { case EAP_EKE_ID: eap_eke_process_identity(sm, data, respData, pos, end - pos); break; case EAP_EKE_COMMIT: eap_eke_process_commit(sm, data, respData, pos, end - pos); break; case EAP_EKE_CONFIRM: eap_eke_process_confirm(sm, data, respData, pos, end - pos); break; case EAP_EKE_FAILURE: eap_eke_process_failure(sm, data, respData, pos, end - pos); break; } } static Boolean eap_eke_isDone(struct eap_sm *sm, void *priv) { struct eap_eke_data *data = priv; return data->state == SUCCESS || data->state == FAILURE; } static u8 * eap_eke_getKey(struct eap_sm *sm, void *priv, size_t *len) { struct eap_eke_data *data = priv; u8 *key; if (data->state != SUCCESS) return NULL; key = os_malloc(EAP_MSK_LEN); if (key == NULL) return NULL; os_memcpy(key, data->msk, EAP_MSK_LEN); *len = EAP_MSK_LEN; return key; } static u8 * eap_eke_get_emsk(struct eap_sm *sm, void *priv, size_t *len) { struct eap_eke_data *data = priv; u8 *key; if (data->state != SUCCESS) return NULL; key = os_malloc(EAP_EMSK_LEN); if (key == NULL) return NULL; os_memcpy(key, data->emsk, EAP_EMSK_LEN); *len = EAP_EMSK_LEN; return key; } static Boolean eap_eke_isSuccess(struct eap_sm *sm, void *priv) { struct eap_eke_data *data = priv; return data->state == SUCCESS; } int eap_server_eke_register(void) { struct eap_method *eap; int ret; eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_EKE, "EKE"); if (eap == NULL) return -1; eap->init = eap_eke_init; eap->reset = eap_eke_reset; eap->buildReq = eap_eke_buildReq; eap->check = eap_eke_check; eap->process = eap_eke_process; eap->isDone = eap_eke_isDone; eap->getKey = eap_eke_getKey; eap->isSuccess = eap_eke_isSuccess; eap->get_emsk = eap_eke_get_emsk; ret = eap_server_method_register(eap); if (ret) eap_server_method_free(eap); return ret; } wpa-2.1/src/eap_server/eap_server_fast.c000066400000000000000000001230611227414666700204230ustar00rootroot00000000000000/* * EAP-FAST server (RFC 4851) * Copyright (c) 2004-2008, 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/aes_wrap.h" #include "crypto/sha1.h" #include "crypto/tls.h" #include "crypto/random.h" #include "eap_common/eap_tlv_common.h" #include "eap_common/eap_fast_common.h" #include "eap_i.h" #include "eap_tls_common.h" static void eap_fast_reset(struct eap_sm *sm, void *priv); /* Private PAC-Opaque TLV types */ #define PAC_OPAQUE_TYPE_PAD 0 #define PAC_OPAQUE_TYPE_KEY 1 #define PAC_OPAQUE_TYPE_LIFETIME 2 #define PAC_OPAQUE_TYPE_IDENTITY 3 struct eap_fast_data { struct eap_ssl_data ssl; enum { START, PHASE1, PHASE2_START, PHASE2_ID, PHASE2_METHOD, CRYPTO_BINDING, REQUEST_PAC, SUCCESS, FAILURE } state; int fast_version; const struct eap_method *phase2_method; void *phase2_priv; int force_version; int peer_version; u8 crypto_binding_nonce[32]; int final_result; struct eap_fast_key_block_provisioning *key_block_p; u8 simck[EAP_FAST_SIMCK_LEN]; u8 cmk[EAP_FAST_CMK_LEN]; int simck_idx; u8 pac_opaque_encr[16]; u8 *srv_id; size_t srv_id_len; char *srv_id_info; int anon_provisioning; int send_new_pac; /* server triggered re-keying of Tunnel PAC */ struct wpabuf *pending_phase2_resp; u8 *identity; /* from PAC-Opaque */ size_t identity_len; int eap_seq; int tnc_started; int pac_key_lifetime; int pac_key_refresh_time; }; static int eap_fast_process_phase2_start(struct eap_sm *sm, struct eap_fast_data *data); static const char * eap_fast_state_txt(int state) { switch (state) { case START: return "START"; case PHASE1: return "PHASE1"; case PHASE2_START: return "PHASE2_START"; case PHASE2_ID: return "PHASE2_ID"; case PHASE2_METHOD: return "PHASE2_METHOD"; case CRYPTO_BINDING: return "CRYPTO_BINDING"; case REQUEST_PAC: return "REQUEST_PAC"; case SUCCESS: return "SUCCESS"; case FAILURE: return "FAILURE"; default: return "Unknown?!"; } } static void eap_fast_state(struct eap_fast_data *data, int state) { wpa_printf(MSG_DEBUG, "EAP-FAST: %s -> %s", eap_fast_state_txt(data->state), eap_fast_state_txt(state)); data->state = state; } static EapType eap_fast_req_failure(struct eap_sm *sm, struct eap_fast_data *data) { /* TODO: send Result TLV(FAILURE) */ eap_fast_state(data, FAILURE); return EAP_TYPE_NONE; } static int eap_fast_session_ticket_cb(void *ctx, const u8 *ticket, size_t len, const u8 *client_random, const u8 *server_random, u8 *master_secret) { struct eap_fast_data *data = ctx; const u8 *pac_opaque; size_t pac_opaque_len; u8 *buf, *pos, *end, *pac_key = NULL; os_time_t lifetime = 0; struct os_time now; u8 *identity = NULL; size_t identity_len = 0; wpa_printf(MSG_DEBUG, "EAP-FAST: SessionTicket callback"); wpa_hexdump(MSG_DEBUG, "EAP-FAST: SessionTicket (PAC-Opaque)", ticket, len); if (len < 4 || WPA_GET_BE16(ticket) != PAC_TYPE_PAC_OPAQUE) { wpa_printf(MSG_DEBUG, "EAP-FAST: Ignore invalid " "SessionTicket"); return 0; } pac_opaque_len = WPA_GET_BE16(ticket + 2); pac_opaque = ticket + 4; if (pac_opaque_len < 8 || pac_opaque_len % 8 || pac_opaque_len > len - 4) { wpa_printf(MSG_DEBUG, "EAP-FAST: Ignore invalid PAC-Opaque " "(len=%lu left=%lu)", (unsigned long) pac_opaque_len, (unsigned long) len); return 0; } wpa_hexdump(MSG_DEBUG, "EAP-FAST: Received PAC-Opaque", pac_opaque, pac_opaque_len); buf = os_malloc(pac_opaque_len - 8); if (buf == NULL) { wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to allocate memory " "for decrypting PAC-Opaque"); return 0; } if (aes_unwrap(data->pac_opaque_encr, (pac_opaque_len - 8) / 8, pac_opaque, buf) < 0) { wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to decrypt " "PAC-Opaque"); os_free(buf); /* * This may have been caused by server changing the PAC-Opaque * encryption key, so just ignore this PAC-Opaque instead of * failing the authentication completely. Provisioning can now * be used to provision a new PAC. */ return 0; } end = buf + pac_opaque_len - 8; wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: Decrypted PAC-Opaque", buf, end - buf); pos = buf; while (pos + 1 < end) { if (pos + 2 + pos[1] > end) break; switch (*pos) { case PAC_OPAQUE_TYPE_PAD: pos = end; break; case PAC_OPAQUE_TYPE_KEY: if (pos[1] != EAP_FAST_PAC_KEY_LEN) { wpa_printf(MSG_DEBUG, "EAP-FAST: Invalid " "PAC-Key length %d", pos[1]); os_free(buf); return -1; } pac_key = pos + 2; wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: PAC-Key from " "decrypted PAC-Opaque", pac_key, EAP_FAST_PAC_KEY_LEN); break; case PAC_OPAQUE_TYPE_LIFETIME: if (pos[1] != 4) { wpa_printf(MSG_DEBUG, "EAP-FAST: Invalid " "PAC-Key lifetime length %d", pos[1]); os_free(buf); return -1; } lifetime = WPA_GET_BE32(pos + 2); break; case PAC_OPAQUE_TYPE_IDENTITY: identity = pos + 2; identity_len = pos[1]; break; } pos += 2 + pos[1]; } if (pac_key == NULL) { wpa_printf(MSG_DEBUG, "EAP-FAST: No PAC-Key included in " "PAC-Opaque"); os_free(buf); return -1; } if (identity) { wpa_hexdump_ascii(MSG_DEBUG, "EAP-FAST: Identity from " "PAC-Opaque", identity, identity_len); os_free(data->identity); data->identity = os_malloc(identity_len); if (data->identity) { os_memcpy(data->identity, identity, identity_len); data->identity_len = identity_len; } } if (os_get_time(&now) < 0 || lifetime <= 0 || now.sec > lifetime) { wpa_printf(MSG_DEBUG, "EAP-FAST: PAC-Key not valid anymore " "(lifetime=%ld now=%ld)", lifetime, now.sec); data->send_new_pac = 2; /* * Allow PAC to be used to allow a PAC update with some level * of server authentication (i.e., do not fall back to full TLS * handshake since we cannot be sure that the peer would be * able to validate server certificate now). However, reject * the authentication since the PAC was not valid anymore. Peer * can connect again with the newly provisioned PAC after this. */ } else if (lifetime - now.sec < data->pac_key_refresh_time) { wpa_printf(MSG_DEBUG, "EAP-FAST: PAC-Key soft timeout; send " "an update if authentication succeeds"); data->send_new_pac = 1; } eap_fast_derive_master_secret(pac_key, server_random, client_random, master_secret); os_free(buf); return 1; } static void eap_fast_derive_key_auth(struct eap_sm *sm, struct eap_fast_data *data) { u8 *sks; /* RFC 4851, Section 5.1: * Extra key material after TLS key_block: session_key_seed[40] */ sks = eap_fast_derive_key(sm->ssl_ctx, data->ssl.conn, "key expansion", EAP_FAST_SKS_LEN); if (sks == NULL) { wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to derive " "session_key_seed"); return; } /* * RFC 4851, Section 5.2: * S-IMCK[0] = session_key_seed */ wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: session_key_seed (SKS = S-IMCK[0])", sks, EAP_FAST_SKS_LEN); data->simck_idx = 0; os_memcpy(data->simck, sks, EAP_FAST_SIMCK_LEN); os_free(sks); } static void eap_fast_derive_key_provisioning(struct eap_sm *sm, struct eap_fast_data *data) { os_free(data->key_block_p); data->key_block_p = (struct eap_fast_key_block_provisioning *) eap_fast_derive_key(sm->ssl_ctx, data->ssl.conn, "key expansion", sizeof(*data->key_block_p)); if (data->key_block_p == NULL) { wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to derive key block"); return; } /* * RFC 4851, Section 5.2: * S-IMCK[0] = session_key_seed */ wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: session_key_seed (SKS = S-IMCK[0])", data->key_block_p->session_key_seed, sizeof(data->key_block_p->session_key_seed)); data->simck_idx = 0; os_memcpy(data->simck, data->key_block_p->session_key_seed, EAP_FAST_SIMCK_LEN); wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: server_challenge", data->key_block_p->server_challenge, sizeof(data->key_block_p->server_challenge)); wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: client_challenge", data->key_block_p->client_challenge, sizeof(data->key_block_p->client_challenge)); } static int eap_fast_get_phase2_key(struct eap_sm *sm, struct eap_fast_data *data, u8 *isk, size_t isk_len) { u8 *key; size_t key_len; os_memset(isk, 0, isk_len); if (data->phase2_method == NULL || data->phase2_priv == NULL) { wpa_printf(MSG_DEBUG, "EAP-FAST: Phase 2 method not " "available"); return -1; } if (data->phase2_method->getKey == NULL) return 0; if ((key = data->phase2_method->getKey(sm, data->phase2_priv, &key_len)) == NULL) { wpa_printf(MSG_DEBUG, "EAP-FAST: Could not get key material " "from Phase 2"); return -1; } if (key_len > isk_len) key_len = isk_len; if (key_len == 32 && data->phase2_method->vendor == EAP_VENDOR_IETF && data->phase2_method->method == EAP_TYPE_MSCHAPV2) { /* * EAP-FAST uses reverse order for MS-MPPE keys when deriving * MSK from EAP-MSCHAPv2. Swap the keys here to get the correct * ISK for EAP-FAST cryptobinding. */ os_memcpy(isk, key + 16, 16); os_memcpy(isk + 16, key, 16); } else os_memcpy(isk, key, key_len); os_free(key); return 0; } static int eap_fast_update_icmk(struct eap_sm *sm, struct eap_fast_data *data) { u8 isk[32], imck[60]; wpa_printf(MSG_DEBUG, "EAP-FAST: Deriving ICMK[%d] (S-IMCK and CMK)", data->simck_idx + 1); /* * RFC 4851, Section 5.2: * IMCK[j] = T-PRF(S-IMCK[j-1], "Inner Methods Compound Keys", * MSK[j], 60) * S-IMCK[j] = first 40 octets of IMCK[j] * CMK[j] = last 20 octets of IMCK[j] */ if (eap_fast_get_phase2_key(sm, data, isk, sizeof(isk)) < 0) return -1; wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: ISK[j]", isk, sizeof(isk)); sha1_t_prf(data->simck, EAP_FAST_SIMCK_LEN, "Inner Methods Compound Keys", isk, sizeof(isk), imck, sizeof(imck)); data->simck_idx++; os_memcpy(data->simck, imck, EAP_FAST_SIMCK_LEN); wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: S-IMCK[j]", data->simck, EAP_FAST_SIMCK_LEN); os_memcpy(data->cmk, imck + EAP_FAST_SIMCK_LEN, EAP_FAST_CMK_LEN); wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: CMK[j]", data->cmk, EAP_FAST_CMK_LEN); return 0; } static void * eap_fast_init(struct eap_sm *sm) { struct eap_fast_data *data; u8 ciphers[5] = { TLS_CIPHER_ANON_DH_AES128_SHA, TLS_CIPHER_AES128_SHA, TLS_CIPHER_RSA_DHE_AES128_SHA, TLS_CIPHER_RC4_SHA, TLS_CIPHER_NONE }; data = os_zalloc(sizeof(*data)); if (data == NULL) return NULL; data->fast_version = EAP_FAST_VERSION; data->force_version = -1; if (sm->user && sm->user->force_version >= 0) { data->force_version = sm->user->force_version; wpa_printf(MSG_DEBUG, "EAP-FAST: forcing version %d", data->force_version); data->fast_version = data->force_version; } data->state = START; if (eap_server_tls_ssl_init(sm, &data->ssl, 0)) { wpa_printf(MSG_INFO, "EAP-FAST: Failed to initialize SSL."); eap_fast_reset(sm, data); return NULL; } if (tls_connection_set_cipher_list(sm->ssl_ctx, data->ssl.conn, ciphers) < 0) { wpa_printf(MSG_INFO, "EAP-FAST: Failed to set TLS cipher " "suites"); eap_fast_reset(sm, data); return NULL; } if (tls_connection_set_session_ticket_cb(sm->ssl_ctx, data->ssl.conn, eap_fast_session_ticket_cb, data) < 0) { wpa_printf(MSG_INFO, "EAP-FAST: Failed to set SessionTicket " "callback"); eap_fast_reset(sm, data); return NULL; } if (sm->pac_opaque_encr_key == NULL) { wpa_printf(MSG_INFO, "EAP-FAST: No PAC-Opaque encryption key " "configured"); eap_fast_reset(sm, data); return NULL; } os_memcpy(data->pac_opaque_encr, sm->pac_opaque_encr_key, sizeof(data->pac_opaque_encr)); if (sm->eap_fast_a_id == NULL) { wpa_printf(MSG_INFO, "EAP-FAST: No A-ID configured"); eap_fast_reset(sm, data); return NULL; } data->srv_id = os_malloc(sm->eap_fast_a_id_len); if (data->srv_id == NULL) { eap_fast_reset(sm, data); return NULL; } os_memcpy(data->srv_id, sm->eap_fast_a_id, sm->eap_fast_a_id_len); data->srv_id_len = sm->eap_fast_a_id_len; if (sm->eap_fast_a_id_info == NULL) { wpa_printf(MSG_INFO, "EAP-FAST: No A-ID-Info configured"); eap_fast_reset(sm, data); return NULL; } data->srv_id_info = os_strdup(sm->eap_fast_a_id_info); if (data->srv_id_info == NULL) { eap_fast_reset(sm, data); return NULL; } /* PAC-Key lifetime in seconds (hard limit) */ data->pac_key_lifetime = sm->pac_key_lifetime; /* * 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. */ data->pac_key_refresh_time = sm->pac_key_refresh_time; return data; } static void eap_fast_reset(struct eap_sm *sm, void *priv) { struct eap_fast_data *data = priv; if (data == NULL) return; if (data->phase2_priv && data->phase2_method) data->phase2_method->reset(sm, data->phase2_priv); eap_server_tls_ssl_deinit(sm, &data->ssl); os_free(data->srv_id); os_free(data->srv_id_info); os_free(data->key_block_p); wpabuf_free(data->pending_phase2_resp); os_free(data->identity); os_free(data); } static struct wpabuf * eap_fast_build_start(struct eap_sm *sm, struct eap_fast_data *data, u8 id) { struct wpabuf *req; req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_FAST, 1 + sizeof(struct pac_tlv_hdr) + data->srv_id_len, EAP_CODE_REQUEST, id); if (req == NULL) { wpa_printf(MSG_ERROR, "EAP-FAST: Failed to allocate memory for" " request"); eap_fast_state(data, FAILURE); return NULL; } wpabuf_put_u8(req, EAP_TLS_FLAGS_START | data->fast_version); /* RFC 4851, 4.1.1. Authority ID Data */ eap_fast_put_tlv(req, PAC_TYPE_A_ID, data->srv_id, data->srv_id_len); eap_fast_state(data, PHASE1); return req; } static int eap_fast_phase1_done(struct eap_sm *sm, struct eap_fast_data *data) { char cipher[64]; wpa_printf(MSG_DEBUG, "EAP-FAST: Phase1 done, starting Phase2"); if (tls_get_cipher(sm->ssl_ctx, data->ssl.conn, cipher, sizeof(cipher)) < 0) { wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to get cipher " "information"); eap_fast_state(data, FAILURE); return -1; } data->anon_provisioning = os_strstr(cipher, "ADH") != NULL; if (data->anon_provisioning) { wpa_printf(MSG_DEBUG, "EAP-FAST: Anonymous provisioning"); eap_fast_derive_key_provisioning(sm, data); } else eap_fast_derive_key_auth(sm, data); eap_fast_state(data, PHASE2_START); return 0; } static struct wpabuf * eap_fast_build_phase2_req(struct eap_sm *sm, struct eap_fast_data *data, u8 id) { struct wpabuf *req; if (data->phase2_priv == NULL) { wpa_printf(MSG_DEBUG, "EAP-FAST: Phase 2 method not " "initialized"); return NULL; } req = data->phase2_method->buildReq(sm, data->phase2_priv, id); if (req == NULL) return NULL; wpa_hexdump_buf_key(MSG_MSGDUMP, "EAP-FAST: Phase 2 EAP-Request", req); return eap_fast_tlv_eap_payload(req); } static struct wpabuf * eap_fast_build_crypto_binding( struct eap_sm *sm, struct eap_fast_data *data) { struct wpabuf *buf; struct eap_tlv_result_tlv *result; struct eap_tlv_crypto_binding_tlv *binding; buf = wpabuf_alloc(2 * sizeof(*result) + sizeof(*binding)); if (buf == NULL) return NULL; if (data->send_new_pac || data->anon_provisioning || data->phase2_method) data->final_result = 0; else data->final_result = 1; if (!data->final_result || data->eap_seq > 1) { /* Intermediate-Result */ wpa_printf(MSG_DEBUG, "EAP-FAST: Add Intermediate-Result TLV " "(status=SUCCESS)"); result = wpabuf_put(buf, sizeof(*result)); result->tlv_type = host_to_be16( EAP_TLV_TYPE_MANDATORY | EAP_TLV_INTERMEDIATE_RESULT_TLV); result->length = host_to_be16(2); result->status = host_to_be16(EAP_TLV_RESULT_SUCCESS); } if (data->final_result) { /* Result TLV */ wpa_printf(MSG_DEBUG, "EAP-FAST: Add Result TLV " "(status=SUCCESS)"); result = wpabuf_put(buf, sizeof(*result)); result->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY | EAP_TLV_RESULT_TLV); result->length = host_to_be16(2); result->status = host_to_be16(EAP_TLV_RESULT_SUCCESS); } /* Crypto-Binding TLV */ binding = wpabuf_put(buf, sizeof(*binding)); binding->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY | EAP_TLV_CRYPTO_BINDING_TLV); binding->length = host_to_be16(sizeof(*binding) - sizeof(struct eap_tlv_hdr)); binding->version = EAP_FAST_VERSION; binding->received_version = data->peer_version; binding->subtype = EAP_TLV_CRYPTO_BINDING_SUBTYPE_REQUEST; if (random_get_bytes(binding->nonce, sizeof(binding->nonce)) < 0) { wpabuf_free(buf); return NULL; } /* * RFC 4851, Section 4.2.8: * The nonce in a request MUST have its least significant bit set to 0. */ binding->nonce[sizeof(binding->nonce) - 1] &= ~0x01; os_memcpy(data->crypto_binding_nonce, binding->nonce, sizeof(binding->nonce)); /* * RFC 4851, Section 5.3: * CMK = CMK[j] * Compound-MAC = HMAC-SHA1( CMK, Crypto-Binding TLV ) */ hmac_sha1(data->cmk, EAP_FAST_CMK_LEN, (u8 *) binding, sizeof(*binding), binding->compound_mac); wpa_printf(MSG_DEBUG, "EAP-FAST: Add Crypto-Binding TLV: Version %d " "Received Version %d SubType %d", binding->version, binding->received_version, binding->subtype); wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: NONCE", binding->nonce, sizeof(binding->nonce)); wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Compound MAC", binding->compound_mac, sizeof(binding->compound_mac)); return buf; } static struct wpabuf * eap_fast_build_pac(struct eap_sm *sm, struct eap_fast_data *data) { u8 pac_key[EAP_FAST_PAC_KEY_LEN]; u8 *pac_buf, *pac_opaque; struct wpabuf *buf; u8 *pos; size_t buf_len, srv_id_info_len, pac_len; struct eap_tlv_hdr *pac_tlv; struct pac_tlv_hdr *pac_info; struct eap_tlv_result_tlv *result; struct os_time now; if (random_get_bytes(pac_key, EAP_FAST_PAC_KEY_LEN) < 0 || os_get_time(&now) < 0) return NULL; wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: Generated PAC-Key", pac_key, EAP_FAST_PAC_KEY_LEN); pac_len = (2 + EAP_FAST_PAC_KEY_LEN) + (2 + 4) + (2 + sm->identity_len) + 8; pac_buf = os_malloc(pac_len); if (pac_buf == NULL) return NULL; srv_id_info_len = os_strlen(data->srv_id_info); pos = pac_buf; *pos++ = PAC_OPAQUE_TYPE_KEY; *pos++ = EAP_FAST_PAC_KEY_LEN; os_memcpy(pos, pac_key, EAP_FAST_PAC_KEY_LEN); pos += EAP_FAST_PAC_KEY_LEN; *pos++ = PAC_OPAQUE_TYPE_LIFETIME; *pos++ = 4; WPA_PUT_BE32(pos, now.sec + data->pac_key_lifetime); pos += 4; if (sm->identity) { *pos++ = PAC_OPAQUE_TYPE_IDENTITY; *pos++ = sm->identity_len; os_memcpy(pos, sm->identity, sm->identity_len); pos += sm->identity_len; } pac_len = pos - pac_buf; while (pac_len % 8) { *pos++ = PAC_OPAQUE_TYPE_PAD; pac_len++; } pac_opaque = os_malloc(pac_len + 8); if (pac_opaque == NULL) { os_free(pac_buf); return NULL; } if (aes_wrap(data->pac_opaque_encr, pac_len / 8, pac_buf, pac_opaque) < 0) { os_free(pac_buf); os_free(pac_opaque); return NULL; } os_free(pac_buf); pac_len += 8; wpa_hexdump(MSG_DEBUG, "EAP-FAST: PAC-Opaque", pac_opaque, pac_len); buf_len = sizeof(*pac_tlv) + sizeof(struct pac_tlv_hdr) + EAP_FAST_PAC_KEY_LEN + sizeof(struct pac_tlv_hdr) + pac_len + data->srv_id_len + srv_id_info_len + 100 + sizeof(*result); buf = wpabuf_alloc(buf_len); if (buf == NULL) { os_free(pac_opaque); return NULL; } /* Result TLV */ wpa_printf(MSG_DEBUG, "EAP-FAST: Add Result TLV (status=SUCCESS)"); result = wpabuf_put(buf, sizeof(*result)); WPA_PUT_BE16((u8 *) &result->tlv_type, EAP_TLV_TYPE_MANDATORY | EAP_TLV_RESULT_TLV); WPA_PUT_BE16((u8 *) &result->length, 2); WPA_PUT_BE16((u8 *) &result->status, EAP_TLV_RESULT_SUCCESS); /* PAC TLV */ wpa_printf(MSG_DEBUG, "EAP-FAST: Add PAC TLV"); pac_tlv = wpabuf_put(buf, sizeof(*pac_tlv)); pac_tlv->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY | EAP_TLV_PAC_TLV); /* PAC-Key */ eap_fast_put_tlv(buf, PAC_TYPE_PAC_KEY, pac_key, EAP_FAST_PAC_KEY_LEN); /* PAC-Opaque */ eap_fast_put_tlv(buf, PAC_TYPE_PAC_OPAQUE, pac_opaque, pac_len); os_free(pac_opaque); /* PAC-Info */ pac_info = wpabuf_put(buf, sizeof(*pac_info)); pac_info->type = host_to_be16(PAC_TYPE_PAC_INFO); /* PAC-Lifetime (inside PAC-Info) */ eap_fast_put_tlv_hdr(buf, PAC_TYPE_CRED_LIFETIME, 4); wpabuf_put_be32(buf, now.sec + data->pac_key_lifetime); /* A-ID (inside PAC-Info) */ eap_fast_put_tlv(buf, PAC_TYPE_A_ID, data->srv_id, data->srv_id_len); /* Note: headers may be misaligned after A-ID */ if (sm->identity) { eap_fast_put_tlv(buf, PAC_TYPE_I_ID, sm->identity, sm->identity_len); } /* A-ID-Info (inside PAC-Info) */ eap_fast_put_tlv(buf, PAC_TYPE_A_ID_INFO, data->srv_id_info, srv_id_info_len); /* PAC-Type (inside PAC-Info) */ eap_fast_put_tlv_hdr(buf, PAC_TYPE_PAC_TYPE, 2); wpabuf_put_be16(buf, PAC_TYPE_TUNNEL_PAC); /* Update PAC-Info and PAC TLV Length fields */ pos = wpabuf_put(buf, 0); pac_info->len = host_to_be16(pos - (u8 *) (pac_info + 1)); pac_tlv->length = host_to_be16(pos - (u8 *) (pac_tlv + 1)); return buf; } static int eap_fast_encrypt_phase2(struct eap_sm *sm, struct eap_fast_data *data, struct wpabuf *plain, int piggyback) { struct wpabuf *encr; wpa_hexdump_buf_key(MSG_DEBUG, "EAP-FAST: Encrypting Phase 2 TLVs", plain); encr = eap_server_tls_encrypt(sm, &data->ssl, plain); wpabuf_free(plain); if (data->ssl.tls_out && piggyback) { wpa_printf(MSG_DEBUG, "EAP-FAST: Piggyback Phase 2 data " "(len=%d) with last Phase 1 Message (len=%d " "used=%d)", (int) wpabuf_len(encr), (int) wpabuf_len(data->ssl.tls_out), (int) data->ssl.tls_out_pos); if (wpabuf_resize(&data->ssl.tls_out, wpabuf_len(encr)) < 0) { wpa_printf(MSG_WARNING, "EAP-FAST: Failed to resize " "output buffer"); wpabuf_free(encr); return -1; } wpabuf_put_buf(data->ssl.tls_out, encr); wpabuf_free(encr); } else { wpabuf_free(data->ssl.tls_out); data->ssl.tls_out_pos = 0; data->ssl.tls_out = encr; } return 0; } static struct wpabuf * eap_fast_buildReq(struct eap_sm *sm, void *priv, u8 id) { struct eap_fast_data *data = priv; struct wpabuf *req = NULL; int piggyback = 0; if (data->ssl.state == FRAG_ACK) { return eap_server_tls_build_ack(id, EAP_TYPE_FAST, data->fast_version); } if (data->ssl.state == WAIT_FRAG_ACK) { return eap_server_tls_build_msg(&data->ssl, EAP_TYPE_FAST, data->fast_version, id); } switch (data->state) { case START: return eap_fast_build_start(sm, data, id); case PHASE1: if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) { if (eap_fast_phase1_done(sm, data) < 0) return NULL; if (data->state == PHASE2_START) { /* * Try to generate Phase 2 data to piggyback * with the end of Phase 1 to avoid extra * roundtrip. */ wpa_printf(MSG_DEBUG, "EAP-FAST: Try to start " "Phase 2"); if (eap_fast_process_phase2_start(sm, data)) break; req = eap_fast_build_phase2_req(sm, data, id); piggyback = 1; } } break; case PHASE2_ID: case PHASE2_METHOD: req = eap_fast_build_phase2_req(sm, data, id); break; case CRYPTO_BINDING: req = eap_fast_build_crypto_binding(sm, data); if (data->phase2_method) { /* * Include the start of the next EAP method in the * sequence in the same message with Crypto-Binding to * save a round-trip. */ struct wpabuf *eap; eap = eap_fast_build_phase2_req(sm, data, id); req = wpabuf_concat(req, eap); eap_fast_state(data, PHASE2_METHOD); } break; case REQUEST_PAC: req = eap_fast_build_pac(sm, data); break; default: wpa_printf(MSG_DEBUG, "EAP-FAST: %s - unexpected state %d", __func__, data->state); return NULL; } if (req && eap_fast_encrypt_phase2(sm, data, req, piggyback) < 0) return NULL; return eap_server_tls_build_msg(&data->ssl, EAP_TYPE_FAST, data->fast_version, id); } static Boolean eap_fast_check(struct eap_sm *sm, void *priv, struct wpabuf *respData) { const u8 *pos; size_t len; pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_FAST, respData, &len); if (pos == NULL || len < 1) { wpa_printf(MSG_INFO, "EAP-FAST: Invalid frame"); return TRUE; } return FALSE; } static int eap_fast_phase2_init(struct eap_sm *sm, struct eap_fast_data *data, EapType eap_type) { if (data->phase2_priv && data->phase2_method) { data->phase2_method->reset(sm, data->phase2_priv); data->phase2_method = NULL; data->phase2_priv = NULL; } data->phase2_method = eap_server_get_eap_method(EAP_VENDOR_IETF, eap_type); if (!data->phase2_method) return -1; if (data->key_block_p) { sm->auth_challenge = data->key_block_p->server_challenge; sm->peer_challenge = data->key_block_p->client_challenge; } sm->init_phase2 = 1; data->phase2_priv = data->phase2_method->init(sm); sm->init_phase2 = 0; sm->auth_challenge = NULL; sm->peer_challenge = NULL; return data->phase2_priv == NULL ? -1 : 0; } static void eap_fast_process_phase2_response(struct eap_sm *sm, struct eap_fast_data *data, u8 *in_data, size_t in_len) { u8 next_type = EAP_TYPE_NONE; struct eap_hdr *hdr; u8 *pos; size_t left; struct wpabuf buf; const struct eap_method *m = data->phase2_method; void *priv = data->phase2_priv; if (priv == NULL) { wpa_printf(MSG_DEBUG, "EAP-FAST: %s - Phase2 not " "initialized?!", __func__); return; } hdr = (struct eap_hdr *) in_data; pos = (u8 *) (hdr + 1); if (in_len > sizeof(*hdr) && *pos == EAP_TYPE_NAK) { left = in_len - sizeof(*hdr); wpa_hexdump(MSG_DEBUG, "EAP-FAST: Phase2 type Nak'ed; " "allowed types", pos + 1, left - 1); #ifdef EAP_SERVER_TNC if (m && m->vendor == EAP_VENDOR_IETF && m->method == EAP_TYPE_TNC) { wpa_printf(MSG_DEBUG, "EAP-FAST: Peer Nak'ed required " "TNC negotiation"); next_type = eap_fast_req_failure(sm, data); eap_fast_phase2_init(sm, data, next_type); return; } #endif /* EAP_SERVER_TNC */ eap_sm_process_nak(sm, pos + 1, left - 1); if (sm->user && sm->user_eap_method_index < EAP_MAX_METHODS && sm->user->methods[sm->user_eap_method_index].method != EAP_TYPE_NONE) { next_type = sm->user->methods[ sm->user_eap_method_index++].method; wpa_printf(MSG_DEBUG, "EAP-FAST: try EAP type %d", next_type); } else { next_type = eap_fast_req_failure(sm, data); } eap_fast_phase2_init(sm, data, next_type); return; } wpabuf_set(&buf, in_data, in_len); if (m->check(sm, priv, &buf)) { wpa_printf(MSG_DEBUG, "EAP-FAST: Phase2 check() asked to " "ignore the packet"); next_type = eap_fast_req_failure(sm, data); return; } m->process(sm, priv, &buf); if (!m->isDone(sm, priv)) return; if (!m->isSuccess(sm, priv)) { wpa_printf(MSG_DEBUG, "EAP-FAST: Phase2 method failed"); next_type = eap_fast_req_failure(sm, data); eap_fast_phase2_init(sm, data, next_type); return; } switch (data->state) { case PHASE2_ID: if (eap_user_get(sm, sm->identity, sm->identity_len, 1) != 0) { wpa_hexdump_ascii(MSG_DEBUG, "EAP-FAST: Phase2 " "Identity not found in the user " "database", sm->identity, sm->identity_len); next_type = eap_fast_req_failure(sm, data); break; } eap_fast_state(data, PHASE2_METHOD); if (data->anon_provisioning) { /* * Only EAP-MSCHAPv2 is allowed for anonymous * provisioning. */ next_type = EAP_TYPE_MSCHAPV2; sm->user_eap_method_index = 0; } else { next_type = sm->user->methods[0].method; sm->user_eap_method_index = 1; } wpa_printf(MSG_DEBUG, "EAP-FAST: try EAP type %d", next_type); break; case PHASE2_METHOD: case CRYPTO_BINDING: eap_fast_update_icmk(sm, data); eap_fast_state(data, CRYPTO_BINDING); data->eap_seq++; next_type = EAP_TYPE_NONE; #ifdef EAP_SERVER_TNC if (sm->tnc && !data->tnc_started) { wpa_printf(MSG_DEBUG, "EAP-FAST: Initialize TNC"); next_type = EAP_TYPE_TNC; data->tnc_started = 1; } #endif /* EAP_SERVER_TNC */ break; case FAILURE: break; default: wpa_printf(MSG_DEBUG, "EAP-FAST: %s - unexpected state %d", __func__, data->state); break; } eap_fast_phase2_init(sm, data, next_type); } static void eap_fast_process_phase2_eap(struct eap_sm *sm, struct eap_fast_data *data, u8 *in_data, size_t in_len) { struct eap_hdr *hdr; size_t len; hdr = (struct eap_hdr *) in_data; if (in_len < (int) sizeof(*hdr)) { wpa_printf(MSG_INFO, "EAP-FAST: Too short Phase 2 " "EAP frame (len=%lu)", (unsigned long) in_len); eap_fast_req_failure(sm, data); return; } len = be_to_host16(hdr->length); if (len > in_len) { wpa_printf(MSG_INFO, "EAP-FAST: Length mismatch in " "Phase 2 EAP frame (len=%lu hdr->length=%lu)", (unsigned long) in_len, (unsigned long) len); eap_fast_req_failure(sm, data); return; } wpa_printf(MSG_DEBUG, "EAP-FAST: Received Phase 2: code=%d " "identifier=%d length=%lu", hdr->code, hdr->identifier, (unsigned long) len); switch (hdr->code) { case EAP_CODE_RESPONSE: eap_fast_process_phase2_response(sm, data, (u8 *) hdr, len); break; default: wpa_printf(MSG_INFO, "EAP-FAST: Unexpected code=%d in " "Phase 2 EAP header", hdr->code); break; } } static int eap_fast_parse_tlvs(struct wpabuf *data, struct eap_fast_tlv_parse *tlv) { int mandatory, tlv_type, len, res; u8 *pos, *end; os_memset(tlv, 0, sizeof(*tlv)); pos = wpabuf_mhead(data); end = pos + wpabuf_len(data); while (pos + 4 < end) { mandatory = pos[0] & 0x80; tlv_type = WPA_GET_BE16(pos) & 0x3fff; pos += 2; len = WPA_GET_BE16(pos); pos += 2; if (pos + len > end) { wpa_printf(MSG_INFO, "EAP-FAST: TLV overflow"); return -1; } wpa_printf(MSG_DEBUG, "EAP-FAST: Received Phase 2: " "TLV type %d length %d%s", tlv_type, len, mandatory ? " (mandatory)" : ""); res = eap_fast_parse_tlv(tlv, tlv_type, pos, len); if (res == -2) break; if (res < 0) { if (mandatory) { wpa_printf(MSG_DEBUG, "EAP-FAST: Nak unknown " "mandatory TLV type %d", tlv_type); /* TODO: generate Nak TLV */ break; } else { wpa_printf(MSG_DEBUG, "EAP-FAST: Ignored " "unknown optional TLV type %d", tlv_type); } } pos += len; } return 0; } static int eap_fast_validate_crypto_binding( struct eap_fast_data *data, struct eap_tlv_crypto_binding_tlv *b, size_t bind_len) { u8 cmac[SHA1_MAC_LEN]; wpa_printf(MSG_DEBUG, "EAP-FAST: Reply Crypto-Binding TLV: " "Version %d Received Version %d SubType %d", b->version, b->received_version, b->subtype); wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: NONCE", b->nonce, sizeof(b->nonce)); wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Compound MAC", b->compound_mac, sizeof(b->compound_mac)); if (b->version != EAP_FAST_VERSION || b->received_version != EAP_FAST_VERSION) { wpa_printf(MSG_DEBUG, "EAP-FAST: Unexpected version " "in Crypto-Binding: version %d " "received_version %d", b->version, b->received_version); return -1; } if (b->subtype != EAP_TLV_CRYPTO_BINDING_SUBTYPE_RESPONSE) { wpa_printf(MSG_DEBUG, "EAP-FAST: Unexpected subtype in " "Crypto-Binding: %d", b->subtype); return -1; } if (os_memcmp(data->crypto_binding_nonce, b->nonce, 31) != 0 || (data->crypto_binding_nonce[31] | 1) != b->nonce[31]) { wpa_printf(MSG_DEBUG, "EAP-FAST: Invalid nonce in " "Crypto-Binding"); return -1; } os_memcpy(cmac, b->compound_mac, sizeof(cmac)); os_memset(b->compound_mac, 0, sizeof(cmac)); wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Crypto-Binding TLV for " "Compound MAC calculation", (u8 *) b, bind_len); hmac_sha1(data->cmk, EAP_FAST_CMK_LEN, (u8 *) b, bind_len, b->compound_mac); if (os_memcmp(cmac, b->compound_mac, sizeof(cmac)) != 0) { wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Calculated Compound MAC", b->compound_mac, sizeof(cmac)); wpa_printf(MSG_INFO, "EAP-FAST: Compound MAC did not " "match"); return -1; } return 0; } static int eap_fast_pac_type(u8 *pac, size_t len, u16 type) { struct eap_tlv_pac_type_tlv *tlv; if (pac == NULL || len != sizeof(*tlv)) return 0; tlv = (struct eap_tlv_pac_type_tlv *) pac; return be_to_host16(tlv->tlv_type) == PAC_TYPE_PAC_TYPE && be_to_host16(tlv->length) == 2 && be_to_host16(tlv->pac_type) == type; } static void eap_fast_process_phase2_tlvs(struct eap_sm *sm, struct eap_fast_data *data, struct wpabuf *in_data) { struct eap_fast_tlv_parse tlv; int check_crypto_binding = data->state == CRYPTO_BINDING; if (eap_fast_parse_tlvs(in_data, &tlv) < 0) { wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to parse received " "Phase 2 TLVs"); return; } if (tlv.result == EAP_TLV_RESULT_FAILURE) { wpa_printf(MSG_DEBUG, "EAP-FAST: Result TLV indicated " "failure"); eap_fast_state(data, FAILURE); return; } if (data->state == REQUEST_PAC) { u16 type, len, res; if (tlv.pac == NULL || tlv.pac_len < 6) { wpa_printf(MSG_DEBUG, "EAP-FAST: No PAC " "Acknowledgement received"); eap_fast_state(data, FAILURE); return; } type = WPA_GET_BE16(tlv.pac); len = WPA_GET_BE16(tlv.pac + 2); res = WPA_GET_BE16(tlv.pac + 4); if (type != PAC_TYPE_PAC_ACKNOWLEDGEMENT || len != 2 || res != EAP_TLV_RESULT_SUCCESS) { wpa_printf(MSG_DEBUG, "EAP-FAST: PAC TLV did not " "contain acknowledgement"); eap_fast_state(data, FAILURE); return; } wpa_printf(MSG_DEBUG, "EAP-FAST: PAC-Acknowledgement received " "- PAC provisioning succeeded"); eap_fast_state(data, (data->anon_provisioning || data->send_new_pac == 2) ? FAILURE : SUCCESS); return; } if (check_crypto_binding) { if (tlv.crypto_binding == NULL) { wpa_printf(MSG_DEBUG, "EAP-FAST: No Crypto-Binding " "TLV received"); eap_fast_state(data, FAILURE); return; } if (data->final_result && tlv.result != EAP_TLV_RESULT_SUCCESS) { wpa_printf(MSG_DEBUG, "EAP-FAST: Crypto-Binding TLV " "without Success Result"); eap_fast_state(data, FAILURE); return; } if (!data->final_result && tlv.iresult != EAP_TLV_RESULT_SUCCESS) { wpa_printf(MSG_DEBUG, "EAP-FAST: Crypto-Binding TLV " "without intermediate Success Result"); eap_fast_state(data, FAILURE); return; } if (eap_fast_validate_crypto_binding(data, tlv.crypto_binding, tlv.crypto_binding_len)) { eap_fast_state(data, FAILURE); return; } wpa_printf(MSG_DEBUG, "EAP-FAST: Valid Crypto-Binding TLV " "received"); if (data->final_result) { wpa_printf(MSG_DEBUG, "EAP-FAST: Authentication " "completed successfully"); } if (data->anon_provisioning && sm->eap_fast_prov != ANON_PROV && sm->eap_fast_prov != BOTH_PROV) { wpa_printf(MSG_DEBUG, "EAP-FAST: Client is trying to " "use unauthenticated provisioning which is " "disabled"); eap_fast_state(data, FAILURE); return; } if (sm->eap_fast_prov != AUTH_PROV && sm->eap_fast_prov != BOTH_PROV && tlv.request_action == EAP_TLV_ACTION_PROCESS_TLV && eap_fast_pac_type(tlv.pac, tlv.pac_len, PAC_TYPE_TUNNEL_PAC)) { wpa_printf(MSG_DEBUG, "EAP-FAST: Client is trying to " "use authenticated provisioning which is " "disabled"); eap_fast_state(data, FAILURE); return; } if (data->anon_provisioning || (tlv.request_action == EAP_TLV_ACTION_PROCESS_TLV && eap_fast_pac_type(tlv.pac, tlv.pac_len, PAC_TYPE_TUNNEL_PAC))) { wpa_printf(MSG_DEBUG, "EAP-FAST: Requested a new " "Tunnel PAC"); eap_fast_state(data, REQUEST_PAC); } else if (data->send_new_pac) { wpa_printf(MSG_DEBUG, "EAP-FAST: Server triggered " "re-keying of Tunnel PAC"); eap_fast_state(data, REQUEST_PAC); } else if (data->final_result) eap_fast_state(data, SUCCESS); } if (tlv.eap_payload_tlv) { eap_fast_process_phase2_eap(sm, data, tlv.eap_payload_tlv, tlv.eap_payload_tlv_len); } } static void eap_fast_process_phase2(struct eap_sm *sm, struct eap_fast_data *data, struct wpabuf *in_buf) { struct wpabuf *in_decrypted; wpa_printf(MSG_DEBUG, "EAP-FAST: Received %lu bytes encrypted data for" " Phase 2", (unsigned long) wpabuf_len(in_buf)); if (data->pending_phase2_resp) { wpa_printf(MSG_DEBUG, "EAP-PEAP: Pending Phase 2 response - " "skip decryption and use old data"); eap_fast_process_phase2_tlvs(sm, data, data->pending_phase2_resp); wpabuf_free(data->pending_phase2_resp); data->pending_phase2_resp = NULL; return; } in_decrypted = tls_connection_decrypt(sm->ssl_ctx, data->ssl.conn, in_buf); if (in_decrypted == NULL) { wpa_printf(MSG_INFO, "EAP-FAST: Failed to decrypt Phase 2 " "data"); eap_fast_state(data, FAILURE); return; } wpa_hexdump_buf_key(MSG_DEBUG, "EAP-FAST: Decrypted Phase 2 TLVs", in_decrypted); eap_fast_process_phase2_tlvs(sm, data, in_decrypted); if (sm->method_pending == METHOD_PENDING_WAIT) { wpa_printf(MSG_DEBUG, "EAP-FAST: Phase2 method is in " "pending wait state - save decrypted response"); wpabuf_free(data->pending_phase2_resp); data->pending_phase2_resp = in_decrypted; return; } wpabuf_free(in_decrypted); } static int eap_fast_process_version(struct eap_sm *sm, void *priv, int peer_version) { struct eap_fast_data *data = priv; data->peer_version = peer_version; if (data->force_version >= 0 && peer_version != data->force_version) { wpa_printf(MSG_INFO, "EAP-FAST: peer did not select the forced" " version (forced=%d peer=%d) - reject", data->force_version, peer_version); return -1; } if (peer_version < data->fast_version) { wpa_printf(MSG_DEBUG, "EAP-FAST: peer ver=%d, own ver=%d; " "use version %d", peer_version, data->fast_version, peer_version); data->fast_version = peer_version; } return 0; } static int eap_fast_process_phase1(struct eap_sm *sm, struct eap_fast_data *data) { if (eap_server_tls_phase1(sm, &data->ssl) < 0) { wpa_printf(MSG_INFO, "EAP-FAST: TLS processing failed"); eap_fast_state(data, FAILURE); return -1; } if (!tls_connection_established(sm->ssl_ctx, data->ssl.conn) || wpabuf_len(data->ssl.tls_out) > 0) return 1; /* * Phase 1 was completed with the received message (e.g., when using * abbreviated handshake), so Phase 2 can be started immediately * without having to send through an empty message to the peer. */ return eap_fast_phase1_done(sm, data); } static int eap_fast_process_phase2_start(struct eap_sm *sm, struct eap_fast_data *data) { u8 next_type; if (data->identity) { os_free(sm->identity); sm->identity = data->identity; data->identity = NULL; sm->identity_len = data->identity_len; data->identity_len = 0; sm->require_identity_match = 1; if (eap_user_get(sm, sm->identity, sm->identity_len, 1) != 0) { wpa_hexdump_ascii(MSG_DEBUG, "EAP-FAST: " "Phase2 Identity not found " "in the user database", sm->identity, sm->identity_len); next_type = eap_fast_req_failure(sm, data); } else { wpa_printf(MSG_DEBUG, "EAP-FAST: Identity already " "known - skip Phase 2 Identity Request"); next_type = sm->user->methods[0].method; sm->user_eap_method_index = 1; } eap_fast_state(data, PHASE2_METHOD); } else { eap_fast_state(data, PHASE2_ID); next_type = EAP_TYPE_IDENTITY; } return eap_fast_phase2_init(sm, data, next_type); } static void eap_fast_process_msg(struct eap_sm *sm, void *priv, const struct wpabuf *respData) { struct eap_fast_data *data = priv; switch (data->state) { case PHASE1: if (eap_fast_process_phase1(sm, data)) break; /* fall through to PHASE2_START */ case PHASE2_START: eap_fast_process_phase2_start(sm, data); break; case PHASE2_ID: case PHASE2_METHOD: case CRYPTO_BINDING: case REQUEST_PAC: eap_fast_process_phase2(sm, data, data->ssl.tls_in); break; default: wpa_printf(MSG_DEBUG, "EAP-FAST: Unexpected state %d in %s", data->state, __func__); break; } } static void eap_fast_process(struct eap_sm *sm, void *priv, struct wpabuf *respData) { struct eap_fast_data *data = priv; if (eap_server_tls_process(sm, &data->ssl, respData, data, EAP_TYPE_FAST, eap_fast_process_version, eap_fast_process_msg) < 0) eap_fast_state(data, FAILURE); } static Boolean eap_fast_isDone(struct eap_sm *sm, void *priv) { struct eap_fast_data *data = priv; return data->state == SUCCESS || data->state == FAILURE; } static u8 * eap_fast_getKey(struct eap_sm *sm, void *priv, size_t *len) { struct eap_fast_data *data = priv; u8 *eapKeyData; if (data->state != SUCCESS) return NULL; eapKeyData = os_malloc(EAP_FAST_KEY_LEN); if (eapKeyData == NULL) return NULL; eap_fast_derive_eap_msk(data->simck, eapKeyData); *len = EAP_FAST_KEY_LEN; return eapKeyData; } static u8 * eap_fast_get_emsk(struct eap_sm *sm, void *priv, size_t *len) { struct eap_fast_data *data = priv; u8 *eapKeyData; if (data->state != SUCCESS) return NULL; eapKeyData = os_malloc(EAP_EMSK_LEN); if (eapKeyData == NULL) return NULL; eap_fast_derive_eap_emsk(data->simck, eapKeyData); *len = EAP_EMSK_LEN; return eapKeyData; } static Boolean eap_fast_isSuccess(struct eap_sm *sm, void *priv) { struct eap_fast_data *data = priv; return data->state == SUCCESS; } int eap_server_fast_register(void) { struct eap_method *eap; int ret; eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_FAST, "FAST"); if (eap == NULL) return -1; eap->init = eap_fast_init; eap->reset = eap_fast_reset; eap->buildReq = eap_fast_buildReq; eap->check = eap_fast_check; eap->process = eap_fast_process; eap->isDone = eap_fast_isDone; eap->getKey = eap_fast_getKey; eap->get_emsk = eap_fast_get_emsk; eap->isSuccess = eap_fast_isSuccess; ret = eap_server_method_register(eap); if (ret) eap_server_method_free(eap); return ret; } wpa-2.1/src/eap_server/eap_server_gpsk.c000066400000000000000000000370551227414666700204410ustar00rootroot00000000000000/* * hostapd / EAP-GPSK (RFC 5433) server * Copyright (c) 2006-2007, 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/random.h" #include "eap_server/eap_i.h" #include "eap_common/eap_gpsk_common.h" struct eap_gpsk_data { enum { GPSK_1, GPSK_3, SUCCESS, FAILURE } state; u8 rand_server[EAP_GPSK_RAND_LEN]; u8 rand_peer[EAP_GPSK_RAND_LEN]; u8 msk[EAP_MSK_LEN]; u8 emsk[EAP_EMSK_LEN]; u8 sk[EAP_GPSK_MAX_SK_LEN]; size_t sk_len; u8 pk[EAP_GPSK_MAX_PK_LEN]; size_t pk_len; u8 *id_peer; size_t id_peer_len; #define MAX_NUM_CSUITES 2 struct eap_gpsk_csuite csuite_list[MAX_NUM_CSUITES]; size_t csuite_count; int vendor; /* CSuite/Vendor */ int specifier; /* CSuite/Specifier */ }; static const char * eap_gpsk_state_txt(int state) { switch (state) { case GPSK_1: return "GPSK-1"; case GPSK_3: return "GPSK-3"; case SUCCESS: return "SUCCESS"; case FAILURE: return "FAILURE"; default: return "?"; } } static void eap_gpsk_state(struct eap_gpsk_data *data, int state) { wpa_printf(MSG_DEBUG, "EAP-GPSK: %s -> %s", eap_gpsk_state_txt(data->state), eap_gpsk_state_txt(state)); data->state = state; } static void * eap_gpsk_init(struct eap_sm *sm) { struct eap_gpsk_data *data; data = os_zalloc(sizeof(*data)); if (data == NULL) return NULL; data->state = GPSK_1; data->csuite_count = 0; if (eap_gpsk_supported_ciphersuite(EAP_GPSK_VENDOR_IETF, EAP_GPSK_CIPHER_AES)) { WPA_PUT_BE32(data->csuite_list[data->csuite_count].vendor, EAP_GPSK_VENDOR_IETF); WPA_PUT_BE16(data->csuite_list[data->csuite_count].specifier, EAP_GPSK_CIPHER_AES); data->csuite_count++; } if (eap_gpsk_supported_ciphersuite(EAP_GPSK_VENDOR_IETF, EAP_GPSK_CIPHER_SHA256)) { WPA_PUT_BE32(data->csuite_list[data->csuite_count].vendor, EAP_GPSK_VENDOR_IETF); WPA_PUT_BE16(data->csuite_list[data->csuite_count].specifier, EAP_GPSK_CIPHER_SHA256); data->csuite_count++; } return data; } static void eap_gpsk_reset(struct eap_sm *sm, void *priv) { struct eap_gpsk_data *data = priv; os_free(data->id_peer); os_free(data); } static struct wpabuf * eap_gpsk_build_gpsk_1(struct eap_sm *sm, struct eap_gpsk_data *data, u8 id) { size_t len; struct wpabuf *req; wpa_printf(MSG_DEBUG, "EAP-GPSK: Request/GPSK-1"); if (random_get_bytes(data->rand_server, EAP_GPSK_RAND_LEN)) { wpa_printf(MSG_ERROR, "EAP-GPSK: Failed to get random data"); eap_gpsk_state(data, FAILURE); return NULL; } wpa_hexdump(MSG_MSGDUMP, "EAP-GPSK: RAND_Server", data->rand_server, EAP_GPSK_RAND_LEN); len = 1 + 2 + sm->server_id_len + EAP_GPSK_RAND_LEN + 2 + data->csuite_count * sizeof(struct eap_gpsk_csuite); req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GPSK, len, EAP_CODE_REQUEST, id); if (req == NULL) { wpa_printf(MSG_ERROR, "EAP-GPSK: Failed to allocate memory " "for request/GPSK-1"); eap_gpsk_state(data, FAILURE); return NULL; } wpabuf_put_u8(req, EAP_GPSK_OPCODE_GPSK_1); wpabuf_put_be16(req, sm->server_id_len); wpabuf_put_data(req, sm->server_id, sm->server_id_len); wpabuf_put_data(req, data->rand_server, EAP_GPSK_RAND_LEN); wpabuf_put_be16(req, data->csuite_count * sizeof(struct eap_gpsk_csuite)); wpabuf_put_data(req, data->csuite_list, data->csuite_count * sizeof(struct eap_gpsk_csuite)); return req; } static struct wpabuf * eap_gpsk_build_gpsk_3(struct eap_sm *sm, struct eap_gpsk_data *data, u8 id) { u8 *pos, *start; size_t len, miclen; struct eap_gpsk_csuite *csuite; struct wpabuf *req; wpa_printf(MSG_DEBUG, "EAP-GPSK: Request/GPSK-3"); miclen = eap_gpsk_mic_len(data->vendor, data->specifier); len = 1 + 2 * EAP_GPSK_RAND_LEN + 2 + sm->server_id_len + sizeof(struct eap_gpsk_csuite) + 2 + miclen; req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GPSK, len, EAP_CODE_REQUEST, id); if (req == NULL) { wpa_printf(MSG_ERROR, "EAP-GPSK: Failed to allocate memory " "for request/GPSK-3"); eap_gpsk_state(data, FAILURE); return NULL; } wpabuf_put_u8(req, EAP_GPSK_OPCODE_GPSK_3); start = wpabuf_put(req, 0); wpabuf_put_data(req, data->rand_peer, EAP_GPSK_RAND_LEN); wpabuf_put_data(req, data->rand_server, EAP_GPSK_RAND_LEN); wpabuf_put_be16(req, sm->server_id_len); wpabuf_put_data(req, sm->server_id, sm->server_id_len); csuite = wpabuf_put(req, sizeof(*csuite)); WPA_PUT_BE32(csuite->vendor, data->vendor); WPA_PUT_BE16(csuite->specifier, data->specifier); /* no PD_Payload_2 */ wpabuf_put_be16(req, 0); pos = wpabuf_put(req, miclen); if (eap_gpsk_compute_mic(data->sk, data->sk_len, data->vendor, data->specifier, start, pos - start, pos) < 0) { os_free(req); eap_gpsk_state(data, FAILURE); return NULL; } return req; } static struct wpabuf * eap_gpsk_buildReq(struct eap_sm *sm, void *priv, u8 id) { struct eap_gpsk_data *data = priv; switch (data->state) { case GPSK_1: return eap_gpsk_build_gpsk_1(sm, data, id); case GPSK_3: return eap_gpsk_build_gpsk_3(sm, data, id); default: wpa_printf(MSG_DEBUG, "EAP-GPSK: Unknown state %d in buildReq", data->state); break; } return NULL; } static Boolean eap_gpsk_check(struct eap_sm *sm, void *priv, struct wpabuf *respData) { struct eap_gpsk_data *data = priv; const u8 *pos; size_t len; pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_GPSK, respData, &len); if (pos == NULL || len < 1) { wpa_printf(MSG_INFO, "EAP-GPSK: Invalid frame"); return TRUE; } wpa_printf(MSG_DEBUG, "EAP-GPSK: Received frame: opcode=%d", *pos); if (data->state == GPSK_1 && *pos == EAP_GPSK_OPCODE_GPSK_2) return FALSE; if (data->state == GPSK_3 && *pos == EAP_GPSK_OPCODE_GPSK_4) return FALSE; wpa_printf(MSG_INFO, "EAP-GPSK: Unexpected opcode=%d in state=%d", *pos, data->state); return TRUE; } static void eap_gpsk_process_gpsk_2(struct eap_sm *sm, struct eap_gpsk_data *data, const u8 *payload, size_t payloadlen) { const u8 *pos, *end; u16 alen; const struct eap_gpsk_csuite *csuite; size_t i, miclen; u8 mic[EAP_GPSK_MAX_MIC_LEN]; if (data->state != GPSK_1) return; wpa_printf(MSG_DEBUG, "EAP-GPSK: Received Response/GPSK-2"); pos = payload; end = payload + payloadlen; if (end - pos < 2) { wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for " "ID_Peer length"); eap_gpsk_state(data, FAILURE); return; } alen = WPA_GET_BE16(pos); pos += 2; if (end - pos < alen) { wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for " "ID_Peer"); eap_gpsk_state(data, FAILURE); return; } os_free(data->id_peer); data->id_peer = os_malloc(alen); if (data->id_peer == NULL) { wpa_printf(MSG_DEBUG, "EAP-GPSK: Not enough memory to store " "%d-octet ID_Peer", alen); return; } os_memcpy(data->id_peer, pos, alen); data->id_peer_len = alen; wpa_hexdump_ascii(MSG_DEBUG, "EAP-GPSK: ID_Peer", data->id_peer, data->id_peer_len); pos += alen; if (end - pos < 2) { wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for " "ID_Server length"); eap_gpsk_state(data, FAILURE); return; } alen = WPA_GET_BE16(pos); pos += 2; if (end - pos < alen) { wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for " "ID_Server"); eap_gpsk_state(data, FAILURE); return; } if (alen != sm->server_id_len || os_memcmp(pos, sm->server_id, alen) != 0) { wpa_printf(MSG_DEBUG, "EAP-GPSK: ID_Server in GPSK-1 and " "GPSK-2 did not match"); eap_gpsk_state(data, FAILURE); return; } pos += alen; if (end - pos < EAP_GPSK_RAND_LEN) { wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for " "RAND_Peer"); eap_gpsk_state(data, FAILURE); return; } os_memcpy(data->rand_peer, pos, EAP_GPSK_RAND_LEN); wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Peer", data->rand_peer, EAP_GPSK_RAND_LEN); pos += EAP_GPSK_RAND_LEN; if (end - pos < EAP_GPSK_RAND_LEN) { wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for " "RAND_Server"); eap_gpsk_state(data, FAILURE); return; } if (os_memcmp(data->rand_server, pos, EAP_GPSK_RAND_LEN) != 0) { wpa_printf(MSG_DEBUG, "EAP-GPSK: RAND_Server in GPSK-1 and " "GPSK-2 did not match"); wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Server in GPSK-1", data->rand_server, EAP_GPSK_RAND_LEN); wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Server in GPSK-2", pos, EAP_GPSK_RAND_LEN); eap_gpsk_state(data, FAILURE); return; } pos += EAP_GPSK_RAND_LEN; if (end - pos < 2) { wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for " "CSuite_List length"); eap_gpsk_state(data, FAILURE); return; } alen = WPA_GET_BE16(pos); pos += 2; if (end - pos < alen) { wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for " "CSuite_List"); eap_gpsk_state(data, FAILURE); return; } if (alen != data->csuite_count * sizeof(struct eap_gpsk_csuite) || os_memcmp(pos, data->csuite_list, alen) != 0) { wpa_printf(MSG_DEBUG, "EAP-GPSK: CSuite_List in GPSK-1 and " "GPSK-2 did not match"); eap_gpsk_state(data, FAILURE); return; } pos += alen; if (end - pos < (int) sizeof(*csuite)) { wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for " "CSuite_Sel"); eap_gpsk_state(data, FAILURE); return; } csuite = (const struct eap_gpsk_csuite *) pos; for (i = 0; i < data->csuite_count; i++) { if (os_memcmp(csuite, &data->csuite_list[i], sizeof(*csuite)) == 0) break; } if (i == data->csuite_count) { wpa_printf(MSG_DEBUG, "EAP-GPSK: Peer selected unsupported " "ciphersuite %d:%d", WPA_GET_BE32(csuite->vendor), WPA_GET_BE16(csuite->specifier)); eap_gpsk_state(data, FAILURE); return; } data->vendor = WPA_GET_BE32(csuite->vendor); data->specifier = WPA_GET_BE16(csuite->specifier); wpa_printf(MSG_DEBUG, "EAP-GPSK: CSuite_Sel %d:%d", data->vendor, data->specifier); pos += sizeof(*csuite); if (end - pos < 2) { wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for " "PD_Payload_1 length"); eap_gpsk_state(data, FAILURE); return; } alen = WPA_GET_BE16(pos); pos += 2; if (end - pos < alen) { wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for " "PD_Payload_1"); eap_gpsk_state(data, FAILURE); return; } wpa_hexdump(MSG_DEBUG, "EAP-GPSK: PD_Payload_1", pos, alen); pos += alen; if (sm->user == NULL || sm->user->password == NULL) { wpa_printf(MSG_INFO, "EAP-GPSK: No PSK/password configured " "for the user"); eap_gpsk_state(data, FAILURE); return; } if (eap_gpsk_derive_keys(sm->user->password, sm->user->password_len, data->vendor, data->specifier, data->rand_peer, data->rand_server, data->id_peer, data->id_peer_len, sm->server_id, sm->server_id_len, data->msk, data->emsk, data->sk, &data->sk_len, data->pk, &data->pk_len) < 0) { wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to derive keys"); eap_gpsk_state(data, FAILURE); return; } miclen = eap_gpsk_mic_len(data->vendor, data->specifier); if (end - pos < (int) miclen) { wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for MIC " "(left=%lu miclen=%lu)", (unsigned long) (end - pos), (unsigned long) miclen); eap_gpsk_state(data, FAILURE); return; } if (eap_gpsk_compute_mic(data->sk, data->sk_len, data->vendor, data->specifier, payload, pos - payload, mic) < 0) { wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to compute MIC"); eap_gpsk_state(data, FAILURE); return; } if (os_memcmp(mic, pos, miclen) != 0) { wpa_printf(MSG_INFO, "EAP-GPSK: Incorrect MIC in GPSK-2"); wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Received MIC", pos, miclen); wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Computed MIC", mic, miclen); eap_gpsk_state(data, FAILURE); return; } pos += miclen; if (pos != end) { wpa_printf(MSG_DEBUG, "EAP-GPSK: Ignored %lu bytes of extra " "data in the end of GPSK-2", (unsigned long) (end - pos)); } eap_gpsk_state(data, GPSK_3); } static void eap_gpsk_process_gpsk_4(struct eap_sm *sm, struct eap_gpsk_data *data, const u8 *payload, size_t payloadlen) { const u8 *pos, *end; u16 alen; size_t miclen; u8 mic[EAP_GPSK_MAX_MIC_LEN]; if (data->state != GPSK_3) return; wpa_printf(MSG_DEBUG, "EAP-GPSK: Received Response/GPSK-4"); pos = payload; end = payload + payloadlen; if (end - pos < 2) { wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for " "PD_Payload_1 length"); eap_gpsk_state(data, FAILURE); return; } alen = WPA_GET_BE16(pos); pos += 2; if (end - pos < alen) { wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for " "PD_Payload_1"); eap_gpsk_state(data, FAILURE); return; } wpa_hexdump(MSG_DEBUG, "EAP-GPSK: PD_Payload_1", pos, alen); pos += alen; miclen = eap_gpsk_mic_len(data->vendor, data->specifier); if (end - pos < (int) miclen) { wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for MIC " "(left=%lu miclen=%lu)", (unsigned long) (end - pos), (unsigned long) miclen); eap_gpsk_state(data, FAILURE); return; } if (eap_gpsk_compute_mic(data->sk, data->sk_len, data->vendor, data->specifier, payload, pos - payload, mic) < 0) { wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to compute MIC"); eap_gpsk_state(data, FAILURE); return; } if (os_memcmp(mic, pos, miclen) != 0) { wpa_printf(MSG_INFO, "EAP-GPSK: Incorrect MIC in GPSK-4"); wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Received MIC", pos, miclen); wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Computed MIC", mic, miclen); eap_gpsk_state(data, FAILURE); return; } pos += miclen; if (pos != end) { wpa_printf(MSG_DEBUG, "EAP-GPSK: Ignored %lu bytes of extra " "data in the end of GPSK-4", (unsigned long) (end - pos)); } eap_gpsk_state(data, SUCCESS); } static void eap_gpsk_process(struct eap_sm *sm, void *priv, struct wpabuf *respData) { struct eap_gpsk_data *data = priv; const u8 *pos; size_t len; pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_GPSK, respData, &len); if (pos == NULL || len < 1) return; switch (*pos) { case EAP_GPSK_OPCODE_GPSK_2: eap_gpsk_process_gpsk_2(sm, data, pos + 1, len - 1); break; case EAP_GPSK_OPCODE_GPSK_4: eap_gpsk_process_gpsk_4(sm, data, pos + 1, len - 1); break; } } static Boolean eap_gpsk_isDone(struct eap_sm *sm, void *priv) { struct eap_gpsk_data *data = priv; return data->state == SUCCESS || data->state == FAILURE; } static u8 * eap_gpsk_getKey(struct eap_sm *sm, void *priv, size_t *len) { struct eap_gpsk_data *data = priv; u8 *key; if (data->state != SUCCESS) return NULL; key = os_malloc(EAP_MSK_LEN); if (key == NULL) return NULL; os_memcpy(key, data->msk, EAP_MSK_LEN); *len = EAP_MSK_LEN; return key; } static u8 * eap_gpsk_get_emsk(struct eap_sm *sm, void *priv, size_t *len) { struct eap_gpsk_data *data = priv; u8 *key; if (data->state != SUCCESS) return NULL; key = os_malloc(EAP_EMSK_LEN); if (key == NULL) return NULL; os_memcpy(key, data->emsk, EAP_EMSK_LEN); *len = EAP_EMSK_LEN; return key; } static Boolean eap_gpsk_isSuccess(struct eap_sm *sm, void *priv) { struct eap_gpsk_data *data = priv; return data->state == SUCCESS; } int eap_server_gpsk_register(void) { struct eap_method *eap; int ret; eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_GPSK, "GPSK"); if (eap == NULL) return -1; eap->init = eap_gpsk_init; eap->reset = eap_gpsk_reset; eap->buildReq = eap_gpsk_buildReq; eap->check = eap_gpsk_check; eap->process = eap_gpsk_process; eap->isDone = eap_gpsk_isDone; eap->getKey = eap_gpsk_getKey; eap->isSuccess = eap_gpsk_isSuccess; eap->get_emsk = eap_gpsk_get_emsk; ret = eap_server_method_register(eap); if (ret) eap_server_method_free(eap); return ret; } wpa-2.1/src/eap_server/eap_server_gtc.c000066400000000000000000000120241227414666700202370ustar00rootroot00000000000000/* * hostapd / EAP-GTC (RFC 3748) * Copyright (c) 2004-2006, 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_i.h" struct eap_gtc_data { enum { CONTINUE, SUCCESS, FAILURE } state; int prefix; }; static void * eap_gtc_init(struct eap_sm *sm) { struct eap_gtc_data *data; data = os_zalloc(sizeof(*data)); if (data == NULL) return NULL; data->state = CONTINUE; #ifdef EAP_SERVER_FAST if (sm->m && sm->m->vendor == EAP_VENDOR_IETF && sm->m->method == EAP_TYPE_FAST) { wpa_printf(MSG_DEBUG, "EAP-GTC: EAP-FAST tunnel - use prefix " "with challenge/response"); data->prefix = 1; } #endif /* EAP_SERVER_FAST */ return data; } static void eap_gtc_reset(struct eap_sm *sm, void *priv) { struct eap_gtc_data *data = priv; os_free(data); } static struct wpabuf * eap_gtc_buildReq(struct eap_sm *sm, void *priv, u8 id) { struct eap_gtc_data *data = priv; struct wpabuf *req; char *msg; size_t msg_len; msg = data->prefix ? "CHALLENGE=Password" : "Password"; msg_len = os_strlen(msg); req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GTC, msg_len, EAP_CODE_REQUEST, id); if (req == NULL) { wpa_printf(MSG_ERROR, "EAP-GTC: Failed to allocate memory for " "request"); data->state = FAILURE; return NULL; } wpabuf_put_data(req, msg, msg_len); data->state = CONTINUE; return req; } static Boolean eap_gtc_check(struct eap_sm *sm, void *priv, struct wpabuf *respData) { const u8 *pos; size_t len; pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_GTC, respData, &len); if (pos == NULL || len < 1) { wpa_printf(MSG_INFO, "EAP-GTC: Invalid frame"); return TRUE; } return FALSE; } static void eap_gtc_process(struct eap_sm *sm, void *priv, struct wpabuf *respData) { struct eap_gtc_data *data = priv; const u8 *pos; size_t rlen; pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_GTC, respData, &rlen); if (pos == NULL || rlen < 1) return; /* Should not happen - frame already validated */ wpa_hexdump_ascii_key(MSG_MSGDUMP, "EAP-GTC: Response", pos, rlen); #ifdef EAP_SERVER_FAST if (data->prefix) { const u8 *pos2, *end; /* "RESPONSE=\0" */ if (rlen < 10) { wpa_printf(MSG_DEBUG, "EAP-GTC: Too short response " "for EAP-FAST prefix"); data->state = FAILURE; return; } end = pos + rlen; pos += 9; pos2 = pos; while (pos2 < end && *pos2) pos2++; if (pos2 == end) { wpa_printf(MSG_DEBUG, "EAP-GTC: No password in " "response to EAP-FAST prefix"); data->state = FAILURE; return; } wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-GTC: Response user", pos, pos2 - pos); if (sm->identity && sm->require_identity_match && (pos2 - pos != (int) sm->identity_len || os_memcmp(pos, sm->identity, sm->identity_len))) { wpa_printf(MSG_DEBUG, "EAP-GTC: Phase 2 Identity did " "not match with required Identity"); wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-GTC: Expected " "identity", sm->identity, sm->identity_len); data->state = FAILURE; return; } else { os_free(sm->identity); sm->identity_len = pos2 - pos; sm->identity = os_malloc(sm->identity_len); if (sm->identity == NULL) { data->state = FAILURE; return; } os_memcpy(sm->identity, pos, sm->identity_len); } if (eap_user_get(sm, sm->identity, sm->identity_len, 1) != 0) { wpa_hexdump_ascii(MSG_DEBUG, "EAP-GTC: Phase2 " "Identity not found in the user " "database", sm->identity, sm->identity_len); data->state = FAILURE; return; } pos = pos2 + 1; rlen = end - pos; wpa_hexdump_ascii_key(MSG_MSGDUMP, "EAP-GTC: Response password", pos, rlen); } #endif /* EAP_SERVER_FAST */ if (sm->user == NULL || sm->user->password == NULL || sm->user->password_hash) { wpa_printf(MSG_INFO, "EAP-GTC: Plaintext password not " "configured"); data->state = FAILURE; return; } if (rlen != sm->user->password_len || os_memcmp(pos, sm->user->password, rlen) != 0) { wpa_printf(MSG_DEBUG, "EAP-GTC: Done - Failure"); data->state = FAILURE; } else { wpa_printf(MSG_DEBUG, "EAP-GTC: Done - Success"); data->state = SUCCESS; } } static Boolean eap_gtc_isDone(struct eap_sm *sm, void *priv) { struct eap_gtc_data *data = priv; return data->state != CONTINUE; } static Boolean eap_gtc_isSuccess(struct eap_sm *sm, void *priv) { struct eap_gtc_data *data = priv; return data->state == SUCCESS; } int eap_server_gtc_register(void) { struct eap_method *eap; int ret; eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_GTC, "GTC"); if (eap == NULL) return -1; eap->init = eap_gtc_init; eap->reset = eap_gtc_reset; eap->buildReq = eap_gtc_buildReq; eap->check = eap_gtc_check; eap->process = eap_gtc_process; eap->isDone = eap_gtc_isDone; eap->isSuccess = eap_gtc_isSuccess; ret = eap_server_method_register(eap); if (ret) eap_server_method_free(eap); return ret; } wpa-2.1/src/eap_server/eap_server_identity.c000066400000000000000000000072151227414666700213210ustar00rootroot00000000000000/* * hostapd / EAP-Identity * Copyright (c) 2004-2006, 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_i.h" struct eap_identity_data { enum { CONTINUE, SUCCESS, FAILURE } state; int pick_up; }; static void * eap_identity_init(struct eap_sm *sm) { struct eap_identity_data *data; data = os_zalloc(sizeof(*data)); if (data == NULL) return NULL; data->state = CONTINUE; return data; } static void * eap_identity_initPickUp(struct eap_sm *sm) { struct eap_identity_data *data; data = eap_identity_init(sm); if (data) { data->pick_up = 1; } return data; } static void eap_identity_reset(struct eap_sm *sm, void *priv) { struct eap_identity_data *data = priv; os_free(data); } static struct wpabuf * eap_identity_buildReq(struct eap_sm *sm, void *priv, u8 id) { struct eap_identity_data *data = priv; struct wpabuf *req; const char *req_data; size_t req_data_len; if (sm->eapol_cb->get_eap_req_id_text) { req_data = sm->eapol_cb->get_eap_req_id_text(sm->eapol_ctx, &req_data_len); } else { req_data = NULL; req_data_len = 0; } req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_IDENTITY, req_data_len, EAP_CODE_REQUEST, id); if (req == NULL) { wpa_printf(MSG_ERROR, "EAP-Identity: Failed to allocate " "memory for request"); data->state = FAILURE; return NULL; } wpabuf_put_data(req, req_data, req_data_len); return req; } static Boolean eap_identity_check(struct eap_sm *sm, void *priv, struct wpabuf *respData) { const u8 *pos; size_t len; pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_IDENTITY, respData, &len); if (pos == NULL) { wpa_printf(MSG_INFO, "EAP-Identity: Invalid frame"); return TRUE; } return FALSE; } static void eap_identity_process(struct eap_sm *sm, void *priv, struct wpabuf *respData) { struct eap_identity_data *data = priv; const u8 *pos; size_t len; if (data->pick_up) { if (eap_identity_check(sm, data, respData)) { wpa_printf(MSG_DEBUG, "EAP-Identity: failed to pick " "up already started negotiation"); data->state = FAILURE; return; } data->pick_up = 0; } pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_IDENTITY, respData, &len); if (pos == NULL) return; /* Should not happen - frame already validated */ wpa_hexdump_ascii(MSG_DEBUG, "EAP-Identity: Peer identity", pos, len); if (sm->identity) sm->update_user = TRUE; os_free(sm->identity); sm->identity = os_malloc(len ? len : 1); if (sm->identity == NULL) { data->state = FAILURE; } else { os_memcpy(sm->identity, pos, len); sm->identity_len = len; data->state = SUCCESS; } } static Boolean eap_identity_isDone(struct eap_sm *sm, void *priv) { struct eap_identity_data *data = priv; return data->state != CONTINUE; } static Boolean eap_identity_isSuccess(struct eap_sm *sm, void *priv) { struct eap_identity_data *data = priv; return data->state == SUCCESS; } int eap_server_identity_register(void) { struct eap_method *eap; int ret; eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_IDENTITY, "Identity"); if (eap == NULL) return -1; eap->init = eap_identity_init; eap->initPickUp = eap_identity_initPickUp; eap->reset = eap_identity_reset; eap->buildReq = eap_identity_buildReq; eap->check = eap_identity_check; eap->process = eap_identity_process; eap->isDone = eap_identity_isDone; eap->isSuccess = eap_identity_isSuccess; ret = eap_server_method_register(eap); if (ret) eap_server_method_free(eap); return ret; } wpa-2.1/src/eap_server/eap_server_ikev2.c000066400000000000000000000313411227414666700205050ustar00rootroot00000000000000/* * EAP-IKEv2 server (RFC 5106) * Copyright (c) 2007, 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_i.h" #include "eap_common/eap_ikev2_common.h" #include "ikev2.h" struct eap_ikev2_data { struct ikev2_initiator_data ikev2; enum { MSG, FRAG_ACK, WAIT_FRAG_ACK, DONE, FAIL } state; struct wpabuf *in_buf; struct wpabuf *out_buf; size_t out_used; size_t fragment_size; int keys_ready; u8 keymat[EAP_MSK_LEN + EAP_EMSK_LEN]; int keymat_ok; }; static const u8 * eap_ikev2_get_shared_secret(void *ctx, const u8 *IDr, size_t IDr_len, size_t *secret_len) { struct eap_sm *sm = ctx; if (IDr == NULL) { wpa_printf(MSG_DEBUG, "EAP-IKEV2: No IDr received - default " "to user identity from EAP-Identity"); IDr = sm->identity; IDr_len = sm->identity_len; } if (eap_user_get(sm, IDr, IDr_len, 0) < 0 || sm->user == NULL || sm->user->password == NULL) { wpa_printf(MSG_DEBUG, "EAP-IKEV2: No user entry found"); return NULL; } *secret_len = sm->user->password_len; return sm->user->password; } static const char * eap_ikev2_state_txt(int state) { switch (state) { case MSG: return "MSG"; case FRAG_ACK: return "FRAG_ACK"; case WAIT_FRAG_ACK: return "WAIT_FRAG_ACK"; case DONE: return "DONE"; case FAIL: return "FAIL"; default: return "?"; } } static void eap_ikev2_state(struct eap_ikev2_data *data, int state) { wpa_printf(MSG_DEBUG, "EAP-IKEV2: %s -> %s", eap_ikev2_state_txt(data->state), eap_ikev2_state_txt(state)); data->state = state; } static void * eap_ikev2_init(struct eap_sm *sm) { struct eap_ikev2_data *data; data = os_zalloc(sizeof(*data)); if (data == NULL) return NULL; data->state = MSG; data->fragment_size = sm->fragment_size > 0 ? sm->fragment_size : IKEV2_FRAGMENT_SIZE; data->ikev2.state = SA_INIT; data->ikev2.peer_auth = PEER_AUTH_SECRET; data->ikev2.key_pad = (u8 *) os_strdup("Key Pad for EAP-IKEv2"); if (data->ikev2.key_pad == NULL) goto failed; data->ikev2.key_pad_len = 21; /* TODO: make proposals configurable */ data->ikev2.proposal.proposal_num = 1; data->ikev2.proposal.integ = AUTH_HMAC_SHA1_96; data->ikev2.proposal.prf = PRF_HMAC_SHA1; data->ikev2.proposal.encr = ENCR_AES_CBC; data->ikev2.proposal.dh = DH_GROUP2_1024BIT_MODP; data->ikev2.IDi = os_malloc(sm->server_id_len); if (data->ikev2.IDi == NULL) goto failed; os_memcpy(data->ikev2.IDi, sm->server_id, sm->server_id_len); data->ikev2.IDi_len = sm->server_id_len; data->ikev2.get_shared_secret = eap_ikev2_get_shared_secret; data->ikev2.cb_ctx = sm; return data; failed: ikev2_initiator_deinit(&data->ikev2); os_free(data); return NULL; } static void eap_ikev2_reset(struct eap_sm *sm, void *priv) { struct eap_ikev2_data *data = priv; wpabuf_free(data->in_buf); wpabuf_free(data->out_buf); ikev2_initiator_deinit(&data->ikev2); os_free(data); } static struct wpabuf * eap_ikev2_build_msg(struct eap_ikev2_data *data, u8 id) { struct wpabuf *req; u8 flags; size_t send_len, plen, icv_len = 0; wpa_printf(MSG_DEBUG, "EAP-IKEV2: Generating Request"); flags = 0; send_len = wpabuf_len(data->out_buf) - data->out_used; if (1 + send_len > data->fragment_size) { send_len = data->fragment_size - 1; flags |= IKEV2_FLAGS_MORE_FRAGMENTS; if (data->out_used == 0) { flags |= IKEV2_FLAGS_LENGTH_INCLUDED; send_len -= 4; } } plen = 1 + send_len; if (flags & IKEV2_FLAGS_LENGTH_INCLUDED) plen += 4; if (data->keys_ready) { const struct ikev2_integ_alg *integ; wpa_printf(MSG_DEBUG, "EAP-IKEV2: Add Integrity Checksum " "Data"); flags |= IKEV2_FLAGS_ICV_INCLUDED; integ = ikev2_get_integ(data->ikev2.proposal.integ); if (integ == NULL) { wpa_printf(MSG_DEBUG, "EAP-IKEV2: Unknown INTEG " "transform / cannot generate ICV"); return NULL; } icv_len = integ->hash_len; plen += icv_len; } req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_IKEV2, plen, EAP_CODE_REQUEST, id); if (req == NULL) return NULL; wpabuf_put_u8(req, flags); /* Flags */ if (flags & IKEV2_FLAGS_LENGTH_INCLUDED) wpabuf_put_be32(req, wpabuf_len(data->out_buf)); wpabuf_put_data(req, wpabuf_head_u8(data->out_buf) + data->out_used, send_len); data->out_used += send_len; if (flags & IKEV2_FLAGS_ICV_INCLUDED) { const u8 *msg = wpabuf_head(req); size_t len = wpabuf_len(req); ikev2_integ_hash(data->ikev2.proposal.integ, data->ikev2.keys.SK_ai, data->ikev2.keys.SK_integ_len, msg, len, wpabuf_put(req, icv_len)); } if (data->out_used == wpabuf_len(data->out_buf)) { wpa_printf(MSG_DEBUG, "EAP-IKEV2: Sending out %lu bytes " "(message sent completely)", (unsigned long) send_len); wpabuf_free(data->out_buf); data->out_buf = NULL; data->out_used = 0; } else { wpa_printf(MSG_DEBUG, "EAP-IKEV2: Sending out %lu bytes " "(%lu more to send)", (unsigned long) send_len, (unsigned long) wpabuf_len(data->out_buf) - data->out_used); eap_ikev2_state(data, WAIT_FRAG_ACK); } return req; } static struct wpabuf * eap_ikev2_buildReq(struct eap_sm *sm, void *priv, u8 id) { struct eap_ikev2_data *data = priv; switch (data->state) { case MSG: if (data->out_buf == NULL) { data->out_buf = ikev2_initiator_build(&data->ikev2); if (data->out_buf == NULL) { wpa_printf(MSG_DEBUG, "EAP-IKEV2: Failed to " "generate IKEv2 message"); return NULL; } data->out_used = 0; } /* pass through */ case WAIT_FRAG_ACK: return eap_ikev2_build_msg(data, id); case FRAG_ACK: return eap_ikev2_build_frag_ack(id, EAP_CODE_REQUEST); default: wpa_printf(MSG_DEBUG, "EAP-IKEV2: Unexpected state %d in " "buildReq", data->state); return NULL; } } static Boolean eap_ikev2_check(struct eap_sm *sm, void *priv, struct wpabuf *respData) { const u8 *pos; size_t len; pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_IKEV2, respData, &len); if (pos == NULL) { wpa_printf(MSG_INFO, "EAP-IKEV2: Invalid frame"); return TRUE; } return FALSE; } static int eap_ikev2_process_icv(struct eap_ikev2_data *data, const struct wpabuf *respData, u8 flags, const u8 *pos, const u8 **end) { if (flags & IKEV2_FLAGS_ICV_INCLUDED) { int icv_len = eap_ikev2_validate_icv( data->ikev2.proposal.integ, &data->ikev2.keys, 0, respData, pos, *end); if (icv_len < 0) return -1; /* Hide Integrity Checksum Data from further processing */ *end -= icv_len; } else if (data->keys_ready) { wpa_printf(MSG_INFO, "EAP-IKEV2: The message should have " "included integrity checksum"); return -1; } return 0; } static int eap_ikev2_process_cont(struct eap_ikev2_data *data, const u8 *buf, size_t len) { /* Process continuation of a pending message */ if (len > wpabuf_tailroom(data->in_buf)) { wpa_printf(MSG_DEBUG, "EAP-IKEV2: Fragment overflow"); eap_ikev2_state(data, FAIL); return -1; } wpabuf_put_data(data->in_buf, buf, len); wpa_printf(MSG_DEBUG, "EAP-IKEV2: Received %lu bytes, waiting for %lu " "bytes more", (unsigned long) len, (unsigned long) wpabuf_tailroom(data->in_buf)); return 0; } static int eap_ikev2_process_fragment(struct eap_ikev2_data *data, u8 flags, u32 message_length, const u8 *buf, size_t len) { /* Process a fragment that is not the last one of the message */ if (data->in_buf == NULL && !(flags & IKEV2_FLAGS_LENGTH_INCLUDED)) { wpa_printf(MSG_DEBUG, "EAP-IKEV2: No Message Length field in " "a fragmented packet"); return -1; } if (data->in_buf == NULL) { /* First fragment of the message */ data->in_buf = wpabuf_alloc(message_length); if (data->in_buf == NULL) { wpa_printf(MSG_DEBUG, "EAP-IKEV2: No memory for " "message"); return -1; } wpabuf_put_data(data->in_buf, buf, len); wpa_printf(MSG_DEBUG, "EAP-IKEV2: Received %lu bytes in first " "fragment, waiting for %lu bytes more", (unsigned long) len, (unsigned long) wpabuf_tailroom(data->in_buf)); } return 0; } static int eap_ikev2_server_keymat(struct eap_ikev2_data *data) { if (eap_ikev2_derive_keymat( data->ikev2.proposal.prf, &data->ikev2.keys, data->ikev2.i_nonce, data->ikev2.i_nonce_len, data->ikev2.r_nonce, data->ikev2.r_nonce_len, data->keymat) < 0) { wpa_printf(MSG_DEBUG, "EAP-IKEV2: Failed to derive " "key material"); return -1; } data->keymat_ok = 1; return 0; } static void eap_ikev2_process(struct eap_sm *sm, void *priv, struct wpabuf *respData) { struct eap_ikev2_data *data = priv; const u8 *start, *pos, *end; size_t len; u8 flags; u32 message_length = 0; struct wpabuf tmpbuf; pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_IKEV2, respData, &len); if (pos == NULL) return; /* Should not happen; message already verified */ start = pos; end = start + len; if (len == 0) { /* fragment ack */ flags = 0; } else flags = *pos++; if (eap_ikev2_process_icv(data, respData, flags, pos, &end) < 0) { eap_ikev2_state(data, FAIL); return; } if (flags & IKEV2_FLAGS_LENGTH_INCLUDED) { if (end - pos < 4) { wpa_printf(MSG_DEBUG, "EAP-IKEV2: Message underflow"); eap_ikev2_state(data, FAIL); return; } message_length = WPA_GET_BE32(pos); pos += 4; if (message_length < (u32) (end - pos)) { wpa_printf(MSG_DEBUG, "EAP-IKEV2: Invalid Message " "Length (%d; %ld remaining in this msg)", message_length, (long) (end - pos)); eap_ikev2_state(data, FAIL); return; } } wpa_printf(MSG_DEBUG, "EAP-IKEV2: Received packet: Flags 0x%x " "Message Length %u", flags, message_length); if (data->state == WAIT_FRAG_ACK) { if (len != 0) { wpa_printf(MSG_DEBUG, "EAP-IKEV2: Unexpected payload " "in WAIT_FRAG_ACK state"); eap_ikev2_state(data, FAIL); return; } wpa_printf(MSG_DEBUG, "EAP-IKEV2: Fragment acknowledged"); eap_ikev2_state(data, MSG); return; } if (data->in_buf && eap_ikev2_process_cont(data, pos, end - pos) < 0) { eap_ikev2_state(data, FAIL); return; } if (flags & IKEV2_FLAGS_MORE_FRAGMENTS) { if (eap_ikev2_process_fragment(data, flags, message_length, pos, end - pos) < 0) eap_ikev2_state(data, FAIL); else eap_ikev2_state(data, FRAG_ACK); return; } else if (data->state == FRAG_ACK) { wpa_printf(MSG_DEBUG, "EAP-TNC: All fragments received"); data->state = MSG; } if (data->in_buf == NULL) { /* Wrap unfragmented messages as wpabuf without extra copy */ wpabuf_set(&tmpbuf, pos, end - pos); data->in_buf = &tmpbuf; } if (ikev2_initiator_process(&data->ikev2, data->in_buf) < 0) { if (data->in_buf == &tmpbuf) data->in_buf = NULL; eap_ikev2_state(data, FAIL); return; } switch (data->ikev2.state) { case SA_AUTH: /* SA_INIT was sent out, so message have to be * integrity protected from now on. */ data->keys_ready = 1; break; case IKEV2_DONE: if (data->state == FAIL) break; wpa_printf(MSG_DEBUG, "EAP-IKEV2: Authentication completed " "successfully"); if (eap_ikev2_server_keymat(data)) break; eap_ikev2_state(data, DONE); break; default: break; } if (data->in_buf != &tmpbuf) wpabuf_free(data->in_buf); data->in_buf = NULL; } static Boolean eap_ikev2_isDone(struct eap_sm *sm, void *priv) { struct eap_ikev2_data *data = priv; return data->state == DONE || data->state == FAIL; } static Boolean eap_ikev2_isSuccess(struct eap_sm *sm, void *priv) { struct eap_ikev2_data *data = priv; return data->state == DONE && data->ikev2.state == IKEV2_DONE && data->keymat_ok; } static u8 * eap_ikev2_getKey(struct eap_sm *sm, void *priv, size_t *len) { struct eap_ikev2_data *data = priv; u8 *key; if (data->state != DONE || !data->keymat_ok) return NULL; key = os_malloc(EAP_MSK_LEN); if (key) { os_memcpy(key, data->keymat, EAP_MSK_LEN); *len = EAP_MSK_LEN; } return key; } static u8 * eap_ikev2_get_emsk(struct eap_sm *sm, void *priv, size_t *len) { struct eap_ikev2_data *data = priv; u8 *key; if (data->state != DONE || !data->keymat_ok) return NULL; key = os_malloc(EAP_EMSK_LEN); if (key) { os_memcpy(key, data->keymat + EAP_MSK_LEN, EAP_EMSK_LEN); *len = EAP_EMSK_LEN; } return key; } int eap_server_ikev2_register(void) { struct eap_method *eap; int ret; eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_IKEV2, "IKEV2"); if (eap == NULL) return -1; eap->init = eap_ikev2_init; eap->reset = eap_ikev2_reset; eap->buildReq = eap_ikev2_buildReq; eap->check = eap_ikev2_check; eap->process = eap_ikev2_process; eap->isDone = eap_ikev2_isDone; eap->getKey = eap_ikev2_getKey; eap->isSuccess = eap_ikev2_isSuccess; eap->get_emsk = eap_ikev2_get_emsk; ret = eap_server_method_register(eap); if (ret) eap_server_method_free(eap); return ret; } wpa-2.1/src/eap_server/eap_server_md5.c000066400000000000000000000076361227414666700201640ustar00rootroot00000000000000/* * hostapd / EAP-MD5 server * Copyright (c) 2004-2012, 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/random.h" #include "eap_i.h" #include "eap_common/chap.h" #define CHALLENGE_LEN 16 struct eap_md5_data { u8 challenge[CHALLENGE_LEN]; enum { CONTINUE, SUCCESS, FAILURE } state; }; static void * eap_md5_init(struct eap_sm *sm) { struct eap_md5_data *data; data = os_zalloc(sizeof(*data)); if (data == NULL) return NULL; data->state = CONTINUE; return data; } static void eap_md5_reset(struct eap_sm *sm, void *priv) { struct eap_md5_data *data = priv; os_free(data); } static struct wpabuf * eap_md5_buildReq(struct eap_sm *sm, void *priv, u8 id) { struct eap_md5_data *data = priv; struct wpabuf *req; if (random_get_bytes(data->challenge, CHALLENGE_LEN)) { wpa_printf(MSG_ERROR, "EAP-MD5: Failed to get random data"); data->state = FAILURE; return NULL; } req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MD5, 1 + CHALLENGE_LEN, EAP_CODE_REQUEST, id); if (req == NULL) { wpa_printf(MSG_ERROR, "EAP-MD5: Failed to allocate memory for " "request"); data->state = FAILURE; return NULL; } wpabuf_put_u8(req, CHALLENGE_LEN); wpabuf_put_data(req, data->challenge, CHALLENGE_LEN); wpa_hexdump(MSG_MSGDUMP, "EAP-MD5: Challenge", data->challenge, CHALLENGE_LEN); data->state = CONTINUE; return req; } static Boolean eap_md5_check(struct eap_sm *sm, void *priv, struct wpabuf *respData) { const u8 *pos; size_t len; pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MD5, respData, &len); if (pos == NULL || len < 1) { wpa_printf(MSG_INFO, "EAP-MD5: Invalid frame"); return TRUE; } if (*pos != CHAP_MD5_LEN || 1 + CHAP_MD5_LEN > len) { wpa_printf(MSG_INFO, "EAP-MD5: Invalid response " "(response_len=%d payload_len=%lu", *pos, (unsigned long) len); return TRUE; } return FALSE; } static void eap_md5_process(struct eap_sm *sm, void *priv, struct wpabuf *respData) { struct eap_md5_data *data = priv; const u8 *pos; size_t plen; u8 hash[CHAP_MD5_LEN], id; if (sm->user == NULL || sm->user->password == NULL || sm->user->password_hash) { wpa_printf(MSG_INFO, "EAP-MD5: Plaintext password not " "configured"); data->state = FAILURE; return; } pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MD5, respData, &plen); if (pos == NULL || *pos != CHAP_MD5_LEN || plen < 1 + CHAP_MD5_LEN) return; /* Should not happen - frame already validated */ pos++; /* Skip response len */ wpa_hexdump(MSG_MSGDUMP, "EAP-MD5: Response", pos, CHAP_MD5_LEN); id = eap_get_id(respData); if (chap_md5(id, sm->user->password, sm->user->password_len, data->challenge, CHALLENGE_LEN, hash)) { wpa_printf(MSG_INFO, "EAP-MD5: CHAP MD5 operation failed"); data->state = FAILURE; return; } if (os_memcmp(hash, pos, CHAP_MD5_LEN) == 0) { wpa_printf(MSG_DEBUG, "EAP-MD5: Done - Success"); data->state = SUCCESS; } else { wpa_printf(MSG_DEBUG, "EAP-MD5: Done - Failure"); data->state = FAILURE; } } static Boolean eap_md5_isDone(struct eap_sm *sm, void *priv) { struct eap_md5_data *data = priv; return data->state != CONTINUE; } static Boolean eap_md5_isSuccess(struct eap_sm *sm, void *priv) { struct eap_md5_data *data = priv; return data->state == SUCCESS; } int eap_server_md5_register(void) { struct eap_method *eap; int ret; eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_MD5, "MD5"); if (eap == NULL) return -1; eap->init = eap_md5_init; eap->reset = eap_md5_reset; eap->buildReq = eap_md5_buildReq; eap->check = eap_md5_check; eap->process = eap_md5_process; eap->isDone = eap_md5_isDone; eap->isSuccess = eap_md5_isSuccess; ret = eap_server_method_register(eap); if (ret) eap_server_method_free(eap); return ret; } wpa-2.1/src/eap_server/eap_server_methods.c000066400000000000000000000101461227414666700211300ustar00rootroot00000000000000/* * EAP server 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_i.h" #include "eap_methods.h" static struct eap_method *eap_methods; /** * eap_server_get_eap_method - Get EAP method based on type number * @vendor: EAP Vendor-Id (0 = IETF) * @method: EAP type number * Returns: Pointer to EAP method or %NULL if not found */ const struct eap_method * eap_server_get_eap_method(int vendor, EapType method) { struct eap_method *m; for (m = eap_methods; m; m = m->next) { if (m->vendor == vendor && m->method == method) return m; } return NULL; } /** * eap_server_get_type - Get EAP type for the given EAP method name * @name: EAP method name, e.g., TLS * @vendor: Buffer for returning EAP Vendor-Id * Returns: EAP method type or %EAP_TYPE_NONE if not found * * This function maps EAP type names into EAP type numbers based on the list of * EAP methods included in the build. */ EapType eap_server_get_type(const char *name, int *vendor) { struct eap_method *m; for (m = eap_methods; m; m = m->next) { if (os_strcmp(m->name, name) == 0) { *vendor = m->vendor; return m->method; } } *vendor = EAP_VENDOR_IETF; return EAP_TYPE_NONE; } /** * eap_server_method_alloc - Allocate EAP server method structure * @version: Version of the EAP server method interface (set to * EAP_SERVER_METHOD_INTERFACE_VERSION) * @vendor: EAP Vendor-ID (EAP_VENDOR_*) (0 = IETF) * @method: EAP type number (EAP_TYPE_*) * @name: Name of the method (e.g., "TLS") * Returns: Allocated EAP method structure or %NULL on failure * * The returned structure should be freed with eap_server_method_free() when it * is not needed anymore. */ struct eap_method * eap_server_method_alloc(int version, int vendor, EapType method, const char *name) { struct eap_method *eap; eap = os_zalloc(sizeof(*eap)); if (eap == NULL) return NULL; eap->version = version; eap->vendor = vendor; eap->method = method; eap->name = name; return eap; } /** * eap_server_method_free - Free EAP server method structure * @method: Method structure allocated with eap_server_method_alloc() */ void eap_server_method_free(struct eap_method *method) { os_free(method); } /** * eap_server_method_register - Register an EAP server method * @method: EAP method to register * Returns: 0 on success, -1 on invalid method, or -2 if a matching EAP method * has already been registered * * Each EAP server method needs to call this function to register itself as a * supported EAP method. */ int eap_server_method_register(struct eap_method *method) { struct eap_method *m, *last = NULL; if (method == NULL || method->name == NULL || method->version != EAP_SERVER_METHOD_INTERFACE_VERSION) return -1; for (m = eap_methods; m; m = m->next) { if ((m->vendor == method->vendor && m->method == method->method) || os_strcmp(m->name, method->name) == 0) return -2; last = m; } if (last) last->next = method; else eap_methods = method; return 0; } /** * eap_server_unregister_methods - Unregister EAP server methods * * This function is called at program termination to unregister all EAP server * methods. */ void eap_server_unregister_methods(void) { struct eap_method *m; while (eap_methods) { m = eap_methods; eap_methods = eap_methods->next; if (m->free) m->free(m); else eap_server_method_free(m); } } /** * eap_server_get_name - Get EAP method name for the given EAP type * @vendor: EAP Vendor-Id (0 = IETF) * @type: EAP method type * Returns: EAP method name, e.g., TLS, or %NULL if not found * * This function maps EAP type numbers into EAP type names based on the list of * EAP methods included in the build. */ const char * eap_server_get_name(int vendor, EapType type) { struct eap_method *m; if (vendor == EAP_VENDOR_IETF && type == EAP_TYPE_EXPANDED) return "expanded"; for (m = eap_methods; m; m = m->next) { if (m->vendor == vendor && m->method == type) return m->name; } return NULL; } wpa-2.1/src/eap_server/eap_server_mschapv2.c000066400000000000000000000350351227414666700212140ustar00rootroot00000000000000/* * hostapd / EAP-MSCHAPv2 (draft-kamath-pppext-eap-mschapv2-00.txt) server * Copyright (c) 2004-2007, 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" #include "crypto/random.h" #include "eap_i.h" struct eap_mschapv2_hdr { u8 op_code; /* MSCHAPV2_OP_* */ u8 mschapv2_id; /* must be changed for challenges, but not for * success/failure */ u8 ms_length[2]; /* Note: misaligned; length - 5 */ /* followed by data */ } STRUCT_PACKED; #define MSCHAPV2_OP_CHALLENGE 1 #define MSCHAPV2_OP_RESPONSE 2 #define MSCHAPV2_OP_SUCCESS 3 #define MSCHAPV2_OP_FAILURE 4 #define MSCHAPV2_OP_CHANGE_PASSWORD 7 #define MSCHAPV2_RESP_LEN 49 #define ERROR_RESTRICTED_LOGON_HOURS 646 #define ERROR_ACCT_DISABLED 647 #define ERROR_PASSWD_EXPIRED 648 #define ERROR_NO_DIALIN_PERMISSION 649 #define ERROR_AUTHENTICATION_FAILURE 691 #define ERROR_CHANGING_PASSWORD 709 #define PASSWD_CHANGE_CHAL_LEN 16 #define MSCHAPV2_KEY_LEN 16 #define CHALLENGE_LEN 16 struct eap_mschapv2_data { u8 auth_challenge[CHALLENGE_LEN]; int auth_challenge_from_tls; u8 *peer_challenge; u8 auth_response[20]; enum { CHALLENGE, SUCCESS_REQ, FAILURE_REQ, SUCCESS, FAILURE } state; u8 resp_mschapv2_id; u8 master_key[16]; int master_key_valid; }; static void * eap_mschapv2_init(struct eap_sm *sm) { struct eap_mschapv2_data *data; data = os_zalloc(sizeof(*data)); if (data == NULL) return NULL; data->state = CHALLENGE; if (sm->auth_challenge) { os_memcpy(data->auth_challenge, sm->auth_challenge, CHALLENGE_LEN); data->auth_challenge_from_tls = 1; } if (sm->peer_challenge) { data->peer_challenge = os_malloc(CHALLENGE_LEN); if (data->peer_challenge == NULL) { os_free(data); return NULL; } os_memcpy(data->peer_challenge, sm->peer_challenge, CHALLENGE_LEN); } return data; } static void eap_mschapv2_reset(struct eap_sm *sm, void *priv) { struct eap_mschapv2_data *data = priv; if (data == NULL) return; os_free(data->peer_challenge); os_free(data); } static struct wpabuf * eap_mschapv2_build_challenge( struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id) { struct wpabuf *req; struct eap_mschapv2_hdr *ms; size_t ms_len; if (!data->auth_challenge_from_tls && random_get_bytes(data->auth_challenge, CHALLENGE_LEN)) { wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to get random " "data"); data->state = FAILURE; return NULL; } ms_len = sizeof(*ms) + 1 + CHALLENGE_LEN + sm->server_id_len; req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len, EAP_CODE_REQUEST, id); if (req == NULL) { wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory" " for request"); data->state = FAILURE; return NULL; } ms = wpabuf_put(req, sizeof(*ms)); ms->op_code = MSCHAPV2_OP_CHALLENGE; ms->mschapv2_id = id; WPA_PUT_BE16(ms->ms_length, ms_len); wpabuf_put_u8(req, CHALLENGE_LEN); if (!data->auth_challenge_from_tls) wpabuf_put_data(req, data->auth_challenge, CHALLENGE_LEN); else wpabuf_put(req, CHALLENGE_LEN); wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Challenge", data->auth_challenge, CHALLENGE_LEN); wpabuf_put_data(req, sm->server_id, sm->server_id_len); return req; } static struct wpabuf * eap_mschapv2_build_success_req( struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id) { struct wpabuf *req; struct eap_mschapv2_hdr *ms; u8 *msg; char *message = "OK"; size_t ms_len; ms_len = sizeof(*ms) + 2 + 2 * sizeof(data->auth_response) + 1 + 2 + os_strlen(message); req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len, EAP_CODE_REQUEST, id); if (req == NULL) { wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory" " for request"); data->state = FAILURE; return NULL; } ms = wpabuf_put(req, sizeof(*ms)); ms->op_code = MSCHAPV2_OP_SUCCESS; ms->mschapv2_id = data->resp_mschapv2_id; WPA_PUT_BE16(ms->ms_length, ms_len); msg = (u8 *) (ms + 1); wpabuf_put_u8(req, 'S'); wpabuf_put_u8(req, '='); wpa_snprintf_hex_uppercase( wpabuf_put(req, sizeof(data->auth_response) * 2), sizeof(data->auth_response) * 2 + 1, data->auth_response, sizeof(data->auth_response)); wpabuf_put_u8(req, ' '); wpabuf_put_u8(req, 'M'); wpabuf_put_u8(req, '='); wpabuf_put_data(req, message, os_strlen(message)); wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Success Request Message", msg, ms_len - sizeof(*ms)); return req; } static struct wpabuf * eap_mschapv2_build_failure_req( struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id) { struct wpabuf *req; struct eap_mschapv2_hdr *ms; char *message = "E=691 R=0 C=00000000000000000000000000000000 V=3 " "M=FAILED"; size_t ms_len; ms_len = sizeof(*ms) + os_strlen(message); req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len, EAP_CODE_REQUEST, id); if (req == NULL) { wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory" " for request"); data->state = FAILURE; return NULL; } ms = wpabuf_put(req, sizeof(*ms)); ms->op_code = MSCHAPV2_OP_FAILURE; ms->mschapv2_id = data->resp_mschapv2_id; WPA_PUT_BE16(ms->ms_length, ms_len); wpabuf_put_data(req, message, os_strlen(message)); wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Failure Request Message", (u8 *) message, os_strlen(message)); return req; } static struct wpabuf * eap_mschapv2_buildReq(struct eap_sm *sm, void *priv, u8 id) { struct eap_mschapv2_data *data = priv; switch (data->state) { case CHALLENGE: return eap_mschapv2_build_challenge(sm, data, id); case SUCCESS_REQ: return eap_mschapv2_build_success_req(sm, data, id); case FAILURE_REQ: return eap_mschapv2_build_failure_req(sm, data, id); default: wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Unknown state %d in " "buildReq", data->state); break; } return NULL; } static Boolean eap_mschapv2_check(struct eap_sm *sm, void *priv, struct wpabuf *respData) { struct eap_mschapv2_data *data = priv; struct eap_mschapv2_hdr *resp; const u8 *pos; size_t len; pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData, &len); if (pos == NULL || len < 1) { wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid frame"); return TRUE; } resp = (struct eap_mschapv2_hdr *) pos; if (data->state == CHALLENGE && resp->op_code != MSCHAPV2_OP_RESPONSE) { wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Expected Response - " "ignore op %d", resp->op_code); return TRUE; } if (data->state == SUCCESS_REQ && resp->op_code != MSCHAPV2_OP_SUCCESS && resp->op_code != MSCHAPV2_OP_FAILURE) { wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Expected Success or " "Failure - ignore op %d", resp->op_code); return TRUE; } if (data->state == FAILURE_REQ && resp->op_code != MSCHAPV2_OP_FAILURE) { wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Expected Failure " "- ignore op %d", resp->op_code); return TRUE; } return FALSE; } static void eap_mschapv2_process_response(struct eap_sm *sm, struct eap_mschapv2_data *data, struct wpabuf *respData) { struct eap_mschapv2_hdr *resp; const u8 *pos, *end, *peer_challenge, *nt_response, *name; u8 flags; size_t len, name_len, i; u8 expected[24]; const u8 *username, *user; size_t username_len, user_len; int res; pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData, &len); if (pos == NULL || len < 1) return; /* Should not happen - frame already validated */ end = pos + len; resp = (struct eap_mschapv2_hdr *) pos; pos = (u8 *) (resp + 1); if (len < sizeof(*resp) + 1 + 49 || resp->op_code != MSCHAPV2_OP_RESPONSE || pos[0] != 49) { wpa_hexdump_buf(MSG_DEBUG, "EAP-MSCHAPV2: Invalid response", respData); data->state = FAILURE; return; } data->resp_mschapv2_id = resp->mschapv2_id; pos++; peer_challenge = pos; pos += 16 + 8; nt_response = pos; pos += 24; flags = *pos++; name = pos; name_len = end - name; if (data->peer_challenge) { wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Using pre-configured " "Peer-Challenge"); peer_challenge = data->peer_challenge; } wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Peer-Challenge", peer_challenge, 16); wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: NT-Response", nt_response, 24); wpa_printf(MSG_MSGDUMP, "EAP-MSCHAPV2: Flags 0x%x", flags); wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Name", name, name_len); /* MSCHAPv2 does not include optional domain name in the * challenge-response calculation, so remove domain prefix * (if present). */ username = sm->identity; username_len = sm->identity_len; for (i = 0; i < username_len; i++) { if (username[i] == '\\') { username_len -= i + 1; username += i + 1; break; } } user = name; user_len = name_len; for (i = 0; i < user_len; i++) { if (user[i] == '\\') { user_len -= i + 1; user += i + 1; break; } } if (username_len != user_len || os_memcmp(username, user, username_len) != 0) { wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Mismatch in user names"); wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Expected user " "name", username, username_len); wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Received user " "name", user, user_len); data->state = FAILURE; return; } wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: User name", username, username_len); if (sm->user->password_hash) { res = generate_nt_response_pwhash(data->auth_challenge, peer_challenge, username, username_len, sm->user->password, expected); } else { res = generate_nt_response(data->auth_challenge, peer_challenge, username, username_len, sm->user->password, sm->user->password_len, expected); } if (res) { data->state = FAILURE; return; } if (os_memcmp(nt_response, expected, 24) == 0) { const u8 *pw_hash; u8 pw_hash_buf[16], pw_hash_hash[16]; wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Correct NT-Response"); data->state = SUCCESS_REQ; /* Authenticator response is not really needed yet, but * calculate it here so that peer_challenge and username need * not be saved. */ if (sm->user->password_hash) { pw_hash = sm->user->password; } else { if (nt_password_hash(sm->user->password, sm->user->password_len, pw_hash_buf) < 0) { data->state = FAILURE; return; } pw_hash = pw_hash_buf; } generate_authenticator_response_pwhash( pw_hash, peer_challenge, data->auth_challenge, username, username_len, nt_response, data->auth_response); hash_nt_password_hash(pw_hash, pw_hash_hash); get_master_key(pw_hash_hash, nt_response, data->master_key); data->master_key_valid = 1; wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived Master Key", data->master_key, MSCHAPV2_KEY_LEN); } else { wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Expected NT-Response", expected, 24); wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Invalid NT-Response"); data->state = FAILURE_REQ; } } static void eap_mschapv2_process_success_resp(struct eap_sm *sm, struct eap_mschapv2_data *data, struct wpabuf *respData) { struct eap_mschapv2_hdr *resp; const u8 *pos; size_t len; pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData, &len); if (pos == NULL || len < 1) return; /* Should not happen - frame already validated */ resp = (struct eap_mschapv2_hdr *) pos; if (resp->op_code == MSCHAPV2_OP_SUCCESS) { wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received Success Response" " - authentication completed successfully"); data->state = SUCCESS; } else { wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Did not receive Success " "Response - peer rejected authentication"); data->state = FAILURE; } } static void eap_mschapv2_process_failure_resp(struct eap_sm *sm, struct eap_mschapv2_data *data, struct wpabuf *respData) { struct eap_mschapv2_hdr *resp; const u8 *pos; size_t len; pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData, &len); if (pos == NULL || len < 1) return; /* Should not happen - frame already validated */ resp = (struct eap_mschapv2_hdr *) pos; if (resp->op_code == MSCHAPV2_OP_FAILURE) { wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received Failure Response" " - authentication failed"); } else { wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Did not receive Failure " "Response - authentication failed"); } data->state = FAILURE; } static void eap_mschapv2_process(struct eap_sm *sm, void *priv, struct wpabuf *respData) { struct eap_mschapv2_data *data = priv; if (sm->user == NULL || sm->user->password == NULL) { wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Password not configured"); data->state = FAILURE; return; } switch (data->state) { case CHALLENGE: eap_mschapv2_process_response(sm, data, respData); break; case SUCCESS_REQ: eap_mschapv2_process_success_resp(sm, data, respData); break; case FAILURE_REQ: eap_mschapv2_process_failure_resp(sm, data, respData); break; default: wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Unknown state %d in " "process", data->state); break; } } static Boolean eap_mschapv2_isDone(struct eap_sm *sm, void *priv) { struct eap_mschapv2_data *data = priv; return data->state == SUCCESS || data->state == FAILURE; } static u8 * eap_mschapv2_getKey(struct eap_sm *sm, void *priv, size_t *len) { struct eap_mschapv2_data *data = priv; u8 *key; if (data->state != SUCCESS || !data->master_key_valid) return NULL; *len = 2 * MSCHAPV2_KEY_LEN; key = os_malloc(*len); if (key == NULL) return NULL; /* MSK = server MS-MPPE-Recv-Key | MS-MPPE-Send-Key */ get_asymetric_start_key(data->master_key, key, MSCHAPV2_KEY_LEN, 0, 1); get_asymetric_start_key(data->master_key, key + MSCHAPV2_KEY_LEN, MSCHAPV2_KEY_LEN, 1, 1); wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived key", key, *len); return key; } static Boolean eap_mschapv2_isSuccess(struct eap_sm *sm, void *priv) { struct eap_mschapv2_data *data = priv; return data->state == SUCCESS; } int eap_server_mschapv2_register(void) { struct eap_method *eap; int ret; eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, "MSCHAPV2"); if (eap == NULL) return -1; eap->init = eap_mschapv2_init; eap->reset = eap_mschapv2_reset; eap->buildReq = eap_mschapv2_buildReq; eap->check = eap_mschapv2_check; eap->process = eap_mschapv2_process; eap->isDone = eap_mschapv2_isDone; eap->getKey = eap_mschapv2_getKey; eap->isSuccess = eap_mschapv2_isSuccess; ret = eap_server_method_register(eap); if (ret) eap_server_method_free(eap); return ret; } wpa-2.1/src/eap_server/eap_server_pax.c000066400000000000000000000344721227414666700202650ustar00rootroot00000000000000/* * hostapd / EAP-PAX (RFC 4746) server * Copyright (c) 2005-2007, 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/random.h" #include "eap_server/eap_i.h" #include "eap_common/eap_pax_common.h" /* * Note: only PAX_STD subprotocol is currently supported * * TODO: Add support with PAX_SEC with the mandatory to implement ciphersuite * (HMAC_SHA1_128, IANA DH Group 14 (2048 bits), RSA-PKCS1-V1_5) and * recommended ciphersuite (HMAC_SHA256_128, IANA DH Group 15 (3072 bits), * RSAES-OAEP). */ struct eap_pax_data { enum { PAX_STD_1, PAX_STD_3, SUCCESS, FAILURE } state; u8 mac_id; union { u8 e[2 * EAP_PAX_RAND_LEN]; struct { u8 x[EAP_PAX_RAND_LEN]; /* server rand */ u8 y[EAP_PAX_RAND_LEN]; /* client rand */ } r; } rand; u8 ak[EAP_PAX_AK_LEN]; u8 mk[EAP_PAX_MK_LEN]; u8 ck[EAP_PAX_CK_LEN]; u8 ick[EAP_PAX_ICK_LEN]; int keys_set; char *cid; size_t cid_len; }; static void * eap_pax_init(struct eap_sm *sm) { struct eap_pax_data *data; data = os_zalloc(sizeof(*data)); if (data == NULL) return NULL; data->state = PAX_STD_1; /* * TODO: make this configurable once EAP_PAX_HMAC_SHA256_128 is * supported */ data->mac_id = EAP_PAX_MAC_HMAC_SHA1_128; return data; } static void eap_pax_reset(struct eap_sm *sm, void *priv) { struct eap_pax_data *data = priv; os_free(data->cid); os_free(data); } static struct wpabuf * eap_pax_build_std_1(struct eap_sm *sm, struct eap_pax_data *data, u8 id) { struct wpabuf *req; struct eap_pax_hdr *pax; u8 *pos; wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-1 (sending)"); if (random_get_bytes(data->rand.r.x, EAP_PAX_RAND_LEN)) { wpa_printf(MSG_ERROR, "EAP-PAX: Failed to get random data"); data->state = FAILURE; return NULL; } req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PAX, sizeof(*pax) + 2 + EAP_PAX_RAND_LEN + EAP_PAX_ICV_LEN, EAP_CODE_REQUEST, id); if (req == NULL) { wpa_printf(MSG_ERROR, "EAP-PAX: Failed to allocate memory " "request"); data->state = FAILURE; return NULL; } pax = wpabuf_put(req, sizeof(*pax)); pax->op_code = EAP_PAX_OP_STD_1; pax->flags = 0; pax->mac_id = data->mac_id; pax->dh_group_id = EAP_PAX_DH_GROUP_NONE; pax->public_key_id = EAP_PAX_PUBLIC_KEY_NONE; wpabuf_put_be16(req, EAP_PAX_RAND_LEN); wpabuf_put_data(req, data->rand.r.x, EAP_PAX_RAND_LEN); wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: A = X (server rand)", data->rand.r.x, EAP_PAX_RAND_LEN); pos = wpabuf_put(req, EAP_PAX_MAC_LEN); eap_pax_mac(data->mac_id, (u8 *) "", 0, wpabuf_mhead(req), wpabuf_len(req) - EAP_PAX_ICV_LEN, NULL, 0, NULL, 0, pos); wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", pos, EAP_PAX_ICV_LEN); return req; } static struct wpabuf * eap_pax_build_std_3(struct eap_sm *sm, struct eap_pax_data *data, u8 id) { struct wpabuf *req; struct eap_pax_hdr *pax; u8 *pos; wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-3 (sending)"); req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PAX, sizeof(*pax) + 2 + EAP_PAX_MAC_LEN + EAP_PAX_ICV_LEN, EAP_CODE_REQUEST, id); if (req == NULL) { wpa_printf(MSG_ERROR, "EAP-PAX: Failed to allocate memory " "request"); data->state = FAILURE; return NULL; } pax = wpabuf_put(req, sizeof(*pax)); pax->op_code = EAP_PAX_OP_STD_3; pax->flags = 0; pax->mac_id = data->mac_id; pax->dh_group_id = EAP_PAX_DH_GROUP_NONE; pax->public_key_id = EAP_PAX_PUBLIC_KEY_NONE; wpabuf_put_be16(req, EAP_PAX_MAC_LEN); pos = wpabuf_put(req, EAP_PAX_MAC_LEN); eap_pax_mac(data->mac_id, data->ck, EAP_PAX_CK_LEN, data->rand.r.y, EAP_PAX_RAND_LEN, (u8 *) data->cid, data->cid_len, NULL, 0, pos); wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: MAC_CK(B, CID)", pos, EAP_PAX_MAC_LEN); pos += EAP_PAX_MAC_LEN; /* Optional ADE could be added here, if needed */ pos = wpabuf_put(req, EAP_PAX_MAC_LEN); eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN, wpabuf_mhead(req), wpabuf_len(req) - EAP_PAX_ICV_LEN, NULL, 0, NULL, 0, pos); wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", pos, EAP_PAX_ICV_LEN); return req; } static struct wpabuf * eap_pax_buildReq(struct eap_sm *sm, void *priv, u8 id) { struct eap_pax_data *data = priv; switch (data->state) { case PAX_STD_1: return eap_pax_build_std_1(sm, data, id); case PAX_STD_3: return eap_pax_build_std_3(sm, data, id); default: wpa_printf(MSG_DEBUG, "EAP-PAX: Unknown state %d in buildReq", data->state); break; } return NULL; } static Boolean eap_pax_check(struct eap_sm *sm, void *priv, struct wpabuf *respData) { struct eap_pax_data *data = priv; struct eap_pax_hdr *resp; const u8 *pos; size_t len, mlen; u8 icvbuf[EAP_PAX_ICV_LEN], *icv; pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PAX, respData, &len); if (pos == NULL || len < sizeof(*resp)) { wpa_printf(MSG_INFO, "EAP-PAX: Invalid frame"); return TRUE; } mlen = sizeof(struct eap_hdr) + 1 + len; resp = (struct eap_pax_hdr *) pos; wpa_printf(MSG_DEBUG, "EAP-PAX: received frame: op_code 0x%x " "flags 0x%x mac_id 0x%x dh_group_id 0x%x " "public_key_id 0x%x", resp->op_code, resp->flags, resp->mac_id, resp->dh_group_id, resp->public_key_id); wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: received payload", (u8 *) (resp + 1), len - sizeof(*resp) - EAP_PAX_ICV_LEN); if (data->state == PAX_STD_1 && resp->op_code != EAP_PAX_OP_STD_2) { wpa_printf(MSG_DEBUG, "EAP-PAX: Expected PAX_STD-2 - " "ignore op %d", resp->op_code); return TRUE; } if (data->state == PAX_STD_3 && resp->op_code != EAP_PAX_OP_ACK) { wpa_printf(MSG_DEBUG, "EAP-PAX: Expected PAX-ACK - " "ignore op %d", resp->op_code); return TRUE; } if (resp->op_code != EAP_PAX_OP_STD_2 && resp->op_code != EAP_PAX_OP_ACK) { wpa_printf(MSG_DEBUG, "EAP-PAX: Unknown op_code 0x%x", resp->op_code); } if (data->mac_id != resp->mac_id) { wpa_printf(MSG_DEBUG, "EAP-PAX: Expected MAC ID 0x%x, " "received 0x%x", data->mac_id, resp->mac_id); return TRUE; } if (resp->dh_group_id != EAP_PAX_DH_GROUP_NONE) { wpa_printf(MSG_INFO, "EAP-PAX: Expected DH Group ID 0x%x, " "received 0x%x", EAP_PAX_DH_GROUP_NONE, resp->dh_group_id); return TRUE; } if (resp->public_key_id != EAP_PAX_PUBLIC_KEY_NONE) { wpa_printf(MSG_INFO, "EAP-PAX: Expected Public Key ID 0x%x, " "received 0x%x", EAP_PAX_PUBLIC_KEY_NONE, resp->public_key_id); return TRUE; } if (resp->flags & EAP_PAX_FLAGS_MF) { /* TODO: add support for reassembling fragments */ wpa_printf(MSG_INFO, "EAP-PAX: fragmentation not supported"); return TRUE; } if (resp->flags & EAP_PAX_FLAGS_CE) { wpa_printf(MSG_INFO, "EAP-PAX: Unexpected CE flag"); return TRUE; } if (data->keys_set) { if (len - sizeof(*resp) < EAP_PAX_ICV_LEN) { wpa_printf(MSG_INFO, "EAP-PAX: No ICV in the packet"); return TRUE; } icv = wpabuf_mhead_u8(respData) + mlen - EAP_PAX_ICV_LEN; wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", icv, EAP_PAX_ICV_LEN); eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN, wpabuf_mhead(respData), wpabuf_len(respData) - EAP_PAX_ICV_LEN, NULL, 0, NULL, 0, icvbuf); if (os_memcmp(icvbuf, icv, EAP_PAX_ICV_LEN) != 0) { wpa_printf(MSG_INFO, "EAP-PAX: Invalid ICV"); wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Expected ICV", icvbuf, EAP_PAX_ICV_LEN); return TRUE; } } return FALSE; } static void eap_pax_process_std_2(struct eap_sm *sm, struct eap_pax_data *data, struct wpabuf *respData) { struct eap_pax_hdr *resp; u8 mac[EAP_PAX_MAC_LEN], icvbuf[EAP_PAX_ICV_LEN]; const u8 *pos; size_t len, left; int i; if (data->state != PAX_STD_1) return; wpa_printf(MSG_DEBUG, "EAP-PAX: Received PAX_STD-2"); pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PAX, respData, &len); if (pos == NULL || len < sizeof(*resp) + EAP_PAX_ICV_LEN) return; resp = (struct eap_pax_hdr *) pos; pos = (u8 *) (resp + 1); left = len - sizeof(*resp); if (left < 2 + EAP_PAX_RAND_LEN || WPA_GET_BE16(pos) != EAP_PAX_RAND_LEN) { wpa_printf(MSG_INFO, "EAP-PAX: Too short PAX_STD-2 (B)"); return; } pos += 2; left -= 2; os_memcpy(data->rand.r.y, pos, EAP_PAX_RAND_LEN); wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Y (client rand)", data->rand.r.y, EAP_PAX_RAND_LEN); pos += EAP_PAX_RAND_LEN; left -= EAP_PAX_RAND_LEN; if (left < 2 || (size_t) 2 + WPA_GET_BE16(pos) > left) { wpa_printf(MSG_INFO, "EAP-PAX: Too short PAX_STD-2 (CID)"); return; } data->cid_len = WPA_GET_BE16(pos); os_free(data->cid); data->cid = os_malloc(data->cid_len); if (data->cid == NULL) { wpa_printf(MSG_INFO, "EAP-PAX: Failed to allocate memory for " "CID"); return; } os_memcpy(data->cid, pos + 2, data->cid_len); pos += 2 + data->cid_len; left -= 2 + data->cid_len; wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-PAX: CID", (u8 *) data->cid, data->cid_len); if (left < 2 + EAP_PAX_MAC_LEN || WPA_GET_BE16(pos) != EAP_PAX_MAC_LEN) { wpa_printf(MSG_INFO, "EAP-PAX: Too short PAX_STD-2 (MAC_CK)"); return; } pos += 2; left -= 2; wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: MAC_CK(A, B, CID)", pos, EAP_PAX_MAC_LEN); if (eap_user_get(sm, (u8 *) data->cid, data->cid_len, 0) < 0) { wpa_hexdump_ascii(MSG_DEBUG, "EAP-PAX: unknown CID", (u8 *) data->cid, data->cid_len); data->state = FAILURE; return; } for (i = 0; i < EAP_MAX_METHODS && (sm->user->methods[i].vendor != EAP_VENDOR_IETF || sm->user->methods[i].method != EAP_TYPE_NONE); i++) { if (sm->user->methods[i].vendor == EAP_VENDOR_IETF && sm->user->methods[i].method == EAP_TYPE_PAX) break; } if (i >= EAP_MAX_METHODS || sm->user->methods[i].vendor != EAP_VENDOR_IETF || sm->user->methods[i].method != EAP_TYPE_PAX) { wpa_hexdump_ascii(MSG_DEBUG, "EAP-PAX: EAP-PAX not enabled for CID", (u8 *) data->cid, data->cid_len); data->state = FAILURE; return; } if (sm->user->password == NULL || sm->user->password_len != EAP_PAX_AK_LEN) { wpa_hexdump_ascii(MSG_DEBUG, "EAP-PAX: invalid password in " "user database for CID", (u8 *) data->cid, data->cid_len); data->state = FAILURE; return; } os_memcpy(data->ak, sm->user->password, EAP_PAX_AK_LEN); if (eap_pax_initial_key_derivation(data->mac_id, data->ak, data->rand.e, data->mk, data->ck, data->ick) < 0) { wpa_printf(MSG_INFO, "EAP-PAX: Failed to complete initial " "key derivation"); data->state = FAILURE; return; } data->keys_set = 1; eap_pax_mac(data->mac_id, data->ck, EAP_PAX_CK_LEN, data->rand.r.x, EAP_PAX_RAND_LEN, data->rand.r.y, EAP_PAX_RAND_LEN, (u8 *) data->cid, data->cid_len, mac); if (os_memcmp(mac, pos, EAP_PAX_MAC_LEN) != 0) { wpa_printf(MSG_INFO, "EAP-PAX: Invalid MAC_CK(A, B, CID) in " "PAX_STD-2"); wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Expected MAC_CK(A, B, CID)", mac, EAP_PAX_MAC_LEN); data->state = FAILURE; return; } pos += EAP_PAX_MAC_LEN; left -= EAP_PAX_MAC_LEN; if (left < EAP_PAX_ICV_LEN) { wpa_printf(MSG_INFO, "EAP-PAX: Too short ICV (%lu) in " "PAX_STD-2", (unsigned long) left); return; } wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", pos, EAP_PAX_ICV_LEN); eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN, wpabuf_head(respData), wpabuf_len(respData) - EAP_PAX_ICV_LEN, NULL, 0, NULL, 0, icvbuf); if (os_memcmp(icvbuf, pos, EAP_PAX_ICV_LEN) != 0) { wpa_printf(MSG_INFO, "EAP-PAX: Invalid ICV in PAX_STD-2"); wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Expected ICV", icvbuf, EAP_PAX_ICV_LEN); return; } pos += EAP_PAX_ICV_LEN; left -= EAP_PAX_ICV_LEN; if (left > 0) { wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ignored extra payload", pos, left); } data->state = PAX_STD_3; } static void eap_pax_process_ack(struct eap_sm *sm, struct eap_pax_data *data, struct wpabuf *respData) { if (data->state != PAX_STD_3) return; wpa_printf(MSG_DEBUG, "EAP-PAX: Received PAX-ACK - authentication " "completed successfully"); data->state = SUCCESS; } static void eap_pax_process(struct eap_sm *sm, void *priv, struct wpabuf *respData) { struct eap_pax_data *data = priv; struct eap_pax_hdr *resp; const u8 *pos; size_t len; if (sm->user == NULL || sm->user->password == NULL) { wpa_printf(MSG_INFO, "EAP-PAX: Plaintext password not " "configured"); data->state = FAILURE; return; } pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PAX, respData, &len); if (pos == NULL || len < sizeof(*resp)) return; resp = (struct eap_pax_hdr *) pos; switch (resp->op_code) { case EAP_PAX_OP_STD_2: eap_pax_process_std_2(sm, data, respData); break; case EAP_PAX_OP_ACK: eap_pax_process_ack(sm, data, respData); break; } } static Boolean eap_pax_isDone(struct eap_sm *sm, void *priv) { struct eap_pax_data *data = priv; return data->state == SUCCESS || data->state == FAILURE; } static u8 * eap_pax_getKey(struct eap_sm *sm, void *priv, size_t *len) { struct eap_pax_data *data = priv; u8 *key; if (data->state != SUCCESS) return NULL; key = os_malloc(EAP_MSK_LEN); if (key == NULL) return NULL; *len = EAP_MSK_LEN; eap_pax_kdf(data->mac_id, data->mk, EAP_PAX_MK_LEN, "Master Session Key", data->rand.e, 2 * EAP_PAX_RAND_LEN, EAP_MSK_LEN, key); return key; } static u8 * eap_pax_get_emsk(struct eap_sm *sm, void *priv, size_t *len) { struct eap_pax_data *data = priv; u8 *key; if (data->state != SUCCESS) return NULL; key = os_malloc(EAP_EMSK_LEN); if (key == NULL) return NULL; *len = EAP_EMSK_LEN; eap_pax_kdf(data->mac_id, data->mk, EAP_PAX_MK_LEN, "Extended Master Session Key", data->rand.e, 2 * EAP_PAX_RAND_LEN, EAP_EMSK_LEN, key); return key; } static Boolean eap_pax_isSuccess(struct eap_sm *sm, void *priv) { struct eap_pax_data *data = priv; return data->state == SUCCESS; } int eap_server_pax_register(void) { struct eap_method *eap; int ret; eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_PAX, "PAX"); if (eap == NULL) return -1; eap->init = eap_pax_init; eap->reset = eap_pax_reset; eap->buildReq = eap_pax_buildReq; eap->check = eap_pax_check; eap->process = eap_pax_process; eap->isDone = eap_pax_isDone; eap->getKey = eap_pax_getKey; eap->isSuccess = eap_pax_isSuccess; eap->get_emsk = eap_pax_get_emsk; ret = eap_server_method_register(eap); if (ret) eap_server_method_free(eap); return ret; } wpa-2.1/src/eap_server/eap_server_peap.c000066400000000000000000000770321227414666700204210ustar00rootroot00000000000000/* * hostapd / EAP-PEAP (draft-josefsson-pppext-eap-tls-eap-10.txt) * Copyright (c) 2004-2008, 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/sha1.h" #include "crypto/tls.h" #include "crypto/random.h" #include "eap_i.h" #include "eap_tls_common.h" #include "eap_common/eap_tlv_common.h" #include "eap_common/eap_peap_common.h" #include "tncs.h" /* Maximum supported PEAP version * 0 = Microsoft's PEAP version 0; draft-kamath-pppext-peapv0-00.txt * 1 = draft-josefsson-ppext-eap-tls-eap-05.txt */ #define EAP_PEAP_VERSION 1 static void eap_peap_reset(struct eap_sm *sm, void *priv); struct eap_peap_data { struct eap_ssl_data ssl; enum { START, PHASE1, PHASE1_ID2, PHASE2_START, PHASE2_ID, PHASE2_METHOD, PHASE2_SOH, PHASE2_TLV, SUCCESS_REQ, FAILURE_REQ, SUCCESS, FAILURE } state; int peap_version; int recv_version; const struct eap_method *phase2_method; void *phase2_priv; int force_version; struct wpabuf *pending_phase2_resp; enum { TLV_REQ_NONE, TLV_REQ_SUCCESS, TLV_REQ_FAILURE } tlv_request; int crypto_binding_sent; int crypto_binding_used; enum { NO_BINDING, OPTIONAL_BINDING, REQUIRE_BINDING } crypto_binding; u8 binding_nonce[32]; u8 ipmk[40]; u8 cmk[20]; u8 *phase2_key; size_t phase2_key_len; struct wpabuf *soh_response; }; static const char * eap_peap_state_txt(int state) { switch (state) { case START: return "START"; case PHASE1: return "PHASE1"; case PHASE1_ID2: return "PHASE1_ID2"; case PHASE2_START: return "PHASE2_START"; case PHASE2_ID: return "PHASE2_ID"; case PHASE2_METHOD: return "PHASE2_METHOD"; case PHASE2_SOH: return "PHASE2_SOH"; case PHASE2_TLV: return "PHASE2_TLV"; case SUCCESS_REQ: return "SUCCESS_REQ"; case FAILURE_REQ: return "FAILURE_REQ"; case SUCCESS: return "SUCCESS"; case FAILURE: return "FAILURE"; default: return "Unknown?!"; } } static void eap_peap_state(struct eap_peap_data *data, int state) { wpa_printf(MSG_DEBUG, "EAP-PEAP: %s -> %s", eap_peap_state_txt(data->state), eap_peap_state_txt(state)); data->state = state; } static void eap_peap_req_success(struct eap_sm *sm, struct eap_peap_data *data) { if (data->state == FAILURE || data->state == FAILURE_REQ) { eap_peap_state(data, FAILURE); return; } if (data->peap_version == 0) { data->tlv_request = TLV_REQ_SUCCESS; eap_peap_state(data, PHASE2_TLV); } else { eap_peap_state(data, SUCCESS_REQ); } } static void eap_peap_req_failure(struct eap_sm *sm, struct eap_peap_data *data) { if (data->state == FAILURE || data->state == FAILURE_REQ || data->state == SUCCESS_REQ || data->tlv_request != TLV_REQ_NONE) { eap_peap_state(data, FAILURE); return; } if (data->peap_version == 0) { data->tlv_request = TLV_REQ_FAILURE; eap_peap_state(data, PHASE2_TLV); } else { eap_peap_state(data, FAILURE_REQ); } } static void * eap_peap_init(struct eap_sm *sm) { struct eap_peap_data *data; data = os_zalloc(sizeof(*data)); if (data == NULL) return NULL; data->peap_version = EAP_PEAP_VERSION; data->force_version = -1; if (sm->user && sm->user->force_version >= 0) { data->force_version = sm->user->force_version; wpa_printf(MSG_DEBUG, "EAP-PEAP: forcing version %d", data->force_version); data->peap_version = data->force_version; } data->state = START; data->crypto_binding = OPTIONAL_BINDING; if (eap_server_tls_ssl_init(sm, &data->ssl, 0)) { wpa_printf(MSG_INFO, "EAP-PEAP: Failed to initialize SSL."); eap_peap_reset(sm, data); return NULL; } return data; } static void eap_peap_reset(struct eap_sm *sm, void *priv) { struct eap_peap_data *data = priv; if (data == NULL) return; if (data->phase2_priv && data->phase2_method) data->phase2_method->reset(sm, data->phase2_priv); eap_server_tls_ssl_deinit(sm, &data->ssl); wpabuf_free(data->pending_phase2_resp); os_free(data->phase2_key); wpabuf_free(data->soh_response); os_free(data); } static struct wpabuf * eap_peap_build_start(struct eap_sm *sm, struct eap_peap_data *data, u8 id) { struct wpabuf *req; req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PEAP, 1, EAP_CODE_REQUEST, id); if (req == NULL) { wpa_printf(MSG_ERROR, "EAP-PEAP: Failed to allocate memory for" " request"); eap_peap_state(data, FAILURE); return NULL; } wpabuf_put_u8(req, EAP_TLS_FLAGS_START | data->peap_version); eap_peap_state(data, PHASE1); return req; } static struct wpabuf * eap_peap_build_phase2_req(struct eap_sm *sm, struct eap_peap_data *data, u8 id) { struct wpabuf *buf, *encr_req, msgbuf; const u8 *req; size_t req_len; if (data->phase2_method == NULL || data->phase2_priv == NULL) { wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 method not ready"); return NULL; } buf = data->phase2_method->buildReq(sm, data->phase2_priv, id); if (buf == NULL) return NULL; req = wpabuf_head(buf); req_len = wpabuf_len(buf); wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: Encrypting Phase 2 data", req, req_len); if (data->peap_version == 0 && data->phase2_method->method != EAP_TYPE_TLV) { req += sizeof(struct eap_hdr); req_len -= sizeof(struct eap_hdr); } wpabuf_set(&msgbuf, req, req_len); encr_req = eap_server_tls_encrypt(sm, &data->ssl, &msgbuf); wpabuf_free(buf); return encr_req; } #ifdef EAP_SERVER_TNC static struct wpabuf * eap_peap_build_phase2_soh(struct eap_sm *sm, struct eap_peap_data *data, u8 id) { struct wpabuf *buf1, *buf, *encr_req, msgbuf; const u8 *req; size_t req_len; buf1 = tncs_build_soh_request(); if (buf1 == NULL) return NULL; buf = eap_msg_alloc(EAP_VENDOR_MICROSOFT, 0x21, wpabuf_len(buf1), EAP_CODE_REQUEST, id); if (buf == NULL) { wpabuf_free(buf1); return NULL; } wpabuf_put_buf(buf, buf1); wpabuf_free(buf1); req = wpabuf_head(buf); req_len = wpabuf_len(buf); wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: Encrypting Phase 2 SOH data", req, req_len); req += sizeof(struct eap_hdr); req_len -= sizeof(struct eap_hdr); wpabuf_set(&msgbuf, req, req_len); encr_req = eap_server_tls_encrypt(sm, &data->ssl, &msgbuf); wpabuf_free(buf); return encr_req; } #endif /* EAP_SERVER_TNC */ static void eap_peap_get_isk(struct eap_peap_data *data, u8 *isk, size_t isk_len) { size_t key_len; os_memset(isk, 0, isk_len); if (data->phase2_key == NULL) return; key_len = data->phase2_key_len; if (key_len > isk_len) key_len = isk_len; os_memcpy(isk, data->phase2_key, key_len); } static int eap_peap_derive_cmk(struct eap_sm *sm, struct eap_peap_data *data) { u8 *tk; u8 isk[32], imck[60]; /* * Tunnel key (TK) is the first 60 octets of the key generated by * phase 1 of PEAP (based on TLS). */ tk = eap_server_tls_derive_key(sm, &data->ssl, "client EAP encryption", EAP_TLS_KEY_LEN); if (tk == NULL) return -1; wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TK", tk, 60); eap_peap_get_isk(data, isk, sizeof(isk)); wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: ISK", isk, sizeof(isk)); /* * IPMK Seed = "Inner Methods Compound Keys" | ISK * TempKey = First 40 octets of TK * IPMK|CMK = PRF+(TempKey, IPMK Seed, 60) * (note: draft-josefsson-pppext-eap-tls-eap-10.txt includes a space * in the end of the label just before ISK; is that just a typo?) */ wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TempKey", tk, 40); if (peap_prfplus(data->peap_version, tk, 40, "Inner Methods Compound Keys", isk, sizeof(isk), imck, sizeof(imck)) < 0) { os_free(tk); return -1; } wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IMCK (IPMKj)", imck, sizeof(imck)); os_free(tk); /* TODO: fast-connect: IPMK|CMK = TK */ os_memcpy(data->ipmk, imck, 40); wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IPMK (S-IPMKj)", data->ipmk, 40); os_memcpy(data->cmk, imck + 40, 20); wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CMK (CMKj)", data->cmk, 20); return 0; } static struct wpabuf * eap_peap_build_phase2_tlv(struct eap_sm *sm, struct eap_peap_data *data, u8 id) { struct wpabuf *buf, *encr_req; size_t mlen; mlen = 6; /* Result TLV */ if (data->crypto_binding != NO_BINDING) mlen += 60; /* Cryptobinding TLV */ #ifdef EAP_SERVER_TNC if (data->soh_response) mlen += wpabuf_len(data->soh_response); #endif /* EAP_SERVER_TNC */ buf = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TLV, mlen, EAP_CODE_REQUEST, id); if (buf == NULL) return NULL; wpabuf_put_u8(buf, 0x80); /* Mandatory */ wpabuf_put_u8(buf, EAP_TLV_RESULT_TLV); /* Length */ wpabuf_put_be16(buf, 2); /* Status */ wpabuf_put_be16(buf, data->tlv_request == TLV_REQ_SUCCESS ? EAP_TLV_RESULT_SUCCESS : EAP_TLV_RESULT_FAILURE); if (data->peap_version == 0 && data->tlv_request == TLV_REQ_SUCCESS && data->crypto_binding != NO_BINDING) { u8 *mac; u8 eap_type = EAP_TYPE_PEAP; const u8 *addr[2]; size_t len[2]; u16 tlv_type; #ifdef EAP_SERVER_TNC if (data->soh_response) { wpa_printf(MSG_DEBUG, "EAP-PEAP: Adding MS-SOH " "Response TLV"); wpabuf_put_buf(buf, data->soh_response); wpabuf_free(data->soh_response); data->soh_response = NULL; } #endif /* EAP_SERVER_TNC */ if (eap_peap_derive_cmk(sm, data) < 0 || random_get_bytes(data->binding_nonce, 32)) { wpabuf_free(buf); return NULL; } /* Compound_MAC: HMAC-SHA1-160(cryptobinding TLV | EAP type) */ addr[0] = wpabuf_put(buf, 0); len[0] = 60; addr[1] = &eap_type; len[1] = 1; tlv_type = EAP_TLV_CRYPTO_BINDING_TLV; wpabuf_put_be16(buf, tlv_type); wpabuf_put_be16(buf, 56); wpabuf_put_u8(buf, 0); /* Reserved */ wpabuf_put_u8(buf, data->peap_version); /* Version */ wpabuf_put_u8(buf, data->recv_version); /* RecvVersion */ wpabuf_put_u8(buf, 0); /* SubType: 0 = Request, 1 = Response */ wpabuf_put_data(buf, data->binding_nonce, 32); /* Nonce */ mac = wpabuf_put(buf, 20); /* Compound_MAC */ wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC CMK", data->cmk, 20); wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC data 1", addr[0], len[0]); wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC data 2", addr[1], len[1]); hmac_sha1_vector(data->cmk, 20, 2, addr, len, mac); wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC", mac, SHA1_MAC_LEN); data->crypto_binding_sent = 1; } wpa_hexdump_buf_key(MSG_DEBUG, "EAP-PEAP: Encrypting Phase 2 TLV data", buf); encr_req = eap_server_tls_encrypt(sm, &data->ssl, buf); wpabuf_free(buf); return encr_req; } static struct wpabuf * eap_peap_build_phase2_term(struct eap_sm *sm, struct eap_peap_data *data, u8 id, int success) { struct wpabuf *encr_req, msgbuf; size_t req_len; struct eap_hdr *hdr; req_len = sizeof(*hdr); hdr = os_zalloc(req_len); if (hdr == NULL) return NULL; hdr->code = success ? EAP_CODE_SUCCESS : EAP_CODE_FAILURE; hdr->identifier = id; hdr->length = host_to_be16(req_len); wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: Encrypting Phase 2 data", (u8 *) hdr, req_len); wpabuf_set(&msgbuf, hdr, req_len); encr_req = eap_server_tls_encrypt(sm, &data->ssl, &msgbuf); os_free(hdr); return encr_req; } static struct wpabuf * eap_peap_buildReq(struct eap_sm *sm, void *priv, u8 id) { struct eap_peap_data *data = priv; if (data->ssl.state == FRAG_ACK) { return eap_server_tls_build_ack(id, EAP_TYPE_PEAP, data->peap_version); } if (data->ssl.state == WAIT_FRAG_ACK) { return eap_server_tls_build_msg(&data->ssl, EAP_TYPE_PEAP, data->peap_version, id); } switch (data->state) { case START: return eap_peap_build_start(sm, data, id); case PHASE1: case PHASE1_ID2: if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) { wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase1 done, " "starting Phase2"); eap_peap_state(data, PHASE2_START); } break; case PHASE2_ID: case PHASE2_METHOD: wpabuf_free(data->ssl.tls_out); data->ssl.tls_out_pos = 0; data->ssl.tls_out = eap_peap_build_phase2_req(sm, data, id); break; #ifdef EAP_SERVER_TNC case PHASE2_SOH: wpabuf_free(data->ssl.tls_out); data->ssl.tls_out_pos = 0; data->ssl.tls_out = eap_peap_build_phase2_soh(sm, data, id); break; #endif /* EAP_SERVER_TNC */ case PHASE2_TLV: wpabuf_free(data->ssl.tls_out); data->ssl.tls_out_pos = 0; data->ssl.tls_out = eap_peap_build_phase2_tlv(sm, data, id); break; case SUCCESS_REQ: wpabuf_free(data->ssl.tls_out); data->ssl.tls_out_pos = 0; data->ssl.tls_out = eap_peap_build_phase2_term(sm, data, id, 1); break; case FAILURE_REQ: wpabuf_free(data->ssl.tls_out); data->ssl.tls_out_pos = 0; data->ssl.tls_out = eap_peap_build_phase2_term(sm, data, id, 0); break; default: wpa_printf(MSG_DEBUG, "EAP-PEAP: %s - unexpected state %d", __func__, data->state); return NULL; } return eap_server_tls_build_msg(&data->ssl, EAP_TYPE_PEAP, data->peap_version, id); } static Boolean eap_peap_check(struct eap_sm *sm, void *priv, struct wpabuf *respData) { const u8 *pos; size_t len; pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PEAP, respData, &len); if (pos == NULL || len < 1) { wpa_printf(MSG_INFO, "EAP-PEAP: Invalid frame"); return TRUE; } return FALSE; } static int eap_peap_phase2_init(struct eap_sm *sm, struct eap_peap_data *data, EapType eap_type) { if (data->phase2_priv && data->phase2_method) { data->phase2_method->reset(sm, data->phase2_priv); data->phase2_method = NULL; data->phase2_priv = NULL; } data->phase2_method = eap_server_get_eap_method(EAP_VENDOR_IETF, eap_type); if (!data->phase2_method) return -1; sm->init_phase2 = 1; data->phase2_priv = data->phase2_method->init(sm); sm->init_phase2 = 0; return 0; } static int eap_tlv_validate_cryptobinding(struct eap_sm *sm, struct eap_peap_data *data, const u8 *crypto_tlv, size_t crypto_tlv_len) { u8 buf[61], mac[SHA1_MAC_LEN]; const u8 *pos; if (crypto_tlv_len != 4 + 56) { wpa_printf(MSG_DEBUG, "EAP-PEAP: Invalid cryptobinding TLV " "length %d", (int) crypto_tlv_len); return -1; } pos = crypto_tlv; pos += 4; /* TLV header */ if (pos[1] != data->peap_version) { wpa_printf(MSG_DEBUG, "EAP-PEAP: Cryptobinding TLV Version " "mismatch (was %d; expected %d)", pos[1], data->peap_version); return -1; } if (pos[3] != 1) { wpa_printf(MSG_DEBUG, "EAP-PEAP: Unexpected Cryptobinding TLV " "SubType %d", pos[3]); return -1; } pos += 4; pos += 32; /* Nonce */ /* Compound_MAC: HMAC-SHA1-160(cryptobinding TLV | EAP type) */ os_memcpy(buf, crypto_tlv, 60); os_memset(buf + 4 + 4 + 32, 0, 20); /* Compound_MAC */ buf[60] = EAP_TYPE_PEAP; hmac_sha1(data->cmk, 20, buf, sizeof(buf), mac); if (os_memcmp(mac, pos, SHA1_MAC_LEN) != 0) { wpa_printf(MSG_DEBUG, "EAP-PEAP: Invalid Compound_MAC in " "cryptobinding TLV"); wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CMK", data->cmk, 20); wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Cryptobinding seed data", buf, 61); return -1; } wpa_printf(MSG_DEBUG, "EAP-PEAP: Valid cryptobinding TLV received"); return 0; } static void eap_peap_process_phase2_tlv(struct eap_sm *sm, struct eap_peap_data *data, struct wpabuf *in_data) { const u8 *pos; size_t left; const u8 *result_tlv = NULL, *crypto_tlv = NULL; size_t result_tlv_len = 0, crypto_tlv_len = 0; int tlv_type, mandatory, tlv_len; pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TLV, in_data, &left); if (pos == NULL) { wpa_printf(MSG_DEBUG, "EAP-PEAP: Invalid EAP-TLV header"); return; } /* Parse TLVs */ wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Received TLVs", pos, left); while (left >= 4) { mandatory = !!(pos[0] & 0x80); tlv_type = pos[0] & 0x3f; tlv_type = (tlv_type << 8) | pos[1]; tlv_len = ((int) pos[2] << 8) | pos[3]; pos += 4; left -= 4; if ((size_t) tlv_len > left) { wpa_printf(MSG_DEBUG, "EAP-PEAP: TLV underrun " "(tlv_len=%d left=%lu)", tlv_len, (unsigned long) left); eap_peap_state(data, FAILURE); return; } switch (tlv_type) { case EAP_TLV_RESULT_TLV: result_tlv = pos; result_tlv_len = tlv_len; break; case EAP_TLV_CRYPTO_BINDING_TLV: crypto_tlv = pos; crypto_tlv_len = tlv_len; break; default: wpa_printf(MSG_DEBUG, "EAP-PEAP: Unsupported TLV Type " "%d%s", tlv_type, mandatory ? " (mandatory)" : ""); if (mandatory) { eap_peap_state(data, FAILURE); return; } /* Ignore this TLV, but process other TLVs */ break; } pos += tlv_len; left -= tlv_len; } if (left) { wpa_printf(MSG_DEBUG, "EAP-PEAP: Last TLV too short in " "Request (left=%lu)", (unsigned long) left); eap_peap_state(data, FAILURE); return; } /* Process supported TLVs */ if (crypto_tlv && data->crypto_binding_sent) { wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Cryptobinding TLV", crypto_tlv, crypto_tlv_len); if (eap_tlv_validate_cryptobinding(sm, data, crypto_tlv - 4, crypto_tlv_len + 4) < 0) { eap_peap_state(data, FAILURE); return; } data->crypto_binding_used = 1; } else if (!crypto_tlv && data->crypto_binding_sent && data->crypto_binding == REQUIRE_BINDING) { wpa_printf(MSG_DEBUG, "EAP-PEAP: No cryptobinding TLV"); eap_peap_state(data, FAILURE); return; } if (result_tlv) { int status; const char *requested; wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Result TLV", result_tlv, result_tlv_len); if (result_tlv_len < 2) { wpa_printf(MSG_INFO, "EAP-PEAP: Too short Result TLV " "(len=%lu)", (unsigned long) result_tlv_len); eap_peap_state(data, FAILURE); return; } requested = data->tlv_request == TLV_REQ_SUCCESS ? "Success" : "Failure"; status = WPA_GET_BE16(result_tlv); if (status == EAP_TLV_RESULT_SUCCESS) { wpa_printf(MSG_INFO, "EAP-PEAP: TLV Result - Success " "- requested %s", requested); if (data->tlv_request == TLV_REQ_SUCCESS) eap_peap_state(data, SUCCESS); else eap_peap_state(data, FAILURE); } else if (status == EAP_TLV_RESULT_FAILURE) { wpa_printf(MSG_INFO, "EAP-PEAP: TLV Result - Failure " "- requested %s", requested); eap_peap_state(data, FAILURE); } else { wpa_printf(MSG_INFO, "EAP-PEAP: Unknown TLV Result " "Status %d", status); eap_peap_state(data, FAILURE); } } } #ifdef EAP_SERVER_TNC static void eap_peap_process_phase2_soh(struct eap_sm *sm, struct eap_peap_data *data, struct wpabuf *in_data) { const u8 *pos, *vpos; size_t left; const u8 *soh_tlv = NULL; size_t soh_tlv_len = 0; int tlv_type, mandatory, tlv_len, vtlv_len; u8 next_type; u32 vendor_id; pos = eap_hdr_validate(EAP_VENDOR_MICROSOFT, 0x21, in_data, &left); if (pos == NULL) { wpa_printf(MSG_DEBUG, "EAP-PEAP: Not a valid SoH EAP " "Extensions Method header - skip TNC"); goto auth_method; } /* Parse TLVs */ wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Received TLVs (SoH)", pos, left); while (left >= 4) { mandatory = !!(pos[0] & 0x80); tlv_type = pos[0] & 0x3f; tlv_type = (tlv_type << 8) | pos[1]; tlv_len = ((int) pos[2] << 8) | pos[3]; pos += 4; left -= 4; if ((size_t) tlv_len > left) { wpa_printf(MSG_DEBUG, "EAP-PEAP: TLV underrun " "(tlv_len=%d left=%lu)", tlv_len, (unsigned long) left); eap_peap_state(data, FAILURE); return; } switch (tlv_type) { case EAP_TLV_VENDOR_SPECIFIC_TLV: if (tlv_len < 4) { wpa_printf(MSG_DEBUG, "EAP-PEAP: Too short " "vendor specific TLV (len=%d)", (int) tlv_len); eap_peap_state(data, FAILURE); return; } vendor_id = WPA_GET_BE32(pos); if (vendor_id != EAP_VENDOR_MICROSOFT) { if (mandatory) { eap_peap_state(data, FAILURE); return; } break; } vpos = pos + 4; mandatory = !!(vpos[0] & 0x80); tlv_type = vpos[0] & 0x3f; tlv_type = (tlv_type << 8) | vpos[1]; vtlv_len = ((int) vpos[2] << 8) | vpos[3]; vpos += 4; if (vpos + vtlv_len > pos + left) { wpa_printf(MSG_DEBUG, "EAP-PEAP: Vendor TLV " "underrun"); eap_peap_state(data, FAILURE); return; } if (tlv_type == 1) { soh_tlv = vpos; soh_tlv_len = vtlv_len; break; } wpa_printf(MSG_DEBUG, "EAP-PEAP: Unsupported MS-TLV " "Type %d%s", tlv_type, mandatory ? " (mandatory)" : ""); if (mandatory) { eap_peap_state(data, FAILURE); return; } /* Ignore this TLV, but process other TLVs */ break; default: wpa_printf(MSG_DEBUG, "EAP-PEAP: Unsupported TLV Type " "%d%s", tlv_type, mandatory ? " (mandatory)" : ""); if (mandatory) { eap_peap_state(data, FAILURE); return; } /* Ignore this TLV, but process other TLVs */ break; } pos += tlv_len; left -= tlv_len; } if (left) { wpa_printf(MSG_DEBUG, "EAP-PEAP: Last TLV too short in " "Request (left=%lu)", (unsigned long) left); eap_peap_state(data, FAILURE); return; } /* Process supported TLVs */ if (soh_tlv) { int failure = 0; wpabuf_free(data->soh_response); data->soh_response = tncs_process_soh(soh_tlv, soh_tlv_len, &failure); if (failure) { eap_peap_state(data, FAILURE); return; } } else { wpa_printf(MSG_DEBUG, "EAP-PEAP: No SoH TLV received"); eap_peap_state(data, FAILURE); return; } auth_method: eap_peap_state(data, PHASE2_METHOD); next_type = sm->user->methods[0].method; sm->user_eap_method_index = 1; wpa_printf(MSG_DEBUG, "EAP-PEAP: try EAP type %d", next_type); eap_peap_phase2_init(sm, data, next_type); } #endif /* EAP_SERVER_TNC */ static void eap_peap_process_phase2_response(struct eap_sm *sm, struct eap_peap_data *data, struct wpabuf *in_data) { u8 next_type = EAP_TYPE_NONE; const struct eap_hdr *hdr; const u8 *pos; size_t left; if (data->state == PHASE2_TLV) { eap_peap_process_phase2_tlv(sm, data, in_data); return; } #ifdef EAP_SERVER_TNC if (data->state == PHASE2_SOH) { eap_peap_process_phase2_soh(sm, data, in_data); return; } #endif /* EAP_SERVER_TNC */ if (data->phase2_priv == NULL) { wpa_printf(MSG_DEBUG, "EAP-PEAP: %s - Phase2 not " "initialized?!", __func__); return; } hdr = wpabuf_head(in_data); pos = (const u8 *) (hdr + 1); if (wpabuf_len(in_data) > sizeof(*hdr) && *pos == EAP_TYPE_NAK) { left = wpabuf_len(in_data) - sizeof(*hdr); wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Phase2 type Nak'ed; " "allowed types", pos + 1, left - 1); eap_sm_process_nak(sm, pos + 1, left - 1); if (sm->user && sm->user_eap_method_index < EAP_MAX_METHODS && sm->user->methods[sm->user_eap_method_index].method != EAP_TYPE_NONE) { next_type = sm->user->methods[ sm->user_eap_method_index++].method; wpa_printf(MSG_DEBUG, "EAP-PEAP: try EAP type %d", next_type); } else { eap_peap_req_failure(sm, data); next_type = EAP_TYPE_NONE; } eap_peap_phase2_init(sm, data, next_type); return; } if (data->phase2_method->check(sm, data->phase2_priv, in_data)) { wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase2 check() asked to " "ignore the packet"); return; } data->phase2_method->process(sm, data->phase2_priv, in_data); if (sm->method_pending == METHOD_PENDING_WAIT) { wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase2 method is in " "pending wait state - save decrypted response"); wpabuf_free(data->pending_phase2_resp); data->pending_phase2_resp = wpabuf_dup(in_data); } if (!data->phase2_method->isDone(sm, data->phase2_priv)) return; if (!data->phase2_method->isSuccess(sm, data->phase2_priv)) { wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase2 method failed"); eap_peap_req_failure(sm, data); next_type = EAP_TYPE_NONE; eap_peap_phase2_init(sm, data, next_type); return; } os_free(data->phase2_key); if (data->phase2_method->getKey) { data->phase2_key = data->phase2_method->getKey( sm, data->phase2_priv, &data->phase2_key_len); if (data->phase2_key == NULL) { wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase2 getKey " "failed"); eap_peap_req_failure(sm, data); eap_peap_phase2_init(sm, data, EAP_TYPE_NONE); return; } } switch (data->state) { case PHASE1_ID2: case PHASE2_ID: case PHASE2_SOH: if (eap_user_get(sm, sm->identity, sm->identity_len, 1) != 0) { wpa_hexdump_ascii(MSG_DEBUG, "EAP_PEAP: Phase2 " "Identity not found in the user " "database", sm->identity, sm->identity_len); eap_peap_req_failure(sm, data); next_type = EAP_TYPE_NONE; break; } #ifdef EAP_SERVER_TNC if (data->state != PHASE2_SOH && sm->tnc && data->peap_version == 0) { eap_peap_state(data, PHASE2_SOH); wpa_printf(MSG_DEBUG, "EAP-PEAP: Try to initialize " "TNC (NAP SOH)"); next_type = EAP_TYPE_NONE; break; } #endif /* EAP_SERVER_TNC */ eap_peap_state(data, PHASE2_METHOD); next_type = sm->user->methods[0].method; sm->user_eap_method_index = 1; wpa_printf(MSG_DEBUG, "EAP-PEAP: try EAP type %d", next_type); break; case PHASE2_METHOD: eap_peap_req_success(sm, data); next_type = EAP_TYPE_NONE; break; case FAILURE: break; default: wpa_printf(MSG_DEBUG, "EAP-PEAP: %s - unexpected state %d", __func__, data->state); break; } eap_peap_phase2_init(sm, data, next_type); } static void eap_peap_process_phase2(struct eap_sm *sm, struct eap_peap_data *data, const struct wpabuf *respData, struct wpabuf *in_buf) { struct wpabuf *in_decrypted; const struct eap_hdr *hdr; size_t len; wpa_printf(MSG_DEBUG, "EAP-PEAP: received %lu bytes encrypted data for" " Phase 2", (unsigned long) wpabuf_len(in_buf)); if (data->pending_phase2_resp) { wpa_printf(MSG_DEBUG, "EAP-PEAP: Pending Phase 2 response - " "skip decryption and use old data"); eap_peap_process_phase2_response(sm, data, data->pending_phase2_resp); wpabuf_free(data->pending_phase2_resp); data->pending_phase2_resp = NULL; return; } in_decrypted = tls_connection_decrypt(sm->ssl_ctx, data->ssl.conn, in_buf); if (in_decrypted == NULL) { wpa_printf(MSG_INFO, "EAP-PEAP: Failed to decrypt Phase 2 " "data"); eap_peap_state(data, FAILURE); return; } wpa_hexdump_buf_key(MSG_DEBUG, "EAP-PEAP: Decrypted Phase 2 EAP", in_decrypted); if (data->peap_version == 0 && data->state != PHASE2_TLV) { const struct eap_hdr *resp; struct eap_hdr *nhdr; struct wpabuf *nbuf = wpabuf_alloc(sizeof(struct eap_hdr) + wpabuf_len(in_decrypted)); if (nbuf == NULL) { wpabuf_free(in_decrypted); return; } resp = wpabuf_head(respData); nhdr = wpabuf_put(nbuf, sizeof(*nhdr)); nhdr->code = resp->code; nhdr->identifier = resp->identifier; nhdr->length = host_to_be16(sizeof(struct eap_hdr) + wpabuf_len(in_decrypted)); wpabuf_put_buf(nbuf, in_decrypted); wpabuf_free(in_decrypted); in_decrypted = nbuf; } hdr = wpabuf_head(in_decrypted); if (wpabuf_len(in_decrypted) < (int) sizeof(*hdr)) { wpa_printf(MSG_INFO, "EAP-PEAP: Too short Phase 2 " "EAP frame (len=%lu)", (unsigned long) wpabuf_len(in_decrypted)); wpabuf_free(in_decrypted); eap_peap_req_failure(sm, data); return; } len = be_to_host16(hdr->length); if (len > wpabuf_len(in_decrypted)) { wpa_printf(MSG_INFO, "EAP-PEAP: Length mismatch in " "Phase 2 EAP frame (len=%lu hdr->length=%lu)", (unsigned long) wpabuf_len(in_decrypted), (unsigned long) len); wpabuf_free(in_decrypted); eap_peap_req_failure(sm, data); return; } wpa_printf(MSG_DEBUG, "EAP-PEAP: received Phase 2: code=%d " "identifier=%d length=%lu", hdr->code, hdr->identifier, (unsigned long) len); switch (hdr->code) { case EAP_CODE_RESPONSE: eap_peap_process_phase2_response(sm, data, in_decrypted); break; case EAP_CODE_SUCCESS: wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Success"); if (data->state == SUCCESS_REQ) { eap_peap_state(data, SUCCESS); } break; case EAP_CODE_FAILURE: wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Failure"); eap_peap_state(data, FAILURE); break; default: wpa_printf(MSG_INFO, "EAP-PEAP: Unexpected code=%d in " "Phase 2 EAP header", hdr->code); break; } wpabuf_free(in_decrypted); } static int eap_peap_process_version(struct eap_sm *sm, void *priv, int peer_version) { struct eap_peap_data *data = priv; data->recv_version = peer_version; if (data->force_version >= 0 && peer_version != data->force_version) { wpa_printf(MSG_INFO, "EAP-PEAP: peer did not select the forced" " version (forced=%d peer=%d) - reject", data->force_version, peer_version); return -1; } if (peer_version < data->peap_version) { wpa_printf(MSG_DEBUG, "EAP-PEAP: peer ver=%d, own ver=%d; " "use version %d", peer_version, data->peap_version, peer_version); data->peap_version = peer_version; } return 0; } static void eap_peap_process_msg(struct eap_sm *sm, void *priv, const struct wpabuf *respData) { struct eap_peap_data *data = priv; switch (data->state) { case PHASE1: if (eap_server_tls_phase1(sm, &data->ssl) < 0) { eap_peap_state(data, FAILURE); break; } break; case PHASE2_START: eap_peap_state(data, PHASE2_ID); eap_peap_phase2_init(sm, data, EAP_TYPE_IDENTITY); break; case PHASE1_ID2: case PHASE2_ID: case PHASE2_METHOD: case PHASE2_SOH: case PHASE2_TLV: eap_peap_process_phase2(sm, data, respData, data->ssl.tls_in); break; case SUCCESS_REQ: eap_peap_state(data, SUCCESS); break; case FAILURE_REQ: eap_peap_state(data, FAILURE); break; default: wpa_printf(MSG_DEBUG, "EAP-PEAP: Unexpected state %d in %s", data->state, __func__); break; } } static void eap_peap_process(struct eap_sm *sm, void *priv, struct wpabuf *respData) { struct eap_peap_data *data = priv; if (eap_server_tls_process(sm, &data->ssl, respData, data, EAP_TYPE_PEAP, eap_peap_process_version, eap_peap_process_msg) < 0) eap_peap_state(data, FAILURE); } static Boolean eap_peap_isDone(struct eap_sm *sm, void *priv) { struct eap_peap_data *data = priv; return data->state == SUCCESS || data->state == FAILURE; } static u8 * eap_peap_getKey(struct eap_sm *sm, void *priv, size_t *len) { struct eap_peap_data *data = priv; u8 *eapKeyData; if (data->state != SUCCESS) return NULL; if (data->crypto_binding_used) { u8 csk[128]; /* * Note: It looks like Microsoft implementation requires null * termination for this label while the one used for deriving * IPMK|CMK did not use null termination. */ if (peap_prfplus(data->peap_version, data->ipmk, 40, "Session Key Generating Function", (u8 *) "\00", 1, csk, sizeof(csk)) < 0) return NULL; wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CSK", csk, sizeof(csk)); eapKeyData = os_malloc(EAP_TLS_KEY_LEN); if (eapKeyData) { os_memcpy(eapKeyData, csk, EAP_TLS_KEY_LEN); *len = EAP_TLS_KEY_LEN; wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Derived key", eapKeyData, EAP_TLS_KEY_LEN); } else { wpa_printf(MSG_DEBUG, "EAP-PEAP: Failed to derive " "key"); } return eapKeyData; } /* TODO: PEAPv1 - different label in some cases */ eapKeyData = eap_server_tls_derive_key(sm, &data->ssl, "client EAP encryption", EAP_TLS_KEY_LEN); if (eapKeyData) { *len = EAP_TLS_KEY_LEN; wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Derived key", eapKeyData, EAP_TLS_KEY_LEN); } else { wpa_printf(MSG_DEBUG, "EAP-PEAP: Failed to derive key"); } return eapKeyData; } static Boolean eap_peap_isSuccess(struct eap_sm *sm, void *priv) { struct eap_peap_data *data = priv; return data->state == SUCCESS; } int eap_server_peap_register(void) { struct eap_method *eap; int ret; eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_PEAP, "PEAP"); if (eap == NULL) return -1; eap->init = eap_peap_init; eap->reset = eap_peap_reset; eap->buildReq = eap_peap_buildReq; eap->check = eap_peap_check; eap->process = eap_peap_process; eap->isDone = eap_peap_isDone; eap->getKey = eap_peap_getKey; eap->isSuccess = eap_peap_isSuccess; ret = eap_server_method_register(eap); if (ret) eap_server_method_free(eap); return ret; } wpa-2.1/src/eap_server/eap_server_psk.c000066400000000000000000000306661227414666700202730ustar00rootroot00000000000000/* * hostapd / EAP-PSK (RFC 4764) server * Copyright (c) 2005-2007, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. * * Note: EAP-PSK is an EAP authentication method and as such, completely * different from WPA-PSK. This file is not needed for WPA-PSK functionality. */ #include "includes.h" #include "common.h" #include "crypto/aes_wrap.h" #include "crypto/random.h" #include "eap_common/eap_psk_common.h" #include "eap_server/eap_i.h" struct eap_psk_data { enum { PSK_1, PSK_3, SUCCESS, FAILURE } state; u8 rand_s[EAP_PSK_RAND_LEN]; u8 rand_p[EAP_PSK_RAND_LEN]; u8 *id_p; size_t id_p_len; u8 ak[EAP_PSK_AK_LEN], kdk[EAP_PSK_KDK_LEN], tek[EAP_PSK_TEK_LEN]; u8 msk[EAP_MSK_LEN]; u8 emsk[EAP_EMSK_LEN]; }; static void * eap_psk_init(struct eap_sm *sm) { struct eap_psk_data *data; data = os_zalloc(sizeof(*data)); if (data == NULL) return NULL; data->state = PSK_1; return data; } static void eap_psk_reset(struct eap_sm *sm, void *priv) { struct eap_psk_data *data = priv; os_free(data->id_p); os_free(data); } static struct wpabuf * eap_psk_build_1(struct eap_sm *sm, struct eap_psk_data *data, u8 id) { struct wpabuf *req; struct eap_psk_hdr_1 *psk; wpa_printf(MSG_DEBUG, "EAP-PSK: PSK-1 (sending)"); if (random_get_bytes(data->rand_s, EAP_PSK_RAND_LEN)) { wpa_printf(MSG_ERROR, "EAP-PSK: Failed to get random data"); data->state = FAILURE; return NULL; } wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: RAND_S (server rand)", data->rand_s, EAP_PSK_RAND_LEN); req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PSK, sizeof(*psk) + sm->server_id_len, EAP_CODE_REQUEST, id); if (req == NULL) { wpa_printf(MSG_ERROR, "EAP-PSK: Failed to allocate memory " "request"); data->state = FAILURE; return NULL; } psk = wpabuf_put(req, sizeof(*psk)); psk->flags = EAP_PSK_FLAGS_SET_T(0); /* T=0 */ os_memcpy(psk->rand_s, data->rand_s, EAP_PSK_RAND_LEN); wpabuf_put_data(req, sm->server_id, sm->server_id_len); return req; } static struct wpabuf * eap_psk_build_3(struct eap_sm *sm, struct eap_psk_data *data, u8 id) { struct wpabuf *req; struct eap_psk_hdr_3 *psk; u8 *buf, *pchannel, nonce[16]; size_t buflen; wpa_printf(MSG_DEBUG, "EAP-PSK: PSK-3 (sending)"); req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PSK, sizeof(*psk) + 4 + 16 + 1, EAP_CODE_REQUEST, id); if (req == NULL) { wpa_printf(MSG_ERROR, "EAP-PSK: Failed to allocate memory " "request"); data->state = FAILURE; return NULL; } psk = wpabuf_put(req, sizeof(*psk)); psk->flags = EAP_PSK_FLAGS_SET_T(2); /* T=2 */ os_memcpy(psk->rand_s, data->rand_s, EAP_PSK_RAND_LEN); /* MAC_S = OMAC1-AES-128(AK, ID_S||RAND_P) */ buflen = sm->server_id_len + EAP_PSK_RAND_LEN; buf = os_malloc(buflen); if (buf == NULL) goto fail; os_memcpy(buf, sm->server_id, sm->server_id_len); os_memcpy(buf + sm->server_id_len, data->rand_p, EAP_PSK_RAND_LEN); if (omac1_aes_128(data->ak, buf, buflen, psk->mac_s)) { os_free(buf); goto fail; } os_free(buf); if (eap_psk_derive_keys(data->kdk, data->rand_p, data->tek, data->msk, data->emsk)) goto fail; wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: TEK", data->tek, EAP_PSK_TEK_LEN); wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: MSK", data->msk, EAP_MSK_LEN); wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: EMSK", data->emsk, EAP_EMSK_LEN); os_memset(nonce, 0, sizeof(nonce)); pchannel = wpabuf_put(req, 4 + 16 + 1); os_memcpy(pchannel, nonce + 12, 4); os_memset(pchannel + 4, 0, 16); /* Tag */ pchannel[4 + 16] = EAP_PSK_R_FLAG_DONE_SUCCESS << 6; wpa_hexdump(MSG_DEBUG, "EAP-PSK: PCHANNEL (plaintext)", pchannel, 4 + 16 + 1); if (aes_128_eax_encrypt(data->tek, nonce, sizeof(nonce), wpabuf_head(req), 22, pchannel + 4 + 16, 1, pchannel + 4)) goto fail; wpa_hexdump(MSG_DEBUG, "EAP-PSK: PCHANNEL (encrypted)", pchannel, 4 + 16 + 1); return req; fail: wpabuf_free(req); data->state = FAILURE; return NULL; } static struct wpabuf * eap_psk_buildReq(struct eap_sm *sm, void *priv, u8 id) { struct eap_psk_data *data = priv; switch (data->state) { case PSK_1: return eap_psk_build_1(sm, data, id); case PSK_3: return eap_psk_build_3(sm, data, id); default: wpa_printf(MSG_DEBUG, "EAP-PSK: Unknown state %d in buildReq", data->state); break; } return NULL; } static Boolean eap_psk_check(struct eap_sm *sm, void *priv, struct wpabuf *respData) { struct eap_psk_data *data = priv; size_t len; u8 t; const u8 *pos; pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PSK, respData, &len); if (pos == NULL || len < 1) { wpa_printf(MSG_INFO, "EAP-PSK: Invalid frame"); return TRUE; } t = EAP_PSK_FLAGS_GET_T(*pos); wpa_printf(MSG_DEBUG, "EAP-PSK: received frame: T=%d", t); if (data->state == PSK_1 && t != 1) { wpa_printf(MSG_DEBUG, "EAP-PSK: Expected PSK-2 - " "ignore T=%d", t); return TRUE; } if (data->state == PSK_3 && t != 3) { wpa_printf(MSG_DEBUG, "EAP-PSK: Expected PSK-4 - " "ignore T=%d", t); return TRUE; } if ((t == 1 && len < sizeof(struct eap_psk_hdr_2)) || (t == 3 && len < sizeof(struct eap_psk_hdr_4))) { wpa_printf(MSG_DEBUG, "EAP-PSK: Too short frame"); return TRUE; } return FALSE; } static void eap_psk_process_2(struct eap_sm *sm, struct eap_psk_data *data, struct wpabuf *respData) { const struct eap_psk_hdr_2 *resp; u8 *pos, mac[EAP_PSK_MAC_LEN], *buf; size_t left, buflen; int i; const u8 *cpos; if (data->state != PSK_1) return; wpa_printf(MSG_DEBUG, "EAP-PSK: Received PSK-2"); cpos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PSK, respData, &left); if (cpos == NULL || left < sizeof(*resp)) { wpa_printf(MSG_INFO, "EAP-PSK: Invalid frame"); return; } resp = (const struct eap_psk_hdr_2 *) cpos; cpos = (const u8 *) (resp + 1); left -= sizeof(*resp); os_free(data->id_p); data->id_p = os_malloc(left); if (data->id_p == NULL) { wpa_printf(MSG_INFO, "EAP-PSK: Failed to allocate memory for " "ID_P"); return; } os_memcpy(data->id_p, cpos, left); data->id_p_len = left; wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-PSK: ID_P", data->id_p, data->id_p_len); if (eap_user_get(sm, data->id_p, data->id_p_len, 0) < 0) { wpa_hexdump_ascii(MSG_DEBUG, "EAP-PSK: unknown ID_P", data->id_p, data->id_p_len); data->state = FAILURE; return; } for (i = 0; i < EAP_MAX_METHODS && (sm->user->methods[i].vendor != EAP_VENDOR_IETF || sm->user->methods[i].method != EAP_TYPE_NONE); i++) { if (sm->user->methods[i].vendor == EAP_VENDOR_IETF && sm->user->methods[i].method == EAP_TYPE_PSK) break; } if (i >= EAP_MAX_METHODS || sm->user->methods[i].vendor != EAP_VENDOR_IETF || sm->user->methods[i].method != EAP_TYPE_PSK) { wpa_hexdump_ascii(MSG_DEBUG, "EAP-PSK: EAP-PSK not enabled for ID_P", data->id_p, data->id_p_len); data->state = FAILURE; return; } if (sm->user->password == NULL || sm->user->password_len != EAP_PSK_PSK_LEN) { wpa_hexdump_ascii(MSG_DEBUG, "EAP-PSK: invalid password in " "user database for ID_P", data->id_p, data->id_p_len); data->state = FAILURE; return; } if (eap_psk_key_setup(sm->user->password, data->ak, data->kdk)) { data->state = FAILURE; return; } wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: AK", data->ak, EAP_PSK_AK_LEN); wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: KDK", data->kdk, EAP_PSK_KDK_LEN); wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: RAND_P (client rand)", resp->rand_p, EAP_PSK_RAND_LEN); os_memcpy(data->rand_p, resp->rand_p, EAP_PSK_RAND_LEN); /* MAC_P = OMAC1-AES-128(AK, ID_P||ID_S||RAND_S||RAND_P) */ buflen = data->id_p_len + sm->server_id_len + 2 * EAP_PSK_RAND_LEN; buf = os_malloc(buflen); if (buf == NULL) { data->state = FAILURE; return; } os_memcpy(buf, data->id_p, data->id_p_len); pos = buf + data->id_p_len; os_memcpy(pos, sm->server_id, sm->server_id_len); pos += sm->server_id_len; os_memcpy(pos, data->rand_s, EAP_PSK_RAND_LEN); pos += EAP_PSK_RAND_LEN; os_memcpy(pos, data->rand_p, EAP_PSK_RAND_LEN); if (omac1_aes_128(data->ak, buf, buflen, mac)) { os_free(buf); data->state = FAILURE; return; } os_free(buf); wpa_hexdump(MSG_DEBUG, "EAP-PSK: MAC_P", resp->mac_p, EAP_PSK_MAC_LEN); if (os_memcmp(mac, resp->mac_p, EAP_PSK_MAC_LEN) != 0) { wpa_printf(MSG_INFO, "EAP-PSK: Invalid MAC_P"); wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: Expected MAC_P", mac, EAP_PSK_MAC_LEN); data->state = FAILURE; return; } data->state = PSK_3; } static void eap_psk_process_4(struct eap_sm *sm, struct eap_psk_data *data, struct wpabuf *respData) { const struct eap_psk_hdr_4 *resp; u8 *decrypted, nonce[16]; size_t left; const u8 *pos, *tag; if (data->state != PSK_3) return; wpa_printf(MSG_DEBUG, "EAP-PSK: Received PSK-4"); pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PSK, respData, &left); if (pos == NULL || left < sizeof(*resp)) { wpa_printf(MSG_INFO, "EAP-PSK: Invalid frame"); return; } resp = (const struct eap_psk_hdr_4 *) pos; pos = (const u8 *) (resp + 1); left -= sizeof(*resp); wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: Encrypted PCHANNEL", pos, left); if (left < 4 + 16 + 1) { wpa_printf(MSG_INFO, "EAP-PSK: Too short PCHANNEL data in " "PSK-4 (len=%lu, expected 21)", (unsigned long) left); return; } if (pos[0] == 0 && pos[1] == 0 && pos[2] == 0 && pos[3] == 0) { wpa_printf(MSG_DEBUG, "EAP-PSK: Nonce did not increase"); return; } os_memset(nonce, 0, 12); os_memcpy(nonce + 12, pos, 4); pos += 4; left -= 4; tag = pos; pos += 16; left -= 16; decrypted = os_malloc(left); if (decrypted == NULL) return; os_memcpy(decrypted, pos, left); if (aes_128_eax_decrypt(data->tek, nonce, sizeof(nonce), wpabuf_head(respData), 22, decrypted, left, tag)) { wpa_printf(MSG_WARNING, "EAP-PSK: PCHANNEL decryption failed"); os_free(decrypted); data->state = FAILURE; return; } wpa_hexdump(MSG_DEBUG, "EAP-PSK: Decrypted PCHANNEL message", decrypted, left); /* Verify R flag */ switch (decrypted[0] >> 6) { case EAP_PSK_R_FLAG_CONT: wpa_printf(MSG_DEBUG, "EAP-PSK: R flag - CONT - unsupported"); data->state = FAILURE; break; case EAP_PSK_R_FLAG_DONE_SUCCESS: wpa_printf(MSG_DEBUG, "EAP-PSK: R flag - DONE_SUCCESS"); data->state = SUCCESS; break; case EAP_PSK_R_FLAG_DONE_FAILURE: wpa_printf(MSG_DEBUG, "EAP-PSK: R flag - DONE_FAILURE"); data->state = FAILURE; break; } os_free(decrypted); } static void eap_psk_process(struct eap_sm *sm, void *priv, struct wpabuf *respData) { struct eap_psk_data *data = priv; const u8 *pos; size_t len; if (sm->user == NULL || sm->user->password == NULL) { wpa_printf(MSG_INFO, "EAP-PSK: Plaintext password not " "configured"); data->state = FAILURE; return; } pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PSK, respData, &len); if (pos == NULL || len < 1) return; switch (EAP_PSK_FLAGS_GET_T(*pos)) { case 1: eap_psk_process_2(sm, data, respData); break; case 3: eap_psk_process_4(sm, data, respData); break; } } static Boolean eap_psk_isDone(struct eap_sm *sm, void *priv) { struct eap_psk_data *data = priv; return data->state == SUCCESS || data->state == FAILURE; } static u8 * eap_psk_getKey(struct eap_sm *sm, void *priv, size_t *len) { struct eap_psk_data *data = priv; u8 *key; if (data->state != SUCCESS) return NULL; key = os_malloc(EAP_MSK_LEN); if (key == NULL) return NULL; os_memcpy(key, data->msk, EAP_MSK_LEN); *len = EAP_MSK_LEN; return key; } static u8 * eap_psk_get_emsk(struct eap_sm *sm, void *priv, size_t *len) { struct eap_psk_data *data = priv; u8 *key; if (data->state != SUCCESS) return NULL; key = os_malloc(EAP_EMSK_LEN); if (key == NULL) return NULL; os_memcpy(key, data->emsk, EAP_EMSK_LEN); *len = EAP_EMSK_LEN; return key; } static Boolean eap_psk_isSuccess(struct eap_sm *sm, void *priv) { struct eap_psk_data *data = priv; return data->state == SUCCESS; } int eap_server_psk_register(void) { struct eap_method *eap; int ret; eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_PSK, "PSK"); if (eap == NULL) return -1; eap->init = eap_psk_init; eap->reset = eap_psk_reset; eap->buildReq = eap_psk_buildReq; eap->check = eap_psk_check; eap->process = eap_psk_process; eap->isDone = eap_psk_isDone; eap->getKey = eap_psk_getKey; eap->isSuccess = eap_psk_isSuccess; eap->get_emsk = eap_psk_get_emsk; ret = eap_server_method_register(eap); if (ret) eap_server_method_free(eap); return ret; } wpa-2.1/src/eap_server/eap_server_pwd.c000066400000000000000000000673671227414666700203000ustar00rootroot00000000000000/* * hostapd / EAP-pwd (RFC 5931) server * Copyright (c) 2010, Dan Harkins * * 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/sha256.h" #include "eap_server/eap_i.h" #include "eap_common/eap_pwd_common.h" struct eap_pwd_data { enum { PWD_ID_Req, PWD_Commit_Req, PWD_Confirm_Req, SUCCESS, FAILURE } state; u8 *id_peer; size_t id_peer_len; u8 *id_server; size_t id_server_len; u8 *password; size_t password_len; u32 token; u16 group_num; EAP_PWD_group *grp; struct wpabuf *inbuf; size_t in_frag_pos; struct wpabuf *outbuf; size_t out_frag_pos; size_t mtu; BIGNUM *k; BIGNUM *private_value; BIGNUM *peer_scalar; BIGNUM *my_scalar; EC_POINT *my_element; EC_POINT *peer_element; u8 my_confirm[SHA256_MAC_LEN]; u8 msk[EAP_MSK_LEN]; u8 emsk[EAP_EMSK_LEN]; BN_CTX *bnctx; }; static const char * eap_pwd_state_txt(int state) { switch (state) { case PWD_ID_Req: return "PWD-ID-Req"; case PWD_Commit_Req: return "PWD-Commit-Req"; case PWD_Confirm_Req: return "PWD-Confirm-Req"; case SUCCESS: return "SUCCESS"; case FAILURE: return "FAILURE"; default: return "PWD-Unk"; } } static void eap_pwd_state(struct eap_pwd_data *data, int state) { wpa_printf(MSG_DEBUG, "EAP-pwd: %s -> %s", eap_pwd_state_txt(data->state), eap_pwd_state_txt(state)); data->state = state; } static void * eap_pwd_init(struct eap_sm *sm) { struct eap_pwd_data *data; if (sm->user == NULL || sm->user->password == NULL || sm->user->password_len == 0) { wpa_printf(MSG_INFO, "EAP-PWD (server): Password is not " "configured"); return NULL; } data = os_zalloc(sizeof(*data)); if (data == NULL) return NULL; data->group_num = sm->pwd_group; wpa_printf(MSG_DEBUG, "EAP-pwd: Selected group number %d", data->group_num); data->state = PWD_ID_Req; data->id_server = (u8 *) os_strdup("server"); if (data->id_server) data->id_server_len = os_strlen((char *) data->id_server); data->password = os_malloc(sm->user->password_len); if (data->password == NULL) { wpa_printf(MSG_INFO, "EAP-PWD: Memory allocation password " "fail"); os_free(data->id_server); os_free(data); return NULL; } data->password_len = sm->user->password_len; os_memcpy(data->password, sm->user->password, data->password_len); data->bnctx = BN_CTX_new(); if (data->bnctx == NULL) { wpa_printf(MSG_INFO, "EAP-PWD: bn context allocation fail"); os_free(data->password); os_free(data->id_server); os_free(data); return NULL; } data->in_frag_pos = data->out_frag_pos = 0; data->inbuf = data->outbuf = NULL; data->mtu = 1020; /* default from RFC 5931, make it configurable! */ return data; } static void eap_pwd_reset(struct eap_sm *sm, void *priv) { struct eap_pwd_data *data = priv; BN_free(data->private_value); BN_free(data->peer_scalar); BN_free(data->my_scalar); BN_free(data->k); BN_CTX_free(data->bnctx); EC_POINT_free(data->my_element); EC_POINT_free(data->peer_element); os_free(data->id_peer); os_free(data->id_server); os_free(data->password); if (data->grp) { EC_GROUP_free(data->grp->group); EC_POINT_free(data->grp->pwe); BN_free(data->grp->order); BN_free(data->grp->prime); os_free(data->grp); } os_free(data); } static void eap_pwd_build_id_req(struct eap_sm *sm, struct eap_pwd_data *data, u8 id) { wpa_printf(MSG_DEBUG, "EAP-pwd: ID/Request"); /* * if we're fragmenting then we already have an id request, just return */ if (data->out_frag_pos) return; data->outbuf = wpabuf_alloc(sizeof(struct eap_pwd_id) + data->id_server_len); if (data->outbuf == NULL) { eap_pwd_state(data, FAILURE); return; } /* an lfsr is good enough to generate unpredictable tokens */ data->token = os_random(); wpabuf_put_be16(data->outbuf, data->group_num); wpabuf_put_u8(data->outbuf, EAP_PWD_DEFAULT_RAND_FUNC); wpabuf_put_u8(data->outbuf, EAP_PWD_DEFAULT_PRF); wpabuf_put_data(data->outbuf, &data->token, sizeof(data->token)); wpabuf_put_u8(data->outbuf, EAP_PWD_PREP_NONE); wpabuf_put_data(data->outbuf, data->id_server, data->id_server_len); } static void eap_pwd_build_commit_req(struct eap_sm *sm, struct eap_pwd_data *data, u8 id) { BIGNUM *mask = NULL, *x = NULL, *y = NULL; u8 *scalar = NULL, *element = NULL; u16 offset; wpa_printf(MSG_DEBUG, "EAP-pwd: Commit/Request"); /* * if we're fragmenting then we already have an commit request, just * return */ if (data->out_frag_pos) return; if (((data->private_value = BN_new()) == NULL) || ((data->my_element = EC_POINT_new(data->grp->group)) == NULL) || ((data->my_scalar = BN_new()) == NULL) || ((mask = BN_new()) == NULL)) { wpa_printf(MSG_INFO, "EAP-PWD (server): scalar allocation " "fail"); goto fin; } BN_rand_range(data->private_value, data->grp->order); BN_rand_range(mask, data->grp->order); BN_add(data->my_scalar, data->private_value, mask); BN_mod(data->my_scalar, data->my_scalar, data->grp->order, data->bnctx); if (!EC_POINT_mul(data->grp->group, data->my_element, NULL, data->grp->pwe, mask, data->bnctx)) { wpa_printf(MSG_INFO, "EAP-PWD (server): element allocation " "fail"); eap_pwd_state(data, FAILURE); goto fin; } if (!EC_POINT_invert(data->grp->group, data->my_element, data->bnctx)) { wpa_printf(MSG_INFO, "EAP-PWD (server): element inversion " "fail"); goto fin; } BN_free(mask); if (((x = BN_new()) == NULL) || ((y = BN_new()) == NULL)) { wpa_printf(MSG_INFO, "EAP-PWD (server): point allocation " "fail"); goto fin; } if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, data->my_element, x, y, data->bnctx)) { wpa_printf(MSG_INFO, "EAP-PWD (server): point assignment " "fail"); goto fin; } if (((scalar = os_malloc(BN_num_bytes(data->grp->order))) == NULL) || ((element = os_malloc(BN_num_bytes(data->grp->prime) * 2)) == NULL)) { wpa_printf(MSG_INFO, "EAP-PWD (server): data allocation fail"); goto fin; } /* * bignums occupy as little memory as possible so one that is * sufficiently smaller than the prime or order might need pre-pending * with zeros. */ os_memset(scalar, 0, BN_num_bytes(data->grp->order)); os_memset(element, 0, BN_num_bytes(data->grp->prime) * 2); offset = BN_num_bytes(data->grp->order) - BN_num_bytes(data->my_scalar); BN_bn2bin(data->my_scalar, scalar + offset); offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); BN_bn2bin(x, element + offset); offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y); BN_bn2bin(y, element + BN_num_bytes(data->grp->prime) + offset); data->outbuf = wpabuf_alloc(2 * BN_num_bytes(data->grp->prime) + BN_num_bytes(data->grp->order)); if (data->outbuf == NULL) goto fin; /* We send the element as (x,y) followed by the scalar */ wpabuf_put_data(data->outbuf, element, 2 * BN_num_bytes(data->grp->prime)); wpabuf_put_data(data->outbuf, scalar, BN_num_bytes(data->grp->order)); fin: os_free(scalar); os_free(element); BN_free(x); BN_free(y); if (data->outbuf == NULL) eap_pwd_state(data, FAILURE); } static void eap_pwd_build_confirm_req(struct eap_sm *sm, struct eap_pwd_data *data, u8 id) { BIGNUM *x = NULL, *y = NULL; struct crypto_hash *hash; u8 conf[SHA256_MAC_LEN], *cruft = NULL, *ptr; u16 grp; int offset; wpa_printf(MSG_DEBUG, "EAP-pwd: Confirm/Request"); /* * if we're fragmenting then we already have an confirm request, just * return */ if (data->out_frag_pos) return; /* Each component of the cruft will be at most as big as the prime */ if (((cruft = os_malloc(BN_num_bytes(data->grp->prime))) == NULL) || ((x = BN_new()) == NULL) || ((y = BN_new()) == NULL)) { wpa_printf(MSG_INFO, "EAP-PWD (server): debug allocation " "fail"); goto fin; } /* * commit is H(k | server_element | server_scalar | peer_element | * peer_scalar | ciphersuite) */ hash = eap_pwd_h_init(); if (hash == NULL) goto fin; /* * Zero the memory each time because this is mod prime math and some * value may start with a few zeros and the previous one did not. * * First is k */ os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(data->k); BN_bn2bin(data->k, cruft + offset); eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); /* server element: x, y */ if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, data->my_element, x, y, data->bnctx)) { wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point " "assignment fail"); goto fin; } os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); BN_bn2bin(x, cruft + offset); eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y); BN_bn2bin(y, cruft + offset); eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); /* server scalar */ os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->order) - BN_num_bytes(data->my_scalar); BN_bn2bin(data->my_scalar, cruft + offset); eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order)); /* peer element: x, y */ if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, data->peer_element, x, y, data->bnctx)) { wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point " "assignment fail"); goto fin; } os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); BN_bn2bin(x, cruft + offset); eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y); BN_bn2bin(y, cruft + offset); eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); /* peer scalar */ os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->order) - BN_num_bytes(data->peer_scalar); BN_bn2bin(data->peer_scalar, cruft + offset); eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order)); /* ciphersuite */ grp = htons(data->group_num); os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); ptr = cruft; os_memcpy(ptr, &grp, sizeof(u16)); ptr += sizeof(u16); *ptr = EAP_PWD_DEFAULT_RAND_FUNC; ptr += sizeof(u8); *ptr = EAP_PWD_DEFAULT_PRF; ptr += sizeof(u8); eap_pwd_h_update(hash, cruft, ptr - cruft); /* all done with the random function */ eap_pwd_h_final(hash, conf); os_memcpy(data->my_confirm, conf, SHA256_MAC_LEN); data->outbuf = wpabuf_alloc(SHA256_MAC_LEN); if (data->outbuf == NULL) goto fin; wpabuf_put_data(data->outbuf, conf, SHA256_MAC_LEN); fin: os_free(cruft); BN_free(x); BN_free(y); if (data->outbuf == NULL) eap_pwd_state(data, FAILURE); } static struct wpabuf * eap_pwd_build_req(struct eap_sm *sm, void *priv, u8 id) { struct eap_pwd_data *data = priv; struct wpabuf *req; u8 lm_exch; const u8 *buf; u16 totlen = 0; size_t len; /* * if we're buffering response fragments then just ACK */ if (data->in_frag_pos) { wpa_printf(MSG_DEBUG, "EAP-pwd: ACKing a fragment!!"); req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD, EAP_PWD_HDR_SIZE, EAP_CODE_REQUEST, id); if (req == NULL) { eap_pwd_state(data, FAILURE); return NULL; } switch (data->state) { case PWD_ID_Req: wpabuf_put_u8(req, EAP_PWD_OPCODE_ID_EXCH); break; case PWD_Commit_Req: wpabuf_put_u8(req, EAP_PWD_OPCODE_COMMIT_EXCH); break; case PWD_Confirm_Req: wpabuf_put_u8(req, EAP_PWD_OPCODE_CONFIRM_EXCH); break; default: eap_pwd_state(data, FAILURE); /* just to be sure */ wpabuf_free(req); return NULL; } return req; } /* * build the data portion of a request */ switch (data->state) { case PWD_ID_Req: eap_pwd_build_id_req(sm, data, id); lm_exch = EAP_PWD_OPCODE_ID_EXCH; break; case PWD_Commit_Req: eap_pwd_build_commit_req(sm, data, id); lm_exch = EAP_PWD_OPCODE_COMMIT_EXCH; break; case PWD_Confirm_Req: eap_pwd_build_confirm_req(sm, data, id); lm_exch = EAP_PWD_OPCODE_CONFIRM_EXCH; break; default: wpa_printf(MSG_INFO, "EAP-pwd: Unknown state %d in build_req", data->state); eap_pwd_state(data, FAILURE); lm_exch = 0; /* hush now, sweet compiler */ break; } if (data->state == FAILURE) return NULL; /* * determine whether that data needs to be fragmented */ len = wpabuf_len(data->outbuf) - data->out_frag_pos; if ((len + EAP_PWD_HDR_SIZE) > data->mtu) { len = data->mtu - EAP_PWD_HDR_SIZE; EAP_PWD_SET_MORE_BIT(lm_exch); /* * if this is the first fragment, need to set the M bit * and add the total length to the eap_pwd_hdr */ if (data->out_frag_pos == 0) { EAP_PWD_SET_LENGTH_BIT(lm_exch); totlen = wpabuf_len(data->outbuf) + EAP_PWD_HDR_SIZE + sizeof(u16); len -= sizeof(u16); wpa_printf(MSG_DEBUG, "EAP-pwd: Fragmenting output, " "total length = %d", totlen); } wpa_printf(MSG_DEBUG, "EAP-pwd: Send a %d byte fragment", (int) len); } /* * alloc an eap request and populate it with the data */ req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD, EAP_PWD_HDR_SIZE + len + (totlen ? sizeof(u16) : 0), EAP_CODE_REQUEST, id); if (req == NULL) { eap_pwd_state(data, FAILURE); return NULL; } wpabuf_put_u8(req, lm_exch); if (EAP_PWD_GET_LENGTH_BIT(lm_exch)) wpabuf_put_be16(req, totlen); buf = wpabuf_head_u8(data->outbuf); wpabuf_put_data(req, buf + data->out_frag_pos, len); data->out_frag_pos += len; /* * either not fragged or last fragment, either way free up the data */ if (data->out_frag_pos >= wpabuf_len(data->outbuf)) { wpabuf_free(data->outbuf); data->out_frag_pos = 0; } return req; } static Boolean eap_pwd_check(struct eap_sm *sm, void *priv, struct wpabuf *respData) { struct eap_pwd_data *data = priv; const u8 *pos; size_t len; pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PWD, respData, &len); if (pos == NULL || len < 1) { wpa_printf(MSG_INFO, "EAP-pwd: Invalid frame"); return TRUE; } wpa_printf(MSG_DEBUG, "EAP-pwd: Received frame: exch = %d, len = %d", EAP_PWD_GET_EXCHANGE(*pos), (int) len); if (data->state == PWD_ID_Req && ((EAP_PWD_GET_EXCHANGE(*pos)) == EAP_PWD_OPCODE_ID_EXCH)) return FALSE; if (data->state == PWD_Commit_Req && ((EAP_PWD_GET_EXCHANGE(*pos)) == EAP_PWD_OPCODE_COMMIT_EXCH)) return FALSE; if (data->state == PWD_Confirm_Req && ((EAP_PWD_GET_EXCHANGE(*pos)) == EAP_PWD_OPCODE_CONFIRM_EXCH)) return FALSE; wpa_printf(MSG_INFO, "EAP-pwd: Unexpected opcode=%d in state=%d", *pos, data->state); return TRUE; } static void eap_pwd_process_id_resp(struct eap_sm *sm, struct eap_pwd_data *data, const u8 *payload, size_t payload_len) { struct eap_pwd_id *id; if (payload_len < sizeof(struct eap_pwd_id)) { wpa_printf(MSG_INFO, "EAP-pwd: Invalid ID response"); return; } id = (struct eap_pwd_id *) payload; if ((data->group_num != be_to_host16(id->group_num)) || (id->random_function != EAP_PWD_DEFAULT_RAND_FUNC) || (os_memcmp(id->token, (u8 *)&data->token, sizeof(data->token))) || (id->prf != EAP_PWD_DEFAULT_PRF)) { wpa_printf(MSG_INFO, "EAP-pwd: peer changed parameters"); eap_pwd_state(data, FAILURE); return; } data->id_peer = os_malloc(payload_len - sizeof(struct eap_pwd_id)); if (data->id_peer == NULL) { wpa_printf(MSG_INFO, "EAP-PWD: memory allocation id fail"); return; } data->id_peer_len = payload_len - sizeof(struct eap_pwd_id); os_memcpy(data->id_peer, id->identity, data->id_peer_len); wpa_hexdump_ascii(MSG_DEBUG, "EAP-PWD (server): peer sent id of", data->id_peer, data->id_peer_len); if ((data->grp = os_malloc(sizeof(EAP_PWD_group))) == NULL) { wpa_printf(MSG_INFO, "EAP-PWD: failed to allocate memory for " "group"); return; } if (compute_password_element(data->grp, data->group_num, data->password, data->password_len, data->id_server, data->id_server_len, data->id_peer, data->id_peer_len, (u8 *) &data->token)) { wpa_printf(MSG_INFO, "EAP-PWD (server): unable to compute " "PWE"); return; } wpa_printf(MSG_DEBUG, "EAP-PWD (server): computed %d bit PWE...", BN_num_bits(data->grp->prime)); eap_pwd_state(data, PWD_Commit_Req); } static void eap_pwd_process_commit_resp(struct eap_sm *sm, struct eap_pwd_data *data, const u8 *payload, size_t payload_len) { u8 *ptr; BIGNUM *x = NULL, *y = NULL, *cofactor = NULL; EC_POINT *K = NULL, *point = NULL; int res = 0; wpa_printf(MSG_DEBUG, "EAP-pwd: Received commit response"); if (((data->peer_scalar = BN_new()) == NULL) || ((data->k = BN_new()) == NULL) || ((cofactor = BN_new()) == NULL) || ((x = BN_new()) == NULL) || ((y = BN_new()) == NULL) || ((point = EC_POINT_new(data->grp->group)) == NULL) || ((K = EC_POINT_new(data->grp->group)) == NULL) || ((data->peer_element = EC_POINT_new(data->grp->group)) == NULL)) { wpa_printf(MSG_INFO, "EAP-PWD (server): peer data allocation " "fail"); goto fin; } if (!EC_GROUP_get_cofactor(data->grp->group, cofactor, NULL)) { wpa_printf(MSG_INFO, "EAP-PWD (server): unable to get " "cofactor for curve"); goto fin; } /* element, x then y, followed by scalar */ ptr = (u8 *) payload; BN_bin2bn(ptr, BN_num_bytes(data->grp->prime), x); ptr += BN_num_bytes(data->grp->prime); BN_bin2bn(ptr, BN_num_bytes(data->grp->prime), y); ptr += BN_num_bytes(data->grp->prime); BN_bin2bn(ptr, BN_num_bytes(data->grp->order), data->peer_scalar); if (!EC_POINT_set_affine_coordinates_GFp(data->grp->group, data->peer_element, x, y, data->bnctx)) { wpa_printf(MSG_INFO, "EAP-PWD (server): setting peer element " "fail"); goto fin; } /* check to ensure peer's element is not in a small sub-group */ if (BN_cmp(cofactor, BN_value_one())) { if (!EC_POINT_mul(data->grp->group, point, NULL, data->peer_element, cofactor, NULL)) { wpa_printf(MSG_INFO, "EAP-PWD (server): cannot " "multiply peer element by order"); goto fin; } if (EC_POINT_is_at_infinity(data->grp->group, point)) { wpa_printf(MSG_INFO, "EAP-PWD (server): peer element " "is at infinity!\n"); goto fin; } } /* compute the shared key, k */ if ((!EC_POINT_mul(data->grp->group, K, NULL, data->grp->pwe, data->peer_scalar, data->bnctx)) || (!EC_POINT_add(data->grp->group, K, K, data->peer_element, data->bnctx)) || (!EC_POINT_mul(data->grp->group, K, NULL, K, data->private_value, data->bnctx))) { wpa_printf(MSG_INFO, "EAP-PWD (server): computing shared key " "fail"); goto fin; } /* ensure that the shared key isn't in a small sub-group */ if (BN_cmp(cofactor, BN_value_one())) { if (!EC_POINT_mul(data->grp->group, K, NULL, K, cofactor, NULL)) { wpa_printf(MSG_INFO, "EAP-PWD (server): cannot " "multiply shared key point by order!\n"); goto fin; } } /* * This check is strictly speaking just for the case above where * co-factor > 1 but it was suggested that even though this is probably * never going to happen it is a simple and safe check "just to be * sure" so let's be safe. */ if (EC_POINT_is_at_infinity(data->grp->group, K)) { wpa_printf(MSG_INFO, "EAP-PWD (server): shared key point is " "at infinity"); goto fin; } if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, K, data->k, NULL, data->bnctx)) { wpa_printf(MSG_INFO, "EAP-PWD (server): unable to extract " "shared secret from secret point"); goto fin; } res = 1; fin: EC_POINT_free(K); EC_POINT_free(point); BN_free(cofactor); BN_free(x); BN_free(y); if (res) eap_pwd_state(data, PWD_Confirm_Req); else eap_pwd_state(data, FAILURE); } static void eap_pwd_process_confirm_resp(struct eap_sm *sm, struct eap_pwd_data *data, const u8 *payload, size_t payload_len) { BIGNUM *x = NULL, *y = NULL; struct crypto_hash *hash; u32 cs; u16 grp; u8 conf[SHA256_MAC_LEN], *cruft = NULL, *ptr; int offset; /* build up the ciphersuite: group | random_function | prf */ grp = htons(data->group_num); ptr = (u8 *) &cs; os_memcpy(ptr, &grp, sizeof(u16)); ptr += sizeof(u16); *ptr = EAP_PWD_DEFAULT_RAND_FUNC; ptr += sizeof(u8); *ptr = EAP_PWD_DEFAULT_PRF; /* each component of the cruft will be at most as big as the prime */ if (((cruft = os_malloc(BN_num_bytes(data->grp->prime))) == NULL) || ((x = BN_new()) == NULL) || ((y = BN_new()) == NULL)) { wpa_printf(MSG_INFO, "EAP-PWD (peer): allocation fail"); goto fin; } /* * commit is H(k | peer_element | peer_scalar | server_element | * server_scalar | ciphersuite) */ hash = eap_pwd_h_init(); if (hash == NULL) goto fin; /* k */ os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(data->k); BN_bn2bin(data->k, cruft + offset); eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); /* peer element: x, y */ if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, data->peer_element, x, y, data->bnctx)) { wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point " "assignment fail"); goto fin; } os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); BN_bn2bin(x, cruft + offset); eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y); BN_bn2bin(y, cruft + offset); eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); /* peer scalar */ os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->order) - BN_num_bytes(data->peer_scalar); BN_bn2bin(data->peer_scalar, cruft + offset); eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order)); /* server element: x, y */ if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, data->my_element, x, y, data->bnctx)) { wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point " "assignment fail"); goto fin; } os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); BN_bn2bin(x, cruft + offset); eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y); BN_bn2bin(y, cruft + offset); eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); /* server scalar */ os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->order) - BN_num_bytes(data->my_scalar); BN_bn2bin(data->my_scalar, cruft + offset); eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order)); /* ciphersuite */ os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); eap_pwd_h_update(hash, (u8 *) &cs, sizeof(u32)); /* all done */ eap_pwd_h_final(hash, conf); ptr = (u8 *) payload; if (os_memcmp(conf, ptr, SHA256_MAC_LEN)) { wpa_printf(MSG_INFO, "EAP-PWD (server): confirm did not " "verify"); goto fin; } wpa_printf(MSG_DEBUG, "EAP-pwd (server): confirm verified"); if (compute_keys(data->grp, data->bnctx, data->k, data->peer_scalar, data->my_scalar, conf, data->my_confirm, &cs, data->msk, data->emsk) < 0) eap_pwd_state(data, FAILURE); else eap_pwd_state(data, SUCCESS); fin: os_free(cruft); BN_free(x); BN_free(y); } static void eap_pwd_process(struct eap_sm *sm, void *priv, struct wpabuf *respData) { struct eap_pwd_data *data = priv; const u8 *pos; size_t len; u8 lm_exch; u16 tot_len; pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PWD, respData, &len); if ((pos == NULL) || (len < 1)) { wpa_printf(MSG_INFO, "Bad EAP header! pos %s and len = %d", (pos == NULL) ? "is NULL" : "is not NULL", (int) len); return; } lm_exch = *pos; pos++; /* skip over the bits and the exch */ len--; /* * if we're fragmenting then this should be an ACK with no data, * just return and continue fragmenting in the "build" section above */ if (data->out_frag_pos) { if (len > 1) wpa_printf(MSG_INFO, "EAP-pwd: Bad response! " "Fragmenting but not an ACK"); else wpa_printf(MSG_DEBUG, "EAP-pwd: received ACK from " "peer"); return; } /* * if we're receiving fragmented packets then we need to buffer... * * the first fragment has a total length */ if (EAP_PWD_GET_LENGTH_BIT(lm_exch)) { tot_len = WPA_GET_BE16(pos); wpa_printf(MSG_DEBUG, "EAP-pwd: Incoming fragments, total " "length = %d", tot_len); data->inbuf = wpabuf_alloc(tot_len); if (data->inbuf == NULL) { wpa_printf(MSG_INFO, "EAP-pwd: Out of memory to " "buffer fragments!"); return; } pos += sizeof(u16); len -= sizeof(u16); } /* * the first and all intermediate fragments have the M bit set */ if (EAP_PWD_GET_MORE_BIT(lm_exch)) { if ((data->in_frag_pos + len) > wpabuf_size(data->inbuf)) { wpa_printf(MSG_DEBUG, "EAP-pwd: Buffer overflow " "attack detected! (%d+%d > %d)", (int) data->in_frag_pos, (int) len, (int) wpabuf_size(data->inbuf)); eap_pwd_state(data, FAILURE); return; } wpabuf_put_data(data->inbuf, pos, len); data->in_frag_pos += len; wpa_printf(MSG_DEBUG, "EAP-pwd: Got a %d byte fragment", (int) len); return; } /* * last fragment won't have the M bit set (but we're obviously * buffering fragments so that's how we know it's the last) */ if (data->in_frag_pos) { wpabuf_put_data(data->inbuf, pos, len); data->in_frag_pos += len; pos = wpabuf_head_u8(data->inbuf); len = data->in_frag_pos; wpa_printf(MSG_DEBUG, "EAP-pwd: Last fragment, %d bytes", (int) len); } switch (EAP_PWD_GET_EXCHANGE(lm_exch)) { case EAP_PWD_OPCODE_ID_EXCH: eap_pwd_process_id_resp(sm, data, pos, len); break; case EAP_PWD_OPCODE_COMMIT_EXCH: eap_pwd_process_commit_resp(sm, data, pos, len); break; case EAP_PWD_OPCODE_CONFIRM_EXCH: eap_pwd_process_confirm_resp(sm, data, pos, len); break; } /* * if we had been buffering fragments, here's a great place * to clean up */ if (data->in_frag_pos) { wpabuf_free(data->inbuf); data->in_frag_pos = 0; } } static u8 * eap_pwd_getkey(struct eap_sm *sm, void *priv, size_t *len) { struct eap_pwd_data *data = priv; u8 *key; if (data->state != SUCCESS) return NULL; key = os_malloc(EAP_MSK_LEN); if (key == NULL) return NULL; os_memcpy(key, data->msk, EAP_MSK_LEN); *len = EAP_MSK_LEN; return key; } static u8 * eap_pwd_get_emsk(struct eap_sm *sm, void *priv, size_t *len) { struct eap_pwd_data *data = priv; u8 *key; if (data->state != SUCCESS) return NULL; key = os_malloc(EAP_EMSK_LEN); if (key == NULL) return NULL; os_memcpy(key, data->emsk, EAP_EMSK_LEN); *len = EAP_EMSK_LEN; return key; } static Boolean eap_pwd_is_success(struct eap_sm *sm, void *priv) { struct eap_pwd_data *data = priv; return data->state == SUCCESS; } static Boolean eap_pwd_is_done(struct eap_sm *sm, void *priv) { struct eap_pwd_data *data = priv; return (data->state == SUCCESS) || (data->state == FAILURE); } int eap_server_pwd_register(void) { struct eap_method *eap; int ret; struct timeval tp; struct timezone tz; u32 sr; EVP_add_digest(EVP_sha256()); sr = 0xdeaddada; (void) gettimeofday(&tp, &tz); sr ^= (tp.tv_sec ^ tp.tv_usec); srandom(sr); eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_PWD, "PWD"); if (eap == NULL) return -1; eap->init = eap_pwd_init; eap->reset = eap_pwd_reset; eap->buildReq = eap_pwd_build_req; eap->check = eap_pwd_check; eap->process = eap_pwd_process; eap->isDone = eap_pwd_is_done; eap->getKey = eap_pwd_getkey; eap->get_emsk = eap_pwd_get_emsk; eap->isSuccess = eap_pwd_is_success; ret = eap_server_method_register(eap); if (ret) eap_server_method_free(eap); return ret; } wpa-2.1/src/eap_server/eap_server_sake.c000066400000000000000000000305251227414666700204130ustar00rootroot00000000000000/* * hostapd / EAP-SAKE (RFC 4763) server * Copyright (c) 2006-2007, 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/random.h" #include "eap_server/eap_i.h" #include "eap_common/eap_sake_common.h" struct eap_sake_data { enum { IDENTITY, CHALLENGE, CONFIRM, SUCCESS, FAILURE } state; u8 rand_s[EAP_SAKE_RAND_LEN]; u8 rand_p[EAP_SAKE_RAND_LEN]; struct { u8 auth[EAP_SAKE_TEK_AUTH_LEN]; u8 cipher[EAP_SAKE_TEK_CIPHER_LEN]; } tek; u8 msk[EAP_MSK_LEN]; u8 emsk[EAP_EMSK_LEN]; u8 session_id; u8 *peerid; size_t peerid_len; }; static const char * eap_sake_state_txt(int state) { switch (state) { case IDENTITY: return "IDENTITY"; case CHALLENGE: return "CHALLENGE"; case CONFIRM: return "CONFIRM"; case SUCCESS: return "SUCCESS"; case FAILURE: return "FAILURE"; default: return "?"; } } static void eap_sake_state(struct eap_sake_data *data, int state) { wpa_printf(MSG_DEBUG, "EAP-SAKE: %s -> %s", eap_sake_state_txt(data->state), eap_sake_state_txt(state)); data->state = state; } static void * eap_sake_init(struct eap_sm *sm) { struct eap_sake_data *data; data = os_zalloc(sizeof(*data)); if (data == NULL) return NULL; data->state = CHALLENGE; if (os_get_random(&data->session_id, 1)) { wpa_printf(MSG_ERROR, "EAP-SAKE: Failed to get random data"); os_free(data); return NULL; } wpa_printf(MSG_DEBUG, "EAP-SAKE: Initialized Session ID %d", data->session_id); return data; } static void eap_sake_reset(struct eap_sm *sm, void *priv) { struct eap_sake_data *data = priv; os_free(data->peerid); os_free(data); } static struct wpabuf * eap_sake_build_msg(struct eap_sake_data *data, u8 id, size_t length, u8 subtype) { struct eap_sake_hdr *sake; struct wpabuf *msg; size_t plen; plen = sizeof(struct eap_sake_hdr) + length; msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_SAKE, plen, EAP_CODE_REQUEST, id); if (msg == NULL) { wpa_printf(MSG_ERROR, "EAP-SAKE: Failed to allocate memory " "request"); return NULL; } sake = wpabuf_put(msg, sizeof(*sake)); sake->version = EAP_SAKE_VERSION; sake->session_id = data->session_id; sake->subtype = subtype; return msg; } static struct wpabuf * eap_sake_build_identity(struct eap_sm *sm, struct eap_sake_data *data, u8 id) { struct wpabuf *msg; size_t plen; wpa_printf(MSG_DEBUG, "EAP-SAKE: Request/Identity"); plen = 4; plen += 2 + sm->server_id_len; msg = eap_sake_build_msg(data, id, plen, EAP_SAKE_SUBTYPE_IDENTITY); if (msg == NULL) { data->state = FAILURE; return NULL; } wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_PERM_ID_REQ"); eap_sake_add_attr(msg, EAP_SAKE_AT_PERM_ID_REQ, NULL, 2); wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_SERVERID"); eap_sake_add_attr(msg, EAP_SAKE_AT_SERVERID, sm->server_id, sm->server_id_len); return msg; } static struct wpabuf * eap_sake_build_challenge(struct eap_sm *sm, struct eap_sake_data *data, u8 id) { struct wpabuf *msg; size_t plen; wpa_printf(MSG_DEBUG, "EAP-SAKE: Request/Challenge"); if (random_get_bytes(data->rand_s, EAP_SAKE_RAND_LEN)) { wpa_printf(MSG_ERROR, "EAP-SAKE: Failed to get random data"); data->state = FAILURE; return NULL; } wpa_hexdump(MSG_MSGDUMP, "EAP-SAKE: RAND_S (server rand)", data->rand_s, EAP_SAKE_RAND_LEN); plen = 2 + EAP_SAKE_RAND_LEN + 2 + sm->server_id_len; msg = eap_sake_build_msg(data, id, plen, EAP_SAKE_SUBTYPE_CHALLENGE); if (msg == NULL) { data->state = FAILURE; return NULL; } wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_RAND_S"); eap_sake_add_attr(msg, EAP_SAKE_AT_RAND_S, data->rand_s, EAP_SAKE_RAND_LEN); wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_SERVERID"); eap_sake_add_attr(msg, EAP_SAKE_AT_SERVERID, sm->server_id, sm->server_id_len); return msg; } static struct wpabuf * eap_sake_build_confirm(struct eap_sm *sm, struct eap_sake_data *data, u8 id) { struct wpabuf *msg; u8 *mic; wpa_printf(MSG_DEBUG, "EAP-SAKE: Request/Confirm"); msg = eap_sake_build_msg(data, id, 2 + EAP_SAKE_MIC_LEN, EAP_SAKE_SUBTYPE_CONFIRM); if (msg == NULL) { data->state = FAILURE; return NULL; } wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_MIC_S"); wpabuf_put_u8(msg, EAP_SAKE_AT_MIC_S); wpabuf_put_u8(msg, 2 + EAP_SAKE_MIC_LEN); mic = wpabuf_put(msg, EAP_SAKE_MIC_LEN); if (eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p, sm->server_id, sm->server_id_len, data->peerid, data->peerid_len, 0, wpabuf_head(msg), wpabuf_len(msg), mic, mic)) { wpa_printf(MSG_INFO, "EAP-SAKE: Failed to compute MIC"); data->state = FAILURE; os_free(msg); return NULL; } return msg; } static struct wpabuf * eap_sake_buildReq(struct eap_sm *sm, void *priv, u8 id) { struct eap_sake_data *data = priv; switch (data->state) { case IDENTITY: return eap_sake_build_identity(sm, data, id); case CHALLENGE: return eap_sake_build_challenge(sm, data, id); case CONFIRM: return eap_sake_build_confirm(sm, data, id); default: wpa_printf(MSG_DEBUG, "EAP-SAKE: Unknown state %d in buildReq", data->state); break; } return NULL; } static Boolean eap_sake_check(struct eap_sm *sm, void *priv, struct wpabuf *respData) { struct eap_sake_data *data = priv; struct eap_sake_hdr *resp; size_t len; u8 version, session_id, subtype; const u8 *pos; pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SAKE, respData, &len); if (pos == NULL || len < sizeof(struct eap_sake_hdr)) { wpa_printf(MSG_INFO, "EAP-SAKE: Invalid frame"); return TRUE; } resp = (struct eap_sake_hdr *) pos; version = resp->version; session_id = resp->session_id; subtype = resp->subtype; if (version != EAP_SAKE_VERSION) { wpa_printf(MSG_INFO, "EAP-SAKE: Unknown version %d", version); return TRUE; } if (session_id != data->session_id) { wpa_printf(MSG_INFO, "EAP-SAKE: Session ID mismatch (%d,%d)", session_id, data->session_id); return TRUE; } wpa_printf(MSG_DEBUG, "EAP-SAKE: Received frame: subtype=%d", subtype); if (data->state == IDENTITY && subtype == EAP_SAKE_SUBTYPE_IDENTITY) return FALSE; if (data->state == CHALLENGE && subtype == EAP_SAKE_SUBTYPE_CHALLENGE) return FALSE; if (data->state == CONFIRM && subtype == EAP_SAKE_SUBTYPE_CONFIRM) return FALSE; if (subtype == EAP_SAKE_SUBTYPE_AUTH_REJECT) return FALSE; wpa_printf(MSG_INFO, "EAP-SAKE: Unexpected subtype=%d in state=%d", subtype, data->state); return TRUE; } static void eap_sake_process_identity(struct eap_sm *sm, struct eap_sake_data *data, const struct wpabuf *respData, const u8 *payload, size_t payloadlen) { if (data->state != IDENTITY) return; wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Response/Identity"); /* TODO: update identity and select new user data */ eap_sake_state(data, CHALLENGE); } static void eap_sake_process_challenge(struct eap_sm *sm, struct eap_sake_data *data, const struct wpabuf *respData, const u8 *payload, size_t payloadlen) { struct eap_sake_parse_attr attr; u8 mic_p[EAP_SAKE_MIC_LEN]; if (data->state != CHALLENGE) return; wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Response/Challenge"); if (eap_sake_parse_attributes(payload, payloadlen, &attr)) return; if (!attr.rand_p || !attr.mic_p) { wpa_printf(MSG_INFO, "EAP-SAKE: Response/Challenge did not " "include AT_RAND_P or AT_MIC_P"); return; } os_memcpy(data->rand_p, attr.rand_p, EAP_SAKE_RAND_LEN); os_free(data->peerid); data->peerid = NULL; data->peerid_len = 0; if (attr.peerid) { data->peerid = os_malloc(attr.peerid_len); if (data->peerid == NULL) return; os_memcpy(data->peerid, attr.peerid, attr.peerid_len); data->peerid_len = attr.peerid_len; } if (sm->user == NULL || sm->user->password == NULL || sm->user->password_len != 2 * EAP_SAKE_ROOT_SECRET_LEN) { wpa_printf(MSG_INFO, "EAP-SAKE: Plaintext password with " "%d-byte key not configured", 2 * EAP_SAKE_ROOT_SECRET_LEN); data->state = FAILURE; return; } eap_sake_derive_keys(sm->user->password, sm->user->password + EAP_SAKE_ROOT_SECRET_LEN, data->rand_s, data->rand_p, (u8 *) &data->tek, data->msk, data->emsk); eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p, sm->server_id, sm->server_id_len, data->peerid, data->peerid_len, 1, wpabuf_head(respData), wpabuf_len(respData), attr.mic_p, mic_p); if (os_memcmp(attr.mic_p, mic_p, EAP_SAKE_MIC_LEN) != 0) { wpa_printf(MSG_INFO, "EAP-SAKE: Incorrect AT_MIC_P"); eap_sake_state(data, FAILURE); return; } eap_sake_state(data, CONFIRM); } static void eap_sake_process_confirm(struct eap_sm *sm, struct eap_sake_data *data, const struct wpabuf *respData, const u8 *payload, size_t payloadlen) { struct eap_sake_parse_attr attr; u8 mic_p[EAP_SAKE_MIC_LEN]; if (data->state != CONFIRM) return; wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Response/Confirm"); if (eap_sake_parse_attributes(payload, payloadlen, &attr)) return; if (!attr.mic_p) { wpa_printf(MSG_INFO, "EAP-SAKE: Response/Confirm did not " "include AT_MIC_P"); return; } eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p, sm->server_id, sm->server_id_len, data->peerid, data->peerid_len, 1, wpabuf_head(respData), wpabuf_len(respData), attr.mic_p, mic_p); if (os_memcmp(attr.mic_p, mic_p, EAP_SAKE_MIC_LEN) != 0) { wpa_printf(MSG_INFO, "EAP-SAKE: Incorrect AT_MIC_P"); eap_sake_state(data, FAILURE); } else eap_sake_state(data, SUCCESS); } static void eap_sake_process_auth_reject(struct eap_sm *sm, struct eap_sake_data *data, const struct wpabuf *respData, const u8 *payload, size_t payloadlen) { wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Response/Auth-Reject"); eap_sake_state(data, FAILURE); } static void eap_sake_process(struct eap_sm *sm, void *priv, struct wpabuf *respData) { struct eap_sake_data *data = priv; struct eap_sake_hdr *resp; u8 subtype; size_t len; const u8 *pos, *end; pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SAKE, respData, &len); if (pos == NULL || len < sizeof(struct eap_sake_hdr)) return; resp = (struct eap_sake_hdr *) pos; end = pos + len; subtype = resp->subtype; pos = (u8 *) (resp + 1); wpa_hexdump(MSG_DEBUG, "EAP-SAKE: Received attributes", pos, end - pos); switch (subtype) { case EAP_SAKE_SUBTYPE_IDENTITY: eap_sake_process_identity(sm, data, respData, pos, end - pos); break; case EAP_SAKE_SUBTYPE_CHALLENGE: eap_sake_process_challenge(sm, data, respData, pos, end - pos); break; case EAP_SAKE_SUBTYPE_CONFIRM: eap_sake_process_confirm(sm, data, respData, pos, end - pos); break; case EAP_SAKE_SUBTYPE_AUTH_REJECT: eap_sake_process_auth_reject(sm, data, respData, pos, end - pos); break; } } static Boolean eap_sake_isDone(struct eap_sm *sm, void *priv) { struct eap_sake_data *data = priv; return data->state == SUCCESS || data->state == FAILURE; } static u8 * eap_sake_getKey(struct eap_sm *sm, void *priv, size_t *len) { struct eap_sake_data *data = priv; u8 *key; if (data->state != SUCCESS) return NULL; key = os_malloc(EAP_MSK_LEN); if (key == NULL) return NULL; os_memcpy(key, data->msk, EAP_MSK_LEN); *len = EAP_MSK_LEN; return key; } static u8 * eap_sake_get_emsk(struct eap_sm *sm, void *priv, size_t *len) { struct eap_sake_data *data = priv; u8 *key; if (data->state != SUCCESS) return NULL; key = os_malloc(EAP_EMSK_LEN); if (key == NULL) return NULL; os_memcpy(key, data->emsk, EAP_EMSK_LEN); *len = EAP_EMSK_LEN; return key; } static Boolean eap_sake_isSuccess(struct eap_sm *sm, void *priv) { struct eap_sake_data *data = priv; return data->state == SUCCESS; } int eap_server_sake_register(void) { struct eap_method *eap; int ret; eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_SAKE, "SAKE"); if (eap == NULL) return -1; eap->init = eap_sake_init; eap->reset = eap_sake_reset; eap->buildReq = eap_sake_buildReq; eap->check = eap_sake_check; eap->process = eap_sake_process; eap->isDone = eap_sake_isDone; eap->getKey = eap_sake_getKey; eap->isSuccess = eap_sake_isSuccess; eap->get_emsk = eap_sake_get_emsk; ret = eap_server_method_register(eap); if (ret) eap_server_method_free(eap); return ret; } wpa-2.1/src/eap_server/eap_server_sim.c000066400000000000000000000544521227414666700202650ustar00rootroot00000000000000/* * hostapd / EAP-SIM (RFC 4186) * Copyright (c) 2005-2012, 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/random.h" #include "eap_server/eap_i.h" #include "eap_common/eap_sim_common.h" #include "eap_server/eap_sim_db.h" struct eap_sim_data { u8 mk[EAP_SIM_MK_LEN]; u8 nonce_mt[EAP_SIM_NONCE_MT_LEN]; u8 nonce_s[EAP_SIM_NONCE_S_LEN]; u8 k_aut[EAP_SIM_K_AUT_LEN]; u8 k_encr[EAP_SIM_K_ENCR_LEN]; u8 msk[EAP_SIM_KEYING_DATA_LEN]; u8 emsk[EAP_EMSK_LEN]; u8 kc[EAP_SIM_MAX_CHAL][EAP_SIM_KC_LEN]; u8 sres[EAP_SIM_MAX_CHAL][EAP_SIM_SRES_LEN]; u8 rand[EAP_SIM_MAX_CHAL][GSM_RAND_LEN]; int num_chal; enum { START, CHALLENGE, REAUTH, NOTIFICATION, SUCCESS, FAILURE } state; char *next_pseudonym; char *next_reauth_id; u16 counter; struct eap_sim_reauth *reauth; u16 notification; int use_result_ind; int start_round; char permanent[20]; /* Permanent username */ }; static const char * eap_sim_state_txt(int state) { switch (state) { case START: return "START"; case CHALLENGE: return "CHALLENGE"; case REAUTH: return "REAUTH"; case SUCCESS: return "SUCCESS"; case FAILURE: return "FAILURE"; case NOTIFICATION: return "NOTIFICATION"; default: return "Unknown?!"; } } static void eap_sim_state(struct eap_sim_data *data, int state) { wpa_printf(MSG_DEBUG, "EAP-SIM: %s -> %s", eap_sim_state_txt(data->state), eap_sim_state_txt(state)); data->state = state; } static void * eap_sim_init(struct eap_sm *sm) { struct eap_sim_data *data; if (sm->eap_sim_db_priv == NULL) { wpa_printf(MSG_WARNING, "EAP-SIM: eap_sim_db not configured"); return NULL; } data = os_zalloc(sizeof(*data)); if (data == NULL) return NULL; data->state = START; return data; } static void eap_sim_reset(struct eap_sm *sm, void *priv) { struct eap_sim_data *data = priv; os_free(data->next_pseudonym); os_free(data->next_reauth_id); os_free(data); } static struct wpabuf * eap_sim_build_start(struct eap_sm *sm, struct eap_sim_data *data, u8 id) { struct eap_sim_msg *msg; u8 ver[2]; wpa_printf(MSG_DEBUG, "EAP-SIM: Generating Start"); msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, EAP_TYPE_SIM, EAP_SIM_SUBTYPE_START); data->start_round++; if (data->start_round == 1) { /* * RFC 4186, Chap. 4.2.4 recommends that identity from EAP is * ignored and the SIM/Start is used to request the identity. */ wpa_printf(MSG_DEBUG, " AT_ANY_ID_REQ"); eap_sim_msg_add(msg, EAP_SIM_AT_ANY_ID_REQ, 0, NULL, 0); } else if (data->start_round > 3) { /* Cannot use more than three rounds of Start messages */ eap_sim_msg_free(msg); return NULL; } else if (data->start_round == 0) { /* * This is a special case that is used to recover from * AT_COUNTER_TOO_SMALL during re-authentication. Since we * already know the identity of the peer, there is no need to * request any identity in this case. */ } else if (sm->identity && sm->identity_len > 0 && sm->identity[0] == EAP_SIM_REAUTH_ID_PREFIX) { /* Reauth id may have expired - try fullauth */ wpa_printf(MSG_DEBUG, " AT_FULLAUTH_ID_REQ"); eap_sim_msg_add(msg, EAP_SIM_AT_FULLAUTH_ID_REQ, 0, NULL, 0); } else { wpa_printf(MSG_DEBUG, " AT_PERMANENT_ID_REQ"); eap_sim_msg_add(msg, EAP_SIM_AT_PERMANENT_ID_REQ, 0, NULL, 0); } wpa_printf(MSG_DEBUG, " AT_VERSION_LIST"); ver[0] = 0; ver[1] = EAP_SIM_VERSION; eap_sim_msg_add(msg, EAP_SIM_AT_VERSION_LIST, sizeof(ver), ver, sizeof(ver)); return eap_sim_msg_finish(msg, NULL, NULL, 0); } static int eap_sim_build_encr(struct eap_sm *sm, struct eap_sim_data *data, struct eap_sim_msg *msg, u16 counter, const u8 *nonce_s) { os_free(data->next_pseudonym); if (nonce_s == NULL) { data->next_pseudonym = eap_sim_db_get_next_pseudonym(sm->eap_sim_db_priv, EAP_SIM_DB_SIM); } else { /* Do not update pseudonym during re-authentication */ data->next_pseudonym = NULL; } os_free(data->next_reauth_id); if (data->counter <= EAP_SIM_MAX_FAST_REAUTHS) { data->next_reauth_id = eap_sim_db_get_next_reauth_id(sm->eap_sim_db_priv, EAP_SIM_DB_SIM); } else { wpa_printf(MSG_DEBUG, "EAP-SIM: Max fast re-authentication " "count exceeded - force full authentication"); data->next_reauth_id = NULL; } if (data->next_pseudonym == NULL && data->next_reauth_id == NULL && counter == 0 && nonce_s == NULL) return 0; wpa_printf(MSG_DEBUG, " AT_IV"); wpa_printf(MSG_DEBUG, " AT_ENCR_DATA"); eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV, EAP_SIM_AT_ENCR_DATA); if (counter > 0) { wpa_printf(MSG_DEBUG, " *AT_COUNTER (%u)", counter); eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, counter, NULL, 0); } if (nonce_s) { wpa_printf(MSG_DEBUG, " *AT_NONCE_S"); eap_sim_msg_add(msg, EAP_SIM_AT_NONCE_S, 0, nonce_s, EAP_SIM_NONCE_S_LEN); } if (data->next_pseudonym) { wpa_printf(MSG_DEBUG, " *AT_NEXT_PSEUDONYM (%s)", data->next_pseudonym); eap_sim_msg_add(msg, EAP_SIM_AT_NEXT_PSEUDONYM, os_strlen(data->next_pseudonym), (u8 *) data->next_pseudonym, os_strlen(data->next_pseudonym)); } if (data->next_reauth_id) { wpa_printf(MSG_DEBUG, " *AT_NEXT_REAUTH_ID (%s)", data->next_reauth_id); eap_sim_msg_add(msg, EAP_SIM_AT_NEXT_REAUTH_ID, os_strlen(data->next_reauth_id), (u8 *) data->next_reauth_id, os_strlen(data->next_reauth_id)); } if (eap_sim_msg_add_encr_end(msg, data->k_encr, EAP_SIM_AT_PADDING)) { wpa_printf(MSG_WARNING, "EAP-SIM: Failed to encrypt " "AT_ENCR_DATA"); return -1; } return 0; } static struct wpabuf * eap_sim_build_challenge(struct eap_sm *sm, struct eap_sim_data *data, u8 id) { struct eap_sim_msg *msg; wpa_printf(MSG_DEBUG, "EAP-SIM: Generating Challenge"); msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, EAP_TYPE_SIM, EAP_SIM_SUBTYPE_CHALLENGE); wpa_printf(MSG_DEBUG, " AT_RAND"); eap_sim_msg_add(msg, EAP_SIM_AT_RAND, 0, (u8 *) data->rand, data->num_chal * GSM_RAND_LEN); if (eap_sim_build_encr(sm, data, msg, 0, NULL)) { eap_sim_msg_free(msg); return NULL; } if (sm->eap_sim_aka_result_ind) { wpa_printf(MSG_DEBUG, " AT_RESULT_IND"); eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0); } wpa_printf(MSG_DEBUG, " AT_MAC"); eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); return eap_sim_msg_finish(msg, data->k_aut, data->nonce_mt, EAP_SIM_NONCE_MT_LEN); } static struct wpabuf * eap_sim_build_reauth(struct eap_sm *sm, struct eap_sim_data *data, u8 id) { struct eap_sim_msg *msg; wpa_printf(MSG_DEBUG, "EAP-SIM: Generating Re-authentication"); if (random_get_bytes(data->nonce_s, EAP_SIM_NONCE_S_LEN)) return NULL; wpa_hexdump_key(MSG_MSGDUMP, "EAP-SIM: NONCE_S", data->nonce_s, EAP_SIM_NONCE_S_LEN); eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut, data->msk, data->emsk); eap_sim_derive_keys_reauth(data->counter, sm->identity, sm->identity_len, data->nonce_s, data->mk, data->msk, data->emsk); msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, EAP_TYPE_SIM, EAP_SIM_SUBTYPE_REAUTHENTICATION); if (eap_sim_build_encr(sm, data, msg, data->counter, data->nonce_s)) { eap_sim_msg_free(msg); return NULL; } if (sm->eap_sim_aka_result_ind) { wpa_printf(MSG_DEBUG, " AT_RESULT_IND"); eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0); } wpa_printf(MSG_DEBUG, " AT_MAC"); eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); return eap_sim_msg_finish(msg, data->k_aut, NULL, 0); } static struct wpabuf * eap_sim_build_notification(struct eap_sm *sm, struct eap_sim_data *data, u8 id) { struct eap_sim_msg *msg; wpa_printf(MSG_DEBUG, "EAP-SIM: Generating Notification"); msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, EAP_TYPE_SIM, EAP_SIM_SUBTYPE_NOTIFICATION); wpa_printf(MSG_DEBUG, " AT_NOTIFICATION (%d)", data->notification); eap_sim_msg_add(msg, EAP_SIM_AT_NOTIFICATION, data->notification, NULL, 0); if (data->use_result_ind) { if (data->reauth) { wpa_printf(MSG_DEBUG, " AT_IV"); wpa_printf(MSG_DEBUG, " AT_ENCR_DATA"); eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV, EAP_SIM_AT_ENCR_DATA); wpa_printf(MSG_DEBUG, " *AT_COUNTER (%u)", data->counter); eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, data->counter, NULL, 0); if (eap_sim_msg_add_encr_end(msg, data->k_encr, EAP_SIM_AT_PADDING)) { wpa_printf(MSG_WARNING, "EAP-SIM: Failed to " "encrypt AT_ENCR_DATA"); eap_sim_msg_free(msg); return NULL; } } wpa_printf(MSG_DEBUG, " AT_MAC"); eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); } return eap_sim_msg_finish(msg, data->k_aut, NULL, 0); } static struct wpabuf * eap_sim_buildReq(struct eap_sm *sm, void *priv, u8 id) { struct eap_sim_data *data = priv; switch (data->state) { case START: return eap_sim_build_start(sm, data, id); case CHALLENGE: return eap_sim_build_challenge(sm, data, id); case REAUTH: return eap_sim_build_reauth(sm, data, id); case NOTIFICATION: return eap_sim_build_notification(sm, data, id); default: wpa_printf(MSG_DEBUG, "EAP-SIM: Unknown state %d in " "buildReq", data->state); break; } return NULL; } static Boolean eap_sim_check(struct eap_sm *sm, void *priv, struct wpabuf *respData) { const u8 *pos; size_t len; pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SIM, respData, &len); if (pos == NULL || len < 3) { wpa_printf(MSG_INFO, "EAP-SIM: Invalid frame"); return TRUE; } return FALSE; } static Boolean eap_sim_unexpected_subtype(struct eap_sim_data *data, u8 subtype) { if (subtype == EAP_SIM_SUBTYPE_CLIENT_ERROR) return FALSE; switch (data->state) { case START: if (subtype != EAP_SIM_SUBTYPE_START) { wpa_printf(MSG_INFO, "EAP-SIM: Unexpected response " "subtype %d", subtype); return TRUE; } break; case CHALLENGE: if (subtype != EAP_SIM_SUBTYPE_CHALLENGE) { wpa_printf(MSG_INFO, "EAP-SIM: Unexpected response " "subtype %d", subtype); return TRUE; } break; case REAUTH: if (subtype != EAP_SIM_SUBTYPE_REAUTHENTICATION) { wpa_printf(MSG_INFO, "EAP-SIM: Unexpected response " "subtype %d", subtype); return TRUE; } break; case NOTIFICATION: if (subtype != EAP_SIM_SUBTYPE_NOTIFICATION) { wpa_printf(MSG_INFO, "EAP-SIM: Unexpected response " "subtype %d", subtype); return TRUE; } break; default: wpa_printf(MSG_INFO, "EAP-SIM: Unexpected state (%d) for " "processing a response", data->state); return TRUE; } return FALSE; } static int eap_sim_supported_ver(struct eap_sim_data *data, int version) { return version == EAP_SIM_VERSION; } static void eap_sim_process_start(struct eap_sm *sm, struct eap_sim_data *data, struct wpabuf *respData, struct eap_sim_attrs *attr) { size_t identity_len; u8 ver_list[2]; u8 *new_identity; char *username; wpa_printf(MSG_DEBUG, "EAP-SIM: Receive start response"); if (data->start_round == 0) { /* * Special case for AT_COUNTER_TOO_SMALL recovery - no identity * was requested since we already know it. */ goto skip_id_update; } /* * We always request identity in SIM/Start, so the peer is required to * have replied with one. */ if (!attr->identity || attr->identity_len == 0) { wpa_printf(MSG_DEBUG, "EAP-SIM: Peer did not provide any " "identity"); goto failed; } new_identity = os_malloc(attr->identity_len); if (new_identity == NULL) goto failed; os_free(sm->identity); sm->identity = new_identity; os_memcpy(sm->identity, attr->identity, attr->identity_len); sm->identity_len = attr->identity_len; wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Identity", sm->identity, sm->identity_len); username = sim_get_username(sm->identity, sm->identity_len); if (username == NULL) goto failed; if (username[0] == EAP_SIM_REAUTH_ID_PREFIX) { wpa_printf(MSG_DEBUG, "EAP-SIM: Reauth username '%s'", username); data->reauth = eap_sim_db_get_reauth_entry( sm->eap_sim_db_priv, username); os_free(username); if (data->reauth == NULL) { wpa_printf(MSG_DEBUG, "EAP-SIM: Unknown reauth " "identity - request full auth identity"); /* Remain in START state for another round */ return; } wpa_printf(MSG_DEBUG, "EAP-SIM: Using fast re-authentication"); os_strlcpy(data->permanent, data->reauth->permanent, sizeof(data->permanent)); data->counter = data->reauth->counter; os_memcpy(data->mk, data->reauth->mk, EAP_SIM_MK_LEN); eap_sim_state(data, REAUTH); return; } if (username[0] == EAP_SIM_PSEUDONYM_PREFIX) { const char *permanent; wpa_printf(MSG_DEBUG, "EAP-SIM: Pseudonym username '%s'", username); permanent = eap_sim_db_get_permanent( sm->eap_sim_db_priv, username); os_free(username); if (permanent == NULL) { wpa_printf(MSG_DEBUG, "EAP-SIM: Unknown pseudonym " "identity - request permanent identity"); /* Remain in START state for another round */ return; } os_strlcpy(data->permanent, permanent, sizeof(data->permanent)); } else if (username[0] == EAP_SIM_PERMANENT_PREFIX) { wpa_printf(MSG_DEBUG, "EAP-SIM: Permanent username '%s'", username); os_strlcpy(data->permanent, username, sizeof(data->permanent)); os_free(username); } else { wpa_printf(MSG_DEBUG, "EAP-SIM: Unrecognized username '%s'", username); os_free(username); goto failed; } skip_id_update: /* Full authentication */ if (attr->nonce_mt == NULL || attr->selected_version < 0) { wpa_printf(MSG_DEBUG, "EAP-SIM: Start/Response missing " "required attributes"); goto failed; } if (!eap_sim_supported_ver(data, attr->selected_version)) { wpa_printf(MSG_DEBUG, "EAP-SIM: Peer selected unsupported " "version %d", attr->selected_version); goto failed; } data->counter = 0; /* reset re-auth counter since this is full auth */ data->reauth = NULL; data->num_chal = eap_sim_db_get_gsm_triplets( sm->eap_sim_db_priv, data->permanent, EAP_SIM_MAX_CHAL, (u8 *) data->rand, (u8 *) data->kc, (u8 *) data->sres, sm); if (data->num_chal == EAP_SIM_DB_PENDING) { wpa_printf(MSG_DEBUG, "EAP-SIM: GSM authentication triplets " "not yet available - pending request"); sm->method_pending = METHOD_PENDING_WAIT; return; } if (data->num_chal < 2) { wpa_printf(MSG_INFO, "EAP-SIM: Failed to get GSM " "authentication triplets for the peer"); goto failed; } identity_len = sm->identity_len; while (identity_len > 0 && sm->identity[identity_len - 1] == '\0') { wpa_printf(MSG_DEBUG, "EAP-SIM: Workaround - drop last null " "character from identity"); identity_len--; } wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Identity for MK derivation", sm->identity, identity_len); os_memcpy(data->nonce_mt, attr->nonce_mt, EAP_SIM_NONCE_MT_LEN); WPA_PUT_BE16(ver_list, EAP_SIM_VERSION); eap_sim_derive_mk(sm->identity, identity_len, attr->nonce_mt, attr->selected_version, ver_list, sizeof(ver_list), data->num_chal, (const u8 *) data->kc, data->mk); eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut, data->msk, data->emsk); eap_sim_state(data, CHALLENGE); return; failed: data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; eap_sim_state(data, NOTIFICATION); } static void eap_sim_process_challenge(struct eap_sm *sm, struct eap_sim_data *data, struct wpabuf *respData, struct eap_sim_attrs *attr) { if (attr->mac == NULL || eap_sim_verify_mac(data->k_aut, respData, attr->mac, (u8 *) data->sres, data->num_chal * EAP_SIM_SRES_LEN)) { wpa_printf(MSG_WARNING, "EAP-SIM: Challenge message " "did not include valid AT_MAC"); data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; eap_sim_state(data, NOTIFICATION); return; } wpa_printf(MSG_DEBUG, "EAP-SIM: Challenge response includes the " "correct AT_MAC"); if (sm->eap_sim_aka_result_ind && attr->result_ind) { data->use_result_ind = 1; data->notification = EAP_SIM_SUCCESS; eap_sim_state(data, NOTIFICATION); } else eap_sim_state(data, SUCCESS); if (data->next_pseudonym) { eap_sim_db_add_pseudonym(sm->eap_sim_db_priv, data->permanent, data->next_pseudonym); data->next_pseudonym = NULL; } if (data->next_reauth_id) { eap_sim_db_add_reauth(sm->eap_sim_db_priv, data->permanent, data->next_reauth_id, data->counter + 1, data->mk); data->next_reauth_id = NULL; } } static void eap_sim_process_reauth(struct eap_sm *sm, struct eap_sim_data *data, struct wpabuf *respData, struct eap_sim_attrs *attr) { struct eap_sim_attrs eattr; u8 *decrypted = NULL; if (attr->mac == NULL || eap_sim_verify_mac(data->k_aut, respData, attr->mac, data->nonce_s, EAP_SIM_NONCE_S_LEN)) { wpa_printf(MSG_WARNING, "EAP-SIM: Re-authentication message " "did not include valid AT_MAC"); goto fail; } if (attr->encr_data == NULL || attr->iv == NULL) { wpa_printf(MSG_WARNING, "EAP-SIM: Reauthentication " "message did not include encrypted data"); goto fail; } decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data, attr->encr_data_len, attr->iv, &eattr, 0); if (decrypted == NULL) { wpa_printf(MSG_WARNING, "EAP-SIM: Failed to parse encrypted " "data from reauthentication message"); goto fail; } if (eattr.counter != data->counter) { wpa_printf(MSG_WARNING, "EAP-SIM: Re-authentication message " "used incorrect counter %u, expected %u", eattr.counter, data->counter); goto fail; } os_free(decrypted); decrypted = NULL; wpa_printf(MSG_DEBUG, "EAP-SIM: Re-authentication response includes " "the correct AT_MAC"); if (eattr.counter_too_small) { wpa_printf(MSG_DEBUG, "EAP-AKA: Re-authentication response " "included AT_COUNTER_TOO_SMALL - starting full " "authentication"); data->start_round = -1; eap_sim_state(data, START); return; } if (sm->eap_sim_aka_result_ind && attr->result_ind) { data->use_result_ind = 1; data->notification = EAP_SIM_SUCCESS; eap_sim_state(data, NOTIFICATION); } else eap_sim_state(data, SUCCESS); if (data->next_reauth_id) { eap_sim_db_add_reauth(sm->eap_sim_db_priv, data->permanent, data->next_reauth_id, data->counter + 1, data->mk); data->next_reauth_id = NULL; } else { eap_sim_db_remove_reauth(sm->eap_sim_db_priv, data->reauth); data->reauth = NULL; } return; fail: data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; eap_sim_state(data, NOTIFICATION); eap_sim_db_remove_reauth(sm->eap_sim_db_priv, data->reauth); data->reauth = NULL; os_free(decrypted); } static void eap_sim_process_client_error(struct eap_sm *sm, struct eap_sim_data *data, struct wpabuf *respData, struct eap_sim_attrs *attr) { wpa_printf(MSG_DEBUG, "EAP-SIM: Client reported error %d", attr->client_error_code); if (data->notification == EAP_SIM_SUCCESS && data->use_result_ind) eap_sim_state(data, SUCCESS); else eap_sim_state(data, FAILURE); } static void eap_sim_process_notification(struct eap_sm *sm, struct eap_sim_data *data, struct wpabuf *respData, struct eap_sim_attrs *attr) { wpa_printf(MSG_DEBUG, "EAP-SIM: Client replied to notification"); if (data->notification == EAP_SIM_SUCCESS && data->use_result_ind) eap_sim_state(data, SUCCESS); else eap_sim_state(data, FAILURE); } static void eap_sim_process(struct eap_sm *sm, void *priv, struct wpabuf *respData) { struct eap_sim_data *data = priv; const u8 *pos, *end; u8 subtype; size_t len; struct eap_sim_attrs attr; pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SIM, respData, &len); if (pos == NULL || len < 3) return; end = pos + len; subtype = *pos; pos += 3; if (eap_sim_unexpected_subtype(data, subtype)) { wpa_printf(MSG_DEBUG, "EAP-SIM: Unrecognized or unexpected " "EAP-SIM Subtype in EAP Response"); data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; eap_sim_state(data, NOTIFICATION); return; } if (eap_sim_parse_attr(pos, end, &attr, 0, 0)) { wpa_printf(MSG_DEBUG, "EAP-SIM: Failed to parse attributes"); if (subtype != EAP_SIM_SUBTYPE_CLIENT_ERROR && (data->state == START || data->state == CHALLENGE || data->state == REAUTH)) { data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; eap_sim_state(data, NOTIFICATION); return; } eap_sim_state(data, FAILURE); return; } if (subtype == EAP_SIM_SUBTYPE_CLIENT_ERROR) { eap_sim_process_client_error(sm, data, respData, &attr); return; } switch (data->state) { case START: eap_sim_process_start(sm, data, respData, &attr); break; case CHALLENGE: eap_sim_process_challenge(sm, data, respData, &attr); break; case REAUTH: eap_sim_process_reauth(sm, data, respData, &attr); break; case NOTIFICATION: eap_sim_process_notification(sm, data, respData, &attr); break; default: wpa_printf(MSG_DEBUG, "EAP-SIM: Unknown state %d in " "process", data->state); break; } } static Boolean eap_sim_isDone(struct eap_sm *sm, void *priv) { struct eap_sim_data *data = priv; return data->state == SUCCESS || data->state == FAILURE; } static u8 * eap_sim_getKey(struct eap_sm *sm, void *priv, size_t *len) { struct eap_sim_data *data = priv; u8 *key; if (data->state != SUCCESS) return NULL; key = os_malloc(EAP_SIM_KEYING_DATA_LEN); if (key == NULL) return NULL; os_memcpy(key, data->msk, EAP_SIM_KEYING_DATA_LEN); *len = EAP_SIM_KEYING_DATA_LEN; return key; } static u8 * eap_sim_get_emsk(struct eap_sm *sm, void *priv, size_t *len) { struct eap_sim_data *data = priv; u8 *key; if (data->state != SUCCESS) return NULL; key = os_malloc(EAP_EMSK_LEN); if (key == NULL) return NULL; os_memcpy(key, data->emsk, EAP_EMSK_LEN); *len = EAP_EMSK_LEN; return key; } static Boolean eap_sim_isSuccess(struct eap_sm *sm, void *priv) { struct eap_sim_data *data = priv; return data->state == SUCCESS; } int eap_server_sim_register(void) { struct eap_method *eap; int ret; eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_SIM, "SIM"); if (eap == NULL) return -1; eap->init = eap_sim_init; eap->reset = eap_sim_reset; eap->buildReq = eap_sim_buildReq; eap->check = eap_sim_check; eap->process = eap_sim_process; eap->isDone = eap_sim_isDone; eap->getKey = eap_sim_getKey; eap->isSuccess = eap_sim_isSuccess; eap->get_emsk = eap_sim_get_emsk; ret = eap_server_method_register(eap); if (ret) eap_server_method_free(eap); return ret; } wpa-2.1/src/eap_server/eap_server_tls.c000066400000000000000000000165661227414666700203030ustar00rootroot00000000000000/* * hostapd / EAP-TLS (RFC 2716) * Copyright (c) 2004-2008, 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_i.h" #include "eap_tls_common.h" #include "crypto/tls.h" static void eap_tls_reset(struct eap_sm *sm, void *priv); struct eap_tls_data { struct eap_ssl_data ssl; enum { START, CONTINUE, SUCCESS, FAILURE } state; int established; u8 eap_type; }; static const char * eap_tls_state_txt(int state) { switch (state) { case START: return "START"; case CONTINUE: return "CONTINUE"; case SUCCESS: return "SUCCESS"; case FAILURE: return "FAILURE"; default: return "Unknown?!"; } } static void eap_tls_state(struct eap_tls_data *data, int state) { wpa_printf(MSG_DEBUG, "EAP-TLS: %s -> %s", eap_tls_state_txt(data->state), eap_tls_state_txt(state)); data->state = state; } static void * eap_tls_init(struct eap_sm *sm) { struct eap_tls_data *data; data = os_zalloc(sizeof(*data)); if (data == NULL) return NULL; data->state = START; if (eap_server_tls_ssl_init(sm, &data->ssl, 1)) { wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL."); eap_tls_reset(sm, data); return NULL; } data->eap_type = EAP_TYPE_TLS; return data; } #ifdef EAP_SERVER_UNAUTH_TLS static void * eap_unauth_tls_init(struct eap_sm *sm) { struct eap_tls_data *data; data = os_zalloc(sizeof(*data)); if (data == NULL) return NULL; data->state = START; if (eap_server_tls_ssl_init(sm, &data->ssl, 0)) { wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL."); eap_tls_reset(sm, data); return NULL; } data->eap_type = EAP_UNAUTH_TLS_TYPE; return data; } #endif /* EAP_SERVER_UNAUTH_TLS */ static void eap_tls_reset(struct eap_sm *sm, void *priv) { struct eap_tls_data *data = priv; if (data == NULL) return; eap_server_tls_ssl_deinit(sm, &data->ssl); os_free(data); } static struct wpabuf * eap_tls_build_start(struct eap_sm *sm, struct eap_tls_data *data, u8 id) { struct wpabuf *req; req = eap_tls_msg_alloc(data->eap_type, 1, EAP_CODE_REQUEST, id); if (req == NULL) { wpa_printf(MSG_ERROR, "EAP-TLS: Failed to allocate memory for " "request"); eap_tls_state(data, FAILURE); return NULL; } wpabuf_put_u8(req, EAP_TLS_FLAGS_START); eap_tls_state(data, CONTINUE); return req; } static struct wpabuf * eap_tls_buildReq(struct eap_sm *sm, void *priv, u8 id) { struct eap_tls_data *data = priv; struct wpabuf *res; if (data->ssl.state == FRAG_ACK) { return eap_server_tls_build_ack(id, data->eap_type, 0); } if (data->ssl.state == WAIT_FRAG_ACK) { res = eap_server_tls_build_msg(&data->ssl, data->eap_type, 0, id); goto check_established; } switch (data->state) { case START: return eap_tls_build_start(sm, data, id); case CONTINUE: if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) data->established = 1; break; default: wpa_printf(MSG_DEBUG, "EAP-TLS: %s - unexpected state %d", __func__, data->state); return NULL; } res = eap_server_tls_build_msg(&data->ssl, data->eap_type, 0, id); check_established: if (data->established && data->ssl.state != WAIT_FRAG_ACK) { /* TLS handshake has been completed and there are no more * fragments waiting to be sent out. */ wpa_printf(MSG_DEBUG, "EAP-TLS: Done"); eap_tls_state(data, SUCCESS); } return res; } static Boolean eap_tls_check(struct eap_sm *sm, void *priv, struct wpabuf *respData) { struct eap_tls_data *data = priv; const u8 *pos; size_t len; if (data->eap_type == EAP_UNAUTH_TLS_TYPE) pos = eap_hdr_validate(EAP_VENDOR_UNAUTH_TLS, EAP_VENDOR_TYPE_UNAUTH_TLS, respData, &len); else pos = eap_hdr_validate(EAP_VENDOR_IETF, data->eap_type, respData, &len); if (pos == NULL || len < 1) { wpa_printf(MSG_INFO, "EAP-TLS: Invalid frame"); return TRUE; } return FALSE; } static void eap_tls_process_msg(struct eap_sm *sm, void *priv, const struct wpabuf *respData) { struct eap_tls_data *data = priv; if (data->state == SUCCESS && wpabuf_len(data->ssl.tls_in) == 0) { wpa_printf(MSG_DEBUG, "EAP-TLS: Client acknowledged final TLS " "handshake message"); return; } if (eap_server_tls_phase1(sm, &data->ssl) < 0) eap_tls_state(data, FAILURE); } static void eap_tls_process(struct eap_sm *sm, void *priv, struct wpabuf *respData) { struct eap_tls_data *data = priv; if (eap_server_tls_process(sm, &data->ssl, respData, data, data->eap_type, NULL, eap_tls_process_msg) < 0) eap_tls_state(data, FAILURE); } static Boolean eap_tls_isDone(struct eap_sm *sm, void *priv) { struct eap_tls_data *data = priv; return data->state == SUCCESS || data->state == FAILURE; } static u8 * eap_tls_getKey(struct eap_sm *sm, void *priv, size_t *len) { struct eap_tls_data *data = priv; u8 *eapKeyData; if (data->state != SUCCESS) return NULL; eapKeyData = eap_server_tls_derive_key(sm, &data->ssl, "client EAP encryption", EAP_TLS_KEY_LEN); if (eapKeyData) { *len = EAP_TLS_KEY_LEN; wpa_hexdump(MSG_DEBUG, "EAP-TLS: Derived key", eapKeyData, EAP_TLS_KEY_LEN); } else { wpa_printf(MSG_DEBUG, "EAP-TLS: Failed to derive key"); } return eapKeyData; } static u8 * eap_tls_get_emsk(struct eap_sm *sm, void *priv, size_t *len) { struct eap_tls_data *data = priv; u8 *eapKeyData, *emsk; if (data->state != SUCCESS) return NULL; eapKeyData = eap_server_tls_derive_key(sm, &data->ssl, "client EAP encryption", EAP_TLS_KEY_LEN + EAP_EMSK_LEN); if (eapKeyData) { emsk = os_malloc(EAP_EMSK_LEN); if (emsk) os_memcpy(emsk, eapKeyData + EAP_TLS_KEY_LEN, EAP_EMSK_LEN); os_free(eapKeyData); } else emsk = NULL; if (emsk) { *len = EAP_EMSK_LEN; wpa_hexdump(MSG_DEBUG, "EAP-TLS: Derived EMSK", emsk, EAP_EMSK_LEN); } else { wpa_printf(MSG_DEBUG, "EAP-TLS: Failed to derive EMSK"); } return emsk; } static Boolean eap_tls_isSuccess(struct eap_sm *sm, void *priv) { struct eap_tls_data *data = priv; return data->state == SUCCESS; } int eap_server_tls_register(void) { struct eap_method *eap; int ret; eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_TLS, "TLS"); if (eap == NULL) return -1; eap->init = eap_tls_init; eap->reset = eap_tls_reset; eap->buildReq = eap_tls_buildReq; eap->check = eap_tls_check; eap->process = eap_tls_process; eap->isDone = eap_tls_isDone; eap->getKey = eap_tls_getKey; eap->isSuccess = eap_tls_isSuccess; eap->get_emsk = eap_tls_get_emsk; ret = eap_server_method_register(eap); if (ret) eap_server_method_free(eap); return ret; } #ifdef EAP_SERVER_UNAUTH_TLS int eap_server_unauth_tls_register(void) { struct eap_method *eap; int ret; eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, EAP_VENDOR_UNAUTH_TLS, EAP_VENDOR_TYPE_UNAUTH_TLS, "UNAUTH-TLS"); if (eap == NULL) return -1; eap->init = eap_unauth_tls_init; eap->reset = eap_tls_reset; eap->buildReq = eap_tls_buildReq; eap->check = eap_tls_check; eap->process = eap_tls_process; eap->isDone = eap_tls_isDone; eap->getKey = eap_tls_getKey; eap->isSuccess = eap_tls_isSuccess; eap->get_emsk = eap_tls_get_emsk; ret = eap_server_method_register(eap); if (ret) eap_server_method_free(eap); return ret; } #endif /* EAP_SERVER_UNAUTH_TLS */ wpa-2.1/src/eap_server/eap_server_tls_common.c000066400000000000000000000252521227414666700216430ustar00rootroot00000000000000/* * EAP-TLS/PEAP/TTLS/FAST server common functions * 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 "crypto/sha1.h" #include "crypto/tls.h" #include "eap_i.h" #include "eap_tls_common.h" static void eap_server_tls_free_in_buf(struct eap_ssl_data *data); struct wpabuf * eap_tls_msg_alloc(EapType type, size_t payload_len, u8 code, u8 identifier) { if (type == EAP_UNAUTH_TLS_TYPE) return eap_msg_alloc(EAP_VENDOR_UNAUTH_TLS, EAP_VENDOR_TYPE_UNAUTH_TLS, payload_len, code, identifier); return eap_msg_alloc(EAP_VENDOR_IETF, type, payload_len, code, identifier); } int eap_server_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, int verify_peer) { if (sm->ssl_ctx == NULL) { wpa_printf(MSG_ERROR, "TLS context not initialized - cannot use TLS-based EAP method"); return -1; } data->eap = sm; data->phase2 = sm->init_phase2; data->conn = tls_connection_init(sm->ssl_ctx); if (data->conn == NULL) { wpa_printf(MSG_INFO, "SSL: Failed to initialize new TLS " "connection"); return -1; } if (tls_connection_set_verify(sm->ssl_ctx, data->conn, verify_peer)) { wpa_printf(MSG_INFO, "SSL: Failed to configure verification " "of TLS peer certificate"); tls_connection_deinit(sm->ssl_ctx, data->conn); data->conn = NULL; return -1; } data->tls_out_limit = sm->fragment_size > 0 ? sm->fragment_size : 1398; if (data->phase2) { /* Limit the fragment size in the inner TLS authentication * since the outer authentication with EAP-PEAP does not yet * support fragmentation */ if (data->tls_out_limit > 100) data->tls_out_limit -= 100; } return 0; } void eap_server_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data) { tls_connection_deinit(sm->ssl_ctx, data->conn); eap_server_tls_free_in_buf(data); wpabuf_free(data->tls_out); data->tls_out = NULL; } u8 * eap_server_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, char *label, size_t len) { struct tls_keys keys; u8 *rnd = NULL, *out; out = os_malloc(len); if (out == NULL) return NULL; if (tls_connection_prf(sm->ssl_ctx, data->conn, label, 0, out, len) == 0) return out; if (tls_connection_get_keys(sm->ssl_ctx, data->conn, &keys)) goto fail; if (keys.client_random == NULL || keys.server_random == NULL || keys.master_key == NULL) goto fail; rnd = os_malloc(keys.client_random_len + keys.server_random_len); if (rnd == NULL) goto fail; os_memcpy(rnd, keys.client_random, keys.client_random_len); os_memcpy(rnd + keys.client_random_len, keys.server_random, keys.server_random_len); if (tls_prf_sha1_md5(keys.master_key, keys.master_key_len, label, rnd, keys.client_random_len + keys.server_random_len, out, len)) goto fail; os_free(rnd); return out; fail: os_free(out); os_free(rnd); return NULL; } struct wpabuf * eap_server_tls_build_msg(struct eap_ssl_data *data, int eap_type, int version, u8 id) { struct wpabuf *req; u8 flags; size_t send_len, plen; wpa_printf(MSG_DEBUG, "SSL: Generating Request"); if (data->tls_out == NULL) { wpa_printf(MSG_ERROR, "SSL: tls_out NULL in %s", __func__); return NULL; } flags = version; send_len = wpabuf_len(data->tls_out) - data->tls_out_pos; if (1 + send_len > data->tls_out_limit) { send_len = data->tls_out_limit - 1; flags |= EAP_TLS_FLAGS_MORE_FRAGMENTS; if (data->tls_out_pos == 0) { flags |= EAP_TLS_FLAGS_LENGTH_INCLUDED; send_len -= 4; } } plen = 1 + send_len; if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) plen += 4; req = eap_tls_msg_alloc(eap_type, plen, EAP_CODE_REQUEST, id); if (req == NULL) return NULL; wpabuf_put_u8(req, flags); /* Flags */ if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) wpabuf_put_be32(req, wpabuf_len(data->tls_out)); wpabuf_put_data(req, wpabuf_head_u8(data->tls_out) + data->tls_out_pos, send_len); data->tls_out_pos += send_len; if (data->tls_out_pos == wpabuf_len(data->tls_out)) { wpa_printf(MSG_DEBUG, "SSL: Sending out %lu bytes " "(message sent completely)", (unsigned long) send_len); wpabuf_free(data->tls_out); data->tls_out = NULL; data->tls_out_pos = 0; data->state = MSG; } else { wpa_printf(MSG_DEBUG, "SSL: Sending out %lu bytes " "(%lu more to send)", (unsigned long) send_len, (unsigned long) wpabuf_len(data->tls_out) - data->tls_out_pos); data->state = WAIT_FRAG_ACK; } return req; } struct wpabuf * eap_server_tls_build_ack(u8 id, int eap_type, int version) { struct wpabuf *req; req = eap_tls_msg_alloc(eap_type, 1, EAP_CODE_REQUEST, id); if (req == NULL) return NULL; wpa_printf(MSG_DEBUG, "SSL: Building ACK"); wpabuf_put_u8(req, version); /* Flags */ return req; } static int eap_server_tls_process_cont(struct eap_ssl_data *data, const u8 *buf, size_t len) { /* Process continuation of a pending message */ if (len > wpabuf_tailroom(data->tls_in)) { wpa_printf(MSG_DEBUG, "SSL: Fragment overflow"); return -1; } wpabuf_put_data(data->tls_in, buf, len); wpa_printf(MSG_DEBUG, "SSL: Received %lu bytes, waiting for %lu " "bytes more", (unsigned long) len, (unsigned long) wpabuf_tailroom(data->tls_in)); return 0; } static int eap_server_tls_process_fragment(struct eap_ssl_data *data, u8 flags, u32 message_length, const u8 *buf, size_t len) { /* Process a fragment that is not the last one of the message */ if (data->tls_in == NULL && !(flags & EAP_TLS_FLAGS_LENGTH_INCLUDED)) { wpa_printf(MSG_DEBUG, "SSL: No Message Length field in a " "fragmented packet"); return -1; } if (data->tls_in == NULL) { /* First fragment of the message */ /* Limit length to avoid rogue peers from causing large * memory allocations. */ if (message_length > 65536) { wpa_printf(MSG_INFO, "SSL: Too long TLS fragment (size" " over 64 kB)"); return -1; } if (len > message_length) { wpa_printf(MSG_INFO, "SSL: Too much data (%d bytes) in " "first fragment of frame (TLS Message " "Length %d bytes)", (int) len, (int) message_length); return -1; } data->tls_in = wpabuf_alloc(message_length); if (data->tls_in == NULL) { wpa_printf(MSG_DEBUG, "SSL: No memory for message"); return -1; } wpabuf_put_data(data->tls_in, buf, len); wpa_printf(MSG_DEBUG, "SSL: Received %lu bytes in first " "fragment, waiting for %lu bytes more", (unsigned long) len, (unsigned long) wpabuf_tailroom(data->tls_in)); } return 0; } int eap_server_tls_phase1(struct eap_sm *sm, struct eap_ssl_data *data) { if (data->tls_out) { /* This should not happen.. */ wpa_printf(MSG_INFO, "SSL: pending tls_out data when " "processing new message"); wpabuf_free(data->tls_out); WPA_ASSERT(data->tls_out == NULL); } data->tls_out = tls_connection_server_handshake(sm->ssl_ctx, data->conn, data->tls_in, NULL); if (data->tls_out == NULL) { wpa_printf(MSG_INFO, "SSL: TLS processing failed"); return -1; } if (tls_connection_get_failed(sm->ssl_ctx, data->conn)) { /* TLS processing has failed - return error */ wpa_printf(MSG_DEBUG, "SSL: Failed - tls_out available to " "report error"); return -1; } return 0; } static int eap_server_tls_reassemble(struct eap_ssl_data *data, u8 flags, const u8 **pos, size_t *left) { unsigned int tls_msg_len = 0; const u8 *end = *pos + *left; if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) { if (*left < 4) { wpa_printf(MSG_INFO, "SSL: Short frame with TLS " "length"); return -1; } tls_msg_len = WPA_GET_BE32(*pos); wpa_printf(MSG_DEBUG, "SSL: TLS Message Length: %d", tls_msg_len); *pos += 4; *left -= 4; if (*left > tls_msg_len) { wpa_printf(MSG_INFO, "SSL: TLS Message Length (%d " "bytes) smaller than this fragment (%d " "bytes)", (int) tls_msg_len, (int) *left); return -1; } } wpa_printf(MSG_DEBUG, "SSL: Received packet: Flags 0x%x " "Message Length %u", flags, tls_msg_len); if (data->state == WAIT_FRAG_ACK) { if (*left != 0) { wpa_printf(MSG_DEBUG, "SSL: Unexpected payload in " "WAIT_FRAG_ACK state"); return -1; } wpa_printf(MSG_DEBUG, "SSL: Fragment acknowledged"); return 1; } if (data->tls_in && eap_server_tls_process_cont(data, *pos, end - *pos) < 0) return -1; if (flags & EAP_TLS_FLAGS_MORE_FRAGMENTS) { if (eap_server_tls_process_fragment(data, flags, tls_msg_len, *pos, end - *pos) < 0) return -1; data->state = FRAG_ACK; return 1; } if (data->state == FRAG_ACK) { wpa_printf(MSG_DEBUG, "SSL: All fragments received"); data->state = MSG; } if (data->tls_in == NULL) { /* Wrap unfragmented messages as wpabuf without extra copy */ wpabuf_set(&data->tmpbuf, *pos, end - *pos); data->tls_in = &data->tmpbuf; } return 0; } static void eap_server_tls_free_in_buf(struct eap_ssl_data *data) { if (data->tls_in != &data->tmpbuf) wpabuf_free(data->tls_in); data->tls_in = NULL; } struct wpabuf * eap_server_tls_encrypt(struct eap_sm *sm, struct eap_ssl_data *data, const struct wpabuf *plain) { struct wpabuf *buf; buf = tls_connection_encrypt(sm->ssl_ctx, data->conn, plain); if (buf == NULL) { wpa_printf(MSG_INFO, "SSL: Failed to encrypt Phase 2 data"); return NULL; } return buf; } int eap_server_tls_process(struct eap_sm *sm, struct eap_ssl_data *data, struct wpabuf *respData, void *priv, int eap_type, int (*proc_version)(struct eap_sm *sm, void *priv, int peer_version), void (*proc_msg)(struct eap_sm *sm, void *priv, const struct wpabuf *respData)) { const u8 *pos; u8 flags; size_t left; int ret, res = 0; if (eap_type == EAP_UNAUTH_TLS_TYPE) pos = eap_hdr_validate(EAP_VENDOR_UNAUTH_TLS, EAP_VENDOR_TYPE_UNAUTH_TLS, respData, &left); else pos = eap_hdr_validate(EAP_VENDOR_IETF, eap_type, respData, &left); if (pos == NULL || left < 1) return 0; /* Should not happen - frame already validated */ flags = *pos++; left--; wpa_printf(MSG_DEBUG, "SSL: Received packet(len=%lu) - Flags 0x%02x", (unsigned long) wpabuf_len(respData), flags); if (proc_version && proc_version(sm, priv, flags & EAP_TLS_VERSION_MASK) < 0) return -1; ret = eap_server_tls_reassemble(data, flags, &pos, &left); if (ret < 0) { res = -1; goto done; } else if (ret == 1) return 0; if (proc_msg) proc_msg(sm, priv, respData); if (tls_connection_get_write_alerts(sm->ssl_ctx, data->conn) > 1) { wpa_printf(MSG_INFO, "SSL: Locally detected fatal error in " "TLS processing"); res = -1; } done: eap_server_tls_free_in_buf(data); return res; } wpa-2.1/src/eap_server/eap_server_tnc.c000066400000000000000000000327221227414666700202550ustar00rootroot00000000000000/* * EAP server method: EAP-TNC (Trusted Network Connect) * Copyright (c) 2007-2010, 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_i.h" #include "tncs.h" struct eap_tnc_data { enum eap_tnc_state { START, CONTINUE, RECOMMENDATION, FRAG_ACK, WAIT_FRAG_ACK, DONE, FAIL } state; enum { ALLOW, ISOLATE, NO_ACCESS, NO_RECOMMENDATION } recommendation; struct tncs_data *tncs; struct wpabuf *in_buf; struct wpabuf *out_buf; size_t out_used; size_t fragment_size; unsigned int was_done:1; unsigned int was_fail:1; }; /* EAP-TNC Flags */ #define EAP_TNC_FLAGS_LENGTH_INCLUDED 0x80 #define EAP_TNC_FLAGS_MORE_FRAGMENTS 0x40 #define EAP_TNC_FLAGS_START 0x20 #define EAP_TNC_VERSION_MASK 0x07 #define EAP_TNC_VERSION 1 static const char * eap_tnc_state_txt(enum eap_tnc_state state) { switch (state) { case START: return "START"; case CONTINUE: return "CONTINUE"; case RECOMMENDATION: return "RECOMMENDATION"; case FRAG_ACK: return "FRAG_ACK"; case WAIT_FRAG_ACK: return "WAIT_FRAG_ACK"; case DONE: return "DONE"; case FAIL: return "FAIL"; } return "??"; } static void eap_tnc_set_state(struct eap_tnc_data *data, enum eap_tnc_state new_state) { wpa_printf(MSG_DEBUG, "EAP-TNC: %s -> %s", eap_tnc_state_txt(data->state), eap_tnc_state_txt(new_state)); data->state = new_state; } static void * eap_tnc_init(struct eap_sm *sm) { struct eap_tnc_data *data; data = os_zalloc(sizeof(*data)); if (data == NULL) return NULL; eap_tnc_set_state(data, START); data->tncs = tncs_init(); if (data->tncs == NULL) { os_free(data); return NULL; } data->fragment_size = sm->fragment_size > 100 ? sm->fragment_size - 98 : 1300; return data; } static void eap_tnc_reset(struct eap_sm *sm, void *priv) { struct eap_tnc_data *data = priv; wpabuf_free(data->in_buf); wpabuf_free(data->out_buf); tncs_deinit(data->tncs); os_free(data); } static struct wpabuf * eap_tnc_build_start(struct eap_sm *sm, struct eap_tnc_data *data, u8 id) { struct wpabuf *req; req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TNC, 1, EAP_CODE_REQUEST, id); if (req == NULL) { wpa_printf(MSG_ERROR, "EAP-TNC: Failed to allocate memory for " "request"); eap_tnc_set_state(data, FAIL); return NULL; } wpabuf_put_u8(req, EAP_TNC_FLAGS_START | EAP_TNC_VERSION); eap_tnc_set_state(data, CONTINUE); return req; } static struct wpabuf * eap_tnc_build(struct eap_sm *sm, struct eap_tnc_data *data) { struct wpabuf *req; u8 *rpos, *rpos1; size_t rlen; char *start_buf, *end_buf; size_t start_len, end_len; size_t imv_len; imv_len = tncs_total_send_len(data->tncs); start_buf = tncs_if_tnccs_start(data->tncs); if (start_buf == NULL) return NULL; start_len = os_strlen(start_buf); end_buf = tncs_if_tnccs_end(); if (end_buf == NULL) { os_free(start_buf); return NULL; } end_len = os_strlen(end_buf); rlen = start_len + imv_len + end_len; req = wpabuf_alloc(rlen); if (req == NULL) { os_free(start_buf); os_free(end_buf); return NULL; } wpabuf_put_data(req, start_buf, start_len); os_free(start_buf); rpos1 = wpabuf_put(req, 0); rpos = tncs_copy_send_buf(data->tncs, rpos1); wpabuf_put(req, rpos - rpos1); wpabuf_put_data(req, end_buf, end_len); os_free(end_buf); wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-TNC: Request", wpabuf_head(req), wpabuf_len(req)); return req; } static struct wpabuf * eap_tnc_build_recommendation(struct eap_sm *sm, struct eap_tnc_data *data) { switch (data->recommendation) { case ALLOW: eap_tnc_set_state(data, DONE); break; case ISOLATE: eap_tnc_set_state(data, FAIL); /* TODO: support assignment to a different VLAN */ break; case NO_ACCESS: eap_tnc_set_state(data, FAIL); break; case NO_RECOMMENDATION: eap_tnc_set_state(data, DONE); break; default: wpa_printf(MSG_DEBUG, "EAP-TNC: Unknown recommendation"); return NULL; } return eap_tnc_build(sm, data); } static struct wpabuf * eap_tnc_build_frag_ack(u8 id, u8 code) { struct wpabuf *msg; msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TNC, 1, code, id); if (msg == NULL) { wpa_printf(MSG_ERROR, "EAP-TNC: Failed to allocate memory " "for fragment ack"); return NULL; } wpabuf_put_u8(msg, EAP_TNC_VERSION); /* Flags */ wpa_printf(MSG_DEBUG, "EAP-TNC: Send fragment ack"); return msg; } static struct wpabuf * eap_tnc_build_msg(struct eap_tnc_data *data, u8 id) { struct wpabuf *req; u8 flags; size_t send_len, plen; wpa_printf(MSG_DEBUG, "EAP-TNC: Generating Request"); flags = EAP_TNC_VERSION; send_len = wpabuf_len(data->out_buf) - data->out_used; if (1 + send_len > data->fragment_size) { send_len = data->fragment_size - 1; flags |= EAP_TNC_FLAGS_MORE_FRAGMENTS; if (data->out_used == 0) { flags |= EAP_TNC_FLAGS_LENGTH_INCLUDED; send_len -= 4; } } plen = 1 + send_len; if (flags & EAP_TNC_FLAGS_LENGTH_INCLUDED) plen += 4; req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TNC, plen, EAP_CODE_REQUEST, id); if (req == NULL) return NULL; wpabuf_put_u8(req, flags); /* Flags */ if (flags & EAP_TNC_FLAGS_LENGTH_INCLUDED) wpabuf_put_be32(req, wpabuf_len(data->out_buf)); wpabuf_put_data(req, wpabuf_head_u8(data->out_buf) + data->out_used, send_len); data->out_used += send_len; if (data->out_used == wpabuf_len(data->out_buf)) { wpa_printf(MSG_DEBUG, "EAP-TNC: Sending out %lu bytes " "(message sent completely)", (unsigned long) send_len); wpabuf_free(data->out_buf); data->out_buf = NULL; data->out_used = 0; if (data->was_fail) eap_tnc_set_state(data, FAIL); else if (data->was_done) eap_tnc_set_state(data, DONE); } else { wpa_printf(MSG_DEBUG, "EAP-TNC: Sending out %lu bytes " "(%lu more to send)", (unsigned long) send_len, (unsigned long) wpabuf_len(data->out_buf) - data->out_used); if (data->state == FAIL) data->was_fail = 1; else if (data->state == DONE) data->was_done = 1; eap_tnc_set_state(data, WAIT_FRAG_ACK); } return req; } static struct wpabuf * eap_tnc_buildReq(struct eap_sm *sm, void *priv, u8 id) { struct eap_tnc_data *data = priv; switch (data->state) { case START: tncs_init_connection(data->tncs); return eap_tnc_build_start(sm, data, id); case CONTINUE: if (data->out_buf == NULL) { data->out_buf = eap_tnc_build(sm, data); if (data->out_buf == NULL) { wpa_printf(MSG_DEBUG, "EAP-TNC: Failed to " "generate message"); return NULL; } data->out_used = 0; } return eap_tnc_build_msg(data, id); case RECOMMENDATION: if (data->out_buf == NULL) { data->out_buf = eap_tnc_build_recommendation(sm, data); if (data->out_buf == NULL) { wpa_printf(MSG_DEBUG, "EAP-TNC: Failed to " "generate recommendation message"); return NULL; } data->out_used = 0; } return eap_tnc_build_msg(data, id); case WAIT_FRAG_ACK: return eap_tnc_build_msg(data, id); case FRAG_ACK: return eap_tnc_build_frag_ack(id, EAP_CODE_REQUEST); case DONE: case FAIL: return NULL; } return NULL; } static Boolean eap_tnc_check(struct eap_sm *sm, void *priv, struct wpabuf *respData) { struct eap_tnc_data *data = priv; const u8 *pos; size_t len; pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TNC, respData, &len); if (pos == NULL) { wpa_printf(MSG_INFO, "EAP-TNC: Invalid frame"); return TRUE; } if (len == 0 && data->state != WAIT_FRAG_ACK) { wpa_printf(MSG_INFO, "EAP-TNC: Invalid frame (empty)"); return TRUE; } if (len == 0) return FALSE; /* Fragment ACK does not include flags */ if ((*pos & EAP_TNC_VERSION_MASK) != EAP_TNC_VERSION) { wpa_printf(MSG_DEBUG, "EAP-TNC: Unsupported version %d", *pos & EAP_TNC_VERSION_MASK); return TRUE; } if (*pos & EAP_TNC_FLAGS_START) { wpa_printf(MSG_DEBUG, "EAP-TNC: Peer used Start flag"); return TRUE; } return FALSE; } static void tncs_process(struct eap_tnc_data *data, struct wpabuf *inbuf) { enum tncs_process_res res; res = tncs_process_if_tnccs(data->tncs, wpabuf_head(inbuf), wpabuf_len(inbuf)); switch (res) { case TNCCS_RECOMMENDATION_ALLOW: wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS allowed access"); eap_tnc_set_state(data, RECOMMENDATION); data->recommendation = ALLOW; break; case TNCCS_RECOMMENDATION_NO_RECOMMENDATION: wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS has no recommendation"); eap_tnc_set_state(data, RECOMMENDATION); data->recommendation = NO_RECOMMENDATION; break; case TNCCS_RECOMMENDATION_ISOLATE: wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS requested isolation"); eap_tnc_set_state(data, RECOMMENDATION); data->recommendation = ISOLATE; break; case TNCCS_RECOMMENDATION_NO_ACCESS: wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS rejected access"); eap_tnc_set_state(data, RECOMMENDATION); data->recommendation = NO_ACCESS; break; case TNCCS_PROCESS_ERROR: wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS processing error"); eap_tnc_set_state(data, FAIL); break; default: break; } } static int eap_tnc_process_cont(struct eap_tnc_data *data, const u8 *buf, size_t len) { /* Process continuation of a pending message */ if (len > wpabuf_tailroom(data->in_buf)) { wpa_printf(MSG_DEBUG, "EAP-TNC: Fragment overflow"); eap_tnc_set_state(data, FAIL); return -1; } wpabuf_put_data(data->in_buf, buf, len); wpa_printf(MSG_DEBUG, "EAP-TNC: Received %lu bytes, waiting for %lu " "bytes more", (unsigned long) len, (unsigned long) wpabuf_tailroom(data->in_buf)); return 0; } static int eap_tnc_process_fragment(struct eap_tnc_data *data, u8 flags, u32 message_length, const u8 *buf, size_t len) { /* Process a fragment that is not the last one of the message */ if (data->in_buf == NULL && !(flags & EAP_TNC_FLAGS_LENGTH_INCLUDED)) { wpa_printf(MSG_DEBUG, "EAP-TNC: No Message Length field in a " "fragmented packet"); return -1; } if (data->in_buf == NULL) { /* First fragment of the message */ data->in_buf = wpabuf_alloc(message_length); if (data->in_buf == NULL) { wpa_printf(MSG_DEBUG, "EAP-TNC: No memory for " "message"); return -1; } wpabuf_put_data(data->in_buf, buf, len); wpa_printf(MSG_DEBUG, "EAP-TNC: Received %lu bytes in first " "fragment, waiting for %lu bytes more", (unsigned long) len, (unsigned long) wpabuf_tailroom(data->in_buf)); } return 0; } static void eap_tnc_process(struct eap_sm *sm, void *priv, struct wpabuf *respData) { struct eap_tnc_data *data = priv; const u8 *pos, *end; size_t len; u8 flags; u32 message_length = 0; struct wpabuf tmpbuf; pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TNC, respData, &len); if (pos == NULL) return; /* Should not happen; message already verified */ end = pos + len; if (len == 1 && (data->state == DONE || data->state == FAIL)) { wpa_printf(MSG_DEBUG, "EAP-TNC: Peer acknowledged the last " "message"); return; } if (len == 0) { /* fragment ack */ flags = 0; } else flags = *pos++; if (flags & EAP_TNC_FLAGS_LENGTH_INCLUDED) { if (end - pos < 4) { wpa_printf(MSG_DEBUG, "EAP-TNC: Message underflow"); eap_tnc_set_state(data, FAIL); return; } message_length = WPA_GET_BE32(pos); pos += 4; if (message_length < (u32) (end - pos)) { wpa_printf(MSG_DEBUG, "EAP-TNC: Invalid Message " "Length (%d; %ld remaining in this msg)", message_length, (long) (end - pos)); eap_tnc_set_state(data, FAIL); return; } } wpa_printf(MSG_DEBUG, "EAP-TNC: Received packet: Flags 0x%x " "Message Length %u", flags, message_length); if (data->state == WAIT_FRAG_ACK) { if (len > 1) { wpa_printf(MSG_DEBUG, "EAP-TNC: Unexpected payload " "in WAIT_FRAG_ACK state"); eap_tnc_set_state(data, FAIL); return; } wpa_printf(MSG_DEBUG, "EAP-TNC: Fragment acknowledged"); eap_tnc_set_state(data, CONTINUE); return; } if (data->in_buf && eap_tnc_process_cont(data, pos, end - pos) < 0) { eap_tnc_set_state(data, FAIL); return; } if (flags & EAP_TNC_FLAGS_MORE_FRAGMENTS) { if (eap_tnc_process_fragment(data, flags, message_length, pos, end - pos) < 0) eap_tnc_set_state(data, FAIL); else eap_tnc_set_state(data, FRAG_ACK); return; } else if (data->state == FRAG_ACK) { wpa_printf(MSG_DEBUG, "EAP-TNC: All fragments received"); eap_tnc_set_state(data, CONTINUE); } if (data->in_buf == NULL) { /* Wrap unfragmented messages as wpabuf without extra copy */ wpabuf_set(&tmpbuf, pos, end - pos); data->in_buf = &tmpbuf; } wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-TNC: Received payload", wpabuf_head(data->in_buf), wpabuf_len(data->in_buf)); tncs_process(data, data->in_buf); if (data->in_buf != &tmpbuf) wpabuf_free(data->in_buf); data->in_buf = NULL; } static Boolean eap_tnc_isDone(struct eap_sm *sm, void *priv) { struct eap_tnc_data *data = priv; return data->state == DONE || data->state == FAIL; } static Boolean eap_tnc_isSuccess(struct eap_sm *sm, void *priv) { struct eap_tnc_data *data = priv; return data->state == DONE; } int eap_server_tnc_register(void) { struct eap_method *eap; int ret; eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_TNC, "TNC"); if (eap == NULL) return -1; eap->init = eap_tnc_init; eap->reset = eap_tnc_reset; eap->buildReq = eap_tnc_buildReq; eap->check = eap_tnc_check; eap->process = eap_tnc_process; eap->isDone = eap_tnc_isDone; eap->isSuccess = eap_tnc_isSuccess; ret = eap_server_method_register(eap); if (ret) eap_server_method_free(eap); return ret; } wpa-2.1/src/eap_server/eap_server_ttls.c000066400000000000000000000770771227414666700204730ustar00rootroot00000000000000/* * hostapd / EAP-TTLS (RFC 5281) * Copyright (c) 2004-2011, 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" #include "crypto/sha1.h" #include "crypto/tls.h" #include "eap_server/eap_i.h" #include "eap_server/eap_tls_common.h" #include "eap_common/chap.h" #include "eap_common/eap_ttls.h" #define EAP_TTLS_VERSION 0 static void eap_ttls_reset(struct eap_sm *sm, void *priv); struct eap_ttls_data { struct eap_ssl_data ssl; enum { START, PHASE1, PHASE2_START, PHASE2_METHOD, PHASE2_MSCHAPV2_RESP, SUCCESS, FAILURE } state; int ttls_version; const struct eap_method *phase2_method; void *phase2_priv; int mschapv2_resp_ok; u8 mschapv2_auth_response[20]; u8 mschapv2_ident; struct wpabuf *pending_phase2_eap_resp; int tnc_started; }; static const char * eap_ttls_state_txt(int state) { switch (state) { case START: return "START"; case PHASE1: return "PHASE1"; case PHASE2_START: return "PHASE2_START"; case PHASE2_METHOD: return "PHASE2_METHOD"; case PHASE2_MSCHAPV2_RESP: return "PHASE2_MSCHAPV2_RESP"; case SUCCESS: return "SUCCESS"; case FAILURE: return "FAILURE"; default: return "Unknown?!"; } } static void eap_ttls_state(struct eap_ttls_data *data, int state) { wpa_printf(MSG_DEBUG, "EAP-TTLS: %s -> %s", eap_ttls_state_txt(data->state), eap_ttls_state_txt(state)); data->state = state; } static u8 * eap_ttls_avp_hdr(u8 *avphdr, u32 avp_code, u32 vendor_id, int mandatory, size_t len) { struct ttls_avp_vendor *avp; u8 flags; size_t hdrlen; avp = (struct ttls_avp_vendor *) avphdr; flags = mandatory ? AVP_FLAGS_MANDATORY : 0; if (vendor_id) { flags |= AVP_FLAGS_VENDOR; hdrlen = sizeof(*avp); avp->vendor_id = host_to_be32(vendor_id); } else { hdrlen = sizeof(struct ttls_avp); } avp->avp_code = host_to_be32(avp_code); avp->avp_length = host_to_be32(((u32) flags << 24) | ((u32) (hdrlen + len))); return avphdr + hdrlen; } static struct wpabuf * eap_ttls_avp_encapsulate(struct wpabuf *resp, u32 avp_code, int mandatory) { struct wpabuf *avp; u8 *pos; avp = wpabuf_alloc(sizeof(struct ttls_avp) + wpabuf_len(resp) + 4); if (avp == NULL) { wpabuf_free(resp); return NULL; } pos = eap_ttls_avp_hdr(wpabuf_mhead(avp), avp_code, 0, mandatory, wpabuf_len(resp)); os_memcpy(pos, wpabuf_head(resp), wpabuf_len(resp)); pos += wpabuf_len(resp); AVP_PAD((const u8 *) wpabuf_head(avp), pos); wpabuf_free(resp); wpabuf_put(avp, pos - (u8 *) wpabuf_head(avp)); return avp; } struct eap_ttls_avp { /* Note: eap is allocated memory; caller is responsible for freeing * it. All the other pointers are pointing to the packet data, i.e., * they must not be freed separately. */ u8 *eap; size_t eap_len; u8 *user_name; size_t user_name_len; u8 *user_password; size_t user_password_len; u8 *chap_challenge; size_t chap_challenge_len; u8 *chap_password; size_t chap_password_len; u8 *mschap_challenge; size_t mschap_challenge_len; u8 *mschap_response; size_t mschap_response_len; u8 *mschap2_response; size_t mschap2_response_len; }; static int eap_ttls_avp_parse(struct wpabuf *buf, struct eap_ttls_avp *parse) { struct ttls_avp *avp; u8 *pos; int left; pos = wpabuf_mhead(buf); left = wpabuf_len(buf); os_memset(parse, 0, sizeof(*parse)); while (left > 0) { u32 avp_code, avp_length, vendor_id = 0; u8 avp_flags, *dpos; size_t pad, dlen; avp = (struct ttls_avp *) pos; avp_code = be_to_host32(avp->avp_code); avp_length = be_to_host32(avp->avp_length); avp_flags = (avp_length >> 24) & 0xff; avp_length &= 0xffffff; wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP: code=%d flags=0x%02x " "length=%d", (int) avp_code, avp_flags, (int) avp_length); if ((int) avp_length > left) { wpa_printf(MSG_WARNING, "EAP-TTLS: AVP overflow " "(len=%d, left=%d) - dropped", (int) avp_length, left); goto fail; } if (avp_length < sizeof(*avp)) { wpa_printf(MSG_WARNING, "EAP-TTLS: Invalid AVP length " "%d", avp_length); goto fail; } dpos = (u8 *) (avp + 1); dlen = avp_length - sizeof(*avp); if (avp_flags & AVP_FLAGS_VENDOR) { if (dlen < 4) { wpa_printf(MSG_WARNING, "EAP-TTLS: vendor AVP " "underflow"); goto fail; } vendor_id = be_to_host32(* (be32 *) dpos); wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP vendor_id %d", (int) vendor_id); dpos += 4; dlen -= 4; } wpa_hexdump(MSG_DEBUG, "EAP-TTLS: AVP data", dpos, dlen); if (vendor_id == 0 && avp_code == RADIUS_ATTR_EAP_MESSAGE) { wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP - EAP Message"); if (parse->eap == NULL) { parse->eap = os_malloc(dlen); if (parse->eap == NULL) { wpa_printf(MSG_WARNING, "EAP-TTLS: " "failed to allocate memory " "for Phase 2 EAP data"); goto fail; } os_memcpy(parse->eap, dpos, dlen); parse->eap_len = dlen; } else { u8 *neweap = os_realloc(parse->eap, parse->eap_len + dlen); if (neweap == NULL) { wpa_printf(MSG_WARNING, "EAP-TTLS: " "failed to allocate memory " "for Phase 2 EAP data"); goto fail; } os_memcpy(neweap + parse->eap_len, dpos, dlen); parse->eap = neweap; parse->eap_len += dlen; } } else if (vendor_id == 0 && avp_code == RADIUS_ATTR_USER_NAME) { wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: User-Name", dpos, dlen); parse->user_name = dpos; parse->user_name_len = dlen; } else if (vendor_id == 0 && avp_code == RADIUS_ATTR_USER_PASSWORD) { u8 *password = dpos; size_t password_len = dlen; while (password_len > 0 && password[password_len - 1] == '\0') { password_len--; } wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-TTLS: " "User-Password (PAP)", password, password_len); parse->user_password = password; parse->user_password_len = password_len; } else if (vendor_id == 0 && avp_code == RADIUS_ATTR_CHAP_CHALLENGE) { wpa_hexdump(MSG_DEBUG, "EAP-TTLS: CHAP-Challenge (CHAP)", dpos, dlen); parse->chap_challenge = dpos; parse->chap_challenge_len = dlen; } else if (vendor_id == 0 && avp_code == RADIUS_ATTR_CHAP_PASSWORD) { wpa_hexdump(MSG_DEBUG, "EAP-TTLS: CHAP-Password (CHAP)", dpos, dlen); parse->chap_password = dpos; parse->chap_password_len = dlen; } else if (vendor_id == RADIUS_VENDOR_ID_MICROSOFT && avp_code == RADIUS_ATTR_MS_CHAP_CHALLENGE) { wpa_hexdump(MSG_DEBUG, "EAP-TTLS: MS-CHAP-Challenge", dpos, dlen); parse->mschap_challenge = dpos; parse->mschap_challenge_len = dlen; } else if (vendor_id == RADIUS_VENDOR_ID_MICROSOFT && avp_code == RADIUS_ATTR_MS_CHAP_RESPONSE) { wpa_hexdump(MSG_DEBUG, "EAP-TTLS: MS-CHAP-Response (MSCHAP)", dpos, dlen); parse->mschap_response = dpos; parse->mschap_response_len = dlen; } else if (vendor_id == RADIUS_VENDOR_ID_MICROSOFT && avp_code == RADIUS_ATTR_MS_CHAP2_RESPONSE) { wpa_hexdump(MSG_DEBUG, "EAP-TTLS: MS-CHAP2-Response (MSCHAPV2)", dpos, dlen); parse->mschap2_response = dpos; parse->mschap2_response_len = dlen; } else if (avp_flags & AVP_FLAGS_MANDATORY) { wpa_printf(MSG_WARNING, "EAP-TTLS: Unsupported " "mandatory AVP code %d vendor_id %d - " "dropped", (int) avp_code, (int) vendor_id); goto fail; } else { wpa_printf(MSG_DEBUG, "EAP-TTLS: Ignoring unsupported " "AVP code %d vendor_id %d", (int) avp_code, (int) vendor_id); } pad = (4 - (avp_length & 3)) & 3; pos += avp_length + pad; left -= avp_length + pad; } return 0; fail: os_free(parse->eap); parse->eap = NULL; return -1; } static u8 * eap_ttls_implicit_challenge(struct eap_sm *sm, struct eap_ttls_data *data, size_t len) { return eap_server_tls_derive_key(sm, &data->ssl, "ttls challenge", len); } static void * eap_ttls_init(struct eap_sm *sm) { struct eap_ttls_data *data; data = os_zalloc(sizeof(*data)); if (data == NULL) return NULL; data->ttls_version = EAP_TTLS_VERSION; data->state = START; if (eap_server_tls_ssl_init(sm, &data->ssl, 0)) { wpa_printf(MSG_INFO, "EAP-TTLS: Failed to initialize SSL."); eap_ttls_reset(sm, data); return NULL; } return data; } static void eap_ttls_reset(struct eap_sm *sm, void *priv) { struct eap_ttls_data *data = priv; if (data == NULL) return; if (data->phase2_priv && data->phase2_method) data->phase2_method->reset(sm, data->phase2_priv); eap_server_tls_ssl_deinit(sm, &data->ssl); wpabuf_free(data->pending_phase2_eap_resp); os_free(data); } static struct wpabuf * eap_ttls_build_start(struct eap_sm *sm, struct eap_ttls_data *data, u8 id) { struct wpabuf *req; req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TTLS, 1, EAP_CODE_REQUEST, id); if (req == NULL) { wpa_printf(MSG_ERROR, "EAP-TTLS: Failed to allocate memory for" " request"); eap_ttls_state(data, FAILURE); return NULL; } wpabuf_put_u8(req, EAP_TLS_FLAGS_START | data->ttls_version); eap_ttls_state(data, PHASE1); return req; } static struct wpabuf * eap_ttls_build_phase2_eap_req( struct eap_sm *sm, struct eap_ttls_data *data, u8 id) { struct wpabuf *buf, *encr_req; buf = data->phase2_method->buildReq(sm, data->phase2_priv, id); if (buf == NULL) return NULL; wpa_hexdump_buf_key(MSG_DEBUG, "EAP-TTLS/EAP: Encapsulate Phase 2 data", buf); buf = eap_ttls_avp_encapsulate(buf, RADIUS_ATTR_EAP_MESSAGE, 1); if (buf == NULL) { wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: Failed to encapsulate " "packet"); return NULL; } wpa_hexdump_buf_key(MSG_DEBUG, "EAP-TTLS/EAP: Encrypt encapsulated " "Phase 2 data", buf); encr_req = eap_server_tls_encrypt(sm, &data->ssl, buf); wpabuf_free(buf); return encr_req; } static struct wpabuf * eap_ttls_build_phase2_mschapv2( struct eap_sm *sm, struct eap_ttls_data *data) { struct wpabuf *encr_req, msgbuf; u8 *req, *pos, *end; int ret; pos = req = os_malloc(100); if (req == NULL) return NULL; end = req + 100; if (data->mschapv2_resp_ok) { pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_MS_CHAP2_SUCCESS, RADIUS_VENDOR_ID_MICROSOFT, 1, 43); *pos++ = data->mschapv2_ident; ret = os_snprintf((char *) pos, end - pos, "S="); if (ret >= 0 && ret < end - pos) pos += ret; pos += wpa_snprintf_hex_uppercase( (char *) pos, end - pos, data->mschapv2_auth_response, sizeof(data->mschapv2_auth_response)); } else { pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_MS_CHAP_ERROR, RADIUS_VENDOR_ID_MICROSOFT, 1, 6); os_memcpy(pos, "Failed", 6); pos += 6; AVP_PAD(req, pos); } wpabuf_set(&msgbuf, req, pos - req); wpa_hexdump_buf_key(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Encrypting Phase 2 " "data", &msgbuf); encr_req = eap_server_tls_encrypt(sm, &data->ssl, &msgbuf); os_free(req); return encr_req; } static struct wpabuf * eap_ttls_buildReq(struct eap_sm *sm, void *priv, u8 id) { struct eap_ttls_data *data = priv; if (data->ssl.state == FRAG_ACK) { return eap_server_tls_build_ack(id, EAP_TYPE_TTLS, data->ttls_version); } if (data->ssl.state == WAIT_FRAG_ACK) { return eap_server_tls_build_msg(&data->ssl, EAP_TYPE_TTLS, data->ttls_version, id); } switch (data->state) { case START: return eap_ttls_build_start(sm, data, id); case PHASE1: if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) { wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase1 done, " "starting Phase2"); eap_ttls_state(data, PHASE2_START); } break; case PHASE2_METHOD: wpabuf_free(data->ssl.tls_out); data->ssl.tls_out_pos = 0; data->ssl.tls_out = eap_ttls_build_phase2_eap_req(sm, data, id); break; case PHASE2_MSCHAPV2_RESP: wpabuf_free(data->ssl.tls_out); data->ssl.tls_out_pos = 0; data->ssl.tls_out = eap_ttls_build_phase2_mschapv2(sm, data); break; default: wpa_printf(MSG_DEBUG, "EAP-TTLS: %s - unexpected state %d", __func__, data->state); return NULL; } return eap_server_tls_build_msg(&data->ssl, EAP_TYPE_TTLS, data->ttls_version, id); } static Boolean eap_ttls_check(struct eap_sm *sm, void *priv, struct wpabuf *respData) { const u8 *pos; size_t len; pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TTLS, respData, &len); if (pos == NULL || len < 1) { wpa_printf(MSG_INFO, "EAP-TTLS: Invalid frame"); return TRUE; } return FALSE; } static void eap_ttls_process_phase2_pap(struct eap_sm *sm, struct eap_ttls_data *data, const u8 *user_password, size_t user_password_len) { if (!sm->user || !sm->user->password || sm->user->password_hash || !(sm->user->ttls_auth & EAP_TTLS_AUTH_PAP)) { wpa_printf(MSG_DEBUG, "EAP-TTLS/PAP: No plaintext user " "password configured"); eap_ttls_state(data, FAILURE); return; } if (sm->user->password_len != user_password_len || os_memcmp(sm->user->password, user_password, user_password_len) != 0) { wpa_printf(MSG_DEBUG, "EAP-TTLS/PAP: Invalid user password"); eap_ttls_state(data, FAILURE); return; } wpa_printf(MSG_DEBUG, "EAP-TTLS/PAP: Correct user password"); eap_ttls_state(data, SUCCESS); } static void eap_ttls_process_phase2_chap(struct eap_sm *sm, struct eap_ttls_data *data, const u8 *challenge, size_t challenge_len, const u8 *password, size_t password_len) { u8 *chal, hash[CHAP_MD5_LEN]; if (challenge == NULL || password == NULL || challenge_len != EAP_TTLS_CHAP_CHALLENGE_LEN || password_len != 1 + EAP_TTLS_CHAP_PASSWORD_LEN) { wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: Invalid CHAP attributes " "(challenge len %lu password len %lu)", (unsigned long) challenge_len, (unsigned long) password_len); eap_ttls_state(data, FAILURE); return; } if (!sm->user || !sm->user->password || sm->user->password_hash || !(sm->user->ttls_auth & EAP_TTLS_AUTH_CHAP)) { wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: No plaintext user " "password configured"); eap_ttls_state(data, FAILURE); return; } chal = eap_ttls_implicit_challenge(sm, data, EAP_TTLS_CHAP_CHALLENGE_LEN + 1); if (chal == NULL) { wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: Failed to generate " "challenge from TLS data"); eap_ttls_state(data, FAILURE); return; } if (os_memcmp(challenge, chal, EAP_TTLS_CHAP_CHALLENGE_LEN) != 0 || password[0] != chal[EAP_TTLS_CHAP_CHALLENGE_LEN]) { wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: Challenge mismatch"); os_free(chal); eap_ttls_state(data, FAILURE); return; } os_free(chal); /* MD5(Ident + Password + Challenge) */ chap_md5(password[0], sm->user->password, sm->user->password_len, challenge, challenge_len, hash); if (os_memcmp(hash, password + 1, EAP_TTLS_CHAP_PASSWORD_LEN) == 0) { wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: Correct user password"); eap_ttls_state(data, SUCCESS); } else { wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: Invalid user password"); eap_ttls_state(data, FAILURE); } } static void eap_ttls_process_phase2_mschap(struct eap_sm *sm, struct eap_ttls_data *data, u8 *challenge, size_t challenge_len, u8 *response, size_t response_len) { u8 *chal, nt_response[24]; if (challenge == NULL || response == NULL || challenge_len != EAP_TTLS_MSCHAP_CHALLENGE_LEN || response_len != EAP_TTLS_MSCHAP_RESPONSE_LEN) { wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Invalid MS-CHAP " "attributes (challenge len %lu response len %lu)", (unsigned long) challenge_len, (unsigned long) response_len); eap_ttls_state(data, FAILURE); return; } if (!sm->user || !sm->user->password || !(sm->user->ttls_auth & EAP_TTLS_AUTH_MSCHAP)) { wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: No user password " "configured"); eap_ttls_state(data, FAILURE); return; } chal = eap_ttls_implicit_challenge(sm, data, EAP_TTLS_MSCHAP_CHALLENGE_LEN + 1); if (chal == NULL) { wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Failed to generate " "challenge from TLS data"); eap_ttls_state(data, FAILURE); return; } if (os_memcmp(challenge, chal, EAP_TTLS_MSCHAP_CHALLENGE_LEN) != 0 || response[0] != chal[EAP_TTLS_MSCHAP_CHALLENGE_LEN]) { wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Challenge mismatch"); os_free(chal); eap_ttls_state(data, FAILURE); return; } os_free(chal); if (sm->user->password_hash) challenge_response(challenge, sm->user->password, nt_response); else nt_challenge_response(challenge, sm->user->password, sm->user->password_len, nt_response); if (os_memcmp(nt_response, response + 2 + 24, 24) == 0) { wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Correct response"); eap_ttls_state(data, SUCCESS); } else { wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Invalid NT-Response"); wpa_hexdump(MSG_MSGDUMP, "EAP-TTLS/MSCHAP: Received", response + 2 + 24, 24); wpa_hexdump(MSG_MSGDUMP, "EAP-TTLS/MSCHAP: Expected", nt_response, 24); eap_ttls_state(data, FAILURE); } } static void eap_ttls_process_phase2_mschapv2(struct eap_sm *sm, struct eap_ttls_data *data, u8 *challenge, size_t challenge_len, u8 *response, size_t response_len) { u8 *chal, *username, nt_response[24], *rx_resp, *peer_challenge, *auth_challenge; size_t username_len, i; if (challenge == NULL || response == NULL || challenge_len != EAP_TTLS_MSCHAPV2_CHALLENGE_LEN || response_len != EAP_TTLS_MSCHAPV2_RESPONSE_LEN) { wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Invalid MS-CHAP2 " "attributes (challenge len %lu response len %lu)", (unsigned long) challenge_len, (unsigned long) response_len); eap_ttls_state(data, FAILURE); return; } if (!sm->user || !sm->user->password || !(sm->user->ttls_auth & EAP_TTLS_AUTH_MSCHAPV2)) { wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: No user password " "configured"); eap_ttls_state(data, FAILURE); return; } if (sm->identity == NULL) { wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: No user identity " "known"); eap_ttls_state(data, FAILURE); return; } /* MSCHAPv2 does not include optional domain name in the * challenge-response calculation, so remove domain prefix * (if present). */ username = sm->identity; username_len = sm->identity_len; for (i = 0; i < username_len; i++) { if (username[i] == '\\') { username_len -= i + 1; username += i + 1; break; } } chal = eap_ttls_implicit_challenge( sm, data, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN + 1); if (chal == NULL) { wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Failed to generate " "challenge from TLS data"); eap_ttls_state(data, FAILURE); return; } if (os_memcmp(challenge, chal, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN) != 0 || response[0] != chal[EAP_TTLS_MSCHAPV2_CHALLENGE_LEN]) { wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Challenge mismatch"); os_free(chal); eap_ttls_state(data, FAILURE); return; } os_free(chal); auth_challenge = challenge; peer_challenge = response + 2; wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-TTLS/MSCHAPV2: User", username, username_len); wpa_hexdump(MSG_MSGDUMP, "EAP-TTLS/MSCHAPV2: auth_challenge", auth_challenge, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN); wpa_hexdump(MSG_MSGDUMP, "EAP-TTLS/MSCHAPV2: peer_challenge", peer_challenge, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN); if (sm->user->password_hash) { generate_nt_response_pwhash(auth_challenge, peer_challenge, username, username_len, sm->user->password, nt_response); } else { generate_nt_response(auth_challenge, peer_challenge, username, username_len, sm->user->password, sm->user->password_len, nt_response); } rx_resp = response + 2 + EAP_TTLS_MSCHAPV2_CHALLENGE_LEN + 8; if (os_memcmp(nt_response, rx_resp, 24) == 0) { wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Correct " "NT-Response"); data->mschapv2_resp_ok = 1; if (sm->user->password_hash) { generate_authenticator_response_pwhash( sm->user->password, peer_challenge, auth_challenge, username, username_len, nt_response, data->mschapv2_auth_response); } else { generate_authenticator_response( sm->user->password, sm->user->password_len, peer_challenge, auth_challenge, username, username_len, nt_response, data->mschapv2_auth_response); } } else { wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Invalid " "NT-Response"); wpa_hexdump(MSG_MSGDUMP, "EAP-TTLS/MSCHAPV2: Received", rx_resp, 24); wpa_hexdump(MSG_MSGDUMP, "EAP-TTLS/MSCHAPV2: Expected", nt_response, 24); data->mschapv2_resp_ok = 0; } eap_ttls_state(data, PHASE2_MSCHAPV2_RESP); data->mschapv2_ident = response[0]; } static int eap_ttls_phase2_eap_init(struct eap_sm *sm, struct eap_ttls_data *data, EapType eap_type) { if (data->phase2_priv && data->phase2_method) { data->phase2_method->reset(sm, data->phase2_priv); data->phase2_method = NULL; data->phase2_priv = NULL; } data->phase2_method = eap_server_get_eap_method(EAP_VENDOR_IETF, eap_type); if (!data->phase2_method) return -1; sm->init_phase2 = 1; data->phase2_priv = data->phase2_method->init(sm); sm->init_phase2 = 0; return data->phase2_priv == NULL ? -1 : 0; } static void eap_ttls_process_phase2_eap_response(struct eap_sm *sm, struct eap_ttls_data *data, u8 *in_data, size_t in_len) { u8 next_type = EAP_TYPE_NONE; struct eap_hdr *hdr; u8 *pos; size_t left; struct wpabuf buf; const struct eap_method *m = data->phase2_method; void *priv = data->phase2_priv; if (priv == NULL) { wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: %s - Phase2 not " "initialized?!", __func__); return; } hdr = (struct eap_hdr *) in_data; pos = (u8 *) (hdr + 1); if (in_len > sizeof(*hdr) && *pos == EAP_TYPE_NAK) { left = in_len - sizeof(*hdr); wpa_hexdump(MSG_DEBUG, "EAP-TTLS/EAP: Phase2 type Nak'ed; " "allowed types", pos + 1, left - 1); eap_sm_process_nak(sm, pos + 1, left - 1); if (sm->user && sm->user_eap_method_index < EAP_MAX_METHODS && sm->user->methods[sm->user_eap_method_index].method != EAP_TYPE_NONE) { next_type = sm->user->methods[ sm->user_eap_method_index++].method; wpa_printf(MSG_DEBUG, "EAP-TTLS: try EAP type %d", next_type); if (eap_ttls_phase2_eap_init(sm, data, next_type)) { wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to " "initialize EAP type %d", next_type); eap_ttls_state(data, FAILURE); return; } } else { eap_ttls_state(data, FAILURE); } return; } wpabuf_set(&buf, in_data, in_len); if (m->check(sm, priv, &buf)) { wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: Phase2 check() asked to " "ignore the packet"); return; } m->process(sm, priv, &buf); if (sm->method_pending == METHOD_PENDING_WAIT) { wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: Phase2 method is in " "pending wait state - save decrypted response"); wpabuf_free(data->pending_phase2_eap_resp); data->pending_phase2_eap_resp = wpabuf_dup(&buf); } if (!m->isDone(sm, priv)) return; if (!m->isSuccess(sm, priv)) { wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: Phase2 method failed"); eap_ttls_state(data, FAILURE); return; } switch (data->state) { case PHASE2_START: if (eap_user_get(sm, sm->identity, sm->identity_len, 1) != 0) { wpa_hexdump_ascii(MSG_DEBUG, "EAP_TTLS: Phase2 " "Identity not found in the user " "database", sm->identity, sm->identity_len); eap_ttls_state(data, FAILURE); break; } eap_ttls_state(data, PHASE2_METHOD); next_type = sm->user->methods[0].method; sm->user_eap_method_index = 1; wpa_printf(MSG_DEBUG, "EAP-TTLS: try EAP type %d", next_type); if (eap_ttls_phase2_eap_init(sm, data, next_type)) { wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to initialize " "EAP type %d", next_type); eap_ttls_state(data, FAILURE); } break; case PHASE2_METHOD: eap_ttls_state(data, SUCCESS); break; case FAILURE: break; default: wpa_printf(MSG_DEBUG, "EAP-TTLS: %s - unexpected state %d", __func__, data->state); break; } } static void eap_ttls_process_phase2_eap(struct eap_sm *sm, struct eap_ttls_data *data, const u8 *eap, size_t eap_len) { struct eap_hdr *hdr; size_t len; if (data->state == PHASE2_START) { wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: initializing Phase 2"); if (eap_ttls_phase2_eap_init(sm, data, EAP_TYPE_IDENTITY) < 0) { wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: failed to " "initialize EAP-Identity"); return; } } if (eap_len < sizeof(*hdr)) { wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: too short Phase 2 EAP " "packet (len=%lu)", (unsigned long) eap_len); return; } hdr = (struct eap_hdr *) eap; len = be_to_host16(hdr->length); wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: received Phase 2 EAP: code=%d " "identifier=%d length=%lu", hdr->code, hdr->identifier, (unsigned long) len); if (len > eap_len) { wpa_printf(MSG_INFO, "EAP-TTLS/EAP: Length mismatch in Phase 2" " EAP frame (hdr len=%lu, data len in AVP=%lu)", (unsigned long) len, (unsigned long) eap_len); return; } switch (hdr->code) { case EAP_CODE_RESPONSE: eap_ttls_process_phase2_eap_response(sm, data, (u8 *) hdr, len); break; default: wpa_printf(MSG_INFO, "EAP-TTLS/EAP: Unexpected code=%d in " "Phase 2 EAP header", hdr->code); break; } } static void eap_ttls_process_phase2(struct eap_sm *sm, struct eap_ttls_data *data, struct wpabuf *in_buf) { struct wpabuf *in_decrypted; struct eap_ttls_avp parse; wpa_printf(MSG_DEBUG, "EAP-TTLS: received %lu bytes encrypted data for" " Phase 2", (unsigned long) wpabuf_len(in_buf)); if (data->pending_phase2_eap_resp) { wpa_printf(MSG_DEBUG, "EAP-TTLS: Pending Phase 2 EAP response " "- skip decryption and use old data"); eap_ttls_process_phase2_eap( sm, data, wpabuf_head(data->pending_phase2_eap_resp), wpabuf_len(data->pending_phase2_eap_resp)); wpabuf_free(data->pending_phase2_eap_resp); data->pending_phase2_eap_resp = NULL; return; } in_decrypted = tls_connection_decrypt(sm->ssl_ctx, data->ssl.conn, in_buf); if (in_decrypted == NULL) { wpa_printf(MSG_INFO, "EAP-TTLS: Failed to decrypt Phase 2 " "data"); eap_ttls_state(data, FAILURE); return; } wpa_hexdump_buf_key(MSG_DEBUG, "EAP-TTLS: Decrypted Phase 2 EAP", in_decrypted); if (eap_ttls_avp_parse(in_decrypted, &parse) < 0) { wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to parse AVPs"); wpabuf_free(in_decrypted); eap_ttls_state(data, FAILURE); return; } if (parse.user_name) { os_free(sm->identity); sm->identity = os_malloc(parse.user_name_len); if (sm->identity == NULL) { eap_ttls_state(data, FAILURE); goto done; } os_memcpy(sm->identity, parse.user_name, parse.user_name_len); sm->identity_len = parse.user_name_len; if (eap_user_get(sm, parse.user_name, parse.user_name_len, 1) != 0) { wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase2 Identity not " "found in the user database"); eap_ttls_state(data, FAILURE); goto done; } } #ifdef EAP_SERVER_TNC if (data->tnc_started && parse.eap == NULL) { wpa_printf(MSG_DEBUG, "EAP-TTLS: TNC started but no EAP " "response from peer"); eap_ttls_state(data, FAILURE); goto done; } #endif /* EAP_SERVER_TNC */ if (parse.eap) { eap_ttls_process_phase2_eap(sm, data, parse.eap, parse.eap_len); } else if (parse.user_password) { eap_ttls_process_phase2_pap(sm, data, parse.user_password, parse.user_password_len); } else if (parse.chap_password) { eap_ttls_process_phase2_chap(sm, data, parse.chap_challenge, parse.chap_challenge_len, parse.chap_password, parse.chap_password_len); } else if (parse.mschap_response) { eap_ttls_process_phase2_mschap(sm, data, parse.mschap_challenge, parse.mschap_challenge_len, parse.mschap_response, parse.mschap_response_len); } else if (parse.mschap2_response) { eap_ttls_process_phase2_mschapv2(sm, data, parse.mschap_challenge, parse.mschap_challenge_len, parse.mschap2_response, parse.mschap2_response_len); } done: wpabuf_free(in_decrypted); os_free(parse.eap); } static void eap_ttls_start_tnc(struct eap_sm *sm, struct eap_ttls_data *data) { #ifdef EAP_SERVER_TNC if (!sm->tnc || data->state != SUCCESS || data->tnc_started) return; wpa_printf(MSG_DEBUG, "EAP-TTLS: Initialize TNC"); if (eap_ttls_phase2_eap_init(sm, data, EAP_TYPE_TNC)) { wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to initialize TNC"); eap_ttls_state(data, FAILURE); return; } data->tnc_started = 1; eap_ttls_state(data, PHASE2_METHOD); #endif /* EAP_SERVER_TNC */ } static int eap_ttls_process_version(struct eap_sm *sm, void *priv, int peer_version) { struct eap_ttls_data *data = priv; if (peer_version < data->ttls_version) { wpa_printf(MSG_DEBUG, "EAP-TTLS: peer ver=%d, own ver=%d; " "use version %d", peer_version, data->ttls_version, peer_version); data->ttls_version = peer_version; } return 0; } static void eap_ttls_process_msg(struct eap_sm *sm, void *priv, const struct wpabuf *respData) { struct eap_ttls_data *data = priv; switch (data->state) { case PHASE1: if (eap_server_tls_phase1(sm, &data->ssl) < 0) eap_ttls_state(data, FAILURE); break; case PHASE2_START: case PHASE2_METHOD: eap_ttls_process_phase2(sm, data, data->ssl.tls_in); eap_ttls_start_tnc(sm, data); break; case PHASE2_MSCHAPV2_RESP: if (data->mschapv2_resp_ok && wpabuf_len(data->ssl.tls_in) == 0) { wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Peer " "acknowledged response"); eap_ttls_state(data, SUCCESS); } else if (!data->mschapv2_resp_ok) { wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Peer " "acknowledged error"); eap_ttls_state(data, FAILURE); } else { wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Unexpected " "frame from peer (payload len %lu, " "expected empty frame)", (unsigned long) wpabuf_len(data->ssl.tls_in)); eap_ttls_state(data, FAILURE); } eap_ttls_start_tnc(sm, data); break; default: wpa_printf(MSG_DEBUG, "EAP-TTLS: Unexpected state %d in %s", data->state, __func__); break; } } static void eap_ttls_process(struct eap_sm *sm, void *priv, struct wpabuf *respData) { struct eap_ttls_data *data = priv; if (eap_server_tls_process(sm, &data->ssl, respData, data, EAP_TYPE_TTLS, eap_ttls_process_version, eap_ttls_process_msg) < 0) eap_ttls_state(data, FAILURE); } static Boolean eap_ttls_isDone(struct eap_sm *sm, void *priv) { struct eap_ttls_data *data = priv; return data->state == SUCCESS || data->state == FAILURE; } static u8 * eap_ttls_getKey(struct eap_sm *sm, void *priv, size_t *len) { struct eap_ttls_data *data = priv; u8 *eapKeyData; if (data->state != SUCCESS) return NULL; eapKeyData = eap_server_tls_derive_key(sm, &data->ssl, "ttls keying material", EAP_TLS_KEY_LEN); if (eapKeyData) { *len = EAP_TLS_KEY_LEN; wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Derived key", eapKeyData, EAP_TLS_KEY_LEN); } else { wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to derive key"); } return eapKeyData; } static Boolean eap_ttls_isSuccess(struct eap_sm *sm, void *priv) { struct eap_ttls_data *data = priv; return data->state == SUCCESS; } int eap_server_ttls_register(void) { struct eap_method *eap; int ret; eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_TTLS, "TTLS"); if (eap == NULL) return -1; eap->init = eap_ttls_init; eap->reset = eap_ttls_reset; eap->buildReq = eap_ttls_buildReq; eap->check = eap_ttls_check; eap->process = eap_ttls_process; eap->isDone = eap_ttls_isDone; eap->getKey = eap_ttls_getKey; eap->isSuccess = eap_ttls_isSuccess; ret = eap_server_method_register(eap); if (ret) eap_server_method_free(eap); return ret; } wpa-2.1/src/eap_server/eap_server_vendor_test.c000066400000000000000000000076741227414666700220350ustar00rootroot00000000000000/* * hostapd / Test method for vendor specific (expanded) EAP type * Copyright (c) 2005-2007, 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_i.h" #define EAP_VENDOR_ID EAP_VENDOR_HOSTAP #define EAP_VENDOR_TYPE 0xfcfbfaf9 struct eap_vendor_test_data { enum { INIT, CONFIRM, SUCCESS, FAILURE } state; }; static const char * eap_vendor_test_state_txt(int state) { switch (state) { case INIT: return "INIT"; case CONFIRM: return "CONFIRM"; case SUCCESS: return "SUCCESS"; case FAILURE: return "FAILURE"; default: return "?"; } } static void eap_vendor_test_state(struct eap_vendor_test_data *data, int state) { wpa_printf(MSG_DEBUG, "EAP-VENDOR-TEST: %s -> %s", eap_vendor_test_state_txt(data->state), eap_vendor_test_state_txt(state)); data->state = state; } static void * eap_vendor_test_init(struct eap_sm *sm) { struct eap_vendor_test_data *data; data = os_zalloc(sizeof(*data)); if (data == NULL) return NULL; data->state = INIT; return data; } static void eap_vendor_test_reset(struct eap_sm *sm, void *priv) { struct eap_vendor_test_data *data = priv; os_free(data); } static struct wpabuf * eap_vendor_test_buildReq(struct eap_sm *sm, void *priv, u8 id) { struct eap_vendor_test_data *data = priv; struct wpabuf *req; req = eap_msg_alloc(EAP_VENDOR_ID, EAP_VENDOR_TYPE, 1, EAP_CODE_REQUEST, id); if (req == NULL) { wpa_printf(MSG_ERROR, "EAP-VENDOR-TEST: Failed to allocate " "memory for request"); return NULL; } wpabuf_put_u8(req, data->state == INIT ? 1 : 3); return req; } static Boolean eap_vendor_test_check(struct eap_sm *sm, void *priv, struct wpabuf *respData) { const u8 *pos; size_t len; pos = eap_hdr_validate(EAP_VENDOR_ID, EAP_VENDOR_TYPE, respData, &len); if (pos == NULL || len < 1) { wpa_printf(MSG_INFO, "EAP-VENDOR-TEST: Invalid frame"); return TRUE; } return FALSE; } static void eap_vendor_test_process(struct eap_sm *sm, void *priv, struct wpabuf *respData) { struct eap_vendor_test_data *data = priv; const u8 *pos; size_t len; pos = eap_hdr_validate(EAP_VENDOR_ID, EAP_VENDOR_TYPE, respData, &len); if (pos == NULL || len < 1) return; if (data->state == INIT) { if (*pos == 2) eap_vendor_test_state(data, CONFIRM); else eap_vendor_test_state(data, FAILURE); } else if (data->state == CONFIRM) { if (*pos == 4) eap_vendor_test_state(data, SUCCESS); else eap_vendor_test_state(data, FAILURE); } else eap_vendor_test_state(data, FAILURE); } static Boolean eap_vendor_test_isDone(struct eap_sm *sm, void *priv) { struct eap_vendor_test_data *data = priv; return data->state == SUCCESS; } static u8 * eap_vendor_test_getKey(struct eap_sm *sm, void *priv, size_t *len) { struct eap_vendor_test_data *data = priv; u8 *key; const int key_len = 64; if (data->state != SUCCESS) return NULL; key = os_malloc(key_len); if (key == NULL) return NULL; os_memset(key, 0x11, key_len / 2); os_memset(key + key_len / 2, 0x22, key_len / 2); *len = key_len; return key; } static Boolean eap_vendor_test_isSuccess(struct eap_sm *sm, void *priv) { struct eap_vendor_test_data *data = priv; return data->state == SUCCESS; } int eap_server_vendor_test_register(void) { struct eap_method *eap; int ret; eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, EAP_VENDOR_ID, EAP_VENDOR_TYPE, "VENDOR-TEST"); if (eap == NULL) return -1; eap->init = eap_vendor_test_init; eap->reset = eap_vendor_test_reset; eap->buildReq = eap_vendor_test_buildReq; eap->check = eap_vendor_test_check; eap->process = eap_vendor_test_process; eap->isDone = eap_vendor_test_isDone; eap->getKey = eap_vendor_test_getKey; eap->isSuccess = eap_vendor_test_isSuccess; ret = eap_server_method_register(eap); if (ret) eap_server_method_free(eap); return ret; } wpa-2.1/src/eap_server/eap_server_wsc.c000066400000000000000000000305161227414666700202640ustar00rootroot00000000000000/* * EAP-WSC server for Wi-Fi Protected Setup * Copyright (c) 2007-2008, 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 "eloop.h" #include "eap_i.h" #include "eap_common/eap_wsc_common.h" #include "p2p/p2p.h" #include "wps/wps.h" struct eap_wsc_data { enum { START, MESG, FRAG_ACK, WAIT_FRAG_ACK, DONE, FAIL } state; int registrar; struct wpabuf *in_buf; struct wpabuf *out_buf; enum wsc_op_code in_op_code, out_op_code; size_t out_used; size_t fragment_size; struct wps_data *wps; int ext_reg_timeout; }; #ifndef CONFIG_NO_STDOUT_DEBUG static const char * eap_wsc_state_txt(int state) { switch (state) { case START: return "START"; case MESG: return "MESG"; case FRAG_ACK: return "FRAG_ACK"; case WAIT_FRAG_ACK: return "WAIT_FRAG_ACK"; case DONE: return "DONE"; case FAIL: return "FAIL"; default: return "?"; } } #endif /* CONFIG_NO_STDOUT_DEBUG */ static void eap_wsc_state(struct eap_wsc_data *data, int state) { wpa_printf(MSG_DEBUG, "EAP-WSC: %s -> %s", eap_wsc_state_txt(data->state), eap_wsc_state_txt(state)); data->state = state; } static void eap_wsc_ext_reg_timeout(void *eloop_ctx, void *timeout_ctx) { struct eap_sm *sm = eloop_ctx; struct eap_wsc_data *data = timeout_ctx; if (sm->method_pending != METHOD_PENDING_WAIT) return; wpa_printf(MSG_DEBUG, "EAP-WSC: Timeout while waiting for an External " "Registrar"); data->ext_reg_timeout = 1; eap_sm_pending_cb(sm); } static void * eap_wsc_init(struct eap_sm *sm) { struct eap_wsc_data *data; int registrar; struct wps_config cfg; if (sm->identity && sm->identity_len == WSC_ID_REGISTRAR_LEN && os_memcmp(sm->identity, WSC_ID_REGISTRAR, WSC_ID_REGISTRAR_LEN) == 0) registrar = 0; /* Supplicant is Registrar */ else if (sm->identity && sm->identity_len == WSC_ID_ENROLLEE_LEN && os_memcmp(sm->identity, WSC_ID_ENROLLEE, WSC_ID_ENROLLEE_LEN) == 0) registrar = 1; /* Supplicant is Enrollee */ else { wpa_hexdump_ascii(MSG_INFO, "EAP-WSC: Unexpected identity", sm->identity, sm->identity_len); return NULL; } data = os_zalloc(sizeof(*data)); if (data == NULL) return NULL; data->state = registrar ? START : MESG; data->registrar = registrar; os_memset(&cfg, 0, sizeof(cfg)); cfg.wps = sm->wps; cfg.registrar = registrar; if (registrar) { if (sm->wps == NULL || sm->wps->registrar == NULL) { wpa_printf(MSG_INFO, "EAP-WSC: WPS Registrar not " "initialized"); os_free(data); return NULL; } } else { if (sm->user == NULL || sm->user->password == NULL) { /* * In theory, this should not really be needed, but * Windows 7 uses Registrar mode to probe AP's WPS * capabilities before trying to use Enrollee and fails * if the AP does not allow that probing to happen.. */ wpa_printf(MSG_DEBUG, "EAP-WSC: No AP PIN (password) " "configured for Enrollee functionality - " "allow for probing capabilities (M1)"); } else { cfg.pin = sm->user->password; cfg.pin_len = sm->user->password_len; } } cfg.assoc_wps_ie = sm->assoc_wps_ie; cfg.peer_addr = sm->peer_addr; #ifdef CONFIG_P2P if (sm->assoc_p2p_ie) { wpa_printf(MSG_DEBUG, "EAP-WSC: Prefer PSK format for P2P " "client"); cfg.use_psk_key = 1; cfg.p2p_dev_addr = p2p_get_go_dev_addr(sm->assoc_p2p_ie); } #endif /* CONFIG_P2P */ cfg.pbc_in_m1 = sm->pbc_in_m1; data->wps = wps_init(&cfg); if (data->wps == NULL) { os_free(data); return NULL; } data->fragment_size = sm->fragment_size > 0 ? sm->fragment_size : WSC_FRAGMENT_SIZE; return data; } static void eap_wsc_reset(struct eap_sm *sm, void *priv) { struct eap_wsc_data *data = priv; eloop_cancel_timeout(eap_wsc_ext_reg_timeout, sm, data); wpabuf_free(data->in_buf); wpabuf_free(data->out_buf); wps_deinit(data->wps); os_free(data); } static struct wpabuf * eap_wsc_build_start(struct eap_sm *sm, struct eap_wsc_data *data, u8 id) { struct wpabuf *req; req = eap_msg_alloc(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, 2, EAP_CODE_REQUEST, id); if (req == NULL) { wpa_printf(MSG_ERROR, "EAP-WSC: Failed to allocate memory for " "request"); return NULL; } wpa_printf(MSG_DEBUG, "EAP-WSC: Send WSC/Start"); wpabuf_put_u8(req, WSC_Start); /* Op-Code */ wpabuf_put_u8(req, 0); /* Flags */ return req; } static struct wpabuf * eap_wsc_build_msg(struct eap_wsc_data *data, u8 id) { struct wpabuf *req; u8 flags; size_t send_len, plen; flags = 0; send_len = wpabuf_len(data->out_buf) - data->out_used; if (2 + send_len > data->fragment_size) { send_len = data->fragment_size - 2; flags |= WSC_FLAGS_MF; if (data->out_used == 0) { flags |= WSC_FLAGS_LF; send_len -= 2; } } plen = 2 + send_len; if (flags & WSC_FLAGS_LF) plen += 2; req = eap_msg_alloc(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, plen, EAP_CODE_REQUEST, id); if (req == NULL) { wpa_printf(MSG_ERROR, "EAP-WSC: Failed to allocate memory for " "request"); return NULL; } wpabuf_put_u8(req, data->out_op_code); /* Op-Code */ wpabuf_put_u8(req, flags); /* Flags */ if (flags & WSC_FLAGS_LF) wpabuf_put_be16(req, wpabuf_len(data->out_buf)); wpabuf_put_data(req, wpabuf_head_u8(data->out_buf) + data->out_used, send_len); data->out_used += send_len; if (data->out_used == wpabuf_len(data->out_buf)) { wpa_printf(MSG_DEBUG, "EAP-WSC: Sending out %lu bytes " "(message sent completely)", (unsigned long) send_len); wpabuf_free(data->out_buf); data->out_buf = NULL; data->out_used = 0; eap_wsc_state(data, MESG); } else { wpa_printf(MSG_DEBUG, "EAP-WSC: Sending out %lu bytes " "(%lu more to send)", (unsigned long) send_len, (unsigned long) wpabuf_len(data->out_buf) - data->out_used); eap_wsc_state(data, WAIT_FRAG_ACK); } return req; } static struct wpabuf * eap_wsc_buildReq(struct eap_sm *sm, void *priv, u8 id) { struct eap_wsc_data *data = priv; switch (data->state) { case START: return eap_wsc_build_start(sm, data, id); case MESG: if (data->out_buf == NULL) { data->out_buf = wps_get_msg(data->wps, &data->out_op_code); if (data->out_buf == NULL) { wpa_printf(MSG_DEBUG, "EAP-WSC: Failed to " "receive message from WPS"); return NULL; } data->out_used = 0; } /* pass through */ case WAIT_FRAG_ACK: return eap_wsc_build_msg(data, id); case FRAG_ACK: return eap_wsc_build_frag_ack(id, EAP_CODE_REQUEST); default: wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected state %d in " "buildReq", data->state); return NULL; } } static Boolean eap_wsc_check(struct eap_sm *sm, void *priv, struct wpabuf *respData) { const u8 *pos; size_t len; pos = eap_hdr_validate(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, respData, &len); if (pos == NULL || len < 2) { wpa_printf(MSG_INFO, "EAP-WSC: Invalid frame"); return TRUE; } return FALSE; } static int eap_wsc_process_cont(struct eap_wsc_data *data, const u8 *buf, size_t len, u8 op_code) { /* Process continuation of a pending message */ if (op_code != data->in_op_code) { wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d in " "fragment (expected %d)", op_code, data->in_op_code); eap_wsc_state(data, FAIL); return -1; } if (len > wpabuf_tailroom(data->in_buf)) { wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment overflow"); eap_wsc_state(data, FAIL); return -1; } wpabuf_put_data(data->in_buf, buf, len); wpa_printf(MSG_DEBUG, "EAP-WSC: Received %lu bytes, waiting for %lu " "bytes more", (unsigned long) len, (unsigned long) wpabuf_tailroom(data->in_buf)); return 0; } static int eap_wsc_process_fragment(struct eap_wsc_data *data, u8 flags, u8 op_code, u16 message_length, const u8 *buf, size_t len) { /* Process a fragment that is not the last one of the message */ if (data->in_buf == NULL && !(flags & WSC_FLAGS_LF)) { wpa_printf(MSG_DEBUG, "EAP-WSC: No Message Length " "field in a fragmented packet"); return -1; } if (data->in_buf == NULL) { /* First fragment of the message */ data->in_buf = wpabuf_alloc(message_length); if (data->in_buf == NULL) { wpa_printf(MSG_DEBUG, "EAP-WSC: No memory for " "message"); return -1; } data->in_op_code = op_code; wpabuf_put_data(data->in_buf, buf, len); wpa_printf(MSG_DEBUG, "EAP-WSC: Received %lu bytes in " "first fragment, waiting for %lu bytes more", (unsigned long) len, (unsigned long) wpabuf_tailroom(data->in_buf)); } return 0; } static void eap_wsc_process(struct eap_sm *sm, void *priv, struct wpabuf *respData) { struct eap_wsc_data *data = priv; const u8 *start, *pos, *end; size_t len; u8 op_code, flags; u16 message_length = 0; enum wps_process_res res; struct wpabuf tmpbuf; eloop_cancel_timeout(eap_wsc_ext_reg_timeout, sm, data); if (data->ext_reg_timeout) { eap_wsc_state(data, FAIL); return; } pos = eap_hdr_validate(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, respData, &len); if (pos == NULL || len < 2) return; /* Should not happen; message already verified */ start = pos; end = start + len; op_code = *pos++; flags = *pos++; if (flags & WSC_FLAGS_LF) { if (end - pos < 2) { wpa_printf(MSG_DEBUG, "EAP-WSC: Message underflow"); return; } message_length = WPA_GET_BE16(pos); pos += 2; if (message_length < end - pos) { wpa_printf(MSG_DEBUG, "EAP-WSC: Invalid Message " "Length"); return; } } wpa_printf(MSG_DEBUG, "EAP-WSC: Received packet: Op-Code %d " "Flags 0x%x Message Length %d", op_code, flags, message_length); if (data->state == WAIT_FRAG_ACK) { if (op_code != WSC_FRAG_ACK) { wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d " "in WAIT_FRAG_ACK state", op_code); eap_wsc_state(data, FAIL); return; } wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment acknowledged"); eap_wsc_state(data, MESG); return; } if (op_code != WSC_ACK && op_code != WSC_NACK && op_code != WSC_MSG && op_code != WSC_Done) { wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d", op_code); eap_wsc_state(data, FAIL); return; } if (data->in_buf && eap_wsc_process_cont(data, pos, end - pos, op_code) < 0) { eap_wsc_state(data, FAIL); return; } if (flags & WSC_FLAGS_MF) { if (eap_wsc_process_fragment(data, flags, op_code, message_length, pos, end - pos) < 0) eap_wsc_state(data, FAIL); else eap_wsc_state(data, FRAG_ACK); return; } if (data->in_buf == NULL) { /* Wrap unfragmented messages as wpabuf without extra copy */ wpabuf_set(&tmpbuf, pos, end - pos); data->in_buf = &tmpbuf; } res = wps_process_msg(data->wps, op_code, data->in_buf); switch (res) { case WPS_DONE: wpa_printf(MSG_DEBUG, "EAP-WSC: WPS processing completed " "successfully - report EAP failure"); eap_wsc_state(data, FAIL); break; case WPS_CONTINUE: eap_wsc_state(data, MESG); break; case WPS_FAILURE: wpa_printf(MSG_DEBUG, "EAP-WSC: WPS processing failed"); eap_wsc_state(data, FAIL); break; case WPS_PENDING: eap_wsc_state(data, MESG); sm->method_pending = METHOD_PENDING_WAIT; eloop_cancel_timeout(eap_wsc_ext_reg_timeout, sm, data); eloop_register_timeout(5, 0, eap_wsc_ext_reg_timeout, sm, data); break; } if (data->in_buf != &tmpbuf) wpabuf_free(data->in_buf); data->in_buf = NULL; } static Boolean eap_wsc_isDone(struct eap_sm *sm, void *priv) { struct eap_wsc_data *data = priv; return data->state == FAIL; } static Boolean eap_wsc_isSuccess(struct eap_sm *sm, void *priv) { /* EAP-WSC will always result in EAP-Failure */ return FALSE; } static int eap_wsc_getTimeout(struct eap_sm *sm, void *priv) { /* Recommended retransmit times: retransmit timeout 5 seconds, * per-message timeout 15 seconds, i.e., 3 tries. */ sm->MaxRetrans = 2; /* total 3 attempts */ return 5; } int eap_server_wsc_register(void) { struct eap_method *eap; int ret; eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, "WSC"); if (eap == NULL) return -1; eap->init = eap_wsc_init; eap->reset = eap_wsc_reset; eap->buildReq = eap_wsc_buildReq; eap->check = eap_wsc_check; eap->process = eap_wsc_process; eap->isDone = eap_wsc_isDone; eap->isSuccess = eap_wsc_isSuccess; eap->getTimeout = eap_wsc_getTimeout; ret = eap_server_method_register(eap); if (ret) eap_server_method_free(eap); return ret; } wpa-2.1/src/eap_server/eap_sim_db.c000066400000000000000000001151201227414666700173320ustar00rootroot00000000000000/* * hostapd / EAP-SIM database/authenticator gateway * Copyright (c) 2005-2010, 2012, 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 that is using an external program as an SS7 gateway to * GSM/UMTS authentication center (HLR/AuC). hlr_auc_gw is an example * implementation of such a gateway program. This eap_sim_db.c takes care of * EAP-SIM/AKA pseudonyms and re-auth identities. It can be used with different * gateway implementations for HLR/AuC access. Alternatively, it can also be * completely replaced if the in-memory database of pseudonyms/re-auth * identities is not suitable for some cases. */ #include "includes.h" #include #ifdef CONFIG_SQLITE #include #endif /* CONFIG_SQLITE */ #include "common.h" #include "crypto/random.h" #include "eap_common/eap_sim_common.h" #include "eap_server/eap_sim_db.h" #include "eloop.h" struct eap_sim_pseudonym { struct eap_sim_pseudonym *next; char *permanent; /* permanent username */ char *pseudonym; /* pseudonym username */ }; struct eap_sim_db_pending { struct eap_sim_db_pending *next; char imsi[20]; enum { PENDING, SUCCESS, FAILURE } state; void *cb_session_ctx; int aka; union { struct { u8 kc[EAP_SIM_MAX_CHAL][EAP_SIM_KC_LEN]; u8 sres[EAP_SIM_MAX_CHAL][EAP_SIM_SRES_LEN]; u8 rand[EAP_SIM_MAX_CHAL][GSM_RAND_LEN]; int num_chal; } sim; struct { 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; } aka; } u; }; struct eap_sim_db_data { int sock; char *fname; char *local_sock; void (*get_complete_cb)(void *ctx, void *session_ctx); void *ctx; struct eap_sim_pseudonym *pseudonyms; struct eap_sim_reauth *reauths; struct eap_sim_db_pending *pending; #ifdef CONFIG_SQLITE sqlite3 *sqlite_db; char db_tmp_identity[100]; char db_tmp_pseudonym_str[100]; struct eap_sim_pseudonym db_tmp_pseudonym; struct eap_sim_reauth db_tmp_reauth; #endif /* CONFIG_SQLITE */ }; #ifdef CONFIG_SQLITE 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_pseudonym(sqlite3 *db) { char *err = NULL; const char *sql = "CREATE TABLE pseudonyms(" " permanent CHAR(21) PRIMARY KEY," " pseudonym CHAR(21) NOT NULL" ");"; wpa_printf(MSG_DEBUG, "EAP-SIM DB: Adding database table for " "pseudonym information"); if (sqlite3_exec(db, sql, NULL, NULL, &err) != SQLITE_OK) { wpa_printf(MSG_ERROR, "EAP-SIM DB: SQLite error: %s", err); sqlite3_free(err); return -1; } return 0; } static int db_table_create_reauth(sqlite3 *db) { char *err = NULL; const char *sql = "CREATE TABLE reauth(" " permanent CHAR(21) PRIMARY KEY," " reauth_id CHAR(21) NOT NULL," " counter INTEGER," " mk CHAR(40)," " k_encr CHAR(32)," " k_aut CHAR(64)," " k_re CHAR(64)" ");"; wpa_printf(MSG_DEBUG, "EAP-SIM DB: Adding database table for " "reauth information"); if (sqlite3_exec(db, sql, NULL, NULL, &err) != SQLITE_OK) { wpa_printf(MSG_ERROR, "EAP-SIM DB: SQLite error: %s", err); sqlite3_free(err); return -1; } return 0; } static sqlite3 * db_open(const char *db_file) { sqlite3 *db; if (sqlite3_open(db_file, &db)) { wpa_printf(MSG_ERROR, "EAP-SIM DB: Failed to open database " "%s: %s", db_file, sqlite3_errmsg(db)); sqlite3_close(db); return NULL; } if (!db_table_exists(db, "pseudonyms") && db_table_create_pseudonym(db) < 0) { sqlite3_close(db); return NULL; } if (!db_table_exists(db, "reauth") && db_table_create_reauth(db) < 0) { sqlite3_close(db); return NULL; } return db; } static int valid_db_string(const char *str) { const char *pos = str; while (*pos) { if ((*pos < '0' || *pos > '9') && (*pos < 'a' || *pos > 'f')) return 0; pos++; } return 1; } static int db_add_pseudonym(struct eap_sim_db_data *data, const char *permanent, char *pseudonym) { char cmd[128]; char *err = NULL; if (!valid_db_string(permanent) || !valid_db_string(pseudonym)) { os_free(pseudonym); return -1; } os_snprintf(cmd, sizeof(cmd), "INSERT OR REPLACE INTO pseudonyms " "(permanent, pseudonym) VALUES ('%s', '%s');", permanent, pseudonym); os_free(pseudonym); if (sqlite3_exec(data->sqlite_db, cmd, NULL, NULL, &err) != SQLITE_OK) { wpa_printf(MSG_ERROR, "EAP-SIM DB: SQLite error: %s", err); sqlite3_free(err); return -1; } return 0; } static int get_pseudonym_cb(void *ctx, int argc, char *argv[], char *col[]) { struct eap_sim_db_data *data = ctx; int i; for (i = 0; i < argc; i++) { if (os_strcmp(col[i], "permanent") == 0 && argv[i]) { os_strlcpy(data->db_tmp_identity, argv[i], sizeof(data->db_tmp_identity)); } } return 0; } static char * db_get_pseudonym(struct eap_sim_db_data *data, const char *pseudonym) { char cmd[128]; if (!valid_db_string(pseudonym)) return NULL; os_memset(&data->db_tmp_identity, 0, sizeof(data->db_tmp_identity)); os_snprintf(cmd, sizeof(cmd), "SELECT permanent FROM pseudonyms WHERE pseudonym='%s';", pseudonym); if (sqlite3_exec(data->sqlite_db, cmd, get_pseudonym_cb, data, NULL) != SQLITE_OK) return NULL; if (data->db_tmp_identity[0] == '\0') return NULL; return data->db_tmp_identity; } static int db_add_reauth(struct eap_sim_db_data *data, const char *permanent, char *reauth_id, u16 counter, const u8 *mk, const u8 *k_encr, const u8 *k_aut, const u8 *k_re) { char cmd[2000], *pos, *end; char *err = NULL; if (!valid_db_string(permanent) || !valid_db_string(reauth_id)) { os_free(reauth_id); return -1; } pos = cmd; end = pos + sizeof(cmd); pos += os_snprintf(pos, end - pos, "INSERT OR REPLACE INTO reauth " "(permanent, reauth_id, counter%s%s%s%s) " "VALUES ('%s', '%s', %u", mk ? ", mk" : "", k_encr ? ", k_encr" : "", k_aut ? ", k_aut" : "", k_re ? ", k_re" : "", permanent, reauth_id, counter); os_free(reauth_id); if (mk) { pos += os_snprintf(pos, end - pos, ", '"); pos += wpa_snprintf_hex(pos, end - pos, mk, EAP_SIM_MK_LEN); pos += os_snprintf(pos, end - pos, "'"); } if (k_encr) { pos += os_snprintf(pos, end - pos, ", '"); pos += wpa_snprintf_hex(pos, end - pos, k_encr, EAP_SIM_K_ENCR_LEN); pos += os_snprintf(pos, end - pos, "'"); } if (k_aut) { pos += os_snprintf(pos, end - pos, ", '"); pos += wpa_snprintf_hex(pos, end - pos, k_aut, EAP_AKA_PRIME_K_AUT_LEN); pos += os_snprintf(pos, end - pos, "'"); } if (k_re) { pos += os_snprintf(pos, end - pos, ", '"); pos += wpa_snprintf_hex(pos, end - pos, k_re, EAP_AKA_PRIME_K_RE_LEN); pos += os_snprintf(pos, end - pos, "'"); } os_snprintf(pos, end - pos, ");"); if (sqlite3_exec(data->sqlite_db, cmd, NULL, NULL, &err) != SQLITE_OK) { wpa_printf(MSG_ERROR, "EAP-SIM DB: SQLite error: %s", err); sqlite3_free(err); return -1; } return 0; } static int get_reauth_cb(void *ctx, int argc, char *argv[], char *col[]) { struct eap_sim_db_data *data = ctx; int i; struct eap_sim_reauth *reauth = &data->db_tmp_reauth; for (i = 0; i < argc; i++) { if (os_strcmp(col[i], "permanent") == 0 && argv[i]) { os_strlcpy(data->db_tmp_identity, argv[i], sizeof(data->db_tmp_identity)); reauth->permanent = data->db_tmp_identity; } else if (os_strcmp(col[i], "counter") == 0 && argv[i]) { reauth->counter = atoi(argv[i]); } else if (os_strcmp(col[i], "mk") == 0 && argv[i]) { hexstr2bin(argv[i], reauth->mk, sizeof(reauth->mk)); } else if (os_strcmp(col[i], "k_encr") == 0 && argv[i]) { hexstr2bin(argv[i], reauth->k_encr, sizeof(reauth->k_encr)); } else if (os_strcmp(col[i], "k_aut") == 0 && argv[i]) { hexstr2bin(argv[i], reauth->k_aut, sizeof(reauth->k_aut)); } else if (os_strcmp(col[i], "k_re") == 0 && argv[i]) { hexstr2bin(argv[i], reauth->k_re, sizeof(reauth->k_re)); } } return 0; } static struct eap_sim_reauth * db_get_reauth(struct eap_sim_db_data *data, const char *reauth_id) { char cmd[256]; if (!valid_db_string(reauth_id)) return NULL; os_memset(&data->db_tmp_reauth, 0, sizeof(data->db_tmp_reauth)); os_strlcpy(data->db_tmp_pseudonym_str, reauth_id, sizeof(data->db_tmp_pseudonym_str)); data->db_tmp_reauth.reauth_id = data->db_tmp_pseudonym_str; os_snprintf(cmd, sizeof(cmd), "SELECT * FROM reauth WHERE reauth_id='%s';", reauth_id); if (sqlite3_exec(data->sqlite_db, cmd, get_reauth_cb, data, NULL) != SQLITE_OK) return NULL; if (data->db_tmp_reauth.permanent == NULL) return NULL; return &data->db_tmp_reauth; } static void db_remove_reauth(struct eap_sim_db_data *data, struct eap_sim_reauth *reauth) { char cmd[256]; if (!valid_db_string(reauth->permanent)) return; os_snprintf(cmd, sizeof(cmd), "DELETE FROM reauth WHERE permanent='%s';", reauth->permanent); sqlite3_exec(data->sqlite_db, cmd, NULL, NULL, NULL); } #endif /* CONFIG_SQLITE */ static struct eap_sim_db_pending * eap_sim_db_get_pending(struct eap_sim_db_data *data, const char *imsi, int aka) { struct eap_sim_db_pending *entry, *prev = NULL; entry = data->pending; while (entry) { if (entry->aka == aka && os_strcmp(entry->imsi, imsi) == 0) { if (prev) prev->next = entry->next; else data->pending = entry->next; break; } prev = entry; entry = entry->next; } return entry; } static void eap_sim_db_add_pending(struct eap_sim_db_data *data, struct eap_sim_db_pending *entry) { entry->next = data->pending; data->pending = entry; } static void eap_sim_db_sim_resp_auth(struct eap_sim_db_data *data, const char *imsi, char *buf) { char *start, *end, *pos; struct eap_sim_db_pending *entry; int num_chal; /* * SIM-RESP-AUTH Kc(i):SRES(i):RAND(i) ... * SIM-RESP-AUTH FAILURE * (IMSI = ASCII string, Kc/SRES/RAND = hex string) */ entry = eap_sim_db_get_pending(data, imsi, 0); if (entry == NULL) { wpa_printf(MSG_DEBUG, "EAP-SIM DB: No pending entry for the " "received message found"); return; } start = buf; if (os_strncmp(start, "FAILURE", 7) == 0) { wpa_printf(MSG_DEBUG, "EAP-SIM DB: External server reported " "failure"); entry->state = FAILURE; eap_sim_db_add_pending(data, entry); data->get_complete_cb(data->ctx, entry->cb_session_ctx); return; } num_chal = 0; while (num_chal < EAP_SIM_MAX_CHAL) { end = os_strchr(start, ' '); if (end) *end = '\0'; pos = os_strchr(start, ':'); if (pos == NULL) goto parse_fail; *pos = '\0'; if (hexstr2bin(start, entry->u.sim.kc[num_chal], EAP_SIM_KC_LEN)) goto parse_fail; start = pos + 1; pos = os_strchr(start, ':'); if (pos == NULL) goto parse_fail; *pos = '\0'; if (hexstr2bin(start, entry->u.sim.sres[num_chal], EAP_SIM_SRES_LEN)) goto parse_fail; start = pos + 1; if (hexstr2bin(start, entry->u.sim.rand[num_chal], GSM_RAND_LEN)) goto parse_fail; num_chal++; if (end == NULL) break; else start = end + 1; } entry->u.sim.num_chal = num_chal; entry->state = SUCCESS; wpa_printf(MSG_DEBUG, "EAP-SIM DB: Authentication data parsed " "successfully - callback"); eap_sim_db_add_pending(data, entry); data->get_complete_cb(data->ctx, entry->cb_session_ctx); return; parse_fail: wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failed to parse response string"); os_free(entry); } static void eap_sim_db_aka_resp_auth(struct eap_sim_db_data *data, const char *imsi, char *buf) { char *start, *end; struct eap_sim_db_pending *entry; /* * AKA-RESP-AUTH * AKA-RESP-AUTH FAILURE * (IMSI = ASCII string, RAND/AUTN/IK/CK/RES = hex string) */ entry = eap_sim_db_get_pending(data, imsi, 1); if (entry == NULL) { wpa_printf(MSG_DEBUG, "EAP-SIM DB: No pending entry for the " "received message found"); return; } start = buf; if (os_strncmp(start, "FAILURE", 7) == 0) { wpa_printf(MSG_DEBUG, "EAP-SIM DB: External server reported " "failure"); entry->state = FAILURE; eap_sim_db_add_pending(data, entry); data->get_complete_cb(data->ctx, entry->cb_session_ctx); return; } end = os_strchr(start, ' '); if (end == NULL) goto parse_fail; *end = '\0'; if (hexstr2bin(start, entry->u.aka.rand, EAP_AKA_RAND_LEN)) goto parse_fail; start = end + 1; end = os_strchr(start, ' '); if (end == NULL) goto parse_fail; *end = '\0'; if (hexstr2bin(start, entry->u.aka.autn, EAP_AKA_AUTN_LEN)) goto parse_fail; start = end + 1; end = os_strchr(start, ' '); if (end == NULL) goto parse_fail; *end = '\0'; if (hexstr2bin(start, entry->u.aka.ik, EAP_AKA_IK_LEN)) goto parse_fail; start = end + 1; end = os_strchr(start, ' '); if (end == NULL) goto parse_fail; *end = '\0'; if (hexstr2bin(start, entry->u.aka.ck, EAP_AKA_CK_LEN)) goto parse_fail; start = end + 1; end = os_strchr(start, ' '); if (end) *end = '\0'; else { end = start; while (*end) end++; } entry->u.aka.res_len = (end - start) / 2; if (entry->u.aka.res_len > EAP_AKA_RES_MAX_LEN) { wpa_printf(MSG_DEBUG, "EAP-SIM DB: Too long RES"); entry->u.aka.res_len = 0; goto parse_fail; } if (hexstr2bin(start, entry->u.aka.res, entry->u.aka.res_len)) goto parse_fail; entry->state = SUCCESS; wpa_printf(MSG_DEBUG, "EAP-SIM DB: Authentication data parsed " "successfully - callback"); eap_sim_db_add_pending(data, entry); data->get_complete_cb(data->ctx, entry->cb_session_ctx); return; parse_fail: wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failed to parse response string"); os_free(entry); } static void eap_sim_db_receive(int sock, void *eloop_ctx, void *sock_ctx) { struct eap_sim_db_data *data = eloop_ctx; char buf[1000], *pos, *cmd, *imsi; int res; res = recv(sock, buf, sizeof(buf), 0); if (res < 0) return; wpa_hexdump_ascii_key(MSG_MSGDUMP, "EAP-SIM DB: Received from an " "external source", (u8 *) buf, res); if (res == 0) return; if (res >= (int) sizeof(buf)) res = sizeof(buf) - 1; buf[res] = '\0'; if (data->get_complete_cb == NULL) { wpa_printf(MSG_DEBUG, "EAP-SIM DB: No get_complete_cb " "registered"); return; } /* ... */ cmd = buf; pos = os_strchr(cmd, ' '); if (pos == NULL) goto parse_fail; *pos = '\0'; imsi = pos + 1; pos = os_strchr(imsi, ' '); if (pos == NULL) goto parse_fail; *pos = '\0'; wpa_printf(MSG_DEBUG, "EAP-SIM DB: External response=%s for IMSI %s", cmd, imsi); if (os_strcmp(cmd, "SIM-RESP-AUTH") == 0) eap_sim_db_sim_resp_auth(data, imsi, pos + 1); else if (os_strcmp(cmd, "AKA-RESP-AUTH") == 0) eap_sim_db_aka_resp_auth(data, imsi, pos + 1); else wpa_printf(MSG_INFO, "EAP-SIM DB: Unknown external response " "'%s'", cmd); return; parse_fail: wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failed to parse response string"); } static int eap_sim_db_open_socket(struct eap_sim_db_data *data) { struct sockaddr_un addr; static int counter = 0; if (os_strncmp(data->fname, "unix:", 5) != 0) return -1; data->sock = socket(PF_UNIX, SOCK_DGRAM, 0); if (data->sock < 0) { wpa_printf(MSG_INFO, "socket(eap_sim_db): %s", strerror(errno)); return -1; } os_memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; os_snprintf(addr.sun_path, sizeof(addr.sun_path), "/tmp/eap_sim_db_%d-%d", getpid(), counter++); os_free(data->local_sock); data->local_sock = os_strdup(addr.sun_path); if (bind(data->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { wpa_printf(MSG_INFO, "bind(eap_sim_db): %s", strerror(errno)); close(data->sock); data->sock = -1; return -1; } os_memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; os_strlcpy(addr.sun_path, data->fname + 5, sizeof(addr.sun_path)); if (connect(data->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { wpa_printf(MSG_INFO, "connect(eap_sim_db): %s", strerror(errno)); wpa_hexdump_ascii(MSG_INFO, "HLR/AuC GW socket", (u8 *) addr.sun_path, os_strlen(addr.sun_path)); close(data->sock); data->sock = -1; return -1; } eloop_register_read_sock(data->sock, eap_sim_db_receive, data, NULL); return 0; } static void eap_sim_db_close_socket(struct eap_sim_db_data *data) { if (data->sock >= 0) { eloop_unregister_read_sock(data->sock); close(data->sock); data->sock = -1; } if (data->local_sock) { unlink(data->local_sock); os_free(data->local_sock); data->local_sock = NULL; } } /** * eap_sim_db_init - Initialize EAP-SIM DB / authentication gateway interface * @config: Configuration data (e.g., file name) * @get_complete_cb: Callback function for reporting availability of triplets * @ctx: Context pointer for get_complete_cb * Returns: Pointer to a private data structure or %NULL on failure */ struct eap_sim_db_data * eap_sim_db_init(const char *config, void (*get_complete_cb)(void *ctx, void *session_ctx), void *ctx) { struct eap_sim_db_data *data; char *pos; data = os_zalloc(sizeof(*data)); if (data == NULL) return NULL; data->sock = -1; data->get_complete_cb = get_complete_cb; data->ctx = ctx; data->fname = os_strdup(config); if (data->fname == NULL) goto fail; pos = os_strstr(data->fname, " db="); if (pos) { *pos = '\0'; #ifdef CONFIG_SQLITE pos += 4; data->sqlite_db = db_open(pos); if (data->sqlite_db == NULL) goto fail; #endif /* CONFIG_SQLITE */ } if (os_strncmp(data->fname, "unix:", 5) == 0) { if (eap_sim_db_open_socket(data)) { wpa_printf(MSG_DEBUG, "EAP-SIM DB: External database " "connection not available - will retry " "later"); } } return data; fail: eap_sim_db_close_socket(data); os_free(data->fname); os_free(data); return NULL; } static void eap_sim_db_free_pseudonym(struct eap_sim_pseudonym *p) { os_free(p->permanent); os_free(p->pseudonym); os_free(p); } static void eap_sim_db_free_reauth(struct eap_sim_reauth *r) { os_free(r->permanent); os_free(r->reauth_id); os_free(r); } /** * eap_sim_db_deinit - Deinitialize EAP-SIM DB/authentication gw interface * @priv: Private data pointer from eap_sim_db_init() */ void eap_sim_db_deinit(void *priv) { struct eap_sim_db_data *data = priv; struct eap_sim_pseudonym *p, *prev; struct eap_sim_reauth *r, *prevr; struct eap_sim_db_pending *pending, *prev_pending; #ifdef CONFIG_SQLITE if (data->sqlite_db) { sqlite3_close(data->sqlite_db); data->sqlite_db = NULL; } #endif /* CONFIG_SQLITE */ eap_sim_db_close_socket(data); os_free(data->fname); p = data->pseudonyms; while (p) { prev = p; p = p->next; eap_sim_db_free_pseudonym(prev); } r = data->reauths; while (r) { prevr = r; r = r->next; eap_sim_db_free_reauth(prevr); } pending = data->pending; while (pending) { prev_pending = pending; pending = pending->next; os_free(prev_pending); } os_free(data); } static int eap_sim_db_send(struct eap_sim_db_data *data, const char *msg, size_t len) { int _errno = 0; if (send(data->sock, msg, len, 0) < 0) { _errno = errno; wpa_printf(MSG_INFO, "send[EAP-SIM DB UNIX]: %s", strerror(errno)); } if (_errno == ENOTCONN || _errno == EDESTADDRREQ || _errno == EINVAL || _errno == ECONNREFUSED) { /* Try to reconnect */ eap_sim_db_close_socket(data); if (eap_sim_db_open_socket(data) < 0) return -1; wpa_printf(MSG_DEBUG, "EAP-SIM DB: Reconnected to the " "external server"); if (send(data->sock, msg, len, 0) < 0) { wpa_printf(MSG_INFO, "send[EAP-SIM DB UNIX]: %s", strerror(errno)); return -1; } } return 0; } static void eap_sim_db_expire_pending(struct eap_sim_db_data *data) { /* TODO: add limit for maximum length for pending list; remove latest * (i.e., last) entry from the list if the limit is reached; could also * use timeout to expire pending entries */ } /** * eap_sim_db_get_gsm_triplets - Get GSM triplets * @data: Private data pointer from eap_sim_db_init() * @username: Permanent username (prefix | IMSI) * @max_chal: Maximum number of triplets * @_rand: Buffer for RAND values * @kc: Buffer for Kc values * @sres: Buffer for SRES values * @cb_session_ctx: Session callback context for get_complete_cb() * Returns: Number of triplets received (has to be less than or equal to * max_chal), -1 (EAP_SIM_DB_FAILURE) on error (e.g., user not found), or * -2 (EAP_SIM_DB_PENDING) if results are not yet available. In this case, the * callback function registered with eap_sim_db_init() will be called once the * results become available. * * When using an external server for GSM triplets, this function can always * start a request and return EAP_SIM_DB_PENDING immediately if authentication * triplets are not available. Once the triplets are received, callback * function registered with eap_sim_db_init() is called to notify EAP state * machine to reprocess the message. This eap_sim_db_get_gsm_triplets() * function will then be called again and the newly received triplets will then * be given to the caller. */ int eap_sim_db_get_gsm_triplets(struct eap_sim_db_data *data, const char *username, int max_chal, u8 *_rand, u8 *kc, u8 *sres, void *cb_session_ctx) { struct eap_sim_db_pending *entry; int len, ret; char msg[40]; const char *imsi; size_t imsi_len; if (username == NULL || username[0] != EAP_SIM_PERMANENT_PREFIX || username[1] == '\0' || os_strlen(username) > sizeof(entry->imsi)) { wpa_printf(MSG_DEBUG, "EAP-SIM DB: unexpected username '%s'", username); return EAP_SIM_DB_FAILURE; } imsi = username + 1; wpa_printf(MSG_DEBUG, "EAP-SIM DB: Get GSM triplets for IMSI '%s'", imsi); entry = eap_sim_db_get_pending(data, imsi, 0); if (entry) { int num_chal; if (entry->state == FAILURE) { wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending entry -> " "failure"); os_free(entry); return EAP_SIM_DB_FAILURE; } if (entry->state == PENDING) { wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending entry -> " "still pending"); eap_sim_db_add_pending(data, entry); return EAP_SIM_DB_PENDING; } wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending entry -> " "%d challenges", entry->u.sim.num_chal); num_chal = entry->u.sim.num_chal; if (num_chal > max_chal) num_chal = max_chal; os_memcpy(_rand, entry->u.sim.rand, num_chal * GSM_RAND_LEN); os_memcpy(sres, entry->u.sim.sres, num_chal * EAP_SIM_SRES_LEN); os_memcpy(kc, entry->u.sim.kc, num_chal * EAP_SIM_KC_LEN); os_free(entry); return num_chal; } if (data->sock < 0) { if (eap_sim_db_open_socket(data) < 0) return EAP_SIM_DB_FAILURE; } imsi_len = os_strlen(imsi); len = os_snprintf(msg, sizeof(msg), "SIM-REQ-AUTH "); if (len < 0 || len + imsi_len >= sizeof(msg)) return EAP_SIM_DB_FAILURE; os_memcpy(msg + len, imsi, imsi_len); len += imsi_len; ret = os_snprintf(msg + len, sizeof(msg) - len, " %d", max_chal); if (ret < 0 || (size_t) ret >= sizeof(msg) - len) return EAP_SIM_DB_FAILURE; len += ret; wpa_printf(MSG_DEBUG, "EAP-SIM DB: requesting SIM authentication " "data for IMSI '%s'", imsi); if (eap_sim_db_send(data, msg, len) < 0) return EAP_SIM_DB_FAILURE; entry = os_zalloc(sizeof(*entry)); if (entry == NULL) return EAP_SIM_DB_FAILURE; os_strlcpy(entry->imsi, imsi, sizeof(entry->imsi)); entry->cb_session_ctx = cb_session_ctx; entry->state = PENDING; eap_sim_db_add_pending(data, entry); eap_sim_db_expire_pending(data); return EAP_SIM_DB_PENDING; } static char * eap_sim_db_get_next(struct eap_sim_db_data *data, char prefix) { char *id, *pos, *end; u8 buf[10]; if (random_get_bytes(buf, sizeof(buf))) return NULL; id = os_malloc(sizeof(buf) * 2 + 2); if (id == NULL) return NULL; pos = id; end = id + sizeof(buf) * 2 + 2; *pos++ = prefix; pos += wpa_snprintf_hex(pos, end - pos, buf, sizeof(buf)); return id; } /** * eap_sim_db_get_next_pseudonym - EAP-SIM DB: Get next pseudonym * @data: Private data pointer from eap_sim_db_init() * @method: EAP method (SIM/AKA/AKA') * Returns: Next pseudonym (allocated string) or %NULL on failure * * This function is used to generate a pseudonym for EAP-SIM. The returned * pseudonym is not added to database at this point; it will need to be added * with eap_sim_db_add_pseudonym() once the authentication has been completed * successfully. Caller is responsible for freeing the returned buffer. */ char * eap_sim_db_get_next_pseudonym(struct eap_sim_db_data *data, enum eap_sim_db_method method) { char prefix = EAP_SIM_REAUTH_ID_PREFIX; switch (method) { case EAP_SIM_DB_SIM: prefix = EAP_SIM_PSEUDONYM_PREFIX; break; case EAP_SIM_DB_AKA: prefix = EAP_AKA_PSEUDONYM_PREFIX; break; case EAP_SIM_DB_AKA_PRIME: prefix = EAP_AKA_PRIME_PSEUDONYM_PREFIX; break; } return eap_sim_db_get_next(data, prefix); } /** * eap_sim_db_get_next_reauth_id - EAP-SIM DB: Get next reauth_id * @data: Private data pointer from eap_sim_db_init() * @method: EAP method (SIM/AKA/AKA') * Returns: Next reauth_id (allocated string) or %NULL on failure * * This function is used to generate a fast re-authentication identity for * EAP-SIM. The returned reauth_id is not added to database at this point; it * will need to be added with eap_sim_db_add_reauth() once the authentication * has been completed successfully. Caller is responsible for freeing the * returned buffer. */ char * eap_sim_db_get_next_reauth_id(struct eap_sim_db_data *data, enum eap_sim_db_method method) { char prefix = EAP_SIM_REAUTH_ID_PREFIX; switch (method) { case EAP_SIM_DB_SIM: prefix = EAP_SIM_REAUTH_ID_PREFIX; break; case EAP_SIM_DB_AKA: prefix = EAP_AKA_REAUTH_ID_PREFIX; break; case EAP_SIM_DB_AKA_PRIME: prefix = EAP_AKA_PRIME_REAUTH_ID_PREFIX; break; } return eap_sim_db_get_next(data, prefix); } /** * eap_sim_db_add_pseudonym - EAP-SIM DB: Add new pseudonym * @data: Private data pointer from eap_sim_db_init() * @permanent: Permanent username * @pseudonym: Pseudonym for this user. This needs to be an allocated buffer, * e.g., return value from eap_sim_db_get_next_pseudonym(). Caller must not * free it. * Returns: 0 on success, -1 on failure * * This function adds a new pseudonym for EAP-SIM user. EAP-SIM DB is * responsible of freeing pseudonym buffer once it is not needed anymore. */ int eap_sim_db_add_pseudonym(struct eap_sim_db_data *data, const char *permanent, char *pseudonym) { struct eap_sim_pseudonym *p; wpa_printf(MSG_DEBUG, "EAP-SIM DB: Add pseudonym '%s' for permanent " "username '%s'", pseudonym, permanent); /* TODO: could store last two pseudonyms */ #ifdef CONFIG_SQLITE if (data->sqlite_db) return db_add_pseudonym(data, permanent, pseudonym); #endif /* CONFIG_SQLITE */ for (p = data->pseudonyms; p; p = p->next) { if (os_strcmp(permanent, p->permanent) == 0) break; } if (p) { wpa_printf(MSG_DEBUG, "EAP-SIM DB: Replacing previous " "pseudonym: %s", p->pseudonym); os_free(p->pseudonym); p->pseudonym = pseudonym; return 0; } p = os_zalloc(sizeof(*p)); if (p == NULL) { os_free(pseudonym); return -1; } p->next = data->pseudonyms; p->permanent = os_strdup(permanent); if (p->permanent == NULL) { os_free(p); os_free(pseudonym); return -1; } p->pseudonym = pseudonym; data->pseudonyms = p; wpa_printf(MSG_DEBUG, "EAP-SIM DB: Added new pseudonym entry"); return 0; } static struct eap_sim_reauth * eap_sim_db_add_reauth_data(struct eap_sim_db_data *data, const char *permanent, char *reauth_id, u16 counter) { struct eap_sim_reauth *r; for (r = data->reauths; r; r = r->next) { if (os_strcmp(r->permanent, permanent) == 0) break; } if (r) { wpa_printf(MSG_DEBUG, "EAP-SIM DB: Replacing previous " "reauth_id: %s", r->reauth_id); os_free(r->reauth_id); r->reauth_id = reauth_id; } else { r = os_zalloc(sizeof(*r)); if (r == NULL) { os_free(reauth_id); return NULL; } r->next = data->reauths; r->permanent = os_strdup(permanent); if (r->permanent == NULL) { os_free(r); os_free(reauth_id); return NULL; } r->reauth_id = reauth_id; data->reauths = r; wpa_printf(MSG_DEBUG, "EAP-SIM DB: Added new reauth entry"); } r->counter = counter; return r; } /** * eap_sim_db_add_reauth - EAP-SIM DB: Add new re-authentication entry * @priv: Private data pointer from eap_sim_db_init() * @permanent: Permanent username * @identity_len: Length of identity * @reauth_id: reauth_id for this user. This needs to be an allocated buffer, * e.g., return value from eap_sim_db_get_next_reauth_id(). Caller must not * free it. * @counter: AT_COUNTER value for fast re-authentication * @mk: 16-byte MK from the previous full authentication or %NULL * Returns: 0 on success, -1 on failure * * This function adds a new re-authentication entry for an EAP-SIM user. * EAP-SIM DB is responsible of freeing reauth_id buffer once it is not needed * anymore. */ int eap_sim_db_add_reauth(struct eap_sim_db_data *data, const char *permanent, char *reauth_id, u16 counter, const u8 *mk) { struct eap_sim_reauth *r; wpa_printf(MSG_DEBUG, "EAP-SIM DB: Add reauth_id '%s' for permanent " "identity '%s'", reauth_id, permanent); #ifdef CONFIG_SQLITE if (data->sqlite_db) return db_add_reauth(data, permanent, reauth_id, counter, mk, NULL, NULL, NULL); #endif /* CONFIG_SQLITE */ r = eap_sim_db_add_reauth_data(data, permanent, reauth_id, counter); if (r == NULL) return -1; os_memcpy(r->mk, mk, EAP_SIM_MK_LEN); return 0; } #ifdef EAP_SERVER_AKA_PRIME /** * eap_sim_db_add_reauth_prime - EAP-AKA' DB: Add new re-authentication entry * @data: Private data pointer from eap_sim_db_init() * @permanent: Permanent username * @reauth_id: reauth_id for this user. This needs to be an allocated buffer, * e.g., return value from eap_sim_db_get_next_reauth_id(). Caller must not * free it. * @counter: AT_COUNTER value for fast re-authentication * @k_encr: K_encr from the previous full authentication * @k_aut: K_aut from the previous full authentication * @k_re: 32-byte K_re from the previous full authentication * Returns: 0 on success, -1 on failure * * This function adds a new re-authentication entry for an EAP-AKA' user. * EAP-SIM DB is responsible of freeing reauth_id buffer once it is not needed * anymore. */ int eap_sim_db_add_reauth_prime(struct eap_sim_db_data *data, const char *permanent, char *reauth_id, u16 counter, const u8 *k_encr, const u8 *k_aut, const u8 *k_re) { struct eap_sim_reauth *r; wpa_printf(MSG_DEBUG, "EAP-SIM DB: Add reauth_id '%s' for permanent " "identity '%s'", reauth_id, permanent); #ifdef CONFIG_SQLITE if (data->sqlite_db) return db_add_reauth(data, permanent, reauth_id, counter, NULL, k_encr, k_aut, k_re); #endif /* CONFIG_SQLITE */ r = eap_sim_db_add_reauth_data(data, permanent, reauth_id, counter); if (r == NULL) return -1; os_memcpy(r->k_encr, k_encr, EAP_SIM_K_ENCR_LEN); os_memcpy(r->k_aut, k_aut, EAP_AKA_PRIME_K_AUT_LEN); os_memcpy(r->k_re, k_re, EAP_AKA_PRIME_K_RE_LEN); return 0; } #endif /* EAP_SERVER_AKA_PRIME */ /** * eap_sim_db_get_permanent - EAP-SIM DB: Get permanent identity * @data: Private data pointer from eap_sim_db_init() * @pseudonym: Pseudonym username * Returns: Pointer to permanent username or %NULL if not found */ const char * eap_sim_db_get_permanent(struct eap_sim_db_data *data, const char *pseudonym) { struct eap_sim_pseudonym *p; #ifdef CONFIG_SQLITE if (data->sqlite_db) return db_get_pseudonym(data, pseudonym); #endif /* CONFIG_SQLITE */ p = data->pseudonyms; while (p) { if (os_strcmp(p->pseudonym, pseudonym) == 0) return p->permanent; p = p->next; } return NULL; } /** * eap_sim_db_get_reauth_entry - EAP-SIM DB: Get re-authentication entry * @data: Private data pointer from eap_sim_db_init() * @reauth_id: Fast re-authentication username * Returns: Pointer to the re-auth entry, or %NULL if not found */ struct eap_sim_reauth * eap_sim_db_get_reauth_entry(struct eap_sim_db_data *data, const char *reauth_id) { struct eap_sim_reauth *r; #ifdef CONFIG_SQLITE if (data->sqlite_db) return db_get_reauth(data, reauth_id); #endif /* CONFIG_SQLITE */ r = data->reauths; while (r) { if (os_strcmp(r->reauth_id, reauth_id) == 0) break; r = r->next; } return r; } /** * eap_sim_db_remove_reauth - EAP-SIM DB: Remove re-authentication entry * @data: Private data pointer from eap_sim_db_init() * @reauth: Pointer to re-authentication entry from * eap_sim_db_get_reauth_entry() */ void eap_sim_db_remove_reauth(struct eap_sim_db_data *data, struct eap_sim_reauth *reauth) { struct eap_sim_reauth *r, *prev = NULL; #ifdef CONFIG_SQLITE if (data->sqlite_db) { db_remove_reauth(data, reauth); return; } #endif /* CONFIG_SQLITE */ r = data->reauths; while (r) { if (r == reauth) { if (prev) prev->next = r->next; else data->reauths = r->next; eap_sim_db_free_reauth(r); return; } prev = r; r = r->next; } } /** * eap_sim_db_get_aka_auth - Get AKA authentication values * @data: Private data pointer from eap_sim_db_init() * @username: Permanent username (prefix | IMSI) * @_rand: Buffer for RAND value * @autn: Buffer for AUTN value * @ik: Buffer for IK value * @ck: Buffer for CK value * @res: Buffer for RES value * @res_len: Buffer for RES length * @cb_session_ctx: Session callback context for get_complete_cb() * Returns: 0 on success, -1 (EAP_SIM_DB_FAILURE) on error (e.g., user not * found), or -2 (EAP_SIM_DB_PENDING) if results are not yet available. In this * case, the callback function registered with eap_sim_db_init() will be * called once the results become available. * * When using an external server for AKA authentication, this function can * always start a request and return EAP_SIM_DB_PENDING immediately if * authentication triplets are not available. Once the authentication data are * received, callback function registered with eap_sim_db_init() is called to * notify EAP state machine to reprocess the message. This * eap_sim_db_get_aka_auth() function will then be called again and the newly * received triplets will then be given to the caller. */ int eap_sim_db_get_aka_auth(struct eap_sim_db_data *data, const char *username, u8 *_rand, u8 *autn, u8 *ik, u8 *ck, u8 *res, size_t *res_len, void *cb_session_ctx) { struct eap_sim_db_pending *entry; int len; char msg[40]; const char *imsi; size_t imsi_len; if (username == NULL || (username[0] != EAP_AKA_PERMANENT_PREFIX && username[0] != EAP_AKA_PRIME_PERMANENT_PREFIX) || username[1] == '\0' || os_strlen(username) > sizeof(entry->imsi)) { wpa_printf(MSG_DEBUG, "EAP-SIM DB: unexpected username '%s'", username); return EAP_SIM_DB_FAILURE; } imsi = username + 1; wpa_printf(MSG_DEBUG, "EAP-SIM DB: Get AKA auth for IMSI '%s'", imsi); entry = eap_sim_db_get_pending(data, imsi, 1); if (entry) { if (entry->state == FAILURE) { os_free(entry); wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failure"); return EAP_SIM_DB_FAILURE; } if (entry->state == PENDING) { eap_sim_db_add_pending(data, entry); wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending"); return EAP_SIM_DB_PENDING; } wpa_printf(MSG_DEBUG, "EAP-SIM DB: Returning successfully " "received authentication data"); os_memcpy(_rand, entry->u.aka.rand, EAP_AKA_RAND_LEN); os_memcpy(autn, entry->u.aka.autn, EAP_AKA_AUTN_LEN); os_memcpy(ik, entry->u.aka.ik, EAP_AKA_IK_LEN); os_memcpy(ck, entry->u.aka.ck, EAP_AKA_CK_LEN); os_memcpy(res, entry->u.aka.res, EAP_AKA_RES_MAX_LEN); *res_len = entry->u.aka.res_len; os_free(entry); return 0; } if (data->sock < 0) { if (eap_sim_db_open_socket(data) < 0) return EAP_SIM_DB_FAILURE; } imsi_len = os_strlen(imsi); len = os_snprintf(msg, sizeof(msg), "AKA-REQ-AUTH "); if (len < 0 || len + imsi_len >= sizeof(msg)) return EAP_SIM_DB_FAILURE; os_memcpy(msg + len, imsi, imsi_len); len += imsi_len; wpa_printf(MSG_DEBUG, "EAP-SIM DB: requesting AKA authentication " "data for IMSI '%s'", imsi); if (eap_sim_db_send(data, msg, len) < 0) return EAP_SIM_DB_FAILURE; entry = os_zalloc(sizeof(*entry)); if (entry == NULL) return EAP_SIM_DB_FAILURE; entry->aka = 1; os_strlcpy(entry->imsi, imsi, sizeof(entry->imsi)); entry->cb_session_ctx = cb_session_ctx; entry->state = PENDING; eap_sim_db_add_pending(data, entry); eap_sim_db_expire_pending(data); return EAP_SIM_DB_PENDING; } /** * eap_sim_db_resynchronize - Resynchronize AKA AUTN * @data: Private data pointer from eap_sim_db_init() * @username: Permanent username * @auts: AUTS value from the peer * @_rand: RAND value used in the rejected message * Returns: 0 on success, -1 on failure * * This function is called when the peer reports synchronization failure in the * AUTN value by sending AUTS. The AUTS and RAND values should be sent to * HLR/AuC to allow it to resynchronize with the peer. After this, * eap_sim_db_get_aka_auth() will be called again to to fetch updated * RAND/AUTN values for the next challenge. */ int eap_sim_db_resynchronize(struct eap_sim_db_data *data, const char *username, const u8 *auts, const u8 *_rand) { const char *imsi; size_t imsi_len; if (username == NULL || (username[0] != EAP_AKA_PERMANENT_PREFIX && username[0] != EAP_AKA_PRIME_PERMANENT_PREFIX) || username[1] == '\0' || os_strlen(username) > 20) { wpa_printf(MSG_DEBUG, "EAP-SIM DB: unexpected username '%s'", username); return -1; } imsi = username + 1; wpa_printf(MSG_DEBUG, "EAP-SIM DB: Get AKA auth for IMSI '%s'", imsi); if (data->sock >= 0) { char msg[100]; int len, ret; imsi_len = os_strlen(imsi); len = os_snprintf(msg, sizeof(msg), "AKA-AUTS "); if (len < 0 || len + imsi_len >= sizeof(msg)) return -1; os_memcpy(msg + len, imsi, imsi_len); len += imsi_len; ret = os_snprintf(msg + len, sizeof(msg) - len, " "); if (ret < 0 || (size_t) ret >= sizeof(msg) - len) return -1; len += ret; len += wpa_snprintf_hex(msg + len, sizeof(msg) - len, auts, EAP_AKA_AUTS_LEN); ret = os_snprintf(msg + len, sizeof(msg) - len, " "); if (ret < 0 || (size_t) ret >= sizeof(msg) - len) return -1; len += ret; len += wpa_snprintf_hex(msg + len, sizeof(msg) - len, _rand, EAP_AKA_RAND_LEN); wpa_printf(MSG_DEBUG, "EAP-SIM DB: reporting AKA AUTS for " "IMSI '%s'", imsi); if (eap_sim_db_send(data, msg, len) < 0) return -1; } return 0; } /** * sim_get_username - Extract username from SIM identity * @identity: Identity * @identity_len: Identity length * Returns: Allocated buffer with the username part of the identity * * Caller is responsible for freeing the returned buffer with os_free(). */ char * sim_get_username(const u8 *identity, size_t identity_len) { size_t pos; if (identity == NULL) return NULL; for (pos = 0; pos < identity_len; pos++) { if (identity[pos] == '@' || identity[pos] == '\0') break; } return dup_binstr(identity, pos); } wpa-2.1/src/eap_server/eap_sim_db.h000066400000000000000000000054361227414666700173470ustar00rootroot00000000000000/* * hostapd / EAP-SIM database/authenticator gateway * Copyright (c) 2005-2008, 2012, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef EAP_SIM_DB_H #define EAP_SIM_DB_H #include "eap_common/eap_sim_common.h" /* Identity prefixes */ #define EAP_SIM_PERMANENT_PREFIX '1' #define EAP_SIM_PSEUDONYM_PREFIX '3' #define EAP_SIM_REAUTH_ID_PREFIX '5' #define EAP_AKA_PERMANENT_PREFIX '0' #define EAP_AKA_PSEUDONYM_PREFIX '2' #define EAP_AKA_REAUTH_ID_PREFIX '4' #define EAP_AKA_PRIME_PERMANENT_PREFIX '6' #define EAP_AKA_PRIME_PSEUDONYM_PREFIX '7' #define EAP_AKA_PRIME_REAUTH_ID_PREFIX '8' enum eap_sim_db_method { EAP_SIM_DB_SIM, EAP_SIM_DB_AKA, EAP_SIM_DB_AKA_PRIME }; struct eap_sim_db_data; struct eap_sim_db_data * eap_sim_db_init(const char *config, void (*get_complete_cb)(void *ctx, void *session_ctx), void *ctx); void eap_sim_db_deinit(void *priv); int eap_sim_db_get_gsm_triplets(struct eap_sim_db_data *data, const char *username, int max_chal, u8 *_rand, u8 *kc, u8 *sres, void *cb_session_ctx); #define EAP_SIM_DB_FAILURE -1 #define EAP_SIM_DB_PENDING -2 char * eap_sim_db_get_next_pseudonym(struct eap_sim_db_data *data, enum eap_sim_db_method method); char * eap_sim_db_get_next_reauth_id(struct eap_sim_db_data *data, enum eap_sim_db_method method); int eap_sim_db_add_pseudonym(struct eap_sim_db_data *data, const char *permanent, char *pseudonym); int eap_sim_db_add_reauth(struct eap_sim_db_data *data, const char *permanent, char *reauth_id, u16 counter, const u8 *mk); int eap_sim_db_add_reauth_prime(struct eap_sim_db_data *data, const char *permanent, char *reauth_id, u16 counter, const u8 *k_encr, const u8 *k_aut, const u8 *k_re); const char * eap_sim_db_get_permanent(struct eap_sim_db_data *data, const char *pseudonym); struct eap_sim_reauth { struct eap_sim_reauth *next; char *permanent; /* Permanent username */ char *reauth_id; /* Fast re-authentication username */ u16 counter; u8 mk[EAP_SIM_MK_LEN]; u8 k_encr[EAP_SIM_K_ENCR_LEN]; u8 k_aut[EAP_AKA_PRIME_K_AUT_LEN]; u8 k_re[EAP_AKA_PRIME_K_RE_LEN]; }; struct eap_sim_reauth * eap_sim_db_get_reauth_entry(struct eap_sim_db_data *data, const char *reauth_id); void eap_sim_db_remove_reauth(struct eap_sim_db_data *data, struct eap_sim_reauth *reauth); int eap_sim_db_get_aka_auth(struct eap_sim_db_data *data, const char *username, u8 *_rand, u8 *autn, u8 *ik, u8 *ck, u8 *res, size_t *res_len, void *cb_session_ctx); int eap_sim_db_resynchronize(struct eap_sim_db_data *data, const char *username, const u8 *auts, const u8 *_rand); char * sim_get_username(const u8 *identity, size_t identity_len); #endif /* EAP_SIM_DB_H */ wpa-2.1/src/eap_server/eap_tls_common.h000066400000000000000000000050311227414666700202530ustar00rootroot00000000000000/* * EAP-TLS/PEAP/TTLS/FAST server common functions * 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_TLS_COMMON_H #define EAP_TLS_COMMON_H /** * struct eap_ssl_data - TLS data for EAP methods */ struct eap_ssl_data { /** * conn - TLS connection context data from tls_connection_init() */ struct tls_connection *conn; /** * tls_out - TLS message to be sent out in fragments */ struct wpabuf *tls_out; /** * tls_out_pos - The current position in the outgoing TLS message */ size_t tls_out_pos; /** * tls_out_limit - Maximum fragment size for outgoing TLS messages */ size_t tls_out_limit; /** * tls_in - Received TLS message buffer for re-assembly */ struct wpabuf *tls_in; /** * phase2 - Whether this TLS connection is used in EAP phase 2 (tunnel) */ int phase2; /** * eap - EAP state machine allocated with eap_server_sm_init() */ struct eap_sm *eap; enum { MSG, FRAG_ACK, WAIT_FRAG_ACK } state; struct wpabuf tmpbuf; }; /* EAP TLS Flags */ #define EAP_TLS_FLAGS_LENGTH_INCLUDED 0x80 #define EAP_TLS_FLAGS_MORE_FRAGMENTS 0x40 #define EAP_TLS_FLAGS_START 0x20 #define EAP_TLS_VERSION_MASK 0x07 /* could be up to 128 bytes, but only the first 64 bytes are used */ #define EAP_TLS_KEY_LEN 64 /* dummy type used as a flag for UNAUTH-TLS */ #define EAP_UNAUTH_TLS_TYPE 255 struct wpabuf * eap_tls_msg_alloc(EapType type, size_t payload_len, u8 code, u8 identifier); int eap_server_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, int verify_peer); void eap_server_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data); u8 * eap_server_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, char *label, size_t len); struct wpabuf * eap_server_tls_build_msg(struct eap_ssl_data *data, int eap_type, int version, u8 id); struct wpabuf * eap_server_tls_build_ack(u8 id, int eap_type, int version); int eap_server_tls_phase1(struct eap_sm *sm, struct eap_ssl_data *data); struct wpabuf * eap_server_tls_encrypt(struct eap_sm *sm, struct eap_ssl_data *data, const struct wpabuf *plain); int eap_server_tls_process(struct eap_sm *sm, struct eap_ssl_data *data, struct wpabuf *respData, void *priv, int eap_type, int (*proc_version)(struct eap_sm *sm, void *priv, int peer_version), void (*proc_msg)(struct eap_sm *sm, void *priv, const struct wpabuf *respData)); #endif /* EAP_TLS_COMMON_H */ wpa-2.1/src/eap_server/ikev2.c000066400000000000000000000731721227414666700163020ustar00rootroot00000000000000/* * IKEv2 initiator (RFC 4306) for EAP-IKEV2 * Copyright (c) 2007, 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/dh_groups.h" #include "crypto/random.h" #include "ikev2.h" static int ikev2_process_idr(struct ikev2_initiator_data *data, const u8 *idr, size_t idr_len); void ikev2_initiator_deinit(struct ikev2_initiator_data *data) { ikev2_free_keys(&data->keys); wpabuf_free(data->r_dh_public); wpabuf_free(data->i_dh_private); os_free(data->IDi); os_free(data->IDr); os_free(data->shared_secret); wpabuf_free(data->i_sign_msg); wpabuf_free(data->r_sign_msg); os_free(data->key_pad); } static int ikev2_derive_keys(struct ikev2_initiator_data *data) { u8 *buf, *pos, *pad, skeyseed[IKEV2_MAX_HASH_LEN]; size_t buf_len, pad_len; struct wpabuf *shared; const struct ikev2_integ_alg *integ; const struct ikev2_prf_alg *prf; const struct ikev2_encr_alg *encr; int ret; const u8 *addr[2]; size_t len[2]; /* RFC 4306, Sect. 2.14 */ integ = ikev2_get_integ(data->proposal.integ); prf = ikev2_get_prf(data->proposal.prf); encr = ikev2_get_encr(data->proposal.encr); if (integ == NULL || prf == NULL || encr == NULL) { wpa_printf(MSG_INFO, "IKEV2: Unsupported proposal"); return -1; } shared = dh_derive_shared(data->r_dh_public, data->i_dh_private, data->dh); if (shared == NULL) return -1; /* Construct Ni | Nr | SPIi | SPIr */ buf_len = data->i_nonce_len + data->r_nonce_len + 2 * IKEV2_SPI_LEN; buf = os_malloc(buf_len); if (buf == NULL) { wpabuf_free(shared); return -1; } pos = buf; os_memcpy(pos, data->i_nonce, data->i_nonce_len); pos += data->i_nonce_len; os_memcpy(pos, data->r_nonce, data->r_nonce_len); pos += data->r_nonce_len; os_memcpy(pos, data->i_spi, IKEV2_SPI_LEN); pos += IKEV2_SPI_LEN; os_memcpy(pos, data->r_spi, IKEV2_SPI_LEN); /* SKEYSEED = prf(Ni | Nr, g^ir) */ /* Use zero-padding per RFC 4306, Sect. 2.14 */ pad_len = data->dh->prime_len - wpabuf_len(shared); pad = os_zalloc(pad_len ? pad_len : 1); if (pad == NULL) { wpabuf_free(shared); os_free(buf); return -1; } addr[0] = pad; len[0] = pad_len; addr[1] = wpabuf_head(shared); len[1] = wpabuf_len(shared); if (ikev2_prf_hash(prf->id, buf, data->i_nonce_len + data->r_nonce_len, 2, addr, len, skeyseed) < 0) { wpabuf_free(shared); os_free(buf); os_free(pad); return -1; } os_free(pad); wpabuf_free(shared); /* DH parameters are not needed anymore, so free them */ wpabuf_free(data->r_dh_public); data->r_dh_public = NULL; wpabuf_free(data->i_dh_private); data->i_dh_private = NULL; wpa_hexdump_key(MSG_DEBUG, "IKEV2: SKEYSEED", skeyseed, prf->hash_len); ret = ikev2_derive_sk_keys(prf, integ, encr, skeyseed, buf, buf_len, &data->keys); os_free(buf); return ret; } static int ikev2_parse_transform(struct ikev2_initiator_data *data, struct ikev2_proposal_data *prop, const u8 *pos, const u8 *end) { int transform_len; const struct ikev2_transform *t; u16 transform_id; const u8 *tend; if (end - pos < (int) sizeof(*t)) { wpa_printf(MSG_INFO, "IKEV2: Too short transform"); return -1; } t = (const struct ikev2_transform *) pos; transform_len = WPA_GET_BE16(t->transform_length); if (transform_len < (int) sizeof(*t) || pos + transform_len > end) { wpa_printf(MSG_INFO, "IKEV2: Invalid transform length %d", transform_len); return -1; } tend = pos + transform_len; transform_id = WPA_GET_BE16(t->transform_id); wpa_printf(MSG_DEBUG, "IKEV2: Transform:"); wpa_printf(MSG_DEBUG, "IKEV2: Type: %d Transform Length: %d " "Transform Type: %d Transform ID: %d", t->type, transform_len, t->transform_type, transform_id); if (t->type != 0 && t->type != 3) { wpa_printf(MSG_INFO, "IKEV2: Unexpected Transform type"); return -1; } pos = (const u8 *) (t + 1); if (pos < tend) { wpa_hexdump(MSG_DEBUG, "IKEV2: Transform Attributes", pos, tend - pos); } switch (t->transform_type) { case IKEV2_TRANSFORM_ENCR: if (ikev2_get_encr(transform_id) && transform_id == data->proposal.encr) { if (transform_id == ENCR_AES_CBC) { if (tend - pos != 4) { wpa_printf(MSG_DEBUG, "IKEV2: No " "Transform Attr for AES"); break; } if (WPA_GET_BE16(pos) != 0x800e) { wpa_printf(MSG_DEBUG, "IKEV2: Not a " "Key Size attribute for " "AES"); break; } if (WPA_GET_BE16(pos + 2) != 128) { wpa_printf(MSG_DEBUG, "IKEV2: " "Unsupported AES key size " "%d bits", WPA_GET_BE16(pos + 2)); break; } } prop->encr = transform_id; } break; case IKEV2_TRANSFORM_PRF: if (ikev2_get_prf(transform_id) && transform_id == data->proposal.prf) prop->prf = transform_id; break; case IKEV2_TRANSFORM_INTEG: if (ikev2_get_integ(transform_id) && transform_id == data->proposal.integ) prop->integ = transform_id; break; case IKEV2_TRANSFORM_DH: if (dh_groups_get(transform_id) && transform_id == data->proposal.dh) prop->dh = transform_id; break; } return transform_len; } static int ikev2_parse_proposal(struct ikev2_initiator_data *data, struct ikev2_proposal_data *prop, const u8 *pos, const u8 *end) { const u8 *pend, *ppos; int proposal_len, i; const struct ikev2_proposal *p; if (end - pos < (int) sizeof(*p)) { wpa_printf(MSG_INFO, "IKEV2: Too short proposal"); return -1; } p = (const struct ikev2_proposal *) pos; proposal_len = WPA_GET_BE16(p->proposal_length); if (proposal_len < (int) sizeof(*p) || pos + proposal_len > end) { wpa_printf(MSG_INFO, "IKEV2: Invalid proposal length %d", proposal_len); return -1; } wpa_printf(MSG_DEBUG, "IKEV2: SAi1 Proposal # %d", p->proposal_num); wpa_printf(MSG_DEBUG, "IKEV2: Type: %d Proposal Length: %d " " Protocol ID: %d", p->type, proposal_len, p->protocol_id); wpa_printf(MSG_DEBUG, "IKEV2: SPI Size: %d Transforms: %d", p->spi_size, p->num_transforms); if (p->type != 0 && p->type != 2) { wpa_printf(MSG_INFO, "IKEV2: Unexpected Proposal type"); return -1; } if (p->protocol_id != IKEV2_PROTOCOL_IKE) { wpa_printf(MSG_DEBUG, "IKEV2: Unexpected Protocol ID " "(only IKE allowed for EAP-IKEv2)"); return -1; } if (p->proposal_num != prop->proposal_num) { if (p->proposal_num == prop->proposal_num + 1) prop->proposal_num = p->proposal_num; else { wpa_printf(MSG_INFO, "IKEV2: Unexpected Proposal #"); return -1; } } ppos = (const u8 *) (p + 1); pend = pos + proposal_len; if (ppos + p->spi_size > pend) { wpa_printf(MSG_INFO, "IKEV2: Not enough room for SPI " "in proposal"); return -1; } if (p->spi_size) { wpa_hexdump(MSG_DEBUG, "IKEV2: SPI", ppos, p->spi_size); ppos += p->spi_size; } /* * For initial IKE_SA negotiation, SPI Size MUST be zero; for * subsequent negotiations, it must be 8 for IKE. We only support * initial case for now. */ if (p->spi_size != 0) { wpa_printf(MSG_INFO, "IKEV2: Unexpected SPI Size"); return -1; } if (p->num_transforms == 0) { wpa_printf(MSG_INFO, "IKEV2: At least one transform required"); return -1; } for (i = 0; i < (int) p->num_transforms; i++) { int tlen = ikev2_parse_transform(data, prop, ppos, pend); if (tlen < 0) return -1; ppos += tlen; } if (ppos != pend) { wpa_printf(MSG_INFO, "IKEV2: Unexpected data after " "transforms"); return -1; } return proposal_len; } static int ikev2_process_sar1(struct ikev2_initiator_data *data, const u8 *sar1, size_t sar1_len) { struct ikev2_proposal_data prop; const u8 *pos, *end; int found = 0; /* Security Association Payloads: */ if (sar1 == NULL) { wpa_printf(MSG_INFO, "IKEV2: SAr1 not received"); return -1; } os_memset(&prop, 0, sizeof(prop)); prop.proposal_num = 1; pos = sar1; end = sar1 + sar1_len; while (pos < end) { int plen; prop.integ = -1; prop.prf = -1; prop.encr = -1; prop.dh = -1; plen = ikev2_parse_proposal(data, &prop, pos, end); if (plen < 0) return -1; if (!found && prop.integ != -1 && prop.prf != -1 && prop.encr != -1 && prop.dh != -1) { found = 1; } pos += plen; /* Only one proposal expected in SAr */ break; } if (pos != end) { wpa_printf(MSG_INFO, "IKEV2: Unexpected data after proposal"); return -1; } if (!found) { wpa_printf(MSG_INFO, "IKEV2: No acceptable proposal found"); return -1; } wpa_printf(MSG_DEBUG, "IKEV2: Accepted proposal #%d: ENCR:%d PRF:%d " "INTEG:%d D-H:%d", data->proposal.proposal_num, data->proposal.encr, data->proposal.prf, data->proposal.integ, data->proposal.dh); return 0; } static int ikev2_process_ker(struct ikev2_initiator_data *data, const u8 *ker, size_t ker_len) { u16 group; /* * Key Exchange Payload: * DH Group # (16 bits) * RESERVED (16 bits) * Key Exchange Data (Diffie-Hellman public value) */ if (ker == NULL) { wpa_printf(MSG_INFO, "IKEV2: KEr not received"); return -1; } if (ker_len < 4 + 96) { wpa_printf(MSG_INFO, "IKEV2: Too show Key Exchange Payload"); return -1; } group = WPA_GET_BE16(ker); wpa_printf(MSG_DEBUG, "IKEV2: KEr DH Group #%u", group); if (group != data->proposal.dh) { wpa_printf(MSG_DEBUG, "IKEV2: KEr DH Group #%u does not match " "with the selected proposal (%u)", group, data->proposal.dh); return -1; } if (data->dh == NULL) { wpa_printf(MSG_INFO, "IKEV2: Unsupported DH group"); return -1; } /* RFC 4306, Section 3.4: * The length of DH public value MUST be equal to the length of the * prime modulus. */ if (ker_len - 4 != data->dh->prime_len) { wpa_printf(MSG_INFO, "IKEV2: Invalid DH public value length " "%ld (expected %ld)", (long) (ker_len - 4), (long) data->dh->prime_len); return -1; } wpabuf_free(data->r_dh_public); data->r_dh_public = wpabuf_alloc_copy(ker + 4, ker_len - 4); if (data->r_dh_public == NULL) return -1; wpa_hexdump_buf(MSG_DEBUG, "IKEV2: KEr Diffie-Hellman Public Value", data->r_dh_public); return 0; } static int ikev2_process_nr(struct ikev2_initiator_data *data, const u8 *nr, size_t nr_len) { if (nr == NULL) { wpa_printf(MSG_INFO, "IKEV2: Nr not received"); return -1; } if (nr_len < IKEV2_NONCE_MIN_LEN || nr_len > IKEV2_NONCE_MAX_LEN) { wpa_printf(MSG_INFO, "IKEV2: Invalid Nr length %ld", (long) nr_len); return -1; } data->r_nonce_len = nr_len; os_memcpy(data->r_nonce, nr, nr_len); wpa_hexdump(MSG_MSGDUMP, "IKEV2: Nr", data->r_nonce, data->r_nonce_len); return 0; } static int ikev2_process_sa_init_encr(struct ikev2_initiator_data *data, const struct ikev2_hdr *hdr, const u8 *encrypted, size_t encrypted_len, u8 next_payload) { u8 *decrypted; size_t decrypted_len; struct ikev2_payloads pl; int ret = 0; decrypted = ikev2_decrypt_payload(data->proposal.encr, data->proposal.integ, &data->keys, 0, hdr, encrypted, encrypted_len, &decrypted_len); if (decrypted == NULL) return -1; wpa_printf(MSG_DEBUG, "IKEV2: Processing decrypted payloads"); if (ikev2_parse_payloads(&pl, next_payload, decrypted, decrypted + decrypted_len) < 0) { wpa_printf(MSG_INFO, "IKEV2: Failed to parse decrypted " "payloads"); return -1; } if (pl.idr) ret = ikev2_process_idr(data, pl.idr, pl.idr_len); os_free(decrypted); return ret; } static int ikev2_process_sa_init(struct ikev2_initiator_data *data, const struct ikev2_hdr *hdr, struct ikev2_payloads *pl) { if (ikev2_process_sar1(data, pl->sa, pl->sa_len) < 0 || ikev2_process_ker(data, pl->ke, pl->ke_len) < 0 || ikev2_process_nr(data, pl->nonce, pl->nonce_len) < 0) return -1; os_memcpy(data->r_spi, hdr->r_spi, IKEV2_SPI_LEN); if (ikev2_derive_keys(data) < 0) return -1; if (pl->encrypted) { wpa_printf(MSG_DEBUG, "IKEV2: Encrypted payload in SA_INIT - " "try to get IDr from it"); if (ikev2_process_sa_init_encr(data, hdr, pl->encrypted, pl->encrypted_len, pl->encr_next_payload) < 0) { wpa_printf(MSG_INFO, "IKEV2: Failed to process " "encrypted payload"); return -1; } } data->state = SA_AUTH; return 0; } static int ikev2_process_idr(struct ikev2_initiator_data *data, const u8 *idr, size_t idr_len) { u8 id_type; if (idr == NULL) { wpa_printf(MSG_INFO, "IKEV2: No IDr received"); return -1; } if (idr_len < 4) { wpa_printf(MSG_INFO, "IKEV2: Too short IDr payload"); return -1; } id_type = idr[0]; idr += 4; idr_len -= 4; wpa_printf(MSG_DEBUG, "IKEV2: IDr ID Type %d", id_type); wpa_hexdump_ascii(MSG_DEBUG, "IKEV2: IDr", idr, idr_len); if (data->IDr) { if (id_type != data->IDr_type || idr_len != data->IDr_len || os_memcmp(idr, data->IDr, idr_len) != 0) { wpa_printf(MSG_INFO, "IKEV2: IDr differs from the one " "received earlier"); wpa_printf(MSG_DEBUG, "IKEV2: Previous IDr ID Type %d", id_type); wpa_hexdump_ascii(MSG_DEBUG, "Previous IKEV2: IDr", data->IDr, data->IDr_len); return -1; } os_free(data->IDr); } data->IDr = os_malloc(idr_len); if (data->IDr == NULL) return -1; os_memcpy(data->IDr, idr, idr_len); data->IDr_len = idr_len; data->IDr_type = id_type; return 0; } static int ikev2_process_cert(struct ikev2_initiator_data *data, const u8 *cert, size_t cert_len) { u8 cert_encoding; if (cert == NULL) { if (data->peer_auth == PEER_AUTH_CERT) { wpa_printf(MSG_INFO, "IKEV2: No Certificate received"); return -1; } return 0; } if (cert_len < 1) { wpa_printf(MSG_INFO, "IKEV2: No Cert Encoding field"); return -1; } cert_encoding = cert[0]; cert++; cert_len--; wpa_printf(MSG_DEBUG, "IKEV2: Cert Encoding %d", cert_encoding); wpa_hexdump(MSG_MSGDUMP, "IKEV2: Certificate Data", cert, cert_len); /* TODO: validate certificate */ return 0; } static int ikev2_process_auth_cert(struct ikev2_initiator_data *data, u8 method, const u8 *auth, size_t auth_len) { if (method != AUTH_RSA_SIGN) { wpa_printf(MSG_INFO, "IKEV2: Unsupported authentication " "method %d", method); return -1; } /* TODO: validate AUTH */ return 0; } static int ikev2_process_auth_secret(struct ikev2_initiator_data *data, u8 method, const u8 *auth, size_t auth_len) { u8 auth_data[IKEV2_MAX_HASH_LEN]; const struct ikev2_prf_alg *prf; if (method != AUTH_SHARED_KEY_MIC) { wpa_printf(MSG_INFO, "IKEV2: Unsupported authentication " "method %d", method); return -1; } /* msg | Ni | prf(SK_pr,IDr') */ if (ikev2_derive_auth_data(data->proposal.prf, data->r_sign_msg, data->IDr, data->IDr_len, data->IDr_type, &data->keys, 0, data->shared_secret, data->shared_secret_len, data->i_nonce, data->i_nonce_len, data->key_pad, data->key_pad_len, auth_data) < 0) { wpa_printf(MSG_INFO, "IKEV2: Could not derive AUTH data"); return -1; } wpabuf_free(data->r_sign_msg); data->r_sign_msg = NULL; prf = ikev2_get_prf(data->proposal.prf); if (prf == NULL) return -1; if (auth_len != prf->hash_len || os_memcmp(auth, auth_data, auth_len) != 0) { wpa_printf(MSG_INFO, "IKEV2: Invalid Authentication Data"); wpa_hexdump(MSG_DEBUG, "IKEV2: Received Authentication Data", auth, auth_len); wpa_hexdump(MSG_DEBUG, "IKEV2: Expected Authentication Data", auth_data, prf->hash_len); return -1; } wpa_printf(MSG_DEBUG, "IKEV2: Peer authenticated successfully " "using shared keys"); return 0; } static int ikev2_process_auth(struct ikev2_initiator_data *data, const u8 *auth, size_t auth_len) { u8 auth_method; if (auth == NULL) { wpa_printf(MSG_INFO, "IKEV2: No Authentication Payload"); return -1; } if (auth_len < 4) { wpa_printf(MSG_INFO, "IKEV2: Too short Authentication " "Payload"); return -1; } auth_method = auth[0]; auth += 4; auth_len -= 4; wpa_printf(MSG_DEBUG, "IKEV2: Auth Method %d", auth_method); wpa_hexdump(MSG_MSGDUMP, "IKEV2: Authentication Data", auth, auth_len); switch (data->peer_auth) { case PEER_AUTH_CERT: return ikev2_process_auth_cert(data, auth_method, auth, auth_len); case PEER_AUTH_SECRET: return ikev2_process_auth_secret(data, auth_method, auth, auth_len); } return -1; } static int ikev2_process_sa_auth_decrypted(struct ikev2_initiator_data *data, u8 next_payload, u8 *payload, size_t payload_len) { struct ikev2_payloads pl; wpa_printf(MSG_DEBUG, "IKEV2: Processing decrypted payloads"); if (ikev2_parse_payloads(&pl, next_payload, payload, payload + payload_len) < 0) { wpa_printf(MSG_INFO, "IKEV2: Failed to parse decrypted " "payloads"); return -1; } if (ikev2_process_idr(data, pl.idr, pl.idr_len) < 0 || ikev2_process_cert(data, pl.cert, pl.cert_len) < 0 || ikev2_process_auth(data, pl.auth, pl.auth_len) < 0) return -1; return 0; } static int ikev2_process_sa_auth(struct ikev2_initiator_data *data, const struct ikev2_hdr *hdr, struct ikev2_payloads *pl) { u8 *decrypted; size_t decrypted_len; int ret; decrypted = ikev2_decrypt_payload(data->proposal.encr, data->proposal.integ, &data->keys, 0, hdr, pl->encrypted, pl->encrypted_len, &decrypted_len); if (decrypted == NULL) return -1; ret = ikev2_process_sa_auth_decrypted(data, pl->encr_next_payload, decrypted, decrypted_len); os_free(decrypted); if (ret == 0 && !data->unknown_user) { wpa_printf(MSG_DEBUG, "IKEV2: Authentication completed"); data->state = IKEV2_DONE; } return ret; } static int ikev2_validate_rx_state(struct ikev2_initiator_data *data, u8 exchange_type, u32 message_id) { switch (data->state) { case SA_INIT: /* Expect to receive IKE_SA_INIT: HDR, SAr, KEr, Nr, [CERTREQ], * [SK{IDr}] */ if (exchange_type != IKE_SA_INIT) { wpa_printf(MSG_INFO, "IKEV2: Unexpected Exchange Type " "%u in SA_INIT state", exchange_type); return -1; } if (message_id != 0) { wpa_printf(MSG_INFO, "IKEV2: Unexpected Message ID %u " "in SA_INIT state", message_id); return -1; } break; case SA_AUTH: /* Expect to receive IKE_SA_AUTH: * HDR, SK {IDr, [CERT,] [CERTREQ,] [NFID,] AUTH} */ if (exchange_type != IKE_SA_AUTH) { wpa_printf(MSG_INFO, "IKEV2: Unexpected Exchange Type " "%u in SA_AUTH state", exchange_type); return -1; } if (message_id != 1) { wpa_printf(MSG_INFO, "IKEV2: Unexpected Message ID %u " "in SA_AUTH state", message_id); return -1; } break; case CHILD_SA: if (exchange_type != CREATE_CHILD_SA) { wpa_printf(MSG_INFO, "IKEV2: Unexpected Exchange Type " "%u in CHILD_SA state", exchange_type); return -1; } if (message_id != 2) { wpa_printf(MSG_INFO, "IKEV2: Unexpected Message ID %u " "in CHILD_SA state", message_id); return -1; } break; case IKEV2_DONE: return -1; } return 0; } int ikev2_initiator_process(struct ikev2_initiator_data *data, const struct wpabuf *buf) { const struct ikev2_hdr *hdr; u32 length, message_id; const u8 *pos, *end; struct ikev2_payloads pl; wpa_printf(MSG_MSGDUMP, "IKEV2: Received message (len %lu)", (unsigned long) wpabuf_len(buf)); if (wpabuf_len(buf) < sizeof(*hdr)) { wpa_printf(MSG_INFO, "IKEV2: Too short frame to include HDR"); return -1; } hdr = (const struct ikev2_hdr *) wpabuf_head(buf); end = wpabuf_head_u8(buf) + wpabuf_len(buf); message_id = WPA_GET_BE32(hdr->message_id); length = WPA_GET_BE32(hdr->length); wpa_hexdump(MSG_DEBUG, "IKEV2: IKE_SA Initiator's SPI", hdr->i_spi, IKEV2_SPI_LEN); wpa_hexdump(MSG_DEBUG, "IKEV2: IKE_SA Initiator's SPI", hdr->r_spi, IKEV2_SPI_LEN); wpa_printf(MSG_DEBUG, "IKEV2: Next Payload: %u Version: 0x%x " "Exchange Type: %u", hdr->next_payload, hdr->version, hdr->exchange_type); wpa_printf(MSG_DEBUG, "IKEV2: Message ID: %u Length: %u", message_id, length); if (hdr->version != IKEV2_VERSION) { wpa_printf(MSG_INFO, "IKEV2: Unsupported HDR version 0x%x " "(expected 0x%x)", hdr->version, IKEV2_VERSION); return -1; } if (length != wpabuf_len(buf)) { wpa_printf(MSG_INFO, "IKEV2: Invalid length (HDR: %lu != " "RX: %lu)", (unsigned long) length, (unsigned long) wpabuf_len(buf)); return -1; } if (ikev2_validate_rx_state(data, hdr->exchange_type, message_id) < 0) return -1; if ((hdr->flags & (IKEV2_HDR_INITIATOR | IKEV2_HDR_RESPONSE)) != IKEV2_HDR_RESPONSE) { wpa_printf(MSG_INFO, "IKEV2: Unexpected Flags value 0x%x", hdr->flags); return -1; } if (data->state != SA_INIT) { if (os_memcmp(data->i_spi, hdr->i_spi, IKEV2_SPI_LEN) != 0) { wpa_printf(MSG_INFO, "IKEV2: Unexpected IKE_SA " "Initiator's SPI"); return -1; } if (os_memcmp(data->r_spi, hdr->r_spi, IKEV2_SPI_LEN) != 0) { wpa_printf(MSG_INFO, "IKEV2: Unexpected IKE_SA " "Responder's SPI"); return -1; } } pos = (const u8 *) (hdr + 1); if (ikev2_parse_payloads(&pl, hdr->next_payload, pos, end) < 0) return -1; switch (data->state) { case SA_INIT: if (ikev2_process_sa_init(data, hdr, &pl) < 0) return -1; wpabuf_free(data->r_sign_msg); data->r_sign_msg = wpabuf_dup(buf); break; case SA_AUTH: if (ikev2_process_sa_auth(data, hdr, &pl) < 0) return -1; break; case CHILD_SA: case IKEV2_DONE: break; } return 0; } static void ikev2_build_hdr(struct ikev2_initiator_data *data, struct wpabuf *msg, u8 exchange_type, u8 next_payload, u32 message_id) { struct ikev2_hdr *hdr; wpa_printf(MSG_DEBUG, "IKEV2: Adding HDR"); /* HDR - RFC 4306, Sect. 3.1 */ hdr = wpabuf_put(msg, sizeof(*hdr)); os_memcpy(hdr->i_spi, data->i_spi, IKEV2_SPI_LEN); os_memcpy(hdr->r_spi, data->r_spi, IKEV2_SPI_LEN); hdr->next_payload = next_payload; hdr->version = IKEV2_VERSION; hdr->exchange_type = exchange_type; hdr->flags = IKEV2_HDR_INITIATOR; WPA_PUT_BE32(hdr->message_id, message_id); } static int ikev2_build_sai(struct ikev2_initiator_data *data, struct wpabuf *msg, u8 next_payload) { struct ikev2_payload_hdr *phdr; size_t plen; struct ikev2_proposal *p; struct ikev2_transform *t; wpa_printf(MSG_DEBUG, "IKEV2: Adding SAi payload"); /* SAi1 - RFC 4306, Sect. 2.7 and 3.3 */ phdr = wpabuf_put(msg, sizeof(*phdr)); phdr->next_payload = next_payload; phdr->flags = 0; /* TODO: support for multiple proposals */ p = wpabuf_put(msg, sizeof(*p)); p->proposal_num = data->proposal.proposal_num; p->protocol_id = IKEV2_PROTOCOL_IKE; p->num_transforms = 4; t = wpabuf_put(msg, sizeof(*t)); t->type = 3; t->transform_type = IKEV2_TRANSFORM_ENCR; WPA_PUT_BE16(t->transform_id, data->proposal.encr); if (data->proposal.encr == ENCR_AES_CBC) { /* Transform Attribute: Key Len = 128 bits */ wpabuf_put_be16(msg, 0x800e); /* AF=1, AttrType=14 */ wpabuf_put_be16(msg, 128); /* 128-bit key */ } plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) t; WPA_PUT_BE16(t->transform_length, plen); t = wpabuf_put(msg, sizeof(*t)); t->type = 3; WPA_PUT_BE16(t->transform_length, sizeof(*t)); t->transform_type = IKEV2_TRANSFORM_PRF; WPA_PUT_BE16(t->transform_id, data->proposal.prf); t = wpabuf_put(msg, sizeof(*t)); t->type = 3; WPA_PUT_BE16(t->transform_length, sizeof(*t)); t->transform_type = IKEV2_TRANSFORM_INTEG; WPA_PUT_BE16(t->transform_id, data->proposal.integ); t = wpabuf_put(msg, sizeof(*t)); WPA_PUT_BE16(t->transform_length, sizeof(*t)); t->transform_type = IKEV2_TRANSFORM_DH; WPA_PUT_BE16(t->transform_id, data->proposal.dh); plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) p; WPA_PUT_BE16(p->proposal_length, plen); plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr; WPA_PUT_BE16(phdr->payload_length, plen); return 0; } static int ikev2_build_kei(struct ikev2_initiator_data *data, struct wpabuf *msg, u8 next_payload) { struct ikev2_payload_hdr *phdr; size_t plen; struct wpabuf *pv; wpa_printf(MSG_DEBUG, "IKEV2: Adding KEi payload"); data->dh = dh_groups_get(data->proposal.dh); pv = dh_init(data->dh, &data->i_dh_private); if (pv == NULL) { wpa_printf(MSG_DEBUG, "IKEV2: Failed to initialize DH"); return -1; } /* KEi - RFC 4306, Sect. 3.4 */ phdr = wpabuf_put(msg, sizeof(*phdr)); phdr->next_payload = next_payload; phdr->flags = 0; wpabuf_put_be16(msg, data->proposal.dh); /* DH Group # */ wpabuf_put(msg, 2); /* RESERVED */ /* * RFC 4306, Sect. 3.4: possible zero padding for public value to * match the length of the prime. */ wpabuf_put(msg, data->dh->prime_len - wpabuf_len(pv)); wpabuf_put_buf(msg, pv); wpabuf_free(pv); plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr; WPA_PUT_BE16(phdr->payload_length, plen); return 0; } static int ikev2_build_ni(struct ikev2_initiator_data *data, struct wpabuf *msg, u8 next_payload) { struct ikev2_payload_hdr *phdr; size_t plen; wpa_printf(MSG_DEBUG, "IKEV2: Adding Ni payload"); /* Ni - RFC 4306, Sect. 3.9 */ phdr = wpabuf_put(msg, sizeof(*phdr)); phdr->next_payload = next_payload; phdr->flags = 0; wpabuf_put_data(msg, data->i_nonce, data->i_nonce_len); plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr; WPA_PUT_BE16(phdr->payload_length, plen); return 0; } static int ikev2_build_idi(struct ikev2_initiator_data *data, struct wpabuf *msg, u8 next_payload) { struct ikev2_payload_hdr *phdr; size_t plen; wpa_printf(MSG_DEBUG, "IKEV2: Adding IDi payload"); if (data->IDi == NULL) { wpa_printf(MSG_INFO, "IKEV2: No IDi available"); return -1; } /* IDi - RFC 4306, Sect. 3.5 */ phdr = wpabuf_put(msg, sizeof(*phdr)); phdr->next_payload = next_payload; phdr->flags = 0; wpabuf_put_u8(msg, ID_KEY_ID); wpabuf_put(msg, 3); /* RESERVED */ wpabuf_put_data(msg, data->IDi, data->IDi_len); plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr; WPA_PUT_BE16(phdr->payload_length, plen); return 0; } static int ikev2_build_auth(struct ikev2_initiator_data *data, struct wpabuf *msg, u8 next_payload) { struct ikev2_payload_hdr *phdr; size_t plen; const struct ikev2_prf_alg *prf; wpa_printf(MSG_DEBUG, "IKEV2: Adding AUTH payload"); prf = ikev2_get_prf(data->proposal.prf); if (prf == NULL) return -1; /* Authentication - RFC 4306, Sect. 3.8 */ phdr = wpabuf_put(msg, sizeof(*phdr)); phdr->next_payload = next_payload; phdr->flags = 0; wpabuf_put_u8(msg, AUTH_SHARED_KEY_MIC); wpabuf_put(msg, 3); /* RESERVED */ /* msg | Nr | prf(SK_pi,IDi') */ if (ikev2_derive_auth_data(data->proposal.prf, data->i_sign_msg, data->IDi, data->IDi_len, ID_KEY_ID, &data->keys, 1, data->shared_secret, data->shared_secret_len, data->r_nonce, data->r_nonce_len, data->key_pad, data->key_pad_len, wpabuf_put(msg, prf->hash_len)) < 0) { wpa_printf(MSG_INFO, "IKEV2: Could not derive AUTH data"); return -1; } wpabuf_free(data->i_sign_msg); data->i_sign_msg = NULL; plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr; WPA_PUT_BE16(phdr->payload_length, plen); return 0; } static struct wpabuf * ikev2_build_sa_init(struct ikev2_initiator_data *data) { struct wpabuf *msg; /* build IKE_SA_INIT: HDR, SAi, KEi, Ni */ if (os_get_random(data->i_spi, IKEV2_SPI_LEN)) return NULL; wpa_hexdump(MSG_DEBUG, "IKEV2: IKE_SA Initiator's SPI", data->i_spi, IKEV2_SPI_LEN); data->i_nonce_len = IKEV2_NONCE_MIN_LEN; if (random_get_bytes(data->i_nonce, data->i_nonce_len)) return NULL; wpa_hexdump(MSG_DEBUG, "IKEV2: Ni", data->i_nonce, data->i_nonce_len); msg = wpabuf_alloc(sizeof(struct ikev2_hdr) + 1000); if (msg == NULL) return NULL; ikev2_build_hdr(data, msg, IKE_SA_INIT, IKEV2_PAYLOAD_SA, 0); if (ikev2_build_sai(data, msg, IKEV2_PAYLOAD_KEY_EXCHANGE) || ikev2_build_kei(data, msg, IKEV2_PAYLOAD_NONCE) || ikev2_build_ni(data, msg, IKEV2_PAYLOAD_NO_NEXT_PAYLOAD)) { wpabuf_free(msg); return NULL; } ikev2_update_hdr(msg); wpa_hexdump_buf(MSG_MSGDUMP, "IKEV2: Sending message (SA_INIT)", msg); wpabuf_free(data->i_sign_msg); data->i_sign_msg = wpabuf_dup(msg); return msg; } static struct wpabuf * ikev2_build_sa_auth(struct ikev2_initiator_data *data) { struct wpabuf *msg, *plain; const u8 *secret; size_t secret_len; secret = data->get_shared_secret(data->cb_ctx, data->IDr, data->IDr_len, &secret_len); if (secret == NULL) { wpa_printf(MSG_INFO, "IKEV2: Could not get shared secret - " "use fake value"); /* RFC 5106, Sect. 7: * Use a random key to fake AUTH generation in order to prevent * probing of user identities. */ data->unknown_user = 1; os_free(data->shared_secret); data->shared_secret = os_malloc(16); if (data->shared_secret == NULL) return NULL; data->shared_secret_len = 16; if (random_get_bytes(data->shared_secret, 16)) return NULL; } else { os_free(data->shared_secret); data->shared_secret = os_malloc(secret_len); if (data->shared_secret == NULL) return NULL; os_memcpy(data->shared_secret, secret, secret_len); data->shared_secret_len = secret_len; } /* build IKE_SA_AUTH: HDR, SK {IDi, [CERT,] [CERTREQ,] AUTH} */ msg = wpabuf_alloc(sizeof(struct ikev2_hdr) + data->IDr_len + 1000); if (msg == NULL) return NULL; ikev2_build_hdr(data, msg, IKE_SA_AUTH, IKEV2_PAYLOAD_ENCRYPTED, 1); plain = wpabuf_alloc(data->IDr_len + 1000); if (plain == NULL) { wpabuf_free(msg); return NULL; } if (ikev2_build_idi(data, plain, IKEV2_PAYLOAD_AUTHENTICATION) || ikev2_build_auth(data, plain, IKEV2_PAYLOAD_NO_NEXT_PAYLOAD) || ikev2_build_encrypted(data->proposal.encr, data->proposal.integ, &data->keys, 1, msg, plain, IKEV2_PAYLOAD_IDi)) { wpabuf_free(plain); wpabuf_free(msg); return NULL; } wpabuf_free(plain); wpa_hexdump_buf(MSG_MSGDUMP, "IKEV2: Sending message (SA_AUTH)", msg); return msg; } struct wpabuf * ikev2_initiator_build(struct ikev2_initiator_data *data) { switch (data->state) { case SA_INIT: return ikev2_build_sa_init(data); case SA_AUTH: return ikev2_build_sa_auth(data); case CHILD_SA: return NULL; case IKEV2_DONE: return NULL; } return NULL; } wpa-2.1/src/eap_server/ikev2.h000066400000000000000000000026231227414666700163000ustar00rootroot00000000000000/* * IKEv2 initiator (RFC 4306) for EAP-IKEV2 * Copyright (c) 2007, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef IKEV2_H #define IKEV2_H #include "eap_common/ikev2_common.h" struct ikev2_proposal_data { u8 proposal_num; int integ; int prf; int encr; int dh; }; struct ikev2_initiator_data { enum { SA_INIT, SA_AUTH, CHILD_SA, IKEV2_DONE } state; u8 i_spi[IKEV2_SPI_LEN]; u8 r_spi[IKEV2_SPI_LEN]; u8 i_nonce[IKEV2_NONCE_MAX_LEN]; size_t i_nonce_len; u8 r_nonce[IKEV2_NONCE_MAX_LEN]; size_t r_nonce_len; struct wpabuf *r_dh_public; struct wpabuf *i_dh_private; struct ikev2_proposal_data proposal; const struct dh_group *dh; struct ikev2_keys keys; u8 *IDi; size_t IDi_len; u8 *IDr; size_t IDr_len; u8 IDr_type; struct wpabuf *r_sign_msg; struct wpabuf *i_sign_msg; u8 *shared_secret; size_t shared_secret_len; enum { PEER_AUTH_CERT, PEER_AUTH_SECRET } peer_auth; u8 *key_pad; size_t key_pad_len; const u8 * (*get_shared_secret)(void *ctx, const u8 *IDr, size_t IDr_len, size_t *secret_len); void *cb_ctx; int unknown_user; }; void ikev2_initiator_deinit(struct ikev2_initiator_data *data); int ikev2_initiator_process(struct ikev2_initiator_data *data, const struct wpabuf *buf); struct wpabuf * ikev2_initiator_build(struct ikev2_initiator_data *data); #endif /* IKEV2_H */ wpa-2.1/src/eap_server/tncs.c000066400000000000000000000714631227414666700162320ustar00rootroot00000000000000/* * EAP-TNC - TNCS (IF-IMV, IF-TNCCS, and IF-TNCCS-SOH) * Copyright (c) 2007-2008, 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.h" #include "base64.h" #include "tncs.h" #include "eap_common/eap_tlv_common.h" #include "eap_common/eap_defs.h" /* TODO: TNCS must be thread-safe; review the code and add locking etc. if * needed.. */ #define TNC_CONFIG_FILE "/etc/tnc_config" #define IF_TNCCS_START \ "\n" \ "\n" #define IF_TNCCS_END "\n" /* TNC IF-IMV */ typedef unsigned long TNC_UInt32; typedef unsigned char *TNC_BufferReference; typedef TNC_UInt32 TNC_IMVID; typedef TNC_UInt32 TNC_ConnectionID; typedef TNC_UInt32 TNC_ConnectionState; typedef TNC_UInt32 TNC_RetryReason; typedef TNC_UInt32 TNC_IMV_Action_Recommendation; typedef TNC_UInt32 TNC_IMV_Evaluation_Result; typedef TNC_UInt32 TNC_MessageType; typedef TNC_MessageType *TNC_MessageTypeList; typedef TNC_UInt32 TNC_VendorID; typedef TNC_UInt32 TNC_Subtype; typedef TNC_UInt32 TNC_Version; typedef TNC_UInt32 TNC_Result; typedef TNC_UInt32 TNC_AttributeID; typedef TNC_Result (*TNC_TNCS_BindFunctionPointer)( TNC_IMVID imvID, char *functionName, void **pOutfunctionPointer); #define TNC_RESULT_SUCCESS 0 #define TNC_RESULT_NOT_INITIALIZED 1 #define TNC_RESULT_ALREADY_INITIALIZED 2 #define TNC_RESULT_NO_COMMON_VERSION 3 #define TNC_RESULT_CANT_RETRY 4 #define TNC_RESULT_WONT_RETRY 5 #define TNC_RESULT_INVALID_PARAMETER 6 #define TNC_RESULT_CANT_RESPOND 7 #define TNC_RESULT_ILLEGAL_OPERATION 8 #define TNC_RESULT_OTHER 9 #define TNC_RESULT_FATAL 10 #define TNC_CONNECTION_STATE_CREATE 0 #define TNC_CONNECTION_STATE_HANDSHAKE 1 #define TNC_CONNECTION_STATE_ACCESS_ALLOWED 2 #define TNC_CONNECTION_STATE_ACCESS_ISOLATED 3 #define TNC_CONNECTION_STATE_ACCESS_NONE 4 #define TNC_CONNECTION_STATE_DELETE 5 #define TNC_IFIMV_VERSION_1 1 #define TNC_VENDORID_ANY ((TNC_VendorID) 0xffffff) #define TNC_SUBTYPE_ANY ((TNC_Subtype) 0xff) /* TNCC-TNCS Message Types */ #define TNC_TNCCS_RECOMMENDATION 0x00000001 #define TNC_TNCCS_ERROR 0x00000002 #define TNC_TNCCS_PREFERREDLANGUAGE 0x00000003 #define TNC_TNCCS_REASONSTRINGS 0x00000004 /* Possible TNC_IMV_Action_Recommendation values: */ enum IMV_Action_Recommendation { TNC_IMV_ACTION_RECOMMENDATION_ALLOW, TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS, TNC_IMV_ACTION_RECOMMENDATION_ISOLATE, TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION }; /* Possible TNC_IMV_Evaluation_Result values: */ enum IMV_Evaluation_Result { TNC_IMV_EVALUATION_RESULT_COMPLIANT, TNC_IMV_EVALUATION_RESULT_NONCOMPLIANT_MINOR, TNC_IMV_EVALUATION_RESULT_NONCOMPLIANT_MAJOR, TNC_IMV_EVALUATION_RESULT_ERROR, TNC_IMV_EVALUATION_RESULT_DONT_KNOW }; struct tnc_if_imv { struct tnc_if_imv *next; char *name; char *path; void *dlhandle; /* from dlopen() */ TNC_IMVID imvID; TNC_MessageTypeList supported_types; size_t num_supported_types; /* Functions implemented by IMVs (with TNC_IMV_ prefix) */ TNC_Result (*Initialize)( TNC_IMVID imvID, TNC_Version minVersion, TNC_Version maxVersion, TNC_Version *pOutActualVersion); TNC_Result (*NotifyConnectionChange)( TNC_IMVID imvID, TNC_ConnectionID connectionID, TNC_ConnectionState newState); TNC_Result (*ReceiveMessage)( TNC_IMVID imvID, TNC_ConnectionID connectionID, TNC_BufferReference message, TNC_UInt32 messageLength, TNC_MessageType messageType); TNC_Result (*SolicitRecommendation)( TNC_IMVID imvID, TNC_ConnectionID connectionID); TNC_Result (*BatchEnding)( TNC_IMVID imvID, TNC_ConnectionID connectionID); TNC_Result (*Terminate)(TNC_IMVID imvID); TNC_Result (*ProvideBindFunction)( TNC_IMVID imvID, TNC_TNCS_BindFunctionPointer bindFunction); }; #define TNC_MAX_IMV_ID 10 struct tncs_data { struct tncs_data *next; struct tnc_if_imv *imv; /* local copy of tncs_global_data->imv */ TNC_ConnectionID connectionID; unsigned int last_batchid; enum IMV_Action_Recommendation recommendation; int done; struct conn_imv { u8 *imv_send; size_t imv_send_len; enum IMV_Action_Recommendation recommendation; int recommendation_set; } imv_data[TNC_MAX_IMV_ID]; char *tncs_message; }; struct tncs_global { struct tnc_if_imv *imv; TNC_ConnectionID next_conn_id; struct tncs_data *connections; }; static struct tncs_global *tncs_global_data = NULL; static struct tnc_if_imv * tncs_get_imv(TNC_IMVID imvID) { struct tnc_if_imv *imv; if (imvID >= TNC_MAX_IMV_ID || tncs_global_data == NULL) return NULL; imv = tncs_global_data->imv; while (imv) { if (imv->imvID == imvID) return imv; imv = imv->next; } return NULL; } static struct tncs_data * tncs_get_conn(TNC_ConnectionID connectionID) { struct tncs_data *tncs; if (tncs_global_data == NULL) return NULL; tncs = tncs_global_data->connections; while (tncs) { if (tncs->connectionID == connectionID) return tncs; tncs = tncs->next; } wpa_printf(MSG_DEBUG, "TNC: Connection ID %lu not found", (unsigned long) connectionID); return NULL; } /* TNCS functions that IMVs can call */ TNC_Result TNC_TNCS_ReportMessageTypes( TNC_IMVID imvID, TNC_MessageTypeList supportedTypes, TNC_UInt32 typeCount) { TNC_UInt32 i; struct tnc_if_imv *imv; wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_ReportMessageTypes(imvID=%lu " "typeCount=%lu)", (unsigned long) imvID, (unsigned long) typeCount); for (i = 0; i < typeCount; i++) { wpa_printf(MSG_DEBUG, "TNC: supportedTypes[%lu] = %lu", i, supportedTypes[i]); } imv = tncs_get_imv(imvID); if (imv == NULL) return TNC_RESULT_INVALID_PARAMETER; os_free(imv->supported_types); imv->supported_types = os_malloc(typeCount * sizeof(TNC_MessageType)); if (imv->supported_types == NULL) return TNC_RESULT_FATAL; os_memcpy(imv->supported_types, supportedTypes, typeCount * sizeof(TNC_MessageType)); imv->num_supported_types = typeCount; return TNC_RESULT_SUCCESS; } TNC_Result TNC_TNCS_SendMessage( TNC_IMVID imvID, TNC_ConnectionID connectionID, TNC_BufferReference message, TNC_UInt32 messageLength, TNC_MessageType messageType) { struct tncs_data *tncs; unsigned char *b64; size_t b64len; wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_SendMessage(imvID=%lu " "connectionID=%lu messageType=%lu)", imvID, connectionID, messageType); wpa_hexdump_ascii(MSG_DEBUG, "TNC: TNC_TNCS_SendMessage", message, messageLength); if (tncs_get_imv(imvID) == NULL) return TNC_RESULT_INVALID_PARAMETER; tncs = tncs_get_conn(connectionID); if (tncs == NULL) return TNC_RESULT_INVALID_PARAMETER; b64 = base64_encode(message, messageLength, &b64len); if (b64 == NULL) return TNC_RESULT_FATAL; os_free(tncs->imv_data[imvID].imv_send); tncs->imv_data[imvID].imv_send_len = 0; tncs->imv_data[imvID].imv_send = os_zalloc(b64len + 100); if (tncs->imv_data[imvID].imv_send == NULL) { os_free(b64); return TNC_RESULT_OTHER; } tncs->imv_data[imvID].imv_send_len = os_snprintf((char *) tncs->imv_data[imvID].imv_send, b64len + 100, "%08X" "%s", (unsigned int) messageType, b64); os_free(b64); return TNC_RESULT_SUCCESS; } TNC_Result TNC_TNCS_RequestHandshakeRetry( TNC_IMVID imvID, TNC_ConnectionID connectionID, TNC_RetryReason reason) { wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_RequestHandshakeRetry"); /* TODO */ return TNC_RESULT_SUCCESS; } TNC_Result TNC_TNCS_ProvideRecommendation( TNC_IMVID imvID, TNC_ConnectionID connectionID, TNC_IMV_Action_Recommendation recommendation, TNC_IMV_Evaluation_Result evaluation) { struct tncs_data *tncs; wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_ProvideRecommendation(imvID=%lu " "connectionID=%lu recommendation=%lu evaluation=%lu)", (unsigned long) imvID, (unsigned long) connectionID, (unsigned long) recommendation, (unsigned long) evaluation); if (tncs_get_imv(imvID) == NULL) return TNC_RESULT_INVALID_PARAMETER; tncs = tncs_get_conn(connectionID); if (tncs == NULL) return TNC_RESULT_INVALID_PARAMETER; tncs->imv_data[imvID].recommendation = recommendation; tncs->imv_data[imvID].recommendation_set = 1; return TNC_RESULT_SUCCESS; } TNC_Result TNC_TNCS_GetAttribute( TNC_IMVID imvID, TNC_ConnectionID connectionID, TNC_AttributeID attribureID, TNC_UInt32 bufferLength, TNC_BufferReference buffer, TNC_UInt32 *pOutValueLength) { wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_GetAttribute"); /* TODO */ return TNC_RESULT_SUCCESS; } TNC_Result TNC_TNCS_SetAttribute( TNC_IMVID imvID, TNC_ConnectionID connectionID, TNC_AttributeID attribureID, TNC_UInt32 bufferLength, TNC_BufferReference buffer) { wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_SetAttribute"); /* TODO */ return TNC_RESULT_SUCCESS; } TNC_Result TNC_TNCS_BindFunction( TNC_IMVID imvID, char *functionName, void **pOutFunctionPointer) { wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_BindFunction(imcID=%lu, " "functionName='%s')", (unsigned long) imvID, functionName); if (tncs_get_imv(imvID) == NULL) return TNC_RESULT_INVALID_PARAMETER; if (pOutFunctionPointer == NULL) return TNC_RESULT_INVALID_PARAMETER; if (os_strcmp(functionName, "TNC_TNCS_ReportMessageTypes") == 0) *pOutFunctionPointer = TNC_TNCS_ReportMessageTypes; else if (os_strcmp(functionName, "TNC_TNCS_SendMessage") == 0) *pOutFunctionPointer = TNC_TNCS_SendMessage; else if (os_strcmp(functionName, "TNC_TNCS_RequestHandshakeRetry") == 0) *pOutFunctionPointer = TNC_TNCS_RequestHandshakeRetry; else if (os_strcmp(functionName, "TNC_TNCS_ProvideRecommendation") == 0) *pOutFunctionPointer = TNC_TNCS_ProvideRecommendation; else if (os_strcmp(functionName, "TNC_TNCS_GetAttribute") == 0) *pOutFunctionPointer = TNC_TNCS_GetAttribute; else if (os_strcmp(functionName, "TNC_TNCS_SetAttribute") == 0) *pOutFunctionPointer = TNC_TNCS_SetAttribute; else *pOutFunctionPointer = NULL; return TNC_RESULT_SUCCESS; } static void * tncs_get_sym(void *handle, char *func) { void *fptr; fptr = dlsym(handle, func); return fptr; } static int tncs_imv_resolve_funcs(struct tnc_if_imv *imv) { void *handle = imv->dlhandle; /* Mandatory IMV functions */ imv->Initialize = tncs_get_sym(handle, "TNC_IMV_Initialize"); if (imv->Initialize == NULL) { wpa_printf(MSG_ERROR, "TNC: IMV does not export " "TNC_IMV_Initialize"); return -1; } imv->SolicitRecommendation = tncs_get_sym( handle, "TNC_IMV_SolicitRecommendation"); if (imv->SolicitRecommendation == NULL) { wpa_printf(MSG_ERROR, "TNC: IMV does not export " "TNC_IMV_SolicitRecommendation"); return -1; } imv->ProvideBindFunction = tncs_get_sym(handle, "TNC_IMV_ProvideBindFunction"); if (imv->ProvideBindFunction == NULL) { wpa_printf(MSG_ERROR, "TNC: IMV does not export " "TNC_IMV_ProvideBindFunction"); return -1; } /* Optional IMV functions */ imv->NotifyConnectionChange = tncs_get_sym(handle, "TNC_IMV_NotifyConnectionChange"); imv->ReceiveMessage = tncs_get_sym(handle, "TNC_IMV_ReceiveMessage"); imv->BatchEnding = tncs_get_sym(handle, "TNC_IMV_BatchEnding"); imv->Terminate = tncs_get_sym(handle, "TNC_IMV_Terminate"); return 0; } static int tncs_imv_initialize(struct tnc_if_imv *imv) { TNC_Result res; TNC_Version imv_ver; wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMV_Initialize for IMV '%s'", imv->name); res = imv->Initialize(imv->imvID, TNC_IFIMV_VERSION_1, TNC_IFIMV_VERSION_1, &imv_ver); wpa_printf(MSG_DEBUG, "TNC: TNC_IMV_Initialize: res=%lu imv_ver=%lu", (unsigned long) res, (unsigned long) imv_ver); return res == TNC_RESULT_SUCCESS ? 0 : -1; } static int tncs_imv_terminate(struct tnc_if_imv *imv) { TNC_Result res; if (imv->Terminate == NULL) return 0; wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMV_Terminate for IMV '%s'", imv->name); res = imv->Terminate(imv->imvID); wpa_printf(MSG_DEBUG, "TNC: TNC_IMV_Terminate: %lu", (unsigned long) res); return res == TNC_RESULT_SUCCESS ? 0 : -1; } static int tncs_imv_provide_bind_function(struct tnc_if_imv *imv) { TNC_Result res; wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMV_ProvideBindFunction for " "IMV '%s'", imv->name); res = imv->ProvideBindFunction(imv->imvID, TNC_TNCS_BindFunction); wpa_printf(MSG_DEBUG, "TNC: TNC_IMV_ProvideBindFunction: res=%lu", (unsigned long) res); return res == TNC_RESULT_SUCCESS ? 0 : -1; } static int tncs_imv_notify_connection_change(struct tnc_if_imv *imv, TNC_ConnectionID conn, TNC_ConnectionState state) { TNC_Result res; if (imv->NotifyConnectionChange == NULL) return 0; wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMV_NotifyConnectionChange(%d)" " for IMV '%s'", (int) state, imv->name); res = imv->NotifyConnectionChange(imv->imvID, conn, state); wpa_printf(MSG_DEBUG, "TNC: TNC_IMC_NotifyConnectionChange: %lu", (unsigned long) res); return res == TNC_RESULT_SUCCESS ? 0 : -1; } static int tncs_load_imv(struct tnc_if_imv *imv) { if (imv->path == NULL) { wpa_printf(MSG_DEBUG, "TNC: No IMV configured"); return -1; } wpa_printf(MSG_DEBUG, "TNC: Opening IMV: %s (%s)", imv->name, imv->path); imv->dlhandle = dlopen(imv->path, RTLD_LAZY); if (imv->dlhandle == NULL) { wpa_printf(MSG_ERROR, "TNC: Failed to open IMV '%s' (%s): %s", imv->name, imv->path, dlerror()); return -1; } if (tncs_imv_resolve_funcs(imv) < 0) { wpa_printf(MSG_ERROR, "TNC: Failed to resolve IMV functions"); return -1; } if (tncs_imv_initialize(imv) < 0 || tncs_imv_provide_bind_function(imv) < 0) { wpa_printf(MSG_ERROR, "TNC: Failed to initialize IMV"); return -1; } return 0; } static void tncs_free_imv(struct tnc_if_imv *imv) { os_free(imv->name); os_free(imv->path); os_free(imv->supported_types); } static void tncs_unload_imv(struct tnc_if_imv *imv) { tncs_imv_terminate(imv); if (imv->dlhandle) dlclose(imv->dlhandle); tncs_free_imv(imv); } static int tncs_supported_type(struct tnc_if_imv *imv, unsigned int type) { size_t i; unsigned int vendor, subtype; if (imv == NULL || imv->supported_types == NULL) return 0; vendor = type >> 8; subtype = type & 0xff; for (i = 0; i < imv->num_supported_types; i++) { unsigned int svendor, ssubtype; svendor = imv->supported_types[i] >> 8; ssubtype = imv->supported_types[i] & 0xff; if ((vendor == svendor || svendor == TNC_VENDORID_ANY) && (subtype == ssubtype || ssubtype == TNC_SUBTYPE_ANY)) return 1; } return 0; } static void tncs_send_to_imvs(struct tncs_data *tncs, unsigned int type, const u8 *msg, size_t len) { struct tnc_if_imv *imv; TNC_Result res; wpa_hexdump_ascii(MSG_MSGDUMP, "TNC: Message to IMV(s)", msg, len); for (imv = tncs->imv; imv; imv = imv->next) { if (imv->ReceiveMessage == NULL || !tncs_supported_type(imv, type)) continue; wpa_printf(MSG_DEBUG, "TNC: Call ReceiveMessage for IMV '%s'", imv->name); res = imv->ReceiveMessage(imv->imvID, tncs->connectionID, (TNC_BufferReference) msg, len, type); wpa_printf(MSG_DEBUG, "TNC: ReceiveMessage: %lu", (unsigned long) res); } } static void tncs_batch_ending(struct tncs_data *tncs) { struct tnc_if_imv *imv; TNC_Result res; for (imv = tncs->imv; imv; imv = imv->next) { if (imv->BatchEnding == NULL) continue; wpa_printf(MSG_DEBUG, "TNC: Call BatchEnding for IMV '%s'", imv->name); res = imv->BatchEnding(imv->imvID, tncs->connectionID); wpa_printf(MSG_DEBUG, "TNC: BatchEnding: %lu", (unsigned long) res); } } static void tncs_solicit_recommendation(struct tncs_data *tncs) { struct tnc_if_imv *imv; TNC_Result res; for (imv = tncs->imv; imv; imv = imv->next) { if (tncs->imv_data[imv->imvID].recommendation_set) continue; wpa_printf(MSG_DEBUG, "TNC: Call SolicitRecommendation for " "IMV '%s'", imv->name); res = imv->SolicitRecommendation(imv->imvID, tncs->connectionID); wpa_printf(MSG_DEBUG, "TNC: SolicitRecommendation: %lu", (unsigned long) res); } } void tncs_init_connection(struct tncs_data *tncs) { struct tnc_if_imv *imv; int i; for (imv = tncs->imv; imv; imv = imv->next) { tncs_imv_notify_connection_change( imv, tncs->connectionID, TNC_CONNECTION_STATE_CREATE); tncs_imv_notify_connection_change( imv, tncs->connectionID, TNC_CONNECTION_STATE_HANDSHAKE); } for (i = 0; i < TNC_MAX_IMV_ID; i++) { os_free(tncs->imv_data[i].imv_send); tncs->imv_data[i].imv_send = NULL; tncs->imv_data[i].imv_send_len = 0; } } size_t tncs_total_send_len(struct tncs_data *tncs) { int i; size_t len = 0; for (i = 0; i < TNC_MAX_IMV_ID; i++) len += tncs->imv_data[i].imv_send_len; if (tncs->tncs_message) len += os_strlen(tncs->tncs_message); return len; } u8 * tncs_copy_send_buf(struct tncs_data *tncs, u8 *pos) { int i; for (i = 0; i < TNC_MAX_IMV_ID; i++) { if (tncs->imv_data[i].imv_send == NULL) continue; os_memcpy(pos, tncs->imv_data[i].imv_send, tncs->imv_data[i].imv_send_len); pos += tncs->imv_data[i].imv_send_len; os_free(tncs->imv_data[i].imv_send); tncs->imv_data[i].imv_send = NULL; tncs->imv_data[i].imv_send_len = 0; } if (tncs->tncs_message) { size_t len = os_strlen(tncs->tncs_message); os_memcpy(pos, tncs->tncs_message, len); pos += len; os_free(tncs->tncs_message); tncs->tncs_message = NULL; } return pos; } char * tncs_if_tnccs_start(struct tncs_data *tncs) { char *buf = os_malloc(1000); if (buf == NULL) return NULL; tncs->last_batchid++; os_snprintf(buf, 1000, IF_TNCCS_START, tncs->last_batchid); return buf; } char * tncs_if_tnccs_end(void) { char *buf = os_malloc(100); if (buf == NULL) return NULL; os_snprintf(buf, 100, IF_TNCCS_END); return buf; } static int tncs_get_type(char *start, unsigned int *type) { char *pos = os_strstr(start, ""); if (pos == NULL) return -1; pos += 6; *type = strtoul(pos, NULL, 16); return 0; } static unsigned char * tncs_get_base64(char *start, size_t *decoded_len) { char *pos, *pos2; unsigned char *decoded; pos = os_strstr(start, ""); if (pos == NULL) return NULL; pos += 8; pos2 = os_strstr(pos, ""); if (pos2 == NULL) return NULL; *pos2 = '\0'; decoded = base64_decode((unsigned char *) pos, os_strlen(pos), decoded_len); *pos2 = '<'; if (decoded == NULL) { wpa_printf(MSG_DEBUG, "TNC: Failed to decode Base64 data"); } return decoded; } static enum tncs_process_res tncs_derive_recommendation(struct tncs_data *tncs) { enum IMV_Action_Recommendation rec; struct tnc_if_imv *imv; TNC_ConnectionState state; char *txt; wpa_printf(MSG_DEBUG, "TNC: No more messages from IMVs"); if (tncs->done) return TNCCS_PROCESS_OK_NO_RECOMMENDATION; tncs_solicit_recommendation(tncs); /* Select the most restrictive recommendation */ rec = TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION; for (imv = tncs->imv; imv; imv = imv->next) { TNC_IMV_Action_Recommendation irec; irec = tncs->imv_data[imv->imvID].recommendation; if (irec == TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS) rec = TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS; if (irec == TNC_IMV_ACTION_RECOMMENDATION_ISOLATE && rec != TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS) rec = TNC_IMV_ACTION_RECOMMENDATION_ISOLATE; if (irec == TNC_IMV_ACTION_RECOMMENDATION_ALLOW && rec == TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION) rec = TNC_IMV_ACTION_RECOMMENDATION_ALLOW; } wpa_printf(MSG_DEBUG, "TNC: Recommendation: %d", rec); tncs->recommendation = rec; tncs->done = 1; txt = NULL; switch (rec) { case TNC_IMV_ACTION_RECOMMENDATION_ALLOW: case TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION: txt = "allow"; state = TNC_CONNECTION_STATE_ACCESS_ALLOWED; break; case TNC_IMV_ACTION_RECOMMENDATION_ISOLATE: txt = "isolate"; state = TNC_CONNECTION_STATE_ACCESS_ISOLATED; break; case TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS: txt = "none"; state = TNC_CONNECTION_STATE_ACCESS_NONE; break; default: state = TNC_CONNECTION_STATE_ACCESS_ALLOWED; break; } if (txt) { os_free(tncs->tncs_message); tncs->tncs_message = os_zalloc(200); if (tncs->tncs_message) { os_snprintf(tncs->tncs_message, 199, "%08X" "" "" "", TNC_TNCCS_RECOMMENDATION, txt); } } for (imv = tncs->imv; imv; imv = imv->next) { tncs_imv_notify_connection_change(imv, tncs->connectionID, state); } switch (rec) { case TNC_IMV_ACTION_RECOMMENDATION_ALLOW: return TNCCS_RECOMMENDATION_ALLOW; case TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS: return TNCCS_RECOMMENDATION_NO_ACCESS; case TNC_IMV_ACTION_RECOMMENDATION_ISOLATE: return TNCCS_RECOMMENDATION_ISOLATE; case TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION: return TNCCS_RECOMMENDATION_NO_RECOMMENDATION; default: return TNCCS_PROCESS_ERROR; } } enum tncs_process_res tncs_process_if_tnccs(struct tncs_data *tncs, const u8 *msg, size_t len) { char *buf, *start, *end, *pos, *pos2, *payload; unsigned int batch_id; unsigned char *decoded; size_t decoded_len; buf = dup_binstr(msg, len); if (buf == NULL) return TNCCS_PROCESS_ERROR; start = os_strstr(buf, ""); if (start == NULL || end == NULL || start > end) { os_free(buf); return TNCCS_PROCESS_ERROR; } start += 13; while (*start == ' ') start++; *end = '\0'; pos = os_strstr(start, "BatchId="); if (pos == NULL) { os_free(buf); return TNCCS_PROCESS_ERROR; } pos += 8; if (*pos == '"') pos++; batch_id = atoi(pos); wpa_printf(MSG_DEBUG, "TNC: Received IF-TNCCS BatchId=%u", batch_id); if (batch_id != tncs->last_batchid + 1) { wpa_printf(MSG_DEBUG, "TNC: Unexpected IF-TNCCS BatchId " "%u (expected %u)", batch_id, tncs->last_batchid + 1); os_free(buf); return TNCCS_PROCESS_ERROR; } tncs->last_batchid = batch_id; while (*pos != '\0' && *pos != '>') pos++; if (*pos == '\0') { os_free(buf); return TNCCS_PROCESS_ERROR; } pos++; payload = start; /* * * 01234567 * foo== * */ while (*start) { char *endpos; unsigned int type; pos = os_strstr(start, ""); if (pos == NULL) break; start = pos + 17; end = os_strstr(start, ""); if (end == NULL) break; *end = '\0'; endpos = end; end += 18; if (tncs_get_type(start, &type) < 0) { *endpos = '<'; start = end; continue; } wpa_printf(MSG_DEBUG, "TNC: IMC-IMV-Message Type 0x%x", type); decoded = tncs_get_base64(start, &decoded_len); if (decoded == NULL) { *endpos = '<'; start = end; continue; } tncs_send_to_imvs(tncs, type, decoded, decoded_len); os_free(decoded); start = end; } /* * * 01234567 * * foo== * */ start = payload; while (*start) { unsigned int type; char *xml, *xmlend, *endpos; pos = os_strstr(start, ""); if (pos == NULL) break; start = pos + 19; end = os_strstr(start, ""); if (end == NULL) break; *end = '\0'; endpos = end; end += 20; if (tncs_get_type(start, &type) < 0) { *endpos = '<'; start = end; continue; } wpa_printf(MSG_DEBUG, "TNC: TNCC-TNCS-Message Type 0x%x", type); /* Base64 OR XML */ decoded = NULL; xml = NULL; xmlend = NULL; pos = os_strstr(start, ""); if (pos) { pos += 5; pos2 = os_strstr(pos, ""); if (pos2 == NULL) { *endpos = '<'; start = end; continue; } xmlend = pos2; xml = pos; } else { decoded = tncs_get_base64(start, &decoded_len); if (decoded == NULL) { *endpos = '<'; start = end; continue; } } if (decoded) { wpa_hexdump_ascii(MSG_MSGDUMP, "TNC: TNCC-TNCS-Message Base64", decoded, decoded_len); os_free(decoded); } if (xml) { wpa_hexdump_ascii(MSG_MSGDUMP, "TNC: TNCC-TNCS-Message XML", (unsigned char *) xml, xmlend - xml); } start = end; } os_free(buf); tncs_batch_ending(tncs); if (tncs_total_send_len(tncs) == 0) return tncs_derive_recommendation(tncs); return TNCCS_PROCESS_OK_NO_RECOMMENDATION; } static struct tnc_if_imv * tncs_parse_imv(int id, char *start, char *end, int *error) { struct tnc_if_imv *imv; char *pos, *pos2; if (id >= TNC_MAX_IMV_ID) { wpa_printf(MSG_DEBUG, "TNC: Too many IMVs"); return NULL; } imv = os_zalloc(sizeof(*imv)); if (imv == NULL) { *error = 1; return NULL; } imv->imvID = id; pos = start; wpa_printf(MSG_DEBUG, "TNC: Configured IMV: %s", pos); if (pos + 1 >= end || *pos != '"') { wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMV line '%s' " "(no starting quotation mark)", start); os_free(imv); return NULL; } pos++; pos2 = pos; while (pos2 < end && *pos2 != '"') pos2++; if (pos2 >= end) { wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMV line '%s' " "(no ending quotation mark)", start); os_free(imv); return NULL; } *pos2 = '\0'; wpa_printf(MSG_DEBUG, "TNC: Name: '%s'", pos); imv->name = os_strdup(pos); pos = pos2 + 1; if (pos >= end || *pos != ' ') { wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMV line '%s' " "(no space after name)", start); os_free(imv); return NULL; } pos++; wpa_printf(MSG_DEBUG, "TNC: IMV file: '%s'", pos); imv->path = os_strdup(pos); return imv; } static int tncs_read_config(struct tncs_global *global) { char *config, *end, *pos, *line_end; size_t config_len; struct tnc_if_imv *imv, *last; int id = 0; last = NULL; config = os_readfile(TNC_CONFIG_FILE, &config_len); if (config == NULL) { wpa_printf(MSG_ERROR, "TNC: Could not open TNC configuration " "file '%s'", TNC_CONFIG_FILE); return -1; } end = config + config_len; for (pos = config; pos < end; pos = line_end + 1) { line_end = pos; while (*line_end != '\n' && *line_end != '\r' && line_end < end) line_end++; *line_end = '\0'; if (os_strncmp(pos, "IMV ", 4) == 0) { int error = 0; imv = tncs_parse_imv(id++, pos + 4, line_end, &error); if (error) return -1; if (imv) { if (last == NULL) global->imv = imv; else last->next = imv; last = imv; } } } os_free(config); return 0; } struct tncs_data * tncs_init(void) { struct tncs_data *tncs; if (tncs_global_data == NULL) return NULL; tncs = os_zalloc(sizeof(*tncs)); if (tncs == NULL) return NULL; tncs->imv = tncs_global_data->imv; tncs->connectionID = tncs_global_data->next_conn_id++; tncs->next = tncs_global_data->connections; tncs_global_data->connections = tncs; return tncs; } void tncs_deinit(struct tncs_data *tncs) { int i; struct tncs_data *prev, *conn; if (tncs == NULL) return; for (i = 0; i < TNC_MAX_IMV_ID; i++) os_free(tncs->imv_data[i].imv_send); prev = NULL; conn = tncs_global_data->connections; while (conn) { if (conn == tncs) { if (prev) prev->next = tncs->next; else tncs_global_data->connections = tncs->next; break; } prev = conn; conn = conn->next; } os_free(tncs->tncs_message); os_free(tncs); } int tncs_global_init(void) { struct tnc_if_imv *imv; tncs_global_data = os_zalloc(sizeof(*tncs_global_data)); if (tncs_global_data == NULL) return -1; if (tncs_read_config(tncs_global_data) < 0) { wpa_printf(MSG_ERROR, "TNC: Failed to read TNC configuration"); goto failed; } for (imv = tncs_global_data->imv; imv; imv = imv->next) { if (tncs_load_imv(imv)) { wpa_printf(MSG_ERROR, "TNC: Failed to load IMV '%s'", imv->name); goto failed; } } return 0; failed: tncs_global_deinit(); return -1; } void tncs_global_deinit(void) { struct tnc_if_imv *imv, *prev; if (tncs_global_data == NULL) return; imv = tncs_global_data->imv; while (imv) { tncs_unload_imv(imv); prev = imv; imv = imv->next; os_free(prev); } os_free(tncs_global_data); tncs_global_data = NULL; } struct wpabuf * tncs_build_soh_request(void) { struct wpabuf *buf; /* * Build a SoH Request TLV (to be used inside SoH EAP Extensions * Method) */ buf = wpabuf_alloc(8 + 4); if (buf == NULL) return NULL; /* Vendor-Specific TLV (Microsoft) - SoH Request */ wpabuf_put_be16(buf, EAP_TLV_VENDOR_SPECIFIC_TLV); /* TLV Type */ wpabuf_put_be16(buf, 8); /* Length */ wpabuf_put_be32(buf, EAP_VENDOR_MICROSOFT); /* Vendor_Id */ wpabuf_put_be16(buf, 0x02); /* TLV Type - SoH Request TLV */ wpabuf_put_be16(buf, 0); /* Length */ return buf; } struct wpabuf * tncs_process_soh(const u8 *soh_tlv, size_t soh_tlv_len, int *failure) { wpa_hexdump(MSG_DEBUG, "TNC: SoH TLV", soh_tlv, soh_tlv_len); *failure = 0; /* TODO: return MS-SoH Response TLV */ return NULL; } wpa-2.1/src/eap_server/tncs.h000066400000000000000000000022671227414666700162330ustar00rootroot00000000000000/* * EAP-TNC - TNCS (IF-IMV, IF-TNCCS, and IF-TNCCS-SOH) * Copyright (c) 2007-2008, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef TNCS_H #define TNCS_H struct tncs_data; struct tncs_data * tncs_init(void); void tncs_deinit(struct tncs_data *tncs); void tncs_init_connection(struct tncs_data *tncs); size_t tncs_total_send_len(struct tncs_data *tncs); u8 * tncs_copy_send_buf(struct tncs_data *tncs, u8 *pos); char * tncs_if_tnccs_start(struct tncs_data *tncs); char * tncs_if_tnccs_end(void); enum tncs_process_res { TNCCS_PROCESS_ERROR = -1, TNCCS_PROCESS_OK_NO_RECOMMENDATION = 0, TNCCS_RECOMMENDATION_ERROR, TNCCS_RECOMMENDATION_ALLOW, TNCCS_RECOMMENDATION_NONE, TNCCS_RECOMMENDATION_ISOLATE, TNCCS_RECOMMENDATION_NO_ACCESS, TNCCS_RECOMMENDATION_NO_RECOMMENDATION }; enum tncs_process_res tncs_process_if_tnccs(struct tncs_data *tncs, const u8 *msg, size_t len); int tncs_global_init(void); void tncs_global_deinit(void); struct wpabuf * tncs_build_soh_request(void); struct wpabuf * tncs_process_soh(const u8 *soh_tlv, size_t soh_tlv_len, int *failure); #endif /* TNCS_H */ wpa-2.1/src/eapol_auth/000077500000000000000000000000001227414666700150725ustar00rootroot00000000000000wpa-2.1/src/eapol_auth/Makefile000066400000000000000000000001641227414666700165330ustar00rootroot00000000000000all: @echo Nothing to be made. clean: rm -f *~ *.o *.d *.gcno *.gcda *.gcov install: @echo Nothing to be made. wpa-2.1/src/eapol_auth/eapol_auth_dump.c000066400000000000000000000166521227414666700204160ustar00rootroot00000000000000/* * IEEE 802.1X-2004 Authenticator - State dump * Copyright (c) 2002-2013, 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.h" #include "eapol_auth_sm.h" #include "eapol_auth_sm_i.h" static inline const char * port_type_txt(PortTypes pt) { switch (pt) { case ForceUnauthorized: return "ForceUnauthorized"; case ForceAuthorized: return "ForceAuthorized"; case Auto: return "Auto"; default: return "Unknown"; } } static inline const char * port_state_txt(PortState ps) { switch (ps) { case Unauthorized: return "Unauthorized"; case Authorized: return "Authorized"; default: return "Unknown"; } } static inline const char * ctrl_dir_txt(ControlledDirection dir) { switch (dir) { case Both: return "Both"; case In: return "In"; default: return "Unknown"; } } static inline const char * auth_pae_state_txt(int s) { switch (s) { case AUTH_PAE_INITIALIZE: return "INITIALIZE"; case AUTH_PAE_DISCONNECTED: return "DISCONNECTED"; case AUTH_PAE_CONNECTING: return "CONNECTING"; case AUTH_PAE_AUTHENTICATING: return "AUTHENTICATING"; case AUTH_PAE_AUTHENTICATED: return "AUTHENTICATED"; case AUTH_PAE_ABORTING: return "ABORTING"; case AUTH_PAE_HELD: return "HELD"; case AUTH_PAE_FORCE_AUTH: return "FORCE_AUTH"; case AUTH_PAE_FORCE_UNAUTH: return "FORCE_UNAUTH"; case AUTH_PAE_RESTART: return "RESTART"; default: return "Unknown"; } } static inline const char * be_auth_state_txt(int s) { switch (s) { case BE_AUTH_REQUEST: return "REQUEST"; case BE_AUTH_RESPONSE: return "RESPONSE"; case BE_AUTH_SUCCESS: return "SUCCESS"; case BE_AUTH_FAIL: return "FAIL"; case BE_AUTH_TIMEOUT: return "TIMEOUT"; case BE_AUTH_IDLE: return "IDLE"; case BE_AUTH_INITIALIZE: return "INITIALIZE"; case BE_AUTH_IGNORE: return "IGNORE"; default: return "Unknown"; } } static inline const char * reauth_timer_state_txt(int s) { switch (s) { case REAUTH_TIMER_INITIALIZE: return "INITIALIZE"; case REAUTH_TIMER_REAUTHENTICATE: return "REAUTHENTICATE"; default: return "Unknown"; } } static inline const char * auth_key_tx_state_txt(int s) { switch (s) { case AUTH_KEY_TX_NO_KEY_TRANSMIT: return "NO_KEY_TRANSMIT"; case AUTH_KEY_TX_KEY_TRANSMIT: return "KEY_TRANSMIT"; default: return "Unknown"; } } static inline const char * key_rx_state_txt(int s) { switch (s) { case KEY_RX_NO_KEY_RECEIVE: return "NO_KEY_RECEIVE"; case KEY_RX_KEY_RECEIVE: return "KEY_RECEIVE"; default: return "Unknown"; } } static inline const char * ctrl_dir_state_txt(int s) { switch (s) { case CTRL_DIR_FORCE_BOTH: return "FORCE_BOTH"; case CTRL_DIR_IN_OR_BOTH: return "IN_OR_BOTH"; default: return "Unknown"; } } int eapol_auth_dump_state(struct eapol_state_machine *sm, char *buf, size_t buflen) { char *pos, *end; int ret; pos = buf; end = pos + buflen; ret = os_snprintf(pos, end - pos, "aWhile=%d\nquietWhile=%d\n" "reAuthWhen=%d\n", sm->aWhile, sm->quietWhile, sm->reAuthWhen); if (ret < 0 || ret >= end - pos) return pos - buf; pos += ret; #define _SB(b) ((b) ? "TRUE" : "FALSE") ret = os_snprintf(pos, end - pos, "authAbort=%s\n" "authFail=%s\n" "authPortStatus=%s\n" "authStart=%s\n" "authTimeout=%s\n" "authSuccess=%s\n" "eapFail=%s\n" "eapolEap=%s\n" "eapSuccess=%s\n" "eapTimeout=%s\n" "initialize=%s\n" "keyAvailable=%s\n" "keyDone=%s\n" "keyRun=%s\n" "keyTxEnabled=%s\n" "portControl=%s\n" "portEnabled=%s\n" "portValid=%s\n" "reAuthenticate=%s\n", _SB(sm->authAbort), _SB(sm->authFail), port_state_txt(sm->authPortStatus), _SB(sm->authStart), _SB(sm->authTimeout), _SB(sm->authSuccess), _SB(sm->eap_if->eapFail), _SB(sm->eapolEap), _SB(sm->eap_if->eapSuccess), _SB(sm->eap_if->eapTimeout), _SB(sm->initialize), _SB(sm->eap_if->eapKeyAvailable), _SB(sm->keyDone), _SB(sm->keyRun), _SB(sm->keyTxEnabled), port_type_txt(sm->portControl), _SB(sm->eap_if->portEnabled), _SB(sm->portValid), _SB(sm->reAuthenticate)); if (ret < 0 || ret >= end - pos) return pos - buf; pos += ret; ret = os_snprintf(pos, end - pos, "auth_pae_state=%s\n" "eapolLogoff=%s\n" "eapolStart=%s\n" "eapRestart=%s\n" "portMode=%s\n" "reAuthCount=%d\n" "quietPeriod=%d\n" "reAuthMax=%d\n" "authEntersConnecting=%d\n" "authEapLogoffsWhileConnecting=%d\n" "authEntersAuthenticating=%d\n" "authAuthSuccessesWhileAuthenticating=%d\n" "authAuthTimeoutsWhileAuthenticating=%d\n" "authAuthFailWhileAuthenticating=%d\n" "authAuthEapStartsWhileAuthenticating=%d\n" "authAuthEapLogoffWhileAuthenticating=%d\n" "authAuthReauthsWhileAuthenticated=%d\n" "authAuthEapStartsWhileAuthenticated=%d\n" "authAuthEapLogoffWhileAuthenticated=%d\n", auth_pae_state_txt(sm->auth_pae_state), _SB(sm->eapolLogoff), _SB(sm->eapolStart), _SB(sm->eap_if->eapRestart), port_type_txt(sm->portMode), sm->reAuthCount, sm->quietPeriod, sm->reAuthMax, sm->authEntersConnecting, sm->authEapLogoffsWhileConnecting, sm->authEntersAuthenticating, sm->authAuthSuccessesWhileAuthenticating, sm->authAuthTimeoutsWhileAuthenticating, sm->authAuthFailWhileAuthenticating, sm->authAuthEapStartsWhileAuthenticating, sm->authAuthEapLogoffWhileAuthenticating, sm->authAuthReauthsWhileAuthenticated, sm->authAuthEapStartsWhileAuthenticated, sm->authAuthEapLogoffWhileAuthenticated); if (ret < 0 || ret >= end - pos) return pos - buf; pos += ret; ret = os_snprintf(pos, end - pos, "be_auth_state=%s\n" "eapNoReq=%s\n" "eapReq=%s\n" "eapResp=%s\n" "serverTimeout=%d\n" "backendResponses=%d\n" "backendAccessChallenges=%d\n" "backendOtherRequestsToSupplicant=%d\n" "backendAuthSuccesses=%d\n" "backendAuthFails=%d\n", be_auth_state_txt(sm->be_auth_state), _SB(sm->eap_if->eapNoReq), _SB(sm->eap_if->eapReq), _SB(sm->eap_if->eapResp), sm->serverTimeout, sm->backendResponses, sm->backendAccessChallenges, sm->backendOtherRequestsToSupplicant, sm->backendAuthSuccesses, sm->backendAuthFails); if (ret < 0 || ret >= end - pos) return pos - buf; pos += ret; ret = os_snprintf(pos, end - pos, "reauth_timer_state=%s\n" "reAuthPeriod=%d\n" "reAuthEnabled=%s\n", reauth_timer_state_txt(sm->reauth_timer_state), sm->reAuthPeriod, _SB(sm->reAuthEnabled)); if (ret < 0 || ret >= end - pos) return pos - buf; pos += ret; ret = os_snprintf(pos, end - pos, "auth_key_tx_state=%s\n", auth_key_tx_state_txt(sm->auth_key_tx_state)); if (ret < 0 || ret >= end - pos) return pos - buf; pos += ret; ret = os_snprintf(pos, end - pos, "key_rx_state=%s\n" "rxKey=%s\n", key_rx_state_txt(sm->key_rx_state), _SB(sm->rxKey)); if (ret < 0 || ret >= end - pos) return pos - buf; pos += ret; ret = os_snprintf(pos, end - pos, "ctrl_dir_state=%s\n" "adminControlledDirections=%s\n" "operControlledDirections=%s\n" "operEdge=%s\n", ctrl_dir_state_txt(sm->ctrl_dir_state), ctrl_dir_txt(sm->adminControlledDirections), ctrl_dir_txt(sm->operControlledDirections), _SB(sm->operEdge)); if (ret < 0 || ret >= end - pos) return pos - buf; pos += ret; #undef _SB return pos - buf; } wpa-2.1/src/eapol_auth/eapol_auth_sm.c000066400000000000000000000706651227414666700200740ustar00rootroot00000000000000/* * IEEE 802.1X-2004 Authenticator - EAPOL state machine * Copyright (c) 2002-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 "eloop.h" #include "state_machine.h" #include "common/eapol_common.h" #include "eap_common/eap_defs.h" #include "eap_common/eap_common.h" #include "eap_server/eap.h" #include "eapol_auth_sm.h" #include "eapol_auth_sm_i.h" #define STATE_MACHINE_DATA struct eapol_state_machine #define STATE_MACHINE_DEBUG_PREFIX "IEEE 802.1X" #define STATE_MACHINE_ADDR sm->addr static struct eapol_callbacks eapol_cb; /* EAPOL state machines are described in IEEE Std 802.1X-2004, Chap. 8.2 */ #define setPortAuthorized() \ sm->eapol->cb.set_port_authorized(sm->eapol->conf.ctx, sm->sta, 1) #define setPortUnauthorized() \ sm->eapol->cb.set_port_authorized(sm->eapol->conf.ctx, sm->sta, 0) /* procedures */ #define txCannedFail() eapol_auth_tx_canned_eap(sm, 0) #define txCannedSuccess() eapol_auth_tx_canned_eap(sm, 1) #define txReq() eapol_auth_tx_req(sm) #define abortAuth() sm->eapol->cb.abort_auth(sm->eapol->conf.ctx, sm->sta) #define txKey() sm->eapol->cb.tx_key(sm->eapol->conf.ctx, sm->sta) #define processKey() do { } while (0) static void eapol_sm_step_run(struct eapol_state_machine *sm); static void eapol_sm_step_cb(void *eloop_ctx, void *timeout_ctx); static void eapol_auth_initialize(struct eapol_state_machine *sm); static void eapol_auth_logger(struct eapol_authenticator *eapol, const u8 *addr, eapol_logger_level level, const char *txt) { if (eapol->cb.logger == NULL) return; eapol->cb.logger(eapol->conf.ctx, addr, level, txt); } static void eapol_auth_vlogger(struct eapol_authenticator *eapol, const u8 *addr, eapol_logger_level level, const char *fmt, ...) { char *format; int maxlen; va_list ap; if (eapol->cb.logger == NULL) return; maxlen = os_strlen(fmt) + 100; format = os_malloc(maxlen); if (!format) return; va_start(ap, fmt); vsnprintf(format, maxlen, fmt, ap); va_end(ap); eapol_auth_logger(eapol, addr, level, format); os_free(format); } static void eapol_auth_tx_canned_eap(struct eapol_state_machine *sm, int success) { struct eap_hdr eap; os_memset(&eap, 0, sizeof(eap)); eap.code = success ? EAP_CODE_SUCCESS : EAP_CODE_FAILURE; eap.identifier = ++sm->last_eap_id; eap.length = host_to_be16(sizeof(eap)); eapol_auth_vlogger(sm->eapol, sm->addr, EAPOL_LOGGER_DEBUG, "Sending canned EAP packet %s (identifier %d)", success ? "SUCCESS" : "FAILURE", eap.identifier); sm->eapol->cb.eapol_send(sm->eapol->conf.ctx, sm->sta, IEEE802_1X_TYPE_EAP_PACKET, (u8 *) &eap, sizeof(eap)); sm->dot1xAuthEapolFramesTx++; } static void eapol_auth_tx_req(struct eapol_state_machine *sm) { if (sm->eap_if->eapReqData == NULL || wpabuf_len(sm->eap_if->eapReqData) < sizeof(struct eap_hdr)) { eapol_auth_logger(sm->eapol, sm->addr, EAPOL_LOGGER_DEBUG, "TxReq called, but there is no EAP request " "from authentication server"); return; } if (sm->flags & EAPOL_SM_WAIT_START) { wpa_printf(MSG_DEBUG, "EAPOL: Drop EAPOL TX to " MACSTR " while waiting for EAPOL-Start", MAC2STR(sm->addr)); return; } sm->last_eap_id = eap_get_id(sm->eap_if->eapReqData); eapol_auth_vlogger(sm->eapol, sm->addr, EAPOL_LOGGER_DEBUG, "Sending EAP Packet (identifier %d)", sm->last_eap_id); sm->eapol->cb.eapol_send(sm->eapol->conf.ctx, sm->sta, IEEE802_1X_TYPE_EAP_PACKET, wpabuf_head(sm->eap_if->eapReqData), wpabuf_len(sm->eap_if->eapReqData)); sm->dot1xAuthEapolFramesTx++; if (eap_get_type(sm->eap_if->eapReqData) == EAP_TYPE_IDENTITY) sm->dot1xAuthEapolReqIdFramesTx++; else sm->dot1xAuthEapolReqFramesTx++; } /** * eapol_port_timers_tick - Port Timers state machine * @eloop_ctx: struct eapol_state_machine * * @timeout_ctx: Not used * * This statemachine is implemented as a function that will be called * once a second as a registered event loop timeout. */ static void eapol_port_timers_tick(void *eloop_ctx, void *timeout_ctx) { struct eapol_state_machine *state = timeout_ctx; if (state->aWhile > 0) { state->aWhile--; if (state->aWhile == 0) { wpa_printf(MSG_DEBUG, "IEEE 802.1X: " MACSTR " - aWhile --> 0", MAC2STR(state->addr)); } } if (state->quietWhile > 0) { state->quietWhile--; if (state->quietWhile == 0) { wpa_printf(MSG_DEBUG, "IEEE 802.1X: " MACSTR " - quietWhile --> 0", MAC2STR(state->addr)); } } if (state->reAuthWhen > 0) { state->reAuthWhen--; if (state->reAuthWhen == 0) { wpa_printf(MSG_DEBUG, "IEEE 802.1X: " MACSTR " - reAuthWhen --> 0", MAC2STR(state->addr)); } } if (state->eap_if->retransWhile > 0) { state->eap_if->retransWhile--; if (state->eap_if->retransWhile == 0) { wpa_printf(MSG_DEBUG, "IEEE 802.1X: " MACSTR " - (EAP) retransWhile --> 0", MAC2STR(state->addr)); } } eapol_sm_step_run(state); eloop_register_timeout(1, 0, eapol_port_timers_tick, eloop_ctx, state); } /* Authenticator PAE state machine */ SM_STATE(AUTH_PAE, INITIALIZE) { SM_ENTRY_MA(AUTH_PAE, INITIALIZE, auth_pae); sm->portMode = Auto; } SM_STATE(AUTH_PAE, DISCONNECTED) { int from_initialize = sm->auth_pae_state == AUTH_PAE_INITIALIZE; if (sm->eapolLogoff) { if (sm->auth_pae_state == AUTH_PAE_CONNECTING) sm->authEapLogoffsWhileConnecting++; else if (sm->auth_pae_state == AUTH_PAE_AUTHENTICATED) sm->authAuthEapLogoffWhileAuthenticated++; } SM_ENTRY_MA(AUTH_PAE, DISCONNECTED, auth_pae); sm->authPortStatus = Unauthorized; setPortUnauthorized(); sm->reAuthCount = 0; sm->eapolLogoff = FALSE; if (!from_initialize) { sm->eapol->cb.finished(sm->eapol->conf.ctx, sm->sta, 0, sm->flags & EAPOL_SM_PREAUTH); } } SM_STATE(AUTH_PAE, RESTART) { if (sm->auth_pae_state == AUTH_PAE_AUTHENTICATED) { if (sm->reAuthenticate) sm->authAuthReauthsWhileAuthenticated++; if (sm->eapolStart) sm->authAuthEapStartsWhileAuthenticated++; if (sm->eapolLogoff) sm->authAuthEapLogoffWhileAuthenticated++; } SM_ENTRY_MA(AUTH_PAE, RESTART, auth_pae); sm->eap_if->eapRestart = TRUE; } SM_STATE(AUTH_PAE, CONNECTING) { if (sm->auth_pae_state != AUTH_PAE_CONNECTING) sm->authEntersConnecting++; SM_ENTRY_MA(AUTH_PAE, CONNECTING, auth_pae); sm->reAuthenticate = FALSE; sm->reAuthCount++; } SM_STATE(AUTH_PAE, HELD) { if (sm->auth_pae_state == AUTH_PAE_AUTHENTICATING && sm->authFail) sm->authAuthFailWhileAuthenticating++; SM_ENTRY_MA(AUTH_PAE, HELD, auth_pae); sm->authPortStatus = Unauthorized; setPortUnauthorized(); sm->quietWhile = sm->quietPeriod; sm->eapolLogoff = FALSE; eapol_auth_vlogger(sm->eapol, sm->addr, EAPOL_LOGGER_WARNING, "authentication failed - EAP type: %d (%s)", sm->eap_type_authsrv, eap_server_get_name(0, sm->eap_type_authsrv)); if (sm->eap_type_authsrv != sm->eap_type_supp) { eapol_auth_vlogger(sm->eapol, sm->addr, EAPOL_LOGGER_INFO, "Supplicant used different EAP type: " "%d (%s)", sm->eap_type_supp, eap_server_get_name(0, sm->eap_type_supp)); } sm->eapol->cb.finished(sm->eapol->conf.ctx, sm->sta, 0, sm->flags & EAPOL_SM_PREAUTH); } SM_STATE(AUTH_PAE, AUTHENTICATED) { char *extra = ""; if (sm->auth_pae_state == AUTH_PAE_AUTHENTICATING && sm->authSuccess) sm->authAuthSuccessesWhileAuthenticating++; SM_ENTRY_MA(AUTH_PAE, AUTHENTICATED, auth_pae); sm->authPortStatus = Authorized; setPortAuthorized(); sm->reAuthCount = 0; if (sm->flags & EAPOL_SM_PREAUTH) extra = " (pre-authentication)"; else if (sm->flags & EAPOL_SM_FROM_PMKSA_CACHE) extra = " (PMKSA cache)"; eapol_auth_vlogger(sm->eapol, sm->addr, EAPOL_LOGGER_INFO, "authenticated - EAP type: %d (%s)%s", sm->eap_type_authsrv, eap_server_get_name(0, sm->eap_type_authsrv), extra); sm->eapol->cb.finished(sm->eapol->conf.ctx, sm->sta, 1, sm->flags & EAPOL_SM_PREAUTH); } SM_STATE(AUTH_PAE, AUTHENTICATING) { SM_ENTRY_MA(AUTH_PAE, AUTHENTICATING, auth_pae); sm->eapolStart = FALSE; sm->authSuccess = FALSE; sm->authFail = FALSE; sm->authTimeout = FALSE; sm->authStart = TRUE; sm->keyRun = FALSE; sm->keyDone = FALSE; } SM_STATE(AUTH_PAE, ABORTING) { if (sm->auth_pae_state == AUTH_PAE_AUTHENTICATING) { if (sm->authTimeout) sm->authAuthTimeoutsWhileAuthenticating++; if (sm->eapolStart) sm->authAuthEapStartsWhileAuthenticating++; if (sm->eapolLogoff) sm->authAuthEapLogoffWhileAuthenticating++; } SM_ENTRY_MA(AUTH_PAE, ABORTING, auth_pae); sm->authAbort = TRUE; sm->keyRun = FALSE; sm->keyDone = FALSE; } SM_STATE(AUTH_PAE, FORCE_AUTH) { SM_ENTRY_MA(AUTH_PAE, FORCE_AUTH, auth_pae); sm->authPortStatus = Authorized; setPortAuthorized(); sm->portMode = ForceAuthorized; sm->eapolStart = FALSE; txCannedSuccess(); } SM_STATE(AUTH_PAE, FORCE_UNAUTH) { SM_ENTRY_MA(AUTH_PAE, FORCE_UNAUTH, auth_pae); sm->authPortStatus = Unauthorized; setPortUnauthorized(); sm->portMode = ForceUnauthorized; sm->eapolStart = FALSE; txCannedFail(); } SM_STEP(AUTH_PAE) { if ((sm->portControl == Auto && sm->portMode != sm->portControl) || sm->initialize || !sm->eap_if->portEnabled) SM_ENTER_GLOBAL(AUTH_PAE, INITIALIZE); else if (sm->portControl == ForceAuthorized && sm->portMode != sm->portControl && !(sm->initialize || !sm->eap_if->portEnabled)) SM_ENTER_GLOBAL(AUTH_PAE, FORCE_AUTH); else if (sm->portControl == ForceUnauthorized && sm->portMode != sm->portControl && !(sm->initialize || !sm->eap_if->portEnabled)) SM_ENTER_GLOBAL(AUTH_PAE, FORCE_UNAUTH); else { switch (sm->auth_pae_state) { case AUTH_PAE_INITIALIZE: SM_ENTER(AUTH_PAE, DISCONNECTED); break; case AUTH_PAE_DISCONNECTED: SM_ENTER(AUTH_PAE, RESTART); break; case AUTH_PAE_RESTART: if (!sm->eap_if->eapRestart) SM_ENTER(AUTH_PAE, CONNECTING); break; case AUTH_PAE_HELD: if (sm->quietWhile == 0) SM_ENTER(AUTH_PAE, RESTART); break; case AUTH_PAE_CONNECTING: if (sm->eapolLogoff || sm->reAuthCount > sm->reAuthMax) SM_ENTER(AUTH_PAE, DISCONNECTED); else if ((sm->eap_if->eapReq && sm->reAuthCount <= sm->reAuthMax) || sm->eap_if->eapSuccess || sm->eap_if->eapFail) SM_ENTER(AUTH_PAE, AUTHENTICATING); break; case AUTH_PAE_AUTHENTICATED: if (sm->eapolStart || sm->reAuthenticate) SM_ENTER(AUTH_PAE, RESTART); else if (sm->eapolLogoff || !sm->portValid) SM_ENTER(AUTH_PAE, DISCONNECTED); break; case AUTH_PAE_AUTHENTICATING: if (sm->authSuccess && sm->portValid) SM_ENTER(AUTH_PAE, AUTHENTICATED); else if (sm->authFail || (sm->keyDone && !sm->portValid)) SM_ENTER(AUTH_PAE, HELD); else if (sm->eapolStart || sm->eapolLogoff || sm->authTimeout) SM_ENTER(AUTH_PAE, ABORTING); break; case AUTH_PAE_ABORTING: if (sm->eapolLogoff && !sm->authAbort) SM_ENTER(AUTH_PAE, DISCONNECTED); else if (!sm->eapolLogoff && !sm->authAbort) SM_ENTER(AUTH_PAE, RESTART); break; case AUTH_PAE_FORCE_AUTH: if (sm->eapolStart) SM_ENTER(AUTH_PAE, FORCE_AUTH); break; case AUTH_PAE_FORCE_UNAUTH: if (sm->eapolStart) SM_ENTER(AUTH_PAE, FORCE_UNAUTH); break; } } } /* Backend Authentication state machine */ SM_STATE(BE_AUTH, INITIALIZE) { SM_ENTRY_MA(BE_AUTH, INITIALIZE, be_auth); abortAuth(); sm->eap_if->eapNoReq = FALSE; sm->authAbort = FALSE; } SM_STATE(BE_AUTH, REQUEST) { SM_ENTRY_MA(BE_AUTH, REQUEST, be_auth); txReq(); sm->eap_if->eapReq = FALSE; sm->backendOtherRequestsToSupplicant++; /* * Clearing eapolEap here is not specified in IEEE Std 802.1X-2004, but * it looks like this would be logical thing to do there since the old * EAP response would not be valid anymore after the new EAP request * was sent out. * * A race condition has been reported, in which hostapd ended up * sending out EAP-Response/Identity as a response to the first * EAP-Request from the main EAP method. This can be avoided by * clearing eapolEap here. */ sm->eapolEap = FALSE; } SM_STATE(BE_AUTH, RESPONSE) { SM_ENTRY_MA(BE_AUTH, RESPONSE, be_auth); sm->authTimeout = FALSE; sm->eapolEap = FALSE; sm->eap_if->eapNoReq = FALSE; sm->aWhile = sm->serverTimeout; sm->eap_if->eapResp = TRUE; /* sendRespToServer(); */ sm->backendResponses++; } SM_STATE(BE_AUTH, SUCCESS) { SM_ENTRY_MA(BE_AUTH, SUCCESS, be_auth); txReq(); sm->authSuccess = TRUE; sm->keyRun = TRUE; } SM_STATE(BE_AUTH, FAIL) { SM_ENTRY_MA(BE_AUTH, FAIL, be_auth); txReq(); sm->authFail = TRUE; } SM_STATE(BE_AUTH, TIMEOUT) { SM_ENTRY_MA(BE_AUTH, TIMEOUT, be_auth); sm->authTimeout = TRUE; } SM_STATE(BE_AUTH, IDLE) { SM_ENTRY_MA(BE_AUTH, IDLE, be_auth); sm->authStart = FALSE; } SM_STATE(BE_AUTH, IGNORE) { SM_ENTRY_MA(BE_AUTH, IGNORE, be_auth); sm->eap_if->eapNoReq = FALSE; } SM_STEP(BE_AUTH) { if (sm->portControl != Auto || sm->initialize || sm->authAbort) { SM_ENTER_GLOBAL(BE_AUTH, INITIALIZE); return; } switch (sm->be_auth_state) { case BE_AUTH_INITIALIZE: SM_ENTER(BE_AUTH, IDLE); break; case BE_AUTH_REQUEST: if (sm->eapolEap) SM_ENTER(BE_AUTH, RESPONSE); else if (sm->eap_if->eapReq) SM_ENTER(BE_AUTH, REQUEST); else if (sm->eap_if->eapTimeout) SM_ENTER(BE_AUTH, TIMEOUT); break; case BE_AUTH_RESPONSE: if (sm->eap_if->eapNoReq) SM_ENTER(BE_AUTH, IGNORE); if (sm->eap_if->eapReq) { sm->backendAccessChallenges++; SM_ENTER(BE_AUTH, REQUEST); } else if (sm->aWhile == 0) SM_ENTER(BE_AUTH, TIMEOUT); else if (sm->eap_if->eapFail) { sm->backendAuthFails++; SM_ENTER(BE_AUTH, FAIL); } else if (sm->eap_if->eapSuccess) { sm->backendAuthSuccesses++; SM_ENTER(BE_AUTH, SUCCESS); } break; case BE_AUTH_SUCCESS: SM_ENTER(BE_AUTH, IDLE); break; case BE_AUTH_FAIL: SM_ENTER(BE_AUTH, IDLE); break; case BE_AUTH_TIMEOUT: SM_ENTER(BE_AUTH, IDLE); break; case BE_AUTH_IDLE: if (sm->eap_if->eapFail && sm->authStart) SM_ENTER(BE_AUTH, FAIL); else if (sm->eap_if->eapReq && sm->authStart) SM_ENTER(BE_AUTH, REQUEST); else if (sm->eap_if->eapSuccess && sm->authStart) SM_ENTER(BE_AUTH, SUCCESS); break; case BE_AUTH_IGNORE: if (sm->eapolEap) SM_ENTER(BE_AUTH, RESPONSE); else if (sm->eap_if->eapReq) SM_ENTER(BE_AUTH, REQUEST); else if (sm->eap_if->eapTimeout) SM_ENTER(BE_AUTH, TIMEOUT); break; } } /* Reauthentication Timer state machine */ SM_STATE(REAUTH_TIMER, INITIALIZE) { SM_ENTRY_MA(REAUTH_TIMER, INITIALIZE, reauth_timer); sm->reAuthWhen = sm->reAuthPeriod; } SM_STATE(REAUTH_TIMER, REAUTHENTICATE) { SM_ENTRY_MA(REAUTH_TIMER, REAUTHENTICATE, reauth_timer); sm->reAuthenticate = TRUE; sm->eapol->cb.eapol_event(sm->eapol->conf.ctx, sm->sta, EAPOL_AUTH_REAUTHENTICATE); } SM_STEP(REAUTH_TIMER) { if (sm->portControl != Auto || sm->initialize || sm->authPortStatus == Unauthorized || !sm->reAuthEnabled) { SM_ENTER_GLOBAL(REAUTH_TIMER, INITIALIZE); return; } switch (sm->reauth_timer_state) { case REAUTH_TIMER_INITIALIZE: if (sm->reAuthWhen == 0) SM_ENTER(REAUTH_TIMER, REAUTHENTICATE); break; case REAUTH_TIMER_REAUTHENTICATE: SM_ENTER(REAUTH_TIMER, INITIALIZE); break; } } /* Authenticator Key Transmit state machine */ SM_STATE(AUTH_KEY_TX, NO_KEY_TRANSMIT) { SM_ENTRY_MA(AUTH_KEY_TX, NO_KEY_TRANSMIT, auth_key_tx); } SM_STATE(AUTH_KEY_TX, KEY_TRANSMIT) { SM_ENTRY_MA(AUTH_KEY_TX, KEY_TRANSMIT, auth_key_tx); txKey(); sm->eap_if->eapKeyAvailable = FALSE; sm->keyDone = TRUE; } SM_STEP(AUTH_KEY_TX) { if (sm->initialize || sm->portControl != Auto) { SM_ENTER_GLOBAL(AUTH_KEY_TX, NO_KEY_TRANSMIT); return; } switch (sm->auth_key_tx_state) { case AUTH_KEY_TX_NO_KEY_TRANSMIT: if (sm->keyTxEnabled && sm->eap_if->eapKeyAvailable && sm->keyRun && !(sm->flags & EAPOL_SM_USES_WPA)) SM_ENTER(AUTH_KEY_TX, KEY_TRANSMIT); break; case AUTH_KEY_TX_KEY_TRANSMIT: if (!sm->keyTxEnabled || !sm->keyRun) SM_ENTER(AUTH_KEY_TX, NO_KEY_TRANSMIT); else if (sm->eap_if->eapKeyAvailable) SM_ENTER(AUTH_KEY_TX, KEY_TRANSMIT); break; } } /* Key Receive state machine */ SM_STATE(KEY_RX, NO_KEY_RECEIVE) { SM_ENTRY_MA(KEY_RX, NO_KEY_RECEIVE, key_rx); } SM_STATE(KEY_RX, KEY_RECEIVE) { SM_ENTRY_MA(KEY_RX, KEY_RECEIVE, key_rx); processKey(); sm->rxKey = FALSE; } SM_STEP(KEY_RX) { if (sm->initialize || !sm->eap_if->portEnabled) { SM_ENTER_GLOBAL(KEY_RX, NO_KEY_RECEIVE); return; } switch (sm->key_rx_state) { case KEY_RX_NO_KEY_RECEIVE: if (sm->rxKey) SM_ENTER(KEY_RX, KEY_RECEIVE); break; case KEY_RX_KEY_RECEIVE: if (sm->rxKey) SM_ENTER(KEY_RX, KEY_RECEIVE); break; } } /* Controlled Directions state machine */ SM_STATE(CTRL_DIR, FORCE_BOTH) { SM_ENTRY_MA(CTRL_DIR, FORCE_BOTH, ctrl_dir); sm->operControlledDirections = Both; } SM_STATE(CTRL_DIR, IN_OR_BOTH) { SM_ENTRY_MA(CTRL_DIR, IN_OR_BOTH, ctrl_dir); sm->operControlledDirections = sm->adminControlledDirections; } SM_STEP(CTRL_DIR) { if (sm->initialize) { SM_ENTER_GLOBAL(CTRL_DIR, IN_OR_BOTH); return; } switch (sm->ctrl_dir_state) { case CTRL_DIR_FORCE_BOTH: if (sm->eap_if->portEnabled && sm->operEdge) SM_ENTER(CTRL_DIR, IN_OR_BOTH); break; case CTRL_DIR_IN_OR_BOTH: if (sm->operControlledDirections != sm->adminControlledDirections) SM_ENTER(CTRL_DIR, IN_OR_BOTH); if (!sm->eap_if->portEnabled || !sm->operEdge) SM_ENTER(CTRL_DIR, FORCE_BOTH); break; } } struct eapol_state_machine * eapol_auth_alloc(struct eapol_authenticator *eapol, const u8 *addr, int flags, const struct wpabuf *assoc_wps_ie, const struct wpabuf *assoc_p2p_ie, void *sta_ctx, const char *identity, const char *radius_cui) { struct eapol_state_machine *sm; struct eap_config eap_conf; if (eapol == NULL) return NULL; sm = os_zalloc(sizeof(*sm)); if (sm == NULL) { wpa_printf(MSG_DEBUG, "IEEE 802.1X state machine allocation " "failed"); return NULL; } sm->radius_identifier = -1; os_memcpy(sm->addr, addr, ETH_ALEN); sm->flags = flags; sm->eapol = eapol; sm->sta = sta_ctx; /* Set default values for state machine constants */ sm->auth_pae_state = AUTH_PAE_INITIALIZE; sm->quietPeriod = AUTH_PAE_DEFAULT_quietPeriod; sm->reAuthMax = AUTH_PAE_DEFAULT_reAuthMax; sm->be_auth_state = BE_AUTH_INITIALIZE; sm->serverTimeout = BE_AUTH_DEFAULT_serverTimeout; sm->reauth_timer_state = REAUTH_TIMER_INITIALIZE; sm->reAuthPeriod = eapol->conf.eap_reauth_period; sm->reAuthEnabled = eapol->conf.eap_reauth_period > 0 ? TRUE : FALSE; sm->auth_key_tx_state = AUTH_KEY_TX_NO_KEY_TRANSMIT; sm->key_rx_state = KEY_RX_NO_KEY_RECEIVE; sm->ctrl_dir_state = CTRL_DIR_IN_OR_BOTH; sm->portControl = Auto; if (!eapol->conf.wpa && (eapol->default_wep_key || eapol->conf.individual_wep_key_len > 0)) sm->keyTxEnabled = TRUE; else sm->keyTxEnabled = FALSE; if (eapol->conf.wpa) sm->portValid = FALSE; else sm->portValid = TRUE; os_memset(&eap_conf, 0, sizeof(eap_conf)); eap_conf.eap_server = eapol->conf.eap_server; eap_conf.ssl_ctx = eapol->conf.ssl_ctx; eap_conf.msg_ctx = eapol->conf.msg_ctx; eap_conf.eap_sim_db_priv = eapol->conf.eap_sim_db_priv; eap_conf.pac_opaque_encr_key = eapol->conf.pac_opaque_encr_key; eap_conf.eap_fast_a_id = eapol->conf.eap_fast_a_id; eap_conf.eap_fast_a_id_len = eapol->conf.eap_fast_a_id_len; eap_conf.eap_fast_a_id_info = eapol->conf.eap_fast_a_id_info; eap_conf.eap_fast_prov = eapol->conf.eap_fast_prov; eap_conf.pac_key_lifetime = eapol->conf.pac_key_lifetime; eap_conf.pac_key_refresh_time = eapol->conf.pac_key_refresh_time; eap_conf.eap_sim_aka_result_ind = eapol->conf.eap_sim_aka_result_ind; eap_conf.tnc = eapol->conf.tnc; eap_conf.wps = eapol->conf.wps; eap_conf.assoc_wps_ie = assoc_wps_ie; eap_conf.assoc_p2p_ie = assoc_p2p_ie; eap_conf.peer_addr = addr; eap_conf.fragment_size = eapol->conf.fragment_size; eap_conf.pwd_group = eapol->conf.pwd_group; eap_conf.pbc_in_m1 = eapol->conf.pbc_in_m1; eap_conf.server_id = eapol->conf.server_id; eap_conf.server_id_len = eapol->conf.server_id_len; sm->eap = eap_server_sm_init(sm, &eapol_cb, &eap_conf); if (sm->eap == NULL) { eapol_auth_free(sm); return NULL; } sm->eap_if = eap_get_interface(sm->eap); eapol_auth_initialize(sm); if (identity) { sm->identity = (u8 *) os_strdup(identity); if (sm->identity) sm->identity_len = os_strlen(identity); } if (radius_cui) sm->radius_cui = wpabuf_alloc_copy(radius_cui, os_strlen(radius_cui)); return sm; } void eapol_auth_free(struct eapol_state_machine *sm) { if (sm == NULL) return; eloop_cancel_timeout(eapol_port_timers_tick, NULL, sm); eloop_cancel_timeout(eapol_sm_step_cb, sm, NULL); if (sm->eap) eap_server_sm_deinit(sm->eap); os_free(sm); } static int eapol_sm_sta_entry_alive(struct eapol_authenticator *eapol, const u8 *addr) { return eapol->cb.sta_entry_alive(eapol->conf.ctx, addr); } static void eapol_sm_step_run(struct eapol_state_machine *sm) { struct eapol_authenticator *eapol = sm->eapol; u8 addr[ETH_ALEN]; unsigned int prev_auth_pae, prev_be_auth, prev_reauth_timer, prev_auth_key_tx, prev_key_rx, prev_ctrl_dir; int max_steps = 100; os_memcpy(addr, sm->addr, ETH_ALEN); /* * Allow EAPOL state machines to run as long as there are state * changes, but exit and return here through event loop if more than * 100 steps is needed as a precaution against infinite loops inside * eloop callback. */ restart: prev_auth_pae = sm->auth_pae_state; prev_be_auth = sm->be_auth_state; prev_reauth_timer = sm->reauth_timer_state; prev_auth_key_tx = sm->auth_key_tx_state; prev_key_rx = sm->key_rx_state; prev_ctrl_dir = sm->ctrl_dir_state; SM_STEP_RUN(AUTH_PAE); if (sm->initializing || eapol_sm_sta_entry_alive(eapol, addr)) SM_STEP_RUN(BE_AUTH); if (sm->initializing || eapol_sm_sta_entry_alive(eapol, addr)) SM_STEP_RUN(REAUTH_TIMER); if (sm->initializing || eapol_sm_sta_entry_alive(eapol, addr)) SM_STEP_RUN(AUTH_KEY_TX); if (sm->initializing || eapol_sm_sta_entry_alive(eapol, addr)) SM_STEP_RUN(KEY_RX); if (sm->initializing || eapol_sm_sta_entry_alive(eapol, addr)) SM_STEP_RUN(CTRL_DIR); if (prev_auth_pae != sm->auth_pae_state || prev_be_auth != sm->be_auth_state || prev_reauth_timer != sm->reauth_timer_state || prev_auth_key_tx != sm->auth_key_tx_state || prev_key_rx != sm->key_rx_state || prev_ctrl_dir != sm->ctrl_dir_state) { if (--max_steps > 0) goto restart; /* Re-run from eloop timeout */ eapol_auth_step(sm); return; } if (eapol_sm_sta_entry_alive(eapol, addr) && sm->eap) { if (eap_server_sm_step(sm->eap)) { if (--max_steps > 0) goto restart; /* Re-run from eloop timeout */ eapol_auth_step(sm); return; } /* TODO: find a better location for this */ if (sm->eap_if->aaaEapResp) { sm->eap_if->aaaEapResp = FALSE; if (sm->eap_if->aaaEapRespData == NULL) { wpa_printf(MSG_DEBUG, "EAPOL: aaaEapResp set, " "but no aaaEapRespData available"); return; } sm->eapol->cb.aaa_send( sm->eapol->conf.ctx, sm->sta, wpabuf_head(sm->eap_if->aaaEapRespData), wpabuf_len(sm->eap_if->aaaEapRespData)); } } if (eapol_sm_sta_entry_alive(eapol, addr)) sm->eapol->cb.eapol_event(sm->eapol->conf.ctx, sm->sta, EAPOL_AUTH_SM_CHANGE); } static void eapol_sm_step_cb(void *eloop_ctx, void *timeout_ctx) { struct eapol_state_machine *sm = eloop_ctx; eapol_sm_step_run(sm); } /** * eapol_auth_step - Advance EAPOL state machines * @sm: EAPOL state machine * * This function is called to advance EAPOL state machines after any change * that could affect their state. */ void eapol_auth_step(struct eapol_state_machine *sm) { /* * Run eapol_sm_step_run from a registered timeout to make sure that * other possible timeouts/events are processed and to avoid long * function call chains. */ eloop_register_timeout(0, 0, eapol_sm_step_cb, sm, NULL); } static void eapol_auth_initialize(struct eapol_state_machine *sm) { sm->initializing = TRUE; /* Initialize the state machines by asserting initialize and then * deasserting it after one step */ sm->initialize = TRUE; eapol_sm_step_run(sm); sm->initialize = FALSE; eapol_sm_step_run(sm); sm->initializing = FALSE; /* Start one second tick for port timers state machine */ eloop_cancel_timeout(eapol_port_timers_tick, NULL, sm); eloop_register_timeout(1, 0, eapol_port_timers_tick, NULL, sm); } static int eapol_sm_get_eap_user(void *ctx, const u8 *identity, size_t identity_len, int phase2, struct eap_user *user) { struct eapol_state_machine *sm = ctx; return sm->eapol->cb.get_eap_user(sm->eapol->conf.ctx, identity, identity_len, phase2, user); } static const char * eapol_sm_get_eap_req_id_text(void *ctx, size_t *len) { struct eapol_state_machine *sm = ctx; *len = sm->eapol->conf.eap_req_id_text_len; return sm->eapol->conf.eap_req_id_text; } static struct eapol_callbacks eapol_cb = { eapol_sm_get_eap_user, eapol_sm_get_eap_req_id_text }; int eapol_auth_eap_pending_cb(struct eapol_state_machine *sm, void *ctx) { if (sm == NULL || ctx == NULL || ctx != sm->eap) return -1; eap_sm_pending_cb(sm->eap); eapol_auth_step(sm); return 0; } static int eapol_auth_conf_clone(struct eapol_auth_config *dst, struct eapol_auth_config *src) { dst->ctx = src->ctx; dst->eap_reauth_period = src->eap_reauth_period; dst->wpa = src->wpa; dst->individual_wep_key_len = src->individual_wep_key_len; dst->eap_server = src->eap_server; dst->ssl_ctx = src->ssl_ctx; dst->msg_ctx = src->msg_ctx; dst->eap_sim_db_priv = src->eap_sim_db_priv; os_free(dst->eap_req_id_text); dst->pwd_group = src->pwd_group; dst->pbc_in_m1 = src->pbc_in_m1; dst->server_id = src->server_id; dst->server_id_len = src->server_id_len; if (src->eap_req_id_text) { dst->eap_req_id_text = os_malloc(src->eap_req_id_text_len); if (dst->eap_req_id_text == NULL) return -1; os_memcpy(dst->eap_req_id_text, src->eap_req_id_text, src->eap_req_id_text_len); dst->eap_req_id_text_len = src->eap_req_id_text_len; } else { dst->eap_req_id_text = NULL; dst->eap_req_id_text_len = 0; } if (src->pac_opaque_encr_key) { dst->pac_opaque_encr_key = os_malloc(16); if (dst->pac_opaque_encr_key == NULL) { os_free(dst->eap_req_id_text); return -1; } os_memcpy(dst->pac_opaque_encr_key, src->pac_opaque_encr_key, 16); } else dst->pac_opaque_encr_key = NULL; if (src->eap_fast_a_id) { dst->eap_fast_a_id = os_malloc(src->eap_fast_a_id_len); if (dst->eap_fast_a_id == NULL) { os_free(dst->eap_req_id_text); os_free(dst->pac_opaque_encr_key); return -1; } os_memcpy(dst->eap_fast_a_id, src->eap_fast_a_id, src->eap_fast_a_id_len); dst->eap_fast_a_id_len = src->eap_fast_a_id_len; } else dst->eap_fast_a_id = NULL; if (src->eap_fast_a_id_info) { dst->eap_fast_a_id_info = os_strdup(src->eap_fast_a_id_info); if (dst->eap_fast_a_id_info == NULL) { os_free(dst->eap_req_id_text); os_free(dst->pac_opaque_encr_key); os_free(dst->eap_fast_a_id); return -1; } } else dst->eap_fast_a_id_info = NULL; dst->eap_fast_prov = src->eap_fast_prov; dst->pac_key_lifetime = src->pac_key_lifetime; dst->pac_key_refresh_time = src->pac_key_refresh_time; dst->eap_sim_aka_result_ind = src->eap_sim_aka_result_ind; dst->tnc = src->tnc; dst->wps = src->wps; dst->fragment_size = src->fragment_size; return 0; } static void eapol_auth_conf_free(struct eapol_auth_config *conf) { os_free(conf->eap_req_id_text); conf->eap_req_id_text = NULL; os_free(conf->pac_opaque_encr_key); conf->pac_opaque_encr_key = NULL; os_free(conf->eap_fast_a_id); conf->eap_fast_a_id = NULL; os_free(conf->eap_fast_a_id_info); conf->eap_fast_a_id_info = NULL; } struct eapol_authenticator * eapol_auth_init(struct eapol_auth_config *conf, struct eapol_auth_cb *cb) { struct eapol_authenticator *eapol; eapol = os_zalloc(sizeof(*eapol)); if (eapol == NULL) return NULL; if (eapol_auth_conf_clone(&eapol->conf, conf) < 0) { os_free(eapol); return NULL; } if (conf->individual_wep_key_len > 0) { /* use key0 in individual key and key1 in broadcast key */ eapol->default_wep_key_idx = 1; } eapol->cb.eapol_send = cb->eapol_send; eapol->cb.aaa_send = cb->aaa_send; eapol->cb.finished = cb->finished; eapol->cb.get_eap_user = cb->get_eap_user; eapol->cb.sta_entry_alive = cb->sta_entry_alive; eapol->cb.logger = cb->logger; eapol->cb.set_port_authorized = cb->set_port_authorized; eapol->cb.abort_auth = cb->abort_auth; eapol->cb.tx_key = cb->tx_key; eapol->cb.eapol_event = cb->eapol_event; return eapol; } void eapol_auth_deinit(struct eapol_authenticator *eapol) { if (eapol == NULL) return; eapol_auth_conf_free(&eapol->conf); os_free(eapol->default_wep_key); os_free(eapol); } wpa-2.1/src/eapol_auth/eapol_auth_sm.h000066400000000000000000000053221227414666700200650ustar00rootroot00000000000000/* * IEEE 802.1X-2004 Authenticator - EAPOL state machine * Copyright (c) 2002-2009, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef EAPOL_AUTH_SM_H #define EAPOL_AUTH_SM_H #define EAPOL_SM_PREAUTH BIT(0) #define EAPOL_SM_WAIT_START BIT(1) #define EAPOL_SM_USES_WPA BIT(2) #define EAPOL_SM_FROM_PMKSA_CACHE BIT(3) struct eapol_auth_config { int eap_reauth_period; int wpa; int individual_wep_key_len; int eap_server; void *ssl_ctx; void *msg_ctx; void *eap_sim_db_priv; char *eap_req_id_text; /* a copy of this will be allocated */ size_t eap_req_id_text_len; u8 *pac_opaque_encr_key; u8 *eap_fast_a_id; size_t eap_fast_a_id_len; char *eap_fast_a_id_info; int eap_fast_prov; int pac_key_lifetime; int pac_key_refresh_time; int eap_sim_aka_result_ind; int tnc; struct wps_context *wps; int fragment_size; u16 pwd_group; int pbc_in_m1; const u8 *server_id; size_t server_id_len; /* Opaque context pointer to owner data for callback functions */ void *ctx; }; struct eap_user; typedef enum { EAPOL_LOGGER_DEBUG, EAPOL_LOGGER_INFO, EAPOL_LOGGER_WARNING } eapol_logger_level; enum eapol_event { EAPOL_AUTH_SM_CHANGE, EAPOL_AUTH_REAUTHENTICATE }; struct eapol_auth_cb { void (*eapol_send)(void *ctx, void *sta_ctx, u8 type, const u8 *data, size_t datalen); void (*aaa_send)(void *ctx, void *sta_ctx, const u8 *data, size_t datalen); void (*finished)(void *ctx, void *sta_ctx, int success, int preauth); int (*get_eap_user)(void *ctx, const u8 *identity, size_t identity_len, int phase2, struct eap_user *user); int (*sta_entry_alive)(void *ctx, const u8 *addr); void (*logger)(void *ctx, const u8 *addr, eapol_logger_level level, const char *txt); void (*set_port_authorized)(void *ctx, void *sta_ctx, int authorized); void (*abort_auth)(void *ctx, void *sta_ctx); void (*tx_key)(void *ctx, void *sta_ctx); void (*eapol_event)(void *ctx, void *sta_ctx, enum eapol_event type); }; struct eapol_authenticator * eapol_auth_init(struct eapol_auth_config *conf, struct eapol_auth_cb *cb); void eapol_auth_deinit(struct eapol_authenticator *eapol); struct eapol_state_machine * eapol_auth_alloc(struct eapol_authenticator *eapol, const u8 *addr, int flags, const struct wpabuf *assoc_wps_ie, const struct wpabuf *assoc_p2p_ie, void *sta_ctx, const char *identity, const char *radius_cui); void eapol_auth_free(struct eapol_state_machine *sm); void eapol_auth_step(struct eapol_state_machine *sm); int eapol_auth_dump_state(struct eapol_state_machine *sm, char *buf, size_t buflen); int eapol_auth_eap_pending_cb(struct eapol_state_machine *sm, void *ctx); #endif /* EAPOL_AUTH_SM_H */ wpa-2.1/src/eapol_auth/eapol_auth_sm_i.h000066400000000000000000000122571227414666700204020ustar00rootroot00000000000000/* * IEEE 802.1X-2004 Authenticator - EAPOL state machine (internal definitions) * Copyright (c) 2002-2009, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef EAPOL_AUTH_SM_I_H #define EAPOL_AUTH_SM_I_H #include "common/defs.h" #include "radius/radius.h" /* IEEE Std 802.1X-2004, Ch. 8.2 */ typedef enum { ForceUnauthorized = 1, ForceAuthorized = 3, Auto = 2 } PortTypes; typedef enum { Unauthorized = 2, Authorized = 1 } PortState; typedef enum { Both = 0, In = 1 } ControlledDirection; typedef unsigned int Counter; /** * struct eapol_authenticator - Global EAPOL authenticator data */ struct eapol_authenticator { struct eapol_auth_config conf; struct eapol_auth_cb cb; u8 *default_wep_key; u8 default_wep_key_idx; }; /** * struct eapol_state_machine - Per-Supplicant Authenticator state machines */ struct eapol_state_machine { /* timers */ int aWhile; int quietWhile; int reAuthWhen; /* global variables */ Boolean authAbort; Boolean authFail; PortState authPortStatus; Boolean authStart; Boolean authTimeout; Boolean authSuccess; Boolean eapolEap; Boolean initialize; Boolean keyDone; Boolean keyRun; Boolean keyTxEnabled; PortTypes portControl; Boolean portValid; Boolean reAuthenticate; /* Port Timers state machine */ /* 'Boolean tick' implicitly handled as registered timeout */ /* Authenticator PAE state machine */ enum { AUTH_PAE_INITIALIZE, AUTH_PAE_DISCONNECTED, AUTH_PAE_CONNECTING, AUTH_PAE_AUTHENTICATING, AUTH_PAE_AUTHENTICATED, AUTH_PAE_ABORTING, AUTH_PAE_HELD, AUTH_PAE_FORCE_AUTH, AUTH_PAE_FORCE_UNAUTH, AUTH_PAE_RESTART } auth_pae_state; /* variables */ Boolean eapolLogoff; Boolean eapolStart; PortTypes portMode; unsigned int reAuthCount; /* constants */ unsigned int quietPeriod; /* default 60; 0..65535 */ #define AUTH_PAE_DEFAULT_quietPeriod 60 unsigned int reAuthMax; /* default 2 */ #define AUTH_PAE_DEFAULT_reAuthMax 2 /* counters */ Counter authEntersConnecting; Counter authEapLogoffsWhileConnecting; Counter authEntersAuthenticating; Counter authAuthSuccessesWhileAuthenticating; Counter authAuthTimeoutsWhileAuthenticating; Counter authAuthFailWhileAuthenticating; Counter authAuthEapStartsWhileAuthenticating; Counter authAuthEapLogoffWhileAuthenticating; Counter authAuthReauthsWhileAuthenticated; Counter authAuthEapStartsWhileAuthenticated; Counter authAuthEapLogoffWhileAuthenticated; /* Backend Authentication state machine */ enum { BE_AUTH_REQUEST, BE_AUTH_RESPONSE, BE_AUTH_SUCCESS, BE_AUTH_FAIL, BE_AUTH_TIMEOUT, BE_AUTH_IDLE, BE_AUTH_INITIALIZE, BE_AUTH_IGNORE } be_auth_state; /* constants */ unsigned int serverTimeout; /* default 30; 1..X */ #define BE_AUTH_DEFAULT_serverTimeout 30 /* counters */ Counter backendResponses; Counter backendAccessChallenges; Counter backendOtherRequestsToSupplicant; Counter backendAuthSuccesses; Counter backendAuthFails; /* Reauthentication Timer state machine */ enum { REAUTH_TIMER_INITIALIZE, REAUTH_TIMER_REAUTHENTICATE } reauth_timer_state; /* constants */ unsigned int reAuthPeriod; /* default 3600 s */ Boolean reAuthEnabled; /* Authenticator Key Transmit state machine */ enum { AUTH_KEY_TX_NO_KEY_TRANSMIT, AUTH_KEY_TX_KEY_TRANSMIT } auth_key_tx_state; /* Key Receive state machine */ enum { KEY_RX_NO_KEY_RECEIVE, KEY_RX_KEY_RECEIVE } key_rx_state; /* variables */ Boolean rxKey; /* Controlled Directions state machine */ enum { CTRL_DIR_FORCE_BOTH, CTRL_DIR_IN_OR_BOTH } ctrl_dir_state; /* variables */ ControlledDirection adminControlledDirections; ControlledDirection operControlledDirections; Boolean operEdge; /* Authenticator Statistics Table */ Counter dot1xAuthEapolFramesRx; Counter dot1xAuthEapolFramesTx; Counter dot1xAuthEapolStartFramesRx; Counter dot1xAuthEapolLogoffFramesRx; Counter dot1xAuthEapolRespIdFramesRx; Counter dot1xAuthEapolRespFramesRx; Counter dot1xAuthEapolReqIdFramesTx; Counter dot1xAuthEapolReqFramesTx; Counter dot1xAuthInvalidEapolFramesRx; Counter dot1xAuthEapLengthErrorFramesRx; Counter dot1xAuthLastEapolFrameVersion; /* Other variables - not defined in IEEE 802.1X */ u8 addr[ETH_ALEN]; /* Supplicant address */ int flags; /* EAPOL_SM_* */ /* EAPOL/AAA <-> EAP full authenticator interface */ struct eap_eapol_interface *eap_if; int radius_identifier; /* TODO: check when the last messages can be released */ struct radius_msg *last_recv_radius; u8 last_eap_id; /* last used EAP Identifier */ u8 *identity; size_t identity_len; u8 eap_type_authsrv; /* EAP type of the last EAP packet from * Authentication server */ u8 eap_type_supp; /* EAP type of the last EAP packet from Supplicant */ struct radius_class_data radius_class; struct wpabuf *radius_cui; /* Chargeable-User-Identity */ /* Keys for encrypting and signing EAPOL-Key frames */ u8 *eapol_key_sign; size_t eapol_key_sign_len; u8 *eapol_key_crypt; size_t eapol_key_crypt_len; struct eap_sm *eap; Boolean initializing; /* in process of initializing state machines */ Boolean changed; struct eapol_authenticator *eapol; void *sta; /* station context pointer to use in callbacks */ }; #endif /* EAPOL_AUTH_SM_I_H */ wpa-2.1/src/eapol_supp/000077500000000000000000000000001227414666700151205ustar00rootroot00000000000000wpa-2.1/src/eapol_supp/Makefile000066400000000000000000000001641227414666700165610ustar00rootroot00000000000000all: @echo Nothing to be made. clean: rm -f *~ *.o *.d *.gcno *.gcda *.gcov install: @echo Nothing to be made. wpa-2.1/src/eapol_supp/eapol_supp_sm.c000066400000000000000000001544101227414666700201370ustar00rootroot00000000000000/* * EAPOL supplicant state machines * Copyright (c) 2004-2012, 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 "state_machine.h" #include "wpabuf.h" #include "eloop.h" #include "crypto/crypto.h" #include "crypto/md5.h" #include "common/eapol_common.h" #include "eap_peer/eap.h" #include "eap_peer/eap_proxy.h" #include "eapol_supp_sm.h" #define STATE_MACHINE_DATA struct eapol_sm #define STATE_MACHINE_DEBUG_PREFIX "EAPOL" /* IEEE 802.1X-2004 - Supplicant - EAPOL state machines */ /** * struct eapol_sm - Internal data for EAPOL state machines */ struct eapol_sm { /* Timers */ unsigned int authWhile; unsigned int heldWhile; unsigned int startWhen; unsigned int idleWhile; /* for EAP state machine */ int timer_tick_enabled; /* Global variables */ Boolean eapFail; Boolean eapolEap; Boolean eapSuccess; Boolean initialize; Boolean keyDone; Boolean keyRun; PortControl portControl; Boolean portEnabled; PortStatus suppPortStatus; /* dot1xSuppControlledPortStatus */ Boolean portValid; Boolean suppAbort; Boolean suppFail; Boolean suppStart; Boolean suppSuccess; Boolean suppTimeout; /* Supplicant PAE state machine */ enum { SUPP_PAE_UNKNOWN = 0, SUPP_PAE_DISCONNECTED = 1, SUPP_PAE_LOGOFF = 2, SUPP_PAE_CONNECTING = 3, SUPP_PAE_AUTHENTICATING = 4, SUPP_PAE_AUTHENTICATED = 5, /* unused(6) */ SUPP_PAE_HELD = 7, SUPP_PAE_RESTART = 8, SUPP_PAE_S_FORCE_AUTH = 9, SUPP_PAE_S_FORCE_UNAUTH = 10 } SUPP_PAE_state; /* dot1xSuppPaeState */ /* Variables */ Boolean userLogoff; Boolean logoffSent; unsigned int startCount; Boolean eapRestart; PortControl sPortMode; /* Constants */ unsigned int heldPeriod; /* dot1xSuppHeldPeriod */ unsigned int startPeriod; /* dot1xSuppStartPeriod */ unsigned int maxStart; /* dot1xSuppMaxStart */ /* Key Receive state machine */ enum { KEY_RX_UNKNOWN = 0, KEY_RX_NO_KEY_RECEIVE, KEY_RX_KEY_RECEIVE } KEY_RX_state; /* Variables */ Boolean rxKey; /* Supplicant Backend state machine */ enum { SUPP_BE_UNKNOWN = 0, SUPP_BE_INITIALIZE = 1, SUPP_BE_IDLE = 2, SUPP_BE_REQUEST = 3, SUPP_BE_RECEIVE = 4, SUPP_BE_RESPONSE = 5, SUPP_BE_FAIL = 6, SUPP_BE_TIMEOUT = 7, SUPP_BE_SUCCESS = 8 } SUPP_BE_state; /* dot1xSuppBackendPaeState */ /* Variables */ Boolean eapNoResp; Boolean eapReq; Boolean eapResp; /* Constants */ unsigned int authPeriod; /* dot1xSuppAuthPeriod */ /* Statistics */ unsigned int dot1xSuppEapolFramesRx; unsigned int dot1xSuppEapolFramesTx; unsigned int dot1xSuppEapolStartFramesTx; unsigned int dot1xSuppEapolLogoffFramesTx; unsigned int dot1xSuppEapolRespFramesTx; unsigned int dot1xSuppEapolReqIdFramesRx; unsigned int dot1xSuppEapolReqFramesRx; unsigned int dot1xSuppInvalidEapolFramesRx; unsigned int dot1xSuppEapLengthErrorFramesRx; unsigned int dot1xSuppLastEapolFrameVersion; unsigned char dot1xSuppLastEapolFrameSource[6]; /* Miscellaneous variables (not defined in IEEE 802.1X-2004) */ Boolean changed; struct eap_sm *eap; struct eap_peer_config *config; Boolean initial_req; u8 *last_rx_key; size_t last_rx_key_len; struct wpabuf *eapReqData; /* for EAP */ Boolean altAccept; /* for EAP */ Boolean altReject; /* for EAP */ Boolean replay_counter_valid; u8 last_replay_counter[16]; struct eapol_config conf; struct eapol_ctx *ctx; enum { EAPOL_CB_IN_PROGRESS = 0, EAPOL_CB_SUCCESS, EAPOL_CB_FAILURE } cb_status; Boolean cached_pmk; Boolean unicast_key_received, broadcast_key_received; Boolean force_authorized_update; #ifdef CONFIG_EAP_PROXY Boolean use_eap_proxy; struct eap_proxy_sm *eap_proxy; #endif /* CONFIG_EAP_PROXY */ }; static void eapol_sm_txLogoff(struct eapol_sm *sm); static void eapol_sm_txStart(struct eapol_sm *sm); static void eapol_sm_processKey(struct eapol_sm *sm); static void eapol_sm_getSuppRsp(struct eapol_sm *sm); static void eapol_sm_txSuppRsp(struct eapol_sm *sm); static void eapol_sm_abortSupp(struct eapol_sm *sm); static void eapol_sm_abort_cached(struct eapol_sm *sm); static void eapol_sm_step_timeout(void *eloop_ctx, void *timeout_ctx); static void eapol_sm_set_port_authorized(struct eapol_sm *sm); static void eapol_sm_set_port_unauthorized(struct eapol_sm *sm); /* Port Timers state machine - implemented as a function that will be called * once a second as a registered event loop timeout */ static void eapol_port_timers_tick(void *eloop_ctx, void *timeout_ctx) { struct eapol_sm *sm = timeout_ctx; if (sm->authWhile > 0) { sm->authWhile--; if (sm->authWhile == 0) wpa_printf(MSG_DEBUG, "EAPOL: authWhile --> 0"); } if (sm->heldWhile > 0) { sm->heldWhile--; if (sm->heldWhile == 0) wpa_printf(MSG_DEBUG, "EAPOL: heldWhile --> 0"); } if (sm->startWhen > 0) { sm->startWhen--; if (sm->startWhen == 0) wpa_printf(MSG_DEBUG, "EAPOL: startWhen --> 0"); } if (sm->idleWhile > 0) { sm->idleWhile--; if (sm->idleWhile == 0) wpa_printf(MSG_DEBUG, "EAPOL: idleWhile --> 0"); } if (sm->authWhile | sm->heldWhile | sm->startWhen | sm->idleWhile) { eloop_register_timeout(1, 0, eapol_port_timers_tick, eloop_ctx, sm); } else { wpa_printf(MSG_DEBUG, "EAPOL: disable timer tick"); sm->timer_tick_enabled = 0; } eapol_sm_step(sm); } static void eapol_enable_timer_tick(struct eapol_sm *sm) { if (sm->timer_tick_enabled) return; wpa_printf(MSG_DEBUG, "EAPOL: enable timer tick"); sm->timer_tick_enabled = 1; eloop_cancel_timeout(eapol_port_timers_tick, NULL, sm); eloop_register_timeout(1, 0, eapol_port_timers_tick, NULL, sm); } SM_STATE(SUPP_PAE, LOGOFF) { SM_ENTRY(SUPP_PAE, LOGOFF); eapol_sm_txLogoff(sm); sm->logoffSent = TRUE; eapol_sm_set_port_unauthorized(sm); } SM_STATE(SUPP_PAE, DISCONNECTED) { SM_ENTRY(SUPP_PAE, DISCONNECTED); sm->sPortMode = Auto; sm->startCount = 0; sm->logoffSent = FALSE; eapol_sm_set_port_unauthorized(sm); sm->suppAbort = TRUE; sm->unicast_key_received = FALSE; sm->broadcast_key_received = FALSE; /* * IEEE Std 802.1X-2004 does not clear heldWhile here, but doing so * allows the timer tick to be stopped more quickly when the port is * not enabled. Since this variable is used only within HELD state, * clearing it on initialization does not change actual state machine * behavior. */ sm->heldWhile = 0; } SM_STATE(SUPP_PAE, CONNECTING) { int send_start = sm->SUPP_PAE_state == SUPP_PAE_CONNECTING; SM_ENTRY(SUPP_PAE, CONNECTING); if (send_start) { sm->startWhen = sm->startPeriod; sm->startCount++; } else { /* * Do not send EAPOL-Start immediately since in most cases, * Authenticator is going to start authentication immediately * after association and an extra EAPOL-Start is just going to * delay authentication. Use a short timeout to send the first * EAPOL-Start if Authenticator does not start authentication. */ #ifdef CONFIG_WPS /* Reduce latency on starting WPS negotiation. */ sm->startWhen = 1; #else /* CONFIG_WPS */ sm->startWhen = 3; #endif /* CONFIG_WPS */ } eapol_enable_timer_tick(sm); sm->eapolEap = FALSE; if (send_start) eapol_sm_txStart(sm); } SM_STATE(SUPP_PAE, AUTHENTICATING) { SM_ENTRY(SUPP_PAE, AUTHENTICATING); sm->startCount = 0; sm->suppSuccess = FALSE; sm->suppFail = FALSE; sm->suppTimeout = FALSE; sm->keyRun = FALSE; sm->keyDone = FALSE; sm->suppStart = TRUE; } SM_STATE(SUPP_PAE, HELD) { SM_ENTRY(SUPP_PAE, HELD); sm->heldWhile = sm->heldPeriod; eapol_enable_timer_tick(sm); eapol_sm_set_port_unauthorized(sm); sm->cb_status = EAPOL_CB_FAILURE; } SM_STATE(SUPP_PAE, AUTHENTICATED) { SM_ENTRY(SUPP_PAE, AUTHENTICATED); eapol_sm_set_port_authorized(sm); sm->cb_status = EAPOL_CB_SUCCESS; } SM_STATE(SUPP_PAE, RESTART) { SM_ENTRY(SUPP_PAE, RESTART); sm->eapRestart = TRUE; } SM_STATE(SUPP_PAE, S_FORCE_AUTH) { SM_ENTRY(SUPP_PAE, S_FORCE_AUTH); eapol_sm_set_port_authorized(sm); sm->sPortMode = ForceAuthorized; } SM_STATE(SUPP_PAE, S_FORCE_UNAUTH) { SM_ENTRY(SUPP_PAE, S_FORCE_UNAUTH); eapol_sm_set_port_unauthorized(sm); sm->sPortMode = ForceUnauthorized; eapol_sm_txLogoff(sm); } SM_STEP(SUPP_PAE) { if ((sm->userLogoff && !sm->logoffSent) && !(sm->initialize || !sm->portEnabled)) SM_ENTER_GLOBAL(SUPP_PAE, LOGOFF); else if (((sm->portControl == Auto) && (sm->sPortMode != sm->portControl)) || sm->initialize || !sm->portEnabled) SM_ENTER_GLOBAL(SUPP_PAE, DISCONNECTED); else if ((sm->portControl == ForceAuthorized) && (sm->sPortMode != sm->portControl) && !(sm->initialize || !sm->portEnabled)) SM_ENTER_GLOBAL(SUPP_PAE, S_FORCE_AUTH); else if ((sm->portControl == ForceUnauthorized) && (sm->sPortMode != sm->portControl) && !(sm->initialize || !sm->portEnabled)) SM_ENTER_GLOBAL(SUPP_PAE, S_FORCE_UNAUTH); else switch (sm->SUPP_PAE_state) { case SUPP_PAE_UNKNOWN: break; case SUPP_PAE_LOGOFF: if (!sm->userLogoff) SM_ENTER(SUPP_PAE, DISCONNECTED); break; case SUPP_PAE_DISCONNECTED: SM_ENTER(SUPP_PAE, CONNECTING); break; case SUPP_PAE_CONNECTING: if (sm->startWhen == 0 && sm->startCount < sm->maxStart) SM_ENTER(SUPP_PAE, CONNECTING); else if (sm->startWhen == 0 && sm->startCount >= sm->maxStart && sm->portValid) SM_ENTER(SUPP_PAE, AUTHENTICATED); else if (sm->eapSuccess || sm->eapFail) SM_ENTER(SUPP_PAE, AUTHENTICATING); else if (sm->eapolEap) SM_ENTER(SUPP_PAE, RESTART); else if (sm->startWhen == 0 && sm->startCount >= sm->maxStart && !sm->portValid) SM_ENTER(SUPP_PAE, HELD); break; case SUPP_PAE_AUTHENTICATING: if (sm->eapSuccess && !sm->portValid && sm->conf.accept_802_1x_keys && sm->conf.required_keys == 0) { wpa_printf(MSG_DEBUG, "EAPOL: IEEE 802.1X for " "plaintext connection; no EAPOL-Key frames " "required"); sm->portValid = TRUE; if (sm->ctx->eapol_done_cb) sm->ctx->eapol_done_cb(sm->ctx->ctx); } if (sm->eapSuccess && sm->portValid) SM_ENTER(SUPP_PAE, AUTHENTICATED); else if (sm->eapFail || (sm->keyDone && !sm->portValid)) SM_ENTER(SUPP_PAE, HELD); else if (sm->suppTimeout) SM_ENTER(SUPP_PAE, CONNECTING); break; case SUPP_PAE_HELD: if (sm->heldWhile == 0) SM_ENTER(SUPP_PAE, CONNECTING); else if (sm->eapolEap) SM_ENTER(SUPP_PAE, RESTART); break; case SUPP_PAE_AUTHENTICATED: if (sm->eapolEap && sm->portValid) SM_ENTER(SUPP_PAE, RESTART); else if (!sm->portValid) SM_ENTER(SUPP_PAE, DISCONNECTED); break; case SUPP_PAE_RESTART: if (!sm->eapRestart) SM_ENTER(SUPP_PAE, AUTHENTICATING); break; case SUPP_PAE_S_FORCE_AUTH: break; case SUPP_PAE_S_FORCE_UNAUTH: break; } } SM_STATE(KEY_RX, NO_KEY_RECEIVE) { SM_ENTRY(KEY_RX, NO_KEY_RECEIVE); } SM_STATE(KEY_RX, KEY_RECEIVE) { SM_ENTRY(KEY_RX, KEY_RECEIVE); eapol_sm_processKey(sm); sm->rxKey = FALSE; } SM_STEP(KEY_RX) { if (sm->initialize || !sm->portEnabled) SM_ENTER_GLOBAL(KEY_RX, NO_KEY_RECEIVE); switch (sm->KEY_RX_state) { case KEY_RX_UNKNOWN: break; case KEY_RX_NO_KEY_RECEIVE: if (sm->rxKey) SM_ENTER(KEY_RX, KEY_RECEIVE); break; case KEY_RX_KEY_RECEIVE: if (sm->rxKey) SM_ENTER(KEY_RX, KEY_RECEIVE); break; } } SM_STATE(SUPP_BE, REQUEST) { SM_ENTRY(SUPP_BE, REQUEST); sm->authWhile = 0; sm->eapReq = TRUE; eapol_sm_getSuppRsp(sm); } SM_STATE(SUPP_BE, RESPONSE) { SM_ENTRY(SUPP_BE, RESPONSE); eapol_sm_txSuppRsp(sm); sm->eapResp = FALSE; } SM_STATE(SUPP_BE, SUCCESS) { SM_ENTRY(SUPP_BE, SUCCESS); sm->keyRun = TRUE; sm->suppSuccess = TRUE; #ifdef CONFIG_EAP_PROXY if (sm->use_eap_proxy) { if (eap_proxy_key_available(sm->eap_proxy)) { /* New key received - clear IEEE 802.1X EAPOL-Key replay * counter */ sm->replay_counter_valid = FALSE; } return; } #endif /* CONFIG_EAP_PROXY */ if (eap_key_available(sm->eap)) { /* New key received - clear IEEE 802.1X EAPOL-Key replay * counter */ sm->replay_counter_valid = FALSE; } } SM_STATE(SUPP_BE, FAIL) { SM_ENTRY(SUPP_BE, FAIL); sm->suppFail = TRUE; } SM_STATE(SUPP_BE, TIMEOUT) { SM_ENTRY(SUPP_BE, TIMEOUT); sm->suppTimeout = TRUE; } SM_STATE(SUPP_BE, IDLE) { SM_ENTRY(SUPP_BE, IDLE); sm->suppStart = FALSE; sm->initial_req = TRUE; } SM_STATE(SUPP_BE, INITIALIZE) { SM_ENTRY(SUPP_BE, INITIALIZE); eapol_sm_abortSupp(sm); sm->suppAbort = FALSE; /* * IEEE Std 802.1X-2004 does not clear authWhile here, but doing so * allows the timer tick to be stopped more quickly when the port is * not enabled. Since this variable is used only within RECEIVE state, * clearing it on initialization does not change actual state machine * behavior. */ sm->authWhile = 0; } SM_STATE(SUPP_BE, RECEIVE) { SM_ENTRY(SUPP_BE, RECEIVE); sm->authWhile = sm->authPeriod; eapol_enable_timer_tick(sm); sm->eapolEap = FALSE; sm->eapNoResp = FALSE; sm->initial_req = FALSE; } SM_STEP(SUPP_BE) { if (sm->initialize || sm->suppAbort) SM_ENTER_GLOBAL(SUPP_BE, INITIALIZE); else switch (sm->SUPP_BE_state) { case SUPP_BE_UNKNOWN: break; case SUPP_BE_REQUEST: /* * IEEE Std 802.1X-2004 has transitions from REQUEST to FAIL * and SUCCESS based on eapFail and eapSuccess, respectively. * However, IEEE Std 802.1X-2004 is also specifying that * eapNoResp should be set in conjunction with eapSuccess and * eapFail which would mean that more than one of the * transitions here would be activated at the same time. * Skipping RESPONSE and/or RECEIVE states in these cases can * cause problems and the direct transitions to do not seem * correct. Because of this, the conditions for these * transitions are verified only after eapNoResp. They are * unlikely to be used since eapNoResp should always be set if * either of eapSuccess or eapFail is set. */ if (sm->eapResp && sm->eapNoResp) { wpa_printf(MSG_DEBUG, "EAPOL: SUPP_BE REQUEST: both " "eapResp and eapNoResp set?!"); } if (sm->eapResp) SM_ENTER(SUPP_BE, RESPONSE); else if (sm->eapNoResp) SM_ENTER(SUPP_BE, RECEIVE); else if (sm->eapFail) SM_ENTER(SUPP_BE, FAIL); else if (sm->eapSuccess) SM_ENTER(SUPP_BE, SUCCESS); break; case SUPP_BE_RESPONSE: SM_ENTER(SUPP_BE, RECEIVE); break; case SUPP_BE_SUCCESS: SM_ENTER(SUPP_BE, IDLE); break; case SUPP_BE_FAIL: SM_ENTER(SUPP_BE, IDLE); break; case SUPP_BE_TIMEOUT: SM_ENTER(SUPP_BE, IDLE); break; case SUPP_BE_IDLE: if (sm->eapFail && sm->suppStart) SM_ENTER(SUPP_BE, FAIL); else if (sm->eapolEap && sm->suppStart) SM_ENTER(SUPP_BE, REQUEST); else if (sm->eapSuccess && sm->suppStart) SM_ENTER(SUPP_BE, SUCCESS); break; case SUPP_BE_INITIALIZE: SM_ENTER(SUPP_BE, IDLE); break; case SUPP_BE_RECEIVE: if (sm->eapolEap) SM_ENTER(SUPP_BE, REQUEST); else if (sm->eapFail) SM_ENTER(SUPP_BE, FAIL); else if (sm->authWhile == 0) SM_ENTER(SUPP_BE, TIMEOUT); else if (sm->eapSuccess) SM_ENTER(SUPP_BE, SUCCESS); break; } } static void eapol_sm_txLogoff(struct eapol_sm *sm) { wpa_printf(MSG_DEBUG, "EAPOL: txLogoff"); sm->ctx->eapol_send(sm->ctx->eapol_send_ctx, IEEE802_1X_TYPE_EAPOL_LOGOFF, (u8 *) "", 0); sm->dot1xSuppEapolLogoffFramesTx++; sm->dot1xSuppEapolFramesTx++; } static void eapol_sm_txStart(struct eapol_sm *sm) { wpa_printf(MSG_DEBUG, "EAPOL: txStart"); sm->ctx->eapol_send(sm->ctx->eapol_send_ctx, IEEE802_1X_TYPE_EAPOL_START, (u8 *) "", 0); sm->dot1xSuppEapolStartFramesTx++; sm->dot1xSuppEapolFramesTx++; } #define IEEE8021X_ENCR_KEY_LEN 32 #define IEEE8021X_SIGN_KEY_LEN 32 struct eap_key_data { u8 encr_key[IEEE8021X_ENCR_KEY_LEN]; u8 sign_key[IEEE8021X_SIGN_KEY_LEN]; }; static void eapol_sm_processKey(struct eapol_sm *sm) { #ifndef CONFIG_FIPS struct ieee802_1x_hdr *hdr; struct ieee802_1x_eapol_key *key; struct eap_key_data keydata; u8 orig_key_sign[IEEE8021X_KEY_SIGN_LEN], datakey[32]; u8 ekey[IEEE8021X_KEY_IV_LEN + IEEE8021X_ENCR_KEY_LEN]; int key_len, res, sign_key_len, encr_key_len; u16 rx_key_length; size_t plen; wpa_printf(MSG_DEBUG, "EAPOL: processKey"); if (sm->last_rx_key == NULL) return; if (!sm->conf.accept_802_1x_keys) { wpa_printf(MSG_WARNING, "EAPOL: Received IEEE 802.1X EAPOL-Key" " even though this was not accepted - " "ignoring this packet"); return; } if (sm->last_rx_key_len < sizeof(*hdr) + sizeof(*key)) return; hdr = (struct ieee802_1x_hdr *) sm->last_rx_key; key = (struct ieee802_1x_eapol_key *) (hdr + 1); plen = be_to_host16(hdr->length); if (sizeof(*hdr) + plen > sm->last_rx_key_len || plen < sizeof(*key)) { wpa_printf(MSG_WARNING, "EAPOL: Too short EAPOL-Key frame"); return; } rx_key_length = WPA_GET_BE16(key->key_length); wpa_printf(MSG_DEBUG, "EAPOL: RX IEEE 802.1X ver=%d type=%d len=%d " "EAPOL-Key: type=%d key_length=%d key_index=0x%x", hdr->version, hdr->type, be_to_host16(hdr->length), key->type, rx_key_length, key->key_index); eapol_sm_notify_lower_layer_success(sm, 1); sign_key_len = IEEE8021X_SIGN_KEY_LEN; encr_key_len = IEEE8021X_ENCR_KEY_LEN; res = eapol_sm_get_key(sm, (u8 *) &keydata, sizeof(keydata)); if (res < 0) { wpa_printf(MSG_DEBUG, "EAPOL: Could not get master key for " "decrypting EAPOL-Key keys"); return; } if (res == 16) { /* LEAP derives only 16 bytes of keying material. */ res = eapol_sm_get_key(sm, (u8 *) &keydata, 16); if (res) { wpa_printf(MSG_DEBUG, "EAPOL: Could not get LEAP " "master key for decrypting EAPOL-Key keys"); return; } sign_key_len = 16; encr_key_len = 16; os_memcpy(keydata.sign_key, keydata.encr_key, 16); } else if (res) { wpa_printf(MSG_DEBUG, "EAPOL: Could not get enough master key " "data for decrypting EAPOL-Key keys (res=%d)", res); return; } /* The key replay_counter must increase when same master key */ if (sm->replay_counter_valid && os_memcmp(sm->last_replay_counter, key->replay_counter, IEEE8021X_REPLAY_COUNTER_LEN) >= 0) { wpa_printf(MSG_WARNING, "EAPOL: EAPOL-Key replay counter did " "not increase - ignoring key"); wpa_hexdump(MSG_DEBUG, "EAPOL: last replay counter", sm->last_replay_counter, IEEE8021X_REPLAY_COUNTER_LEN); wpa_hexdump(MSG_DEBUG, "EAPOL: received replay counter", key->replay_counter, IEEE8021X_REPLAY_COUNTER_LEN); return; } /* Verify key signature (HMAC-MD5) */ os_memcpy(orig_key_sign, key->key_signature, IEEE8021X_KEY_SIGN_LEN); os_memset(key->key_signature, 0, IEEE8021X_KEY_SIGN_LEN); hmac_md5(keydata.sign_key, sign_key_len, sm->last_rx_key, sizeof(*hdr) + be_to_host16(hdr->length), key->key_signature); if (os_memcmp(orig_key_sign, key->key_signature, IEEE8021X_KEY_SIGN_LEN) != 0) { wpa_printf(MSG_DEBUG, "EAPOL: Invalid key signature in " "EAPOL-Key packet"); os_memcpy(key->key_signature, orig_key_sign, IEEE8021X_KEY_SIGN_LEN); return; } wpa_printf(MSG_DEBUG, "EAPOL: EAPOL-Key key signature verified"); key_len = plen - sizeof(*key); if (key_len > 32 || rx_key_length > 32) { wpa_printf(MSG_WARNING, "EAPOL: Too long key data length %d", key_len ? key_len : rx_key_length); return; } if (key_len == rx_key_length) { os_memcpy(ekey, key->key_iv, IEEE8021X_KEY_IV_LEN); os_memcpy(ekey + IEEE8021X_KEY_IV_LEN, keydata.encr_key, encr_key_len); os_memcpy(datakey, key + 1, key_len); rc4_skip(ekey, IEEE8021X_KEY_IV_LEN + encr_key_len, 0, datakey, key_len); wpa_hexdump_key(MSG_DEBUG, "EAPOL: Decrypted(RC4) key", datakey, key_len); } else if (key_len == 0) { /* * IEEE 802.1X-2004 specifies that least significant Key Length * octets from MS-MPPE-Send-Key are used as the key if the key * data is not present. This seems to be meaning the beginning * of the MS-MPPE-Send-Key. In addition, MS-MPPE-Send-Key in * Supplicant corresponds to MS-MPPE-Recv-Key in Authenticator. * Anyway, taking the beginning of the keying material from EAP * seems to interoperate with Authenticators. */ key_len = rx_key_length; os_memcpy(datakey, keydata.encr_key, key_len); wpa_hexdump_key(MSG_DEBUG, "EAPOL: using part of EAP keying " "material data encryption key", datakey, key_len); } else { wpa_printf(MSG_DEBUG, "EAPOL: Invalid key data length %d " "(key_length=%d)", key_len, rx_key_length); return; } sm->replay_counter_valid = TRUE; os_memcpy(sm->last_replay_counter, key->replay_counter, IEEE8021X_REPLAY_COUNTER_LEN); wpa_printf(MSG_DEBUG, "EAPOL: Setting dynamic WEP key: %s keyidx %d " "len %d", key->key_index & IEEE8021X_KEY_INDEX_FLAG ? "unicast" : "broadcast", key->key_index & IEEE8021X_KEY_INDEX_MASK, key_len); if (sm->ctx->set_wep_key && sm->ctx->set_wep_key(sm->ctx->ctx, key->key_index & IEEE8021X_KEY_INDEX_FLAG, key->key_index & IEEE8021X_KEY_INDEX_MASK, datakey, key_len) < 0) { wpa_printf(MSG_WARNING, "EAPOL: Failed to set WEP key to the " " driver."); } else { if (key->key_index & IEEE8021X_KEY_INDEX_FLAG) sm->unicast_key_received = TRUE; else sm->broadcast_key_received = TRUE; if ((sm->unicast_key_received || !(sm->conf.required_keys & EAPOL_REQUIRE_KEY_UNICAST)) && (sm->broadcast_key_received || !(sm->conf.required_keys & EAPOL_REQUIRE_KEY_BROADCAST))) { wpa_printf(MSG_DEBUG, "EAPOL: all required EAPOL-Key " "frames received"); sm->portValid = TRUE; if (sm->ctx->eapol_done_cb) sm->ctx->eapol_done_cb(sm->ctx->ctx); } } #endif /* CONFIG_FIPS */ } static void eapol_sm_getSuppRsp(struct eapol_sm *sm) { wpa_printf(MSG_DEBUG, "EAPOL: getSuppRsp"); /* EAP layer processing; no special code is needed, since Supplicant * Backend state machine is waiting for eapNoResp or eapResp to be set * and these are only set in the EAP state machine when the processing * has finished. */ } static void eapol_sm_txSuppRsp(struct eapol_sm *sm) { struct wpabuf *resp; wpa_printf(MSG_DEBUG, "EAPOL: txSuppRsp"); #ifdef CONFIG_EAP_PROXY if (sm->use_eap_proxy) { /* Get EAP Response from EAP Proxy */ resp = eap_proxy_get_eapRespData(sm->eap_proxy); if (resp == NULL) { wpa_printf(MSG_WARNING, "EAPOL: txSuppRsp - EAP Proxy " "response data not available"); return; } } else #endif /* CONFIG_EAP_PROXY */ resp = eap_get_eapRespData(sm->eap); if (resp == NULL) { wpa_printf(MSG_WARNING, "EAPOL: txSuppRsp - EAP response data " "not available"); return; } /* Send EAP-Packet from the EAP layer to the Authenticator */ sm->ctx->eapol_send(sm->ctx->eapol_send_ctx, IEEE802_1X_TYPE_EAP_PACKET, wpabuf_head(resp), wpabuf_len(resp)); /* eapRespData is not used anymore, so free it here */ wpabuf_free(resp); if (sm->initial_req) sm->dot1xSuppEapolReqIdFramesRx++; else sm->dot1xSuppEapolReqFramesRx++; sm->dot1xSuppEapolRespFramesTx++; sm->dot1xSuppEapolFramesTx++; } static void eapol_sm_abortSupp(struct eapol_sm *sm) { /* release system resources that may have been allocated for the * authentication session */ os_free(sm->last_rx_key); sm->last_rx_key = NULL; wpabuf_free(sm->eapReqData); sm->eapReqData = NULL; eap_sm_abort(sm->eap); } static void eapol_sm_step_timeout(void *eloop_ctx, void *timeout_ctx) { eapol_sm_step(timeout_ctx); } static void eapol_sm_set_port_authorized(struct eapol_sm *sm) { int cb; cb = sm->suppPortStatus != Authorized || sm->force_authorized_update; sm->force_authorized_update = FALSE; sm->suppPortStatus = Authorized; if (cb && sm->ctx->port_cb) sm->ctx->port_cb(sm->ctx->ctx, 1); } static void eapol_sm_set_port_unauthorized(struct eapol_sm *sm) { int cb; cb = sm->suppPortStatus != Unauthorized || sm->force_authorized_update; sm->force_authorized_update = FALSE; sm->suppPortStatus = Unauthorized; if (cb && sm->ctx->port_cb) sm->ctx->port_cb(sm->ctx->ctx, 0); } /** * eapol_sm_step - EAPOL state machine step function * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() * * This function is called to notify the state machine about changed external * variables. It will step through the EAPOL state machines in loop to process * all triggered state changes. */ void eapol_sm_step(struct eapol_sm *sm) { int i; /* In theory, it should be ok to run this in loop until !changed. * However, it is better to use a limit on number of iterations to * allow events (e.g., SIGTERM) to stop the program cleanly if the * state machine were to generate a busy loop. */ for (i = 0; i < 100; i++) { sm->changed = FALSE; SM_STEP_RUN(SUPP_PAE); SM_STEP_RUN(KEY_RX); SM_STEP_RUN(SUPP_BE); #ifdef CONFIG_EAP_PROXY if (sm->use_eap_proxy) { /* Drive the EAP proxy state machine */ if (eap_proxy_sm_step(sm->eap_proxy, sm->eap)) sm->changed = TRUE; } else #endif /* CONFIG_EAP_PROXY */ if (eap_peer_sm_step(sm->eap)) sm->changed = TRUE; if (!sm->changed) break; } if (sm->changed) { /* restart EAPOL state machine step from timeout call in order * to allow other events to be processed. */ eloop_cancel_timeout(eapol_sm_step_timeout, NULL, sm); eloop_register_timeout(0, 0, eapol_sm_step_timeout, NULL, sm); } if (sm->ctx->cb && sm->cb_status != EAPOL_CB_IN_PROGRESS) { enum eapol_supp_result result; if (sm->cb_status == EAPOL_CB_SUCCESS) result = EAPOL_SUPP_RESULT_SUCCESS; else if (eap_peer_was_failure_expected(sm->eap)) result = EAPOL_SUPP_RESULT_EXPECTED_FAILURE; else result = EAPOL_SUPP_RESULT_FAILURE; sm->cb_status = EAPOL_CB_IN_PROGRESS; sm->ctx->cb(sm, result, sm->ctx->cb_ctx); } } #ifdef CONFIG_CTRL_IFACE static const char *eapol_supp_pae_state(int state) { switch (state) { case SUPP_PAE_LOGOFF: return "LOGOFF"; case SUPP_PAE_DISCONNECTED: return "DISCONNECTED"; case SUPP_PAE_CONNECTING: return "CONNECTING"; case SUPP_PAE_AUTHENTICATING: return "AUTHENTICATING"; case SUPP_PAE_HELD: return "HELD"; case SUPP_PAE_AUTHENTICATED: return "AUTHENTICATED"; case SUPP_PAE_RESTART: return "RESTART"; default: return "UNKNOWN"; } } static const char *eapol_supp_be_state(int state) { switch (state) { case SUPP_BE_REQUEST: return "REQUEST"; case SUPP_BE_RESPONSE: return "RESPONSE"; case SUPP_BE_SUCCESS: return "SUCCESS"; case SUPP_BE_FAIL: return "FAIL"; case SUPP_BE_TIMEOUT: return "TIMEOUT"; case SUPP_BE_IDLE: return "IDLE"; case SUPP_BE_INITIALIZE: return "INITIALIZE"; case SUPP_BE_RECEIVE: return "RECEIVE"; default: return "UNKNOWN"; } } static const char * eapol_port_status(PortStatus status) { if (status == Authorized) return "Authorized"; else return "Unauthorized"; } #endif /* CONFIG_CTRL_IFACE */ #if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG) static const char * eapol_port_control(PortControl ctrl) { switch (ctrl) { case Auto: return "Auto"; case ForceUnauthorized: return "ForceUnauthorized"; case ForceAuthorized: return "ForceAuthorized"; default: return "Unknown"; } } #endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ /** * eapol_sm_configure - Set EAPOL variables * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() * @heldPeriod: dot1xSuppHeldPeriod * @authPeriod: dot1xSuppAuthPeriod * @startPeriod: dot1xSuppStartPeriod * @maxStart: dot1xSuppMaxStart * * Set configurable EAPOL state machine variables. Each variable can be set to * the given value or ignored if set to -1 (to set only some of the variables). */ void eapol_sm_configure(struct eapol_sm *sm, int heldPeriod, int authPeriod, int startPeriod, int maxStart) { if (sm == NULL) return; if (heldPeriod >= 0) sm->heldPeriod = heldPeriod; if (authPeriod >= 0) sm->authPeriod = authPeriod; if (startPeriod >= 0) sm->startPeriod = startPeriod; if (maxStart >= 0) sm->maxStart = maxStart; } /** * eapol_sm_get_method_name - Get EAPOL method name * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() * Returns: Static string containing name of current eap method or NULL */ const char * eapol_sm_get_method_name(struct eapol_sm *sm) { if (sm->SUPP_PAE_state != SUPP_PAE_AUTHENTICATED || sm->suppPortStatus != Authorized) return NULL; return eap_sm_get_method_name(sm->eap); } #ifdef CONFIG_CTRL_IFACE /** * eapol_sm_get_status - Get EAPOL state machine status * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() * @buf: Buffer for status information * @buflen: Maximum buffer length * @verbose: Whether to include verbose status information * Returns: Number of bytes written to buf. * * Query EAPOL state machine for status information. This function fills in a * text area with current status information from the EAPOL state machine. If * the buffer (buf) is not large enough, status information will be truncated * to fit the buffer. */ int eapol_sm_get_status(struct eapol_sm *sm, char *buf, size_t buflen, int verbose) { int len, ret; if (sm == NULL) return 0; len = os_snprintf(buf, buflen, "Supplicant PAE state=%s\n" "suppPortStatus=%s\n", eapol_supp_pae_state(sm->SUPP_PAE_state), eapol_port_status(sm->suppPortStatus)); if (len < 0 || (size_t) len >= buflen) return 0; if (verbose) { ret = os_snprintf(buf + len, buflen - len, "heldPeriod=%u\n" "authPeriod=%u\n" "startPeriod=%u\n" "maxStart=%u\n" "portControl=%s\n" "Supplicant Backend state=%s\n", sm->heldPeriod, sm->authPeriod, sm->startPeriod, sm->maxStart, eapol_port_control(sm->portControl), eapol_supp_be_state(sm->SUPP_BE_state)); if (ret < 0 || (size_t) ret >= buflen - len) return len; len += ret; } #ifdef CONFIG_EAP_PROXY if (sm->use_eap_proxy) len += eap_proxy_sm_get_status(sm->eap_proxy, buf + len, buflen - len, verbose); else #endif /* CONFIG_EAP_PROXY */ len += eap_sm_get_status(sm->eap, buf + len, buflen - len, verbose); return len; } /** * eapol_sm_get_mib - Get EAPOL state machine MIBs * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() * @buf: Buffer for MIB information * @buflen: Maximum buffer length * Returns: Number of bytes written to buf. * * Query EAPOL state machine for MIB information. This function fills in a * text area with current MIB information from the EAPOL state machine. If * the buffer (buf) is not large enough, MIB information will be truncated to * fit the buffer. */ int eapol_sm_get_mib(struct eapol_sm *sm, char *buf, size_t buflen) { size_t len; int ret; if (sm == NULL) return 0; ret = os_snprintf(buf, buflen, "dot1xSuppPaeState=%d\n" "dot1xSuppHeldPeriod=%u\n" "dot1xSuppAuthPeriod=%u\n" "dot1xSuppStartPeriod=%u\n" "dot1xSuppMaxStart=%u\n" "dot1xSuppSuppControlledPortStatus=%s\n" "dot1xSuppBackendPaeState=%d\n", sm->SUPP_PAE_state, sm->heldPeriod, sm->authPeriod, sm->startPeriod, sm->maxStart, sm->suppPortStatus == Authorized ? "Authorized" : "Unauthorized", sm->SUPP_BE_state); if (ret < 0 || (size_t) ret >= buflen) return 0; len = ret; ret = os_snprintf(buf + len, buflen - len, "dot1xSuppEapolFramesRx=%u\n" "dot1xSuppEapolFramesTx=%u\n" "dot1xSuppEapolStartFramesTx=%u\n" "dot1xSuppEapolLogoffFramesTx=%u\n" "dot1xSuppEapolRespFramesTx=%u\n" "dot1xSuppEapolReqIdFramesRx=%u\n" "dot1xSuppEapolReqFramesRx=%u\n" "dot1xSuppInvalidEapolFramesRx=%u\n" "dot1xSuppEapLengthErrorFramesRx=%u\n" "dot1xSuppLastEapolFrameVersion=%u\n" "dot1xSuppLastEapolFrameSource=" MACSTR "\n", sm->dot1xSuppEapolFramesRx, sm->dot1xSuppEapolFramesTx, sm->dot1xSuppEapolStartFramesTx, sm->dot1xSuppEapolLogoffFramesTx, sm->dot1xSuppEapolRespFramesTx, sm->dot1xSuppEapolReqIdFramesRx, sm->dot1xSuppEapolReqFramesRx, sm->dot1xSuppInvalidEapolFramesRx, sm->dot1xSuppEapLengthErrorFramesRx, sm->dot1xSuppLastEapolFrameVersion, MAC2STR(sm->dot1xSuppLastEapolFrameSource)); if (ret < 0 || (size_t) ret >= buflen - len) return len; len += ret; return len; } #endif /* CONFIG_CTRL_IFACE */ /** * eapol_sm_rx_eapol - Process received EAPOL frames * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() * @src: Source MAC address of the EAPOL packet * @buf: Pointer to the beginning of the EAPOL data (EAPOL header) * @len: Length of the EAPOL frame * Returns: 1 = EAPOL frame processed, 0 = not for EAPOL state machine, * -1 failure */ int eapol_sm_rx_eapol(struct eapol_sm *sm, const u8 *src, const u8 *buf, size_t len) { const struct ieee802_1x_hdr *hdr; const struct ieee802_1x_eapol_key *key; int data_len; int res = 1; size_t plen; if (sm == NULL) return 0; sm->dot1xSuppEapolFramesRx++; if (len < sizeof(*hdr)) { sm->dot1xSuppInvalidEapolFramesRx++; return 0; } hdr = (const struct ieee802_1x_hdr *) buf; sm->dot1xSuppLastEapolFrameVersion = hdr->version; os_memcpy(sm->dot1xSuppLastEapolFrameSource, src, ETH_ALEN); if (hdr->version < EAPOL_VERSION) { /* TODO: backwards compatibility */ } plen = be_to_host16(hdr->length); if (plen > len - sizeof(*hdr)) { sm->dot1xSuppEapLengthErrorFramesRx++; return 0; } #ifdef CONFIG_WPS if (sm->conf.workaround && plen < len - sizeof(*hdr) && hdr->type == IEEE802_1X_TYPE_EAP_PACKET && len - sizeof(*hdr) > sizeof(struct eap_hdr)) { const struct eap_hdr *ehdr = (const struct eap_hdr *) (hdr + 1); u16 elen; elen = be_to_host16(ehdr->length); if (elen > plen && elen <= len - sizeof(*hdr)) { /* * Buffalo WHR-G125 Ver.1.47 seems to send EAP-WPS * packets with too short EAPOL header length field * (14 octets). This is fixed in firmware Ver.1.49. * As a workaround, fix the EAPOL header based on the * correct length in the EAP packet. */ wpa_printf(MSG_DEBUG, "EAPOL: Workaround - fix EAPOL " "payload length based on EAP header: " "%d -> %d", (int) plen, elen); plen = elen; } } #endif /* CONFIG_WPS */ data_len = plen + sizeof(*hdr); switch (hdr->type) { case IEEE802_1X_TYPE_EAP_PACKET: if (sm->conf.workaround) { /* * An AP has been reported to send out EAP message with * undocumented code 10 at some point near the * completion of EAP authentication. This can result in * issues with the unexpected EAP message triggering * restart of EAPOL authentication. Avoid this by * skipping the message without advancing the state * machine. */ const struct eap_hdr *ehdr = (const struct eap_hdr *) (hdr + 1); if (plen >= sizeof(*ehdr) && ehdr->code == 10) { wpa_printf(MSG_DEBUG, "EAPOL: Ignore EAP packet with unknown code 10"); break; } } if (sm->cached_pmk) { /* Trying to use PMKSA caching, but Authenticator did * not seem to have a matching entry. Need to restart * EAPOL state machines. */ eapol_sm_abort_cached(sm); } wpabuf_free(sm->eapReqData); sm->eapReqData = wpabuf_alloc_copy(hdr + 1, plen); if (sm->eapReqData) { wpa_printf(MSG_DEBUG, "EAPOL: Received EAP-Packet " "frame"); sm->eapolEap = TRUE; #ifdef CONFIG_EAP_PROXY if (sm->use_eap_proxy) { eap_proxy_packet_update( sm->eap_proxy, wpabuf_mhead_u8(sm->eapReqData), wpabuf_len(sm->eapReqData)); wpa_printf(MSG_DEBUG, "EAPOL: eap_proxy " "EAP Req updated"); } #endif /* CONFIG_EAP_PROXY */ eapol_sm_step(sm); } break; case IEEE802_1X_TYPE_EAPOL_KEY: if (plen < sizeof(*key)) { wpa_printf(MSG_DEBUG, "EAPOL: Too short EAPOL-Key " "frame received"); break; } key = (const struct ieee802_1x_eapol_key *) (hdr + 1); if (key->type == EAPOL_KEY_TYPE_WPA || key->type == EAPOL_KEY_TYPE_RSN) { /* WPA Supplicant takes care of this frame. */ wpa_printf(MSG_DEBUG, "EAPOL: Ignoring WPA EAPOL-Key " "frame in EAPOL state machines"); res = 0; break; } if (key->type != EAPOL_KEY_TYPE_RC4) { wpa_printf(MSG_DEBUG, "EAPOL: Ignored unknown " "EAPOL-Key type %d", key->type); break; } os_free(sm->last_rx_key); sm->last_rx_key = os_malloc(data_len); if (sm->last_rx_key) { wpa_printf(MSG_DEBUG, "EAPOL: Received EAPOL-Key " "frame"); os_memcpy(sm->last_rx_key, buf, data_len); sm->last_rx_key_len = data_len; sm->rxKey = TRUE; eapol_sm_step(sm); } break; default: wpa_printf(MSG_DEBUG, "EAPOL: Received unknown EAPOL type %d", hdr->type); sm->dot1xSuppInvalidEapolFramesRx++; break; } return res; } /** * eapol_sm_notify_tx_eapol_key - Notification about transmitted EAPOL packet * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() * * Notify EAPOL state machine about transmitted EAPOL packet from an external * component, e.g., WPA. This will update the statistics. */ void eapol_sm_notify_tx_eapol_key(struct eapol_sm *sm) { if (sm) sm->dot1xSuppEapolFramesTx++; } /** * eapol_sm_notify_portEnabled - Notification about portEnabled change * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() * @enabled: New portEnabled value * * Notify EAPOL state machine about new portEnabled value. */ void eapol_sm_notify_portEnabled(struct eapol_sm *sm, Boolean enabled) { if (sm == NULL) return; wpa_printf(MSG_DEBUG, "EAPOL: External notification - " "portEnabled=%d", enabled); if (sm->portEnabled != enabled) sm->force_authorized_update = TRUE; sm->portEnabled = enabled; eapol_sm_step(sm); } /** * eapol_sm_notify_portValid - Notification about portValid change * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() * @valid: New portValid value * * Notify EAPOL state machine about new portValid value. */ void eapol_sm_notify_portValid(struct eapol_sm *sm, Boolean valid) { if (sm == NULL) return; wpa_printf(MSG_DEBUG, "EAPOL: External notification - " "portValid=%d", valid); sm->portValid = valid; eapol_sm_step(sm); } /** * eapol_sm_notify_eap_success - Notification of external EAP success trigger * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() * @success: %TRUE = set success, %FALSE = clear success * * Notify the EAPOL state machine that external event has forced EAP state to * success (success = %TRUE). This can be cleared by setting success = %FALSE. * * This function is called to update EAP state when WPA-PSK key handshake has * been completed successfully since WPA-PSK does not use EAP state machine. */ void eapol_sm_notify_eap_success(struct eapol_sm *sm, Boolean success) { if (sm == NULL) return; wpa_printf(MSG_DEBUG, "EAPOL: External notification - " "EAP success=%d", success); sm->eapSuccess = success; sm->altAccept = success; if (success) eap_notify_success(sm->eap); eapol_sm_step(sm); } /** * eapol_sm_notify_eap_fail - Notification of external EAP failure trigger * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() * @fail: %TRUE = set failure, %FALSE = clear failure * * Notify EAPOL state machine that external event has forced EAP state to * failure (fail = %TRUE). This can be cleared by setting fail = %FALSE. */ void eapol_sm_notify_eap_fail(struct eapol_sm *sm, Boolean fail) { if (sm == NULL) return; wpa_printf(MSG_DEBUG, "EAPOL: External notification - " "EAP fail=%d", fail); sm->eapFail = fail; sm->altReject = fail; eapol_sm_step(sm); } /** * eapol_sm_notify_config - Notification of EAPOL configuration change * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() * @config: Pointer to current network EAP configuration * @conf: Pointer to EAPOL configuration data * * Notify EAPOL state machine that configuration has changed. config will be * stored as a backpointer to network configuration. This can be %NULL to clear * the stored pointed. conf will be copied to local EAPOL/EAP configuration * data. If conf is %NULL, this part of the configuration change will be * skipped. */ void eapol_sm_notify_config(struct eapol_sm *sm, struct eap_peer_config *config, const struct eapol_config *conf) { if (sm == NULL) return; sm->config = config; #ifdef CONFIG_EAP_PROXY sm->use_eap_proxy = eap_proxy_notify_config(sm->eap_proxy, config) > 0; #endif /* CONFIG_EAP_PROXY */ if (conf == NULL) return; sm->conf.accept_802_1x_keys = conf->accept_802_1x_keys; sm->conf.required_keys = conf->required_keys; sm->conf.fast_reauth = conf->fast_reauth; sm->conf.workaround = conf->workaround; #ifdef CONFIG_EAP_PROXY if (sm->use_eap_proxy) { /* Using EAP Proxy, so skip EAP state machine update */ return; } #endif /* CONFIG_EAP_PROXY */ if (sm->eap) { eap_set_fast_reauth(sm->eap, conf->fast_reauth); eap_set_workaround(sm->eap, conf->workaround); eap_set_force_disabled(sm->eap, conf->eap_disabled); eap_set_external_sim(sm->eap, conf->external_sim); } } /** * eapol_sm_get_key - Get master session key (MSK) from EAP * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() * @key: Pointer for key buffer * @len: Number of bytes to copy to key * Returns: 0 on success (len of key available), maximum available key len * (>0) if key is available but it is shorter than len, or -1 on failure. * * Fetch EAP keying material (MSK, eapKeyData) from EAP state machine. The key * is available only after a successful authentication. */ int eapol_sm_get_key(struct eapol_sm *sm, u8 *key, size_t len) { const u8 *eap_key; size_t eap_len; #ifdef CONFIG_EAP_PROXY if (sm->use_eap_proxy) { /* Get key from EAP proxy */ if (sm == NULL || !eap_proxy_key_available(sm->eap_proxy)) { wpa_printf(MSG_DEBUG, "EAPOL: EAP key not available"); return -1; } eap_key = eap_proxy_get_eapKeyData(sm->eap_proxy, &eap_len); if (eap_key == NULL) { wpa_printf(MSG_DEBUG, "EAPOL: Failed to get " "eapKeyData"); return -1; } goto key_fetched; } #endif /* CONFIG_EAP_PROXY */ if (sm == NULL || !eap_key_available(sm->eap)) { wpa_printf(MSG_DEBUG, "EAPOL: EAP key not available"); return -1; } eap_key = eap_get_eapKeyData(sm->eap, &eap_len); if (eap_key == NULL) { wpa_printf(MSG_DEBUG, "EAPOL: Failed to get eapKeyData"); return -1; } #ifdef CONFIG_EAP_PROXY key_fetched: #endif /* CONFIG_EAP_PROXY */ if (len > eap_len) { wpa_printf(MSG_DEBUG, "EAPOL: Requested key length (%lu) not " "available (len=%lu)", (unsigned long) len, (unsigned long) eap_len); return eap_len; } os_memcpy(key, eap_key, len); wpa_printf(MSG_DEBUG, "EAPOL: Successfully fetched key (len=%lu)", (unsigned long) len); return 0; } /** * eapol_sm_notify_logoff - Notification of logon/logoff commands * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() * @logoff: Whether command was logoff * * Notify EAPOL state machines that user requested logon/logoff. */ void eapol_sm_notify_logoff(struct eapol_sm *sm, Boolean logoff) { if (sm) { sm->userLogoff = logoff; if (!logoff) { /* If there is a delayed txStart queued, start now. */ sm->startWhen = 0; } eapol_sm_step(sm); } } /** * eapol_sm_notify_pmkid_attempt - Notification of successful PMKSA caching * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() * * Notify EAPOL state machines that PMKSA caching was successful. This is used * to move EAPOL and EAP state machines into authenticated/successful state. */ void eapol_sm_notify_cached(struct eapol_sm *sm) { if (sm == NULL) return; wpa_printf(MSG_DEBUG, "EAPOL: PMKSA caching was used - skip EAPOL"); sm->eapSuccess = TRUE; eap_notify_success(sm->eap); eapol_sm_step(sm); } /** * eapol_sm_notify_pmkid_attempt - Notification of PMKSA caching * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() * @attempt: Whether PMKSA caching is tried * * Notify EAPOL state machines whether PMKSA caching is used. */ void eapol_sm_notify_pmkid_attempt(struct eapol_sm *sm, int attempt) { if (sm == NULL) return; if (attempt) { wpa_printf(MSG_DEBUG, "RSN: Trying to use cached PMKSA"); sm->cached_pmk = TRUE; } else { wpa_printf(MSG_DEBUG, "RSN: Do not try to use cached PMKSA"); sm->cached_pmk = FALSE; } } static void eapol_sm_abort_cached(struct eapol_sm *sm) { wpa_printf(MSG_DEBUG, "RSN: Authenticator did not accept PMKID, " "doing full EAP authentication"); if (sm == NULL) return; sm->cached_pmk = FALSE; sm->SUPP_PAE_state = SUPP_PAE_CONNECTING; eapol_sm_set_port_unauthorized(sm); /* Make sure we do not start sending EAPOL-Start frames first, but * instead move to RESTART state to start EAPOL authentication. */ sm->startWhen = 3; eapol_enable_timer_tick(sm); if (sm->ctx->aborted_cached) sm->ctx->aborted_cached(sm->ctx->ctx); } /** * eapol_sm_register_scard_ctx - Notification of smart card context * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() * @ctx: Context data for smart card operations * * Notify EAPOL state machines of context data for smart card operations. This * context data will be used as a parameter for scard_*() functions. */ void eapol_sm_register_scard_ctx(struct eapol_sm *sm, void *ctx) { if (sm) { sm->ctx->scard_ctx = ctx; eap_register_scard_ctx(sm->eap, ctx); } } /** * eapol_sm_notify_portControl - Notification of portControl changes * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() * @portControl: New value for portControl variable * * Notify EAPOL state machines that portControl variable has changed. */ void eapol_sm_notify_portControl(struct eapol_sm *sm, PortControl portControl) { if (sm == NULL) return; wpa_printf(MSG_DEBUG, "EAPOL: External notification - " "portControl=%s", eapol_port_control(portControl)); sm->portControl = portControl; eapol_sm_step(sm); } /** * eapol_sm_notify_ctrl_attached - Notification of attached monitor * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() * * Notify EAPOL state machines that a monitor was attached to the control * interface to trigger re-sending of pending requests for user input. */ void eapol_sm_notify_ctrl_attached(struct eapol_sm *sm) { if (sm == NULL) return; eap_sm_notify_ctrl_attached(sm->eap); } /** * eapol_sm_notify_ctrl_response - Notification of received user input * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() * * Notify EAPOL state machines that a control response, i.e., user * input, was received in order to trigger retrying of a pending EAP request. */ void eapol_sm_notify_ctrl_response(struct eapol_sm *sm) { if (sm == NULL) return; if (sm->eapReqData && !sm->eapReq) { wpa_printf(MSG_DEBUG, "EAPOL: received control response (user " "input) notification - retrying pending EAP " "Request"); sm->eapolEap = TRUE; sm->eapReq = TRUE; eapol_sm_step(sm); } } /** * eapol_sm_request_reauth - Request reauthentication * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() * * This function can be used to request EAPOL reauthentication, e.g., when the * current PMKSA entry is nearing expiration. */ void eapol_sm_request_reauth(struct eapol_sm *sm) { if (sm == NULL || sm->SUPP_PAE_state != SUPP_PAE_AUTHENTICATED) return; eapol_sm_txStart(sm); } /** * eapol_sm_notify_lower_layer_success - Notification of lower layer success * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() * @in_eapol_sm: Whether the caller is already running inside EAPOL state * machine loop (eapol_sm_step()) * * Notify EAPOL (and EAP) state machines that a lower layer has detected a * successful authentication. This is used to recover from dropped EAP-Success * messages. */ void eapol_sm_notify_lower_layer_success(struct eapol_sm *sm, int in_eapol_sm) { if (sm == NULL) return; eap_notify_lower_layer_success(sm->eap); if (!in_eapol_sm) eapol_sm_step(sm); } /** * eapol_sm_invalidate_cached_session - Mark cached EAP session data invalid * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() */ void eapol_sm_invalidate_cached_session(struct eapol_sm *sm) { if (sm) eap_invalidate_cached_session(sm->eap); } static struct eap_peer_config * eapol_sm_get_config(void *ctx) { struct eapol_sm *sm = ctx; return sm ? sm->config : NULL; } static struct wpabuf * eapol_sm_get_eapReqData(void *ctx) { struct eapol_sm *sm = ctx; if (sm == NULL || sm->eapReqData == NULL) return NULL; return sm->eapReqData; } static Boolean eapol_sm_get_bool(void *ctx, enum eapol_bool_var variable) { struct eapol_sm *sm = ctx; if (sm == NULL) return FALSE; switch (variable) { case EAPOL_eapSuccess: return sm->eapSuccess; case EAPOL_eapRestart: return sm->eapRestart; case EAPOL_eapFail: return sm->eapFail; case EAPOL_eapResp: return sm->eapResp; case EAPOL_eapNoResp: return sm->eapNoResp; case EAPOL_eapReq: return sm->eapReq; case EAPOL_portEnabled: return sm->portEnabled; case EAPOL_altAccept: return sm->altAccept; case EAPOL_altReject: return sm->altReject; } return FALSE; } static void eapol_sm_set_bool(void *ctx, enum eapol_bool_var variable, Boolean value) { struct eapol_sm *sm = ctx; if (sm == NULL) return; switch (variable) { case EAPOL_eapSuccess: sm->eapSuccess = value; break; case EAPOL_eapRestart: sm->eapRestart = value; break; case EAPOL_eapFail: sm->eapFail = value; break; case EAPOL_eapResp: sm->eapResp = value; break; case EAPOL_eapNoResp: sm->eapNoResp = value; break; case EAPOL_eapReq: sm->eapReq = value; break; case EAPOL_portEnabled: sm->portEnabled = value; break; case EAPOL_altAccept: sm->altAccept = value; break; case EAPOL_altReject: sm->altReject = value; break; } } static unsigned int eapol_sm_get_int(void *ctx, enum eapol_int_var variable) { struct eapol_sm *sm = ctx; if (sm == NULL) return 0; switch (variable) { case EAPOL_idleWhile: return sm->idleWhile; } return 0; } static void eapol_sm_set_int(void *ctx, enum eapol_int_var variable, unsigned int value) { struct eapol_sm *sm = ctx; if (sm == NULL) return; switch (variable) { case EAPOL_idleWhile: sm->idleWhile = value; if (sm->idleWhile > 0) eapol_enable_timer_tick(sm); break; } } static void eapol_sm_set_config_blob(void *ctx, struct wpa_config_blob *blob) { #ifndef CONFIG_NO_CONFIG_BLOBS struct eapol_sm *sm = ctx; if (sm && sm->ctx && sm->ctx->set_config_blob) sm->ctx->set_config_blob(sm->ctx->ctx, blob); #endif /* CONFIG_NO_CONFIG_BLOBS */ } static const struct wpa_config_blob * eapol_sm_get_config_blob(void *ctx, const char *name) { #ifndef CONFIG_NO_CONFIG_BLOBS struct eapol_sm *sm = ctx; if (sm && sm->ctx && sm->ctx->get_config_blob) return sm->ctx->get_config_blob(sm->ctx->ctx, name); else return NULL; #else /* CONFIG_NO_CONFIG_BLOBS */ return NULL; #endif /* CONFIG_NO_CONFIG_BLOBS */ } static void eapol_sm_notify_pending(void *ctx) { struct eapol_sm *sm = ctx; if (sm == NULL) return; if (sm->eapReqData && !sm->eapReq) { wpa_printf(MSG_DEBUG, "EAPOL: received notification from EAP " "state machine - retrying pending EAP Request"); sm->eapolEap = TRUE; sm->eapReq = TRUE; eapol_sm_step(sm); } } #if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG) static void eapol_sm_eap_param_needed(void *ctx, enum wpa_ctrl_req_type field, const char *txt) { struct eapol_sm *sm = ctx; wpa_printf(MSG_DEBUG, "EAPOL: EAP parameter needed"); if (sm->ctx->eap_param_needed) sm->ctx->eap_param_needed(sm->ctx->ctx, field, txt); } #else /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ #define eapol_sm_eap_param_needed NULL #endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ static void eapol_sm_notify_cert(void *ctx, int depth, const char *subject, const char *cert_hash, const struct wpabuf *cert) { struct eapol_sm *sm = ctx; if (sm->ctx->cert_cb) sm->ctx->cert_cb(sm->ctx->ctx, depth, subject, cert_hash, cert); } static void eapol_sm_notify_status(void *ctx, const char *status, const char *parameter) { struct eapol_sm *sm = ctx; if (sm->ctx->status_cb) sm->ctx->status_cb(sm->ctx->ctx, status, parameter); } static void eapol_sm_set_anon_id(void *ctx, const u8 *id, size_t len) { struct eapol_sm *sm = ctx; if (sm->ctx->set_anon_id) sm->ctx->set_anon_id(sm->ctx->ctx, id, len); } static struct eapol_callbacks eapol_cb = { eapol_sm_get_config, eapol_sm_get_bool, eapol_sm_set_bool, eapol_sm_get_int, eapol_sm_set_int, eapol_sm_get_eapReqData, eapol_sm_set_config_blob, eapol_sm_get_config_blob, eapol_sm_notify_pending, eapol_sm_eap_param_needed, eapol_sm_notify_cert, eapol_sm_notify_status, eapol_sm_set_anon_id }; /** * eapol_sm_init - Initialize EAPOL state machine * @ctx: Pointer to EAPOL context data; this needs to be an allocated buffer * and EAPOL state machine will free it in eapol_sm_deinit() * Returns: Pointer to the allocated EAPOL state machine or %NULL on failure * * Allocate and initialize an EAPOL state machine. */ struct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx) { struct eapol_sm *sm; struct eap_config conf; sm = os_zalloc(sizeof(*sm)); if (sm == NULL) return NULL; sm->ctx = ctx; sm->portControl = Auto; /* Supplicant PAE state machine */ sm->heldPeriod = 60; sm->startPeriod = 30; sm->maxStart = 3; /* Supplicant Backend state machine */ sm->authPeriod = 30; os_memset(&conf, 0, sizeof(conf)); conf.opensc_engine_path = ctx->opensc_engine_path; conf.pkcs11_engine_path = ctx->pkcs11_engine_path; conf.pkcs11_module_path = ctx->pkcs11_module_path; conf.wps = ctx->wps; conf.cert_in_cb = ctx->cert_in_cb; sm->eap = eap_peer_sm_init(sm, &eapol_cb, sm->ctx->msg_ctx, &conf); if (sm->eap == NULL) { os_free(sm); return NULL; } #ifdef CONFIG_EAP_PROXY sm->use_eap_proxy = FALSE; sm->eap_proxy = eap_proxy_init(sm, &eapol_cb, sm->ctx->msg_ctx); if (sm->eap_proxy == NULL) { wpa_printf(MSG_ERROR, "Unable to initialize EAP Proxy"); } #endif /* CONFIG_EAP_PROXY */ /* Initialize EAPOL state machines */ sm->force_authorized_update = TRUE; sm->initialize = TRUE; eapol_sm_step(sm); sm->initialize = FALSE; eapol_sm_step(sm); sm->timer_tick_enabled = 1; eloop_register_timeout(1, 0, eapol_port_timers_tick, NULL, sm); return sm; } /** * eapol_sm_deinit - Deinitialize EAPOL state machine * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() * * Deinitialize and free EAPOL state machine. */ void eapol_sm_deinit(struct eapol_sm *sm) { if (sm == NULL) return; eloop_cancel_timeout(eapol_sm_step_timeout, NULL, sm); eloop_cancel_timeout(eapol_port_timers_tick, NULL, sm); eap_peer_sm_deinit(sm->eap); #ifdef CONFIG_EAP_PROXY eap_proxy_deinit(sm->eap_proxy); #endif /* CONFIG_EAP_PROXY */ os_free(sm->last_rx_key); wpabuf_free(sm->eapReqData); os_free(sm->ctx); os_free(sm); } void eapol_sm_set_ext_pw_ctx(struct eapol_sm *sm, struct ext_password_data *ext) { if (sm && sm->eap) eap_sm_set_ext_pw_ctx(sm->eap, ext); } int eapol_sm_failed(struct eapol_sm *sm) { if (sm == NULL) return 0; return !sm->eapSuccess && sm->eapFail; } int eapol_sm_get_eap_proxy_imsi(struct eapol_sm *sm, char *imsi, size_t *len) { #ifdef CONFIG_EAP_PROXY if (sm->eap_proxy == NULL) return -1; return eap_proxy_get_imsi(sm->eap_proxy, imsi, len); #else /* CONFIG_EAP_PROXY */ return -1; #endif /* CONFIG_EAP_PROXY */ } wpa-2.1/src/eapol_supp/eapol_supp_sm.h000066400000000000000000000272131227414666700201440ustar00rootroot00000000000000/* * EAPOL supplicant state machines * Copyright (c) 2004-2012, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef EAPOL_SUPP_SM_H #define EAPOL_SUPP_SM_H #include "common/defs.h" typedef enum { Unauthorized, Authorized } PortStatus; typedef enum { Auto, ForceUnauthorized, ForceAuthorized } PortControl; /** * struct eapol_config - Per network configuration for EAPOL state machines */ struct eapol_config { /** * accept_802_1x_keys - Accept IEEE 802.1X (non-WPA) EAPOL-Key frames * * This variable should be set to 1 when using EAPOL state machines * with non-WPA security policy to generate dynamic WEP keys. When * using WPA, this should be set to 0 so that WPA state machine can * process the EAPOL-Key frames. */ int accept_802_1x_keys; #define EAPOL_REQUIRE_KEY_UNICAST BIT(0) #define EAPOL_REQUIRE_KEY_BROADCAST BIT(1) /** * required_keys - Which EAPOL-Key packets are required * * This variable determines which EAPOL-Key packets are required before * marking connection authenticated. This is a bit field of * EAPOL_REQUIRE_KEY_UNICAST and EAPOL_REQUIRE_KEY_BROADCAST flags. */ int required_keys; /** * fast_reauth - Whether fast EAP reauthentication is enabled */ int fast_reauth; /** * workaround - Whether EAP workarounds are enabled */ unsigned int workaround; /** * eap_disabled - Whether EAP is disabled */ int eap_disabled; /** * external_sim - Use external processing for SIM/USIM operations */ int external_sim; }; struct eapol_sm; struct wpa_config_blob; enum eapol_supp_result { EAPOL_SUPP_RESULT_FAILURE, EAPOL_SUPP_RESULT_SUCCESS, EAPOL_SUPP_RESULT_EXPECTED_FAILURE }; /** * struct eapol_ctx - Global (for all networks) EAPOL state machine context */ struct eapol_ctx { /** * ctx - Pointer to arbitrary upper level context */ void *ctx; /** * preauth - IEEE 802.11i/RSN pre-authentication * * This EAPOL state machine is used for IEEE 802.11i/RSN * pre-authentication */ int preauth; /** * cb - Function to be called when EAPOL negotiation has been completed * @eapol: Pointer to EAPOL state machine data * @result: Whether the authentication was completed successfully * @ctx: Pointer to context data (cb_ctx) * * This optional callback function will be called when the EAPOL * authentication has been completed. This allows the owner of the * EAPOL state machine to process the key and terminate the EAPOL state * machine. Currently, this is used only in RSN pre-authentication. */ void (*cb)(struct eapol_sm *eapol, enum eapol_supp_result result, void *ctx); /** * cb_ctx - Callback context for cb() */ void *cb_ctx; /** * msg_ctx - Callback context for wpa_msg() calls */ void *msg_ctx; /** * scard_ctx - Callback context for PC/SC scard_*() function calls * * This context can be updated with eapol_sm_register_scard_ctx(). */ void *scard_ctx; /** * eapol_send_ctx - Callback context for eapol_send() calls */ void *eapol_send_ctx; /** * eapol_done_cb - Function to be called at successful completion * @ctx: Callback context (ctx) * * This function is called at the successful completion of EAPOL * authentication. If dynamic WEP keys are used, this is called only * after all the expected keys have been received. */ void (*eapol_done_cb)(void *ctx); /** * eapol_send - Send EAPOL packets * @ctx: Callback context (eapol_send_ctx) * @type: EAPOL type (IEEE802_1X_TYPE_*) * @buf: Pointer to EAPOL payload * @len: Length of the EAPOL payload * Returns: 0 on success, -1 on failure */ int (*eapol_send)(void *ctx, int type, const u8 *buf, size_t len); /** * set_wep_key - Configure WEP keys * @ctx: Callback context (ctx) * @unicast: Non-zero = unicast, 0 = multicast/broadcast key * @keyidx: Key index (0..3) * @key: WEP key * @keylen: Length of the WEP key * Returns: 0 on success, -1 on failure */ int (*set_wep_key)(void *ctx, int unicast, int keyidx, const u8 *key, size_t keylen); /** * set_config_blob - Set or add a named configuration blob * @ctx: Callback context (ctx) * @blob: New value for the blob * * Adds a new configuration blob or replaces the current value of an * existing blob. */ void (*set_config_blob)(void *ctx, struct wpa_config_blob *blob); /** * get_config_blob - Get a named configuration blob * @ctx: Callback context (ctx) * @name: Name of the blob * Returns: Pointer to blob data or %NULL if not found */ const struct wpa_config_blob * (*get_config_blob)(void *ctx, const char *name); /** * aborted_cached - Notify that cached PMK attempt was aborted * @ctx: Callback context (ctx) */ void (*aborted_cached)(void *ctx); /** * opensc_engine_path - Path to the OpenSSL engine for opensc * * This is an OpenSSL specific configuration option for loading OpenSC * engine (engine_opensc.so); if %NULL, this engine is not loaded. */ const char *opensc_engine_path; /** * pkcs11_engine_path - Path to the OpenSSL engine for PKCS#11 * * This is an OpenSSL specific configuration option for loading PKCS#11 * engine (engine_pkcs11.so); if %NULL, this engine is not loaded. */ const char *pkcs11_engine_path; /** * pkcs11_module_path - Path to the OpenSSL OpenSC/PKCS#11 module * * This is an OpenSSL specific configuration option for configuring * path to OpenSC/PKCS#11 engine (opensc-pkcs11.so); if %NULL, this * module is not loaded. */ const char *pkcs11_module_path; /** * wps - WPS context data * * This is only used by EAP-WSC and can be left %NULL if not available. */ struct wps_context *wps; /** * eap_param_needed - Notify that EAP parameter is needed * @ctx: Callback context (ctx) * @field: Field indicator (e.g., WPA_CTRL_REQ_EAP_IDENTITY) * @txt: User readable text describing the required parameter */ void (*eap_param_needed)(void *ctx, enum wpa_ctrl_req_type field, const char *txt); /** * port_cb - Set port authorized/unauthorized callback (optional) * @ctx: Callback context (ctx) * @authorized: Whether the supplicant port is now in authorized state */ void (*port_cb)(void *ctx, int authorized); /** * cert_cb - Notification of a peer certificate * @ctx: Callback context (ctx) * @depth: Depth in certificate chain (0 = server) * @subject: Subject of the peer certificate * @cert_hash: SHA-256 hash of the certificate * @cert: Peer certificate */ void (*cert_cb)(void *ctx, int depth, const char *subject, const char *cert_hash, const struct wpabuf *cert); /** * cert_in_cb - Include server certificates in callback */ int cert_in_cb; /** * status_cb - Notification of a change in EAP status * @ctx: Callback context (ctx) * @status: Step in the process of EAP authentication * @parameter: Step-specific parameter, e.g., EAP method name */ void (*status_cb)(void *ctx, const char *status, const char *parameter); /** * set_anon_id - Set or add anonymous identity * @ctx: eapol_ctx from eap_peer_sm_init() call * @id: Anonymous identity (e.g., EAP-SIM pseudonym) * @len: Length of anonymous identity in octets */ void (*set_anon_id)(void *ctx, const u8 *id, size_t len); }; struct eap_peer_config; struct ext_password_data; #ifdef IEEE8021X_EAPOL struct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx); void eapol_sm_deinit(struct eapol_sm *sm); void eapol_sm_step(struct eapol_sm *sm); int eapol_sm_get_status(struct eapol_sm *sm, char *buf, size_t buflen, int verbose); int eapol_sm_get_mib(struct eapol_sm *sm, char *buf, size_t buflen); void eapol_sm_configure(struct eapol_sm *sm, int heldPeriod, int authPeriod, int startPeriod, int maxStart); int eapol_sm_rx_eapol(struct eapol_sm *sm, const u8 *src, const u8 *buf, size_t len); void eapol_sm_notify_tx_eapol_key(struct eapol_sm *sm); void eapol_sm_notify_portEnabled(struct eapol_sm *sm, Boolean enabled); void eapol_sm_notify_portValid(struct eapol_sm *sm, Boolean valid); void eapol_sm_notify_eap_success(struct eapol_sm *sm, Boolean success); void eapol_sm_notify_eap_fail(struct eapol_sm *sm, Boolean fail); void eapol_sm_notify_config(struct eapol_sm *sm, struct eap_peer_config *config, const struct eapol_config *conf); int eapol_sm_get_key(struct eapol_sm *sm, u8 *key, size_t len); void eapol_sm_notify_logoff(struct eapol_sm *sm, Boolean logoff); void eapol_sm_notify_cached(struct eapol_sm *sm); void eapol_sm_notify_pmkid_attempt(struct eapol_sm *sm, int attempt); void eapol_sm_register_scard_ctx(struct eapol_sm *sm, void *ctx); void eapol_sm_notify_portControl(struct eapol_sm *sm, PortControl portControl); void eapol_sm_notify_ctrl_attached(struct eapol_sm *sm); void eapol_sm_notify_ctrl_response(struct eapol_sm *sm); void eapol_sm_request_reauth(struct eapol_sm *sm); void eapol_sm_notify_lower_layer_success(struct eapol_sm *sm, int in_eapol_sm); void eapol_sm_invalidate_cached_session(struct eapol_sm *sm); const char * eapol_sm_get_method_name(struct eapol_sm *sm); void eapol_sm_set_ext_pw_ctx(struct eapol_sm *sm, struct ext_password_data *ext); int eapol_sm_failed(struct eapol_sm *sm); int eapol_sm_get_eap_proxy_imsi(struct eapol_sm *sm, char *imsi, size_t *len); #else /* IEEE8021X_EAPOL */ static inline struct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx) { free(ctx); return (struct eapol_sm *) 1; } static inline void eapol_sm_deinit(struct eapol_sm *sm) { } static inline void eapol_sm_step(struct eapol_sm *sm) { } static inline int eapol_sm_get_status(struct eapol_sm *sm, char *buf, size_t buflen, int verbose) { return 0; } static inline int eapol_sm_get_mib(struct eapol_sm *sm, char *buf, size_t buflen) { return 0; } static inline void eapol_sm_configure(struct eapol_sm *sm, int heldPeriod, int authPeriod, int startPeriod, int maxStart) { } static inline int eapol_sm_rx_eapol(struct eapol_sm *sm, const u8 *src, const u8 *buf, size_t len) { return 0; } static inline void eapol_sm_notify_tx_eapol_key(struct eapol_sm *sm) { } static inline void eapol_sm_notify_portEnabled(struct eapol_sm *sm, Boolean enabled) { } static inline void eapol_sm_notify_portValid(struct eapol_sm *sm, Boolean valid) { } static inline void eapol_sm_notify_eap_success(struct eapol_sm *sm, Boolean success) { } static inline void eapol_sm_notify_eap_fail(struct eapol_sm *sm, Boolean fail) { } static inline void eapol_sm_notify_config(struct eapol_sm *sm, struct eap_peer_config *config, struct eapol_config *conf) { } static inline int eapol_sm_get_key(struct eapol_sm *sm, u8 *key, size_t len) { return -1; } static inline void eapol_sm_notify_logoff(struct eapol_sm *sm, Boolean logoff) { } static inline void eapol_sm_notify_cached(struct eapol_sm *sm) { } #define eapol_sm_notify_pmkid_attempt(sm, attempt) do { } while (0) #define eapol_sm_register_scard_ctx(sm, ctx) do { } while (0) static inline void eapol_sm_notify_portControl(struct eapol_sm *sm, PortControl portControl) { } static inline void eapol_sm_notify_ctrl_attached(struct eapol_sm *sm) { } static inline void eapol_sm_notify_ctrl_response(struct eapol_sm *sm) { } static inline void eapol_sm_request_reauth(struct eapol_sm *sm) { } static inline void eapol_sm_notify_lower_layer_success(struct eapol_sm *sm, int in_eapol_sm) { } static inline void eapol_sm_invalidate_cached_session(struct eapol_sm *sm) { } static inline const char * eapol_sm_get_method_name(struct eapol_sm *sm) { return NULL; } static inline void eapol_sm_set_ext_pw_ctx(struct eapol_sm *sm, struct ext_password_data *ext) { } static inline int eapol_sm_failed(struct eapol_sm *sm) { return 0; } #endif /* IEEE8021X_EAPOL */ #endif /* EAPOL_SUPP_SM_H */ wpa-2.1/src/l2_packet/000077500000000000000000000000001227414666700146155ustar00rootroot00000000000000wpa-2.1/src/l2_packet/Makefile000066400000000000000000000001641227414666700162560ustar00rootroot00000000000000all: @echo Nothing to be made. clean: rm -f *~ *.o *.d *.gcno *.gcda *.gcov install: @echo Nothing to be made. wpa-2.1/src/l2_packet/l2_packet.h000066400000000000000000000115111227414666700166310ustar00rootroot00000000000000/* * WPA Supplicant - Layer2 packet interface definition * Copyright (c) 2003-2005, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. * * This file defines an interface for layer 2 (link layer) packet sending and * receiving. l2_packet_linux.c is one implementation for such a layer 2 * implementation using Linux packet sockets and l2_packet_pcap.c another one * using libpcap and libdnet. When porting %wpa_supplicant to other operating * systems, a new l2_packet implementation may need to be added. */ #ifndef L2_PACKET_H #define L2_PACKET_H /** * struct l2_packet_data - Internal l2_packet data structure * * This structure is used by the l2_packet implementation to store its private * data. Other files use a pointer to this data when calling the l2_packet * functions, but the contents of this structure should not be used directly * outside l2_packet implementation. */ struct l2_packet_data; #ifdef _MSC_VER #pragma pack(push, 1) #endif /* _MSC_VER */ struct l2_ethhdr { u8 h_dest[ETH_ALEN]; u8 h_source[ETH_ALEN]; be16 h_proto; } STRUCT_PACKED; #ifdef _MSC_VER #pragma pack(pop) #endif /* _MSC_VER */ /** * l2_packet_init - Initialize l2_packet interface * @ifname: Interface name * @own_addr: Optional own MAC address if available from driver interface or * %NULL if not available * @protocol: Ethernet protocol number in host byte order * @rx_callback: Callback function that will be called for each received packet * @rx_callback_ctx: Callback data (ctx) for calls to rx_callback() * @l2_hdr: 1 = include layer 2 header, 0 = do not include header * Returns: Pointer to internal data or %NULL on failure * * rx_callback function will be called with src_addr pointing to the source * address (MAC address) of the the packet. If l2_hdr is set to 0, buf * points to len bytes of the payload after the layer 2 header and similarly, * TX buffers start with payload. This behavior can be changed by setting * l2_hdr=1 to include the layer 2 header in the data buffer. */ struct l2_packet_data * l2_packet_init( const char *ifname, const u8 *own_addr, unsigned short protocol, void (*rx_callback)(void *ctx, const u8 *src_addr, const u8 *buf, size_t len), void *rx_callback_ctx, int l2_hdr); /** * l2_packet_deinit - Deinitialize l2_packet interface * @l2: Pointer to internal l2_packet data from l2_packet_init() */ void l2_packet_deinit(struct l2_packet_data *l2); /** * l2_packet_get_own_addr - Get own layer 2 address * @l2: Pointer to internal l2_packet data from l2_packet_init() * @addr: Buffer for the own address (6 bytes) * Returns: 0 on success, -1 on failure */ int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr); /** * l2_packet_send - Send a packet * @l2: Pointer to internal l2_packet data from l2_packet_init() * @dst_addr: Destination address for the packet (only used if l2_hdr == 0) * @proto: Protocol/ethertype for the packet in host byte order (only used if * l2_hdr == 0) * @buf: Packet contents to be sent; including layer 2 header if l2_hdr was * set to 1 in l2_packet_init() call. Otherwise, only the payload of the packet * is included. * @len: Length of the buffer (including l2 header only if l2_hdr == 1) * Returns: >=0 on success, <0 on failure */ int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto, const u8 *buf, size_t len); /** * l2_packet_get_ip_addr - Get the current IP address from the interface * @l2: Pointer to internal l2_packet data from l2_packet_init() * @buf: Buffer for the IP address in text format * @len: Maximum buffer length * Returns: 0 on success, -1 on failure * * This function can be used to get the current IP address from the interface * bound to the l2_packet. This is mainly for status information and the IP * address will be stored as an ASCII string. This function is not essential * for %wpa_supplicant operation, so full implementation is not required. * l2_packet implementation will need to define the function, but it can return * -1 if the IP address information is not available. */ int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len); /** * l2_packet_notify_auth_start - Notify l2_packet about start of authentication * @l2: Pointer to internal l2_packet data from l2_packet_init() * * This function is called when authentication is expected to start, e.g., when * association has been completed, in order to prepare l2_packet implementation * for EAPOL frames. This function is used mainly if the l2_packet code needs * to do polling in which case it can increasing polling frequency. This can * also be an empty function if the l2_packet implementation does not benefit * from knowing about the starting authentication. */ void l2_packet_notify_auth_start(struct l2_packet_data *l2); #endif /* L2_PACKET_H */ wpa-2.1/src/l2_packet/l2_packet_freebsd.c000066400000000000000000000164351227414666700203300ustar00rootroot00000000000000/* * WPA Supplicant - Layer2 packet handling with FreeBSD * Copyright (c) 2003-2005, Jouni Malinen * Copyright (c) 2005, Sam Leffler * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "includes.h" #if defined(__APPLE__) || defined(__GLIBC__) #include #endif /* __APPLE__ */ #include #include #ifdef __sun__ #include #else /* __sun__ */ #include #endif /* __sun__ */ #include #include #include #include #include "common.h" #include "eloop.h" #include "l2_packet.h" static const u8 pae_group_addr[ETH_ALEN] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 }; struct l2_packet_data { pcap_t *pcap; char ifname[100]; u8 own_addr[ETH_ALEN]; void (*rx_callback)(void *ctx, const u8 *src_addr, const u8 *buf, size_t len); void *rx_callback_ctx; int l2_hdr; /* whether to include layer 2 (Ethernet) header data * buffers */ }; int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr) { os_memcpy(addr, l2->own_addr, ETH_ALEN); return 0; } int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto, const u8 *buf, size_t len) { if (!l2->l2_hdr) { int ret; struct l2_ethhdr *eth = os_malloc(sizeof(*eth) + len); if (eth == NULL) return -1; os_memcpy(eth->h_dest, dst_addr, ETH_ALEN); os_memcpy(eth->h_source, l2->own_addr, ETH_ALEN); eth->h_proto = htons(proto); os_memcpy(eth + 1, buf, len); ret = pcap_inject(l2->pcap, (u8 *) eth, len + sizeof(*eth)); os_free(eth); return ret; } else return pcap_inject(l2->pcap, buf, len); } static void l2_packet_receive(int sock, void *eloop_ctx, void *sock_ctx) { struct l2_packet_data *l2 = eloop_ctx; pcap_t *pcap = sock_ctx; struct pcap_pkthdr hdr; const u_char *packet; struct l2_ethhdr *ethhdr; unsigned char *buf; size_t len; packet = pcap_next(pcap, &hdr); if (packet == NULL || hdr.caplen < sizeof(*ethhdr)) return; ethhdr = (struct l2_ethhdr *) packet; if (l2->l2_hdr) { buf = (unsigned char *) ethhdr; len = hdr.caplen; } else { buf = (unsigned char *) (ethhdr + 1); len = hdr.caplen - sizeof(*ethhdr); } l2->rx_callback(l2->rx_callback_ctx, ethhdr->h_source, buf, len); } static int l2_packet_init_libpcap(struct l2_packet_data *l2, unsigned short protocol) { bpf_u_int32 pcap_maskp, pcap_netp; char pcap_filter[200], pcap_err[PCAP_ERRBUF_SIZE]; struct bpf_program pcap_fp; pcap_lookupnet(l2->ifname, &pcap_netp, &pcap_maskp, pcap_err); l2->pcap = pcap_open_live(l2->ifname, 2500, 0, 10, pcap_err); if (l2->pcap == NULL) { fprintf(stderr, "pcap_open_live: %s\n", pcap_err); fprintf(stderr, "ifname='%s'\n", l2->ifname); return -1; } if (pcap_datalink(l2->pcap) != DLT_EN10MB && pcap_set_datalink(l2->pcap, DLT_EN10MB) < 0) { fprintf(stderr, "pcap_set_datalink(DLT_EN10MB): %s\n", pcap_geterr(l2->pcap)); return -1; } os_snprintf(pcap_filter, sizeof(pcap_filter), "not ether src " MACSTR " and " "( ether dst " MACSTR " or ether dst " MACSTR " ) and " "ether proto 0x%x", MAC2STR(l2->own_addr), /* do not receive own packets */ MAC2STR(l2->own_addr), MAC2STR(pae_group_addr), protocol); if (pcap_compile(l2->pcap, &pcap_fp, pcap_filter, 1, pcap_netp) < 0) { fprintf(stderr, "pcap_compile: %s\n", pcap_geterr(l2->pcap)); return -1; } if (pcap_setfilter(l2->pcap, &pcap_fp) < 0) { fprintf(stderr, "pcap_setfilter: %s\n", pcap_geterr(l2->pcap)); return -1; } pcap_freecode(&pcap_fp); #ifndef __sun__ /* * When libpcap uses BPF we must enable "immediate mode" to * receive frames right away; otherwise the system may * buffer them for us. */ { unsigned int on = 1; if (ioctl(pcap_fileno(l2->pcap), BIOCIMMEDIATE, &on) < 0) { fprintf(stderr, "%s: cannot enable immediate mode on " "interface %s: %s\n", __func__, l2->ifname, strerror(errno)); /* XXX should we fail? */ } } #endif /* __sun__ */ eloop_register_read_sock(pcap_get_selectable_fd(l2->pcap), l2_packet_receive, l2, l2->pcap); return 0; } static int eth_get(const char *device, u8 ea[ETH_ALEN]) { #ifdef __sun__ dlpi_handle_t dh; u32 physaddrlen = DLPI_PHYSADDR_MAX; u8 physaddr[DLPI_PHYSADDR_MAX]; int retval; retval = dlpi_open(device, &dh, 0); if (retval != DLPI_SUCCESS) { wpa_printf(MSG_ERROR, "dlpi_open error: %s", dlpi_strerror(retval)); return -1; } retval = dlpi_get_physaddr(dh, DL_CURR_PHYS_ADDR, physaddr, &physaddrlen); if (retval != DLPI_SUCCESS) { wpa_printf(MSG_ERROR, "dlpi_get_physaddr error: %s", dlpi_strerror(retval)); dlpi_close(dh); return -1; } os_memcpy(ea, physaddr, ETH_ALEN); dlpi_close(dh); #else /* __sun__ */ struct if_msghdr *ifm; struct sockaddr_dl *sdl; u_char *p, *buf; size_t len; int mib[] = { CTL_NET, AF_ROUTE, 0, AF_LINK, NET_RT_IFLIST, 0 }; if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) return -1; if ((buf = os_malloc(len)) == NULL) return -1; if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) { os_free(buf); return -1; } for (p = buf; p < buf + len; p += ifm->ifm_msglen) { ifm = (struct if_msghdr *)p; sdl = (struct sockaddr_dl *)(ifm + 1); if (ifm->ifm_type != RTM_IFINFO || (ifm->ifm_addrs & RTA_IFP) == 0) continue; if (sdl->sdl_family != AF_LINK || sdl->sdl_nlen == 0 || os_memcmp(sdl->sdl_data, device, sdl->sdl_nlen) != 0) continue; os_memcpy(ea, LLADDR(sdl), sdl->sdl_alen); break; } os_free(buf); if (p >= buf + len) { errno = ESRCH; return -1; } #endif /* __sun__ */ return 0; } struct l2_packet_data * l2_packet_init( const char *ifname, const u8 *own_addr, unsigned short protocol, void (*rx_callback)(void *ctx, const u8 *src_addr, const u8 *buf, size_t len), void *rx_callback_ctx, int l2_hdr) { struct l2_packet_data *l2; l2 = os_zalloc(sizeof(struct l2_packet_data)); if (l2 == NULL) return NULL; os_strlcpy(l2->ifname, ifname, sizeof(l2->ifname)); l2->rx_callback = rx_callback; l2->rx_callback_ctx = rx_callback_ctx; l2->l2_hdr = l2_hdr; if (eth_get(l2->ifname, l2->own_addr) < 0) { fprintf(stderr, "Failed to get link-level address for " "interface '%s'.\n", l2->ifname); os_free(l2); return NULL; } if (l2_packet_init_libpcap(l2, protocol)) { os_free(l2); return NULL; } return l2; } void l2_packet_deinit(struct l2_packet_data *l2) { if (l2 != NULL) { if (l2->pcap) { eloop_unregister_read_sock( pcap_get_selectable_fd(l2->pcap)); pcap_close(l2->pcap); } os_free(l2); } } int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len) { pcap_if_t *devs, *dev; struct pcap_addr *addr; struct sockaddr_in *saddr; int found = 0; char err[PCAP_ERRBUF_SIZE + 1]; if (pcap_findalldevs(&devs, err) < 0) { wpa_printf(MSG_DEBUG, "pcap_findalldevs: %s\n", err); return -1; } for (dev = devs; dev && !found; dev = dev->next) { if (os_strcmp(dev->name, l2->ifname) != 0) continue; addr = dev->addresses; while (addr) { saddr = (struct sockaddr_in *) addr->addr; if (saddr && saddr->sin_family == AF_INET) { os_strlcpy(buf, inet_ntoa(saddr->sin_addr), len); found = 1; break; } addr = addr->next; } } pcap_freealldevs(devs); return found ? 0 : -1; } void l2_packet_notify_auth_start(struct l2_packet_data *l2) { } wpa-2.1/src/l2_packet/l2_packet_linux.c000066400000000000000000000114061227414666700200460ustar00rootroot00000000000000/* * WPA Supplicant - Layer2 packet handling with Linux packet sockets * Copyright (c) 2003-2005, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "includes.h" #include #include #include #include "common.h" #include "eloop.h" #include "l2_packet.h" struct l2_packet_data { int fd; /* packet socket for EAPOL frames */ char ifname[IFNAMSIZ + 1]; int ifindex; u8 own_addr[ETH_ALEN]; void (*rx_callback)(void *ctx, const u8 *src_addr, const u8 *buf, size_t len); void *rx_callback_ctx; int l2_hdr; /* whether to include layer 2 (Ethernet) header data * buffers */ }; int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr) { os_memcpy(addr, l2->own_addr, ETH_ALEN); return 0; } int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto, const u8 *buf, size_t len) { int ret; if (l2 == NULL) return -1; if (l2->l2_hdr) { ret = send(l2->fd, buf, len, 0); if (ret < 0) wpa_printf(MSG_ERROR, "l2_packet_send - send: %s", strerror(errno)); } else { struct sockaddr_ll ll; os_memset(&ll, 0, sizeof(ll)); ll.sll_family = AF_PACKET; ll.sll_ifindex = l2->ifindex; ll.sll_protocol = htons(proto); ll.sll_halen = ETH_ALEN; os_memcpy(ll.sll_addr, dst_addr, ETH_ALEN); ret = sendto(l2->fd, buf, len, 0, (struct sockaddr *) &ll, sizeof(ll)); if (ret < 0) { wpa_printf(MSG_ERROR, "l2_packet_send - sendto: %s", strerror(errno)); } } return ret; } static void l2_packet_receive(int sock, void *eloop_ctx, void *sock_ctx) { struct l2_packet_data *l2 = eloop_ctx; u8 buf[2300]; int res; struct sockaddr_ll ll; socklen_t fromlen; os_memset(&ll, 0, sizeof(ll)); fromlen = sizeof(ll); res = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *) &ll, &fromlen); if (res < 0) { wpa_printf(MSG_DEBUG, "l2_packet_receive - recvfrom: %s", strerror(errno)); return; } l2->rx_callback(l2->rx_callback_ctx, ll.sll_addr, buf, res); } struct l2_packet_data * l2_packet_init( const char *ifname, const u8 *own_addr, unsigned short protocol, void (*rx_callback)(void *ctx, const u8 *src_addr, const u8 *buf, size_t len), void *rx_callback_ctx, int l2_hdr) { struct l2_packet_data *l2; struct ifreq ifr; struct sockaddr_ll ll; l2 = os_zalloc(sizeof(struct l2_packet_data)); if (l2 == NULL) return NULL; os_strlcpy(l2->ifname, ifname, sizeof(l2->ifname)); l2->rx_callback = rx_callback; l2->rx_callback_ctx = rx_callback_ctx; l2->l2_hdr = l2_hdr; l2->fd = socket(PF_PACKET, l2_hdr ? SOCK_RAW : SOCK_DGRAM, htons(protocol)); if (l2->fd < 0) { wpa_printf(MSG_ERROR, "%s: socket(PF_PACKET): %s", __func__, strerror(errno)); os_free(l2); return NULL; } os_memset(&ifr, 0, sizeof(ifr)); os_strlcpy(ifr.ifr_name, l2->ifname, sizeof(ifr.ifr_name)); if (ioctl(l2->fd, SIOCGIFINDEX, &ifr) < 0) { wpa_printf(MSG_ERROR, "%s: ioctl[SIOCGIFINDEX]: %s", __func__, strerror(errno)); close(l2->fd); os_free(l2); return NULL; } l2->ifindex = ifr.ifr_ifindex; os_memset(&ll, 0, sizeof(ll)); ll.sll_family = PF_PACKET; ll.sll_ifindex = ifr.ifr_ifindex; ll.sll_protocol = htons(protocol); if (bind(l2->fd, (struct sockaddr *) &ll, sizeof(ll)) < 0) { wpa_printf(MSG_ERROR, "%s: bind[PF_PACKET]: %s", __func__, strerror(errno)); close(l2->fd); os_free(l2); return NULL; } if (ioctl(l2->fd, SIOCGIFHWADDR, &ifr) < 0) { wpa_printf(MSG_ERROR, "%s: ioctl[SIOCGIFHWADDR]: %s", __func__, strerror(errno)); close(l2->fd); os_free(l2); return NULL; } os_memcpy(l2->own_addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN); eloop_register_read_sock(l2->fd, l2_packet_receive, l2, NULL); return l2; } void l2_packet_deinit(struct l2_packet_data *l2) { if (l2 == NULL) return; if (l2->fd >= 0) { eloop_unregister_read_sock(l2->fd); close(l2->fd); } os_free(l2); } int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len) { int s; struct ifreq ifr; struct sockaddr_in *saddr; size_t res; s = socket(PF_INET, SOCK_DGRAM, 0); if (s < 0) { wpa_printf(MSG_ERROR, "%s: socket: %s", __func__, strerror(errno)); return -1; } os_memset(&ifr, 0, sizeof(ifr)); os_strlcpy(ifr.ifr_name, l2->ifname, sizeof(ifr.ifr_name)); if (ioctl(s, SIOCGIFADDR, &ifr) < 0) { if (errno != EADDRNOTAVAIL) wpa_printf(MSG_ERROR, "%s: ioctl[SIOCGIFADDR]: %s", __func__, strerror(errno)); close(s); return -1; } close(s); saddr = aliasing_hide_typecast(&ifr.ifr_addr, struct sockaddr_in); if (saddr->sin_family != AF_INET) return -1; res = os_strlcpy(buf, inet_ntoa(saddr->sin_addr), len); if (res >= len) return -1; return 0; } void l2_packet_notify_auth_start(struct l2_packet_data *l2) { } wpa-2.1/src/l2_packet/l2_packet_ndis.c000066400000000000000000000340071227414666700176460ustar00rootroot00000000000000/* * WPA Supplicant - Layer2 packet handling with Microsoft NDISUIO * Copyright (c) 2003-2006, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. * * This implementation requires Windows specific event loop implementation, * i.e., eloop_win.c. In addition, the NDISUIO connection is shared with * driver_ndis.c, so only that driver interface can be used and * CONFIG_USE_NDISUIO must be defined. * * WinXP version of the code uses overlapped I/O and a single threaded design * with callback functions from I/O code. WinCE version uses a separate RX * thread that blocks on ReadFile() whenever the media status is connected. */ #include "includes.h" #include #include #ifdef _WIN32_WCE #include #include #endif /* _WIN32_WCE */ #include "common.h" #include "eloop.h" #include "l2_packet.h" #ifndef _WIN32_WCE /* from nuiouser.h */ #define FSCTL_NDISUIO_BASE FILE_DEVICE_NETWORK #define _NDISUIO_CTL_CODE(_Function, _Method, _Access) \ CTL_CODE(FSCTL_NDISUIO_BASE, _Function, _Method, _Access) #define IOCTL_NDISUIO_SET_ETHER_TYPE \ _NDISUIO_CTL_CODE(0x202, METHOD_BUFFERED, \ FILE_READ_ACCESS | FILE_WRITE_ACCESS) #endif /* _WIN32_WCE */ /* From driver_ndis.c to shared the handle to NDISUIO */ HANDLE driver_ndis_get_ndisuio_handle(void); /* * NDISUIO supports filtering of only one ethertype at the time, so we must * fake support for two (EAPOL and RSN pre-auth) by switching to pre-auth * whenever wpa_supplicant is trying to pre-authenticate and then switching * back to EAPOL when pre-authentication has been completed. */ struct l2_packet_data; struct l2_packet_ndisuio_global { int refcount; unsigned short first_proto; struct l2_packet_data *l2[2]; #ifdef _WIN32_WCE HANDLE rx_thread; HANDLE stop_request; HANDLE ready_for_read; HANDLE rx_processed; #endif /* _WIN32_WCE */ }; static struct l2_packet_ndisuio_global *l2_ndisuio_global = NULL; struct l2_packet_data { char ifname[100]; u8 own_addr[ETH_ALEN]; void (*rx_callback)(void *ctx, const u8 *src_addr, const u8 *buf, size_t len); void *rx_callback_ctx; int l2_hdr; /* whether to include layer 2 (Ethernet) header in calls to * rx_callback and l2_packet_send() */ HANDLE rx_avail; #ifndef _WIN32_WCE OVERLAPPED rx_overlapped; #endif /* _WIN32_WCE */ u8 rx_buf[1514]; DWORD rx_written; }; int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr) { os_memcpy(addr, l2->own_addr, ETH_ALEN); return 0; } int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto, const u8 *buf, size_t len) { BOOL res; DWORD written; struct l2_ethhdr *eth; #ifndef _WIN32_WCE OVERLAPPED overlapped; #endif /* _WIN32_WCE */ OVERLAPPED *o; if (l2 == NULL) return -1; #ifdef _WIN32_WCE o = NULL; #else /* _WIN32_WCE */ os_memset(&overlapped, 0, sizeof(overlapped)); o = &overlapped; #endif /* _WIN32_WCE */ if (l2->l2_hdr) { res = WriteFile(driver_ndis_get_ndisuio_handle(), buf, len, &written, o); } else { size_t mlen = sizeof(*eth) + len; eth = os_malloc(mlen); if (eth == NULL) return -1; os_memcpy(eth->h_dest, dst_addr, ETH_ALEN); os_memcpy(eth->h_source, l2->own_addr, ETH_ALEN); eth->h_proto = htons(proto); os_memcpy(eth + 1, buf, len); res = WriteFile(driver_ndis_get_ndisuio_handle(), eth, mlen, &written, o); os_free(eth); } if (!res) { DWORD err = GetLastError(); #ifndef _WIN32_WCE if (err == ERROR_IO_PENDING) { wpa_printf(MSG_DEBUG, "L2(NDISUIO): Wait for pending " "write to complete"); res = GetOverlappedResult( driver_ndis_get_ndisuio_handle(), &overlapped, &written, TRUE); if (!res) { wpa_printf(MSG_DEBUG, "L2(NDISUIO): " "GetOverlappedResult failed: %d", (int) GetLastError()); return -1; } return 0; } #endif /* _WIN32_WCE */ wpa_printf(MSG_DEBUG, "L2(NDISUIO): WriteFile failed: %d", (int) GetLastError()); return -1; } return 0; } static void l2_packet_callback(struct l2_packet_data *l2); #ifdef _WIN32_WCE static void l2_packet_rx_thread_try_read(struct l2_packet_data *l2) { HANDLE handles[2]; wpa_printf(MSG_MSGDUMP, "l2_packet_rx_thread: -> ReadFile"); if (!ReadFile(driver_ndis_get_ndisuio_handle(), l2->rx_buf, sizeof(l2->rx_buf), &l2->rx_written, NULL)) { DWORD err = GetLastError(); wpa_printf(MSG_DEBUG, "l2_packet_rx_thread: ReadFile failed: " "%d", (int) err); /* * ReadFile on NDISUIO/WinCE returns ERROR_DEVICE_NOT_CONNECTED * error whenever the connection is not up. Yield the thread to * avoid triggering a busy loop. Connection event should stop * us from looping for long, but we need to allow enough CPU * for the main thread to process the media disconnection. */ Sleep(100); return; } wpa_printf(MSG_DEBUG, "l2_packet_rx_thread: Read %d byte packet", (int) l2->rx_written); /* * Notify the main thread about the availability of a frame and wait * for the frame to be processed. */ SetEvent(l2->rx_avail); handles[0] = l2_ndisuio_global->stop_request; handles[1] = l2_ndisuio_global->rx_processed; WaitForMultipleObjects(2, handles, FALSE, INFINITE); ResetEvent(l2_ndisuio_global->rx_processed); } static DWORD WINAPI l2_packet_rx_thread(LPVOID arg) { struct l2_packet_data *l2 = arg; DWORD res; HANDLE handles[2]; int run = 1; wpa_printf(MSG_DEBUG, "L2(NDISUIO): RX thread started"); handles[0] = l2_ndisuio_global->stop_request; handles[1] = l2_ndisuio_global->ready_for_read; /* * Unfortunately, NDISUIO on WinCE does not seem to support waiting * on the handle. There do not seem to be anything else that we could * wait for either. If one were to modify NDISUIO to set a named event * whenever packets are available, this event could be used here to * avoid having to poll for new packets or we could even move to use a * single threaded design. * * In addition, NDISUIO on WinCE is returning * ERROR_DEVICE_NOT_CONNECTED whenever ReadFile() is attempted while * the adapter is not in connected state. For now, we are just using a * local event to allow ReadFile calls only after having received NDIS * media connect event. This event could be easily converted to handle * another event if the protocol driver is replaced with somewhat more * useful design. */ while (l2_ndisuio_global && run) { res = WaitForMultipleObjects(2, handles, FALSE, INFINITE); switch (res) { case WAIT_OBJECT_0: wpa_printf(MSG_DEBUG, "l2_packet_rx_thread: Received " "request to stop RX thread"); run = 0; break; case WAIT_OBJECT_0 + 1: l2_packet_rx_thread_try_read(l2); break; case WAIT_FAILED: default: wpa_printf(MSG_DEBUG, "l2_packet_rx_thread: " "WaitForMultipleObjects failed: %d", (int) GetLastError()); run = 0; break; } } wpa_printf(MSG_DEBUG, "L2(NDISUIO): RX thread stopped"); return 0; } #else /* _WIN32_WCE */ static int l2_ndisuio_start_read(struct l2_packet_data *l2, int recursive) { os_memset(&l2->rx_overlapped, 0, sizeof(l2->rx_overlapped)); l2->rx_overlapped.hEvent = l2->rx_avail; if (!ReadFile(driver_ndis_get_ndisuio_handle(), l2->rx_buf, sizeof(l2->rx_buf), &l2->rx_written, &l2->rx_overlapped)) { DWORD err = GetLastError(); if (err != ERROR_IO_PENDING) { wpa_printf(MSG_DEBUG, "L2(NDISUIO): ReadFile failed: " "%d", (int) err); return -1; } /* * Once read is completed, l2_packet_rx_event() will be * called. */ } else { wpa_printf(MSG_DEBUG, "L2(NDISUIO): ReadFile returned data " "without wait for completion"); if (!recursive) l2_packet_callback(l2); } return 0; } #endif /* _WIN32_WCE */ static void l2_packet_callback(struct l2_packet_data *l2) { const u8 *rx_buf, *rx_src; size_t rx_len; struct l2_ethhdr *ethhdr = (struct l2_ethhdr *) l2->rx_buf; wpa_printf(MSG_DEBUG, "L2(NDISUIO): Read %d bytes", (int) l2->rx_written); if (l2->l2_hdr || l2->rx_written < sizeof(*ethhdr)) { rx_buf = (u8 *) ethhdr; rx_len = l2->rx_written; } else { rx_buf = (u8 *) (ethhdr + 1); rx_len = l2->rx_written - sizeof(*ethhdr); } rx_src = ethhdr->h_source; l2->rx_callback(l2->rx_callback_ctx, rx_src, rx_buf, rx_len); #ifndef _WIN32_WCE l2_ndisuio_start_read(l2, 1); #endif /* _WIN32_WCE */ } static void l2_packet_rx_event(void *eloop_data, void *user_data) { struct l2_packet_data *l2 = eloop_data; if (l2_ndisuio_global) l2 = l2_ndisuio_global->l2[l2_ndisuio_global->refcount - 1]; ResetEvent(l2->rx_avail); #ifndef _WIN32_WCE if (!GetOverlappedResult(driver_ndis_get_ndisuio_handle(), &l2->rx_overlapped, &l2->rx_written, FALSE)) { wpa_printf(MSG_DEBUG, "L2(NDISUIO): GetOverlappedResult " "failed: %d", (int) GetLastError()); return; } #endif /* _WIN32_WCE */ l2_packet_callback(l2); #ifdef _WIN32_WCE SetEvent(l2_ndisuio_global->rx_processed); #endif /* _WIN32_WCE */ } static int l2_ndisuio_set_ether_type(unsigned short protocol) { USHORT proto = htons(protocol); DWORD written; if (!DeviceIoControl(driver_ndis_get_ndisuio_handle(), IOCTL_NDISUIO_SET_ETHER_TYPE, &proto, sizeof(proto), NULL, 0, &written, NULL)) { wpa_printf(MSG_ERROR, "L2(NDISUIO): " "IOCTL_NDISUIO_SET_ETHER_TYPE failed: %d", (int) GetLastError()); return -1; } return 0; } struct l2_packet_data * l2_packet_init( const char *ifname, const u8 *own_addr, unsigned short protocol, void (*rx_callback)(void *ctx, const u8 *src_addr, const u8 *buf, size_t len), void *rx_callback_ctx, int l2_hdr) { struct l2_packet_data *l2; if (l2_ndisuio_global == NULL) { l2_ndisuio_global = os_zalloc(sizeof(*l2_ndisuio_global)); if (l2_ndisuio_global == NULL) return NULL; l2_ndisuio_global->first_proto = protocol; } if (l2_ndisuio_global->refcount >= 2) { wpa_printf(MSG_ERROR, "L2(NDISUIO): Not more than two " "simultaneous connections allowed"); return NULL; } l2_ndisuio_global->refcount++; l2 = os_zalloc(sizeof(struct l2_packet_data)); if (l2 == NULL) return NULL; l2_ndisuio_global->l2[l2_ndisuio_global->refcount - 1] = l2; os_strlcpy(l2->ifname, ifname, sizeof(l2->ifname)); l2->rx_callback = rx_callback; l2->rx_callback_ctx = rx_callback_ctx; l2->l2_hdr = l2_hdr; if (own_addr) os_memcpy(l2->own_addr, own_addr, ETH_ALEN); if (l2_ndisuio_set_ether_type(protocol) < 0) { os_free(l2); return NULL; } if (l2_ndisuio_global->refcount > 1) { wpa_printf(MSG_DEBUG, "L2(NDISUIO): Temporarily setting " "filtering ethertype to %04x", protocol); if (l2_ndisuio_global->l2[0]) l2->rx_avail = l2_ndisuio_global->l2[0]->rx_avail; return l2; } l2->rx_avail = CreateEvent(NULL, TRUE, FALSE, NULL); if (l2->rx_avail == NULL) { os_free(l2); return NULL; } eloop_register_event(l2->rx_avail, sizeof(l2->rx_avail), l2_packet_rx_event, l2, NULL); #ifdef _WIN32_WCE l2_ndisuio_global->stop_request = CreateEvent(NULL, TRUE, FALSE, NULL); /* * This event is being set based on media connect/disconnect * notifications in driver_ndis.c. */ l2_ndisuio_global->ready_for_read = CreateEvent(NULL, TRUE, FALSE, TEXT("WpaSupplicantConnected")); l2_ndisuio_global->rx_processed = CreateEvent(NULL, TRUE, FALSE, NULL); if (l2_ndisuio_global->stop_request == NULL || l2_ndisuio_global->ready_for_read == NULL || l2_ndisuio_global->rx_processed == NULL) { if (l2_ndisuio_global->stop_request) { CloseHandle(l2_ndisuio_global->stop_request); l2_ndisuio_global->stop_request = NULL; } if (l2_ndisuio_global->ready_for_read) { CloseHandle(l2_ndisuio_global->ready_for_read); l2_ndisuio_global->ready_for_read = NULL; } if (l2_ndisuio_global->rx_processed) { CloseHandle(l2_ndisuio_global->rx_processed); l2_ndisuio_global->rx_processed = NULL; } eloop_unregister_event(l2->rx_avail, sizeof(l2->rx_avail)); os_free(l2); return NULL; } l2_ndisuio_global->rx_thread = CreateThread(NULL, 0, l2_packet_rx_thread, l2, 0, NULL); if (l2_ndisuio_global->rx_thread == NULL) { wpa_printf(MSG_INFO, "L2(NDISUIO): Failed to create RX " "thread: %d", (int) GetLastError()); eloop_unregister_event(l2->rx_avail, sizeof(l2->rx_avail)); CloseHandle(l2_ndisuio_global->stop_request); l2_ndisuio_global->stop_request = NULL; os_free(l2); return NULL; } #else /* _WIN32_WCE */ l2_ndisuio_start_read(l2, 0); #endif /* _WIN32_WCE */ return l2; } void l2_packet_deinit(struct l2_packet_data *l2) { if (l2 == NULL) return; if (l2_ndisuio_global) { l2_ndisuio_global->refcount--; l2_ndisuio_global->l2[l2_ndisuio_global->refcount] = NULL; if (l2_ndisuio_global->refcount) { wpa_printf(MSG_DEBUG, "L2(NDISUIO): restore filtering " "ethertype to %04x", l2_ndisuio_global->first_proto); l2_ndisuio_set_ether_type( l2_ndisuio_global->first_proto); return; } #ifdef _WIN32_WCE wpa_printf(MSG_DEBUG, "L2(NDISUIO): Waiting for RX thread to " "stop"); SetEvent(l2_ndisuio_global->stop_request); /* * Cancel pending ReadFile() in the RX thread (if we were still * connected at this point). */ if (!DeviceIoControl(driver_ndis_get_ndisuio_handle(), IOCTL_CANCEL_READ, NULL, 0, NULL, 0, NULL, NULL)) { wpa_printf(MSG_DEBUG, "L2(NDISUIO): IOCTL_CANCEL_READ " "failed: %d", (int) GetLastError()); /* RX thread will exit blocking ReadFile once NDISUIO * notices that the adapter is disconnected. */ } WaitForSingleObject(l2_ndisuio_global->rx_thread, INFINITE); wpa_printf(MSG_DEBUG, "L2(NDISUIO): RX thread exited"); CloseHandle(l2_ndisuio_global->rx_thread); CloseHandle(l2_ndisuio_global->stop_request); CloseHandle(l2_ndisuio_global->ready_for_read); CloseHandle(l2_ndisuio_global->rx_processed); #endif /* _WIN32_WCE */ os_free(l2_ndisuio_global); l2_ndisuio_global = NULL; } #ifndef _WIN32_WCE CancelIo(driver_ndis_get_ndisuio_handle()); #endif /* _WIN32_WCE */ eloop_unregister_event(l2->rx_avail, sizeof(l2->rx_avail)); CloseHandle(l2->rx_avail); os_free(l2); } int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len) { return -1; } void l2_packet_notify_auth_start(struct l2_packet_data *l2) { } wpa-2.1/src/l2_packet/l2_packet_none.c000066400000000000000000000046121227414666700176470ustar00rootroot00000000000000/* * WPA Supplicant - Layer2 packet handling example with dummy functions * Copyright (c) 2003-2005, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. * * This file can be used as a starting point for layer2 packet implementation. */ #include "includes.h" #include "common.h" #include "eloop.h" #include "l2_packet.h" struct l2_packet_data { char ifname[17]; u8 own_addr[ETH_ALEN]; void (*rx_callback)(void *ctx, const u8 *src_addr, const u8 *buf, size_t len); void *rx_callback_ctx; int l2_hdr; /* whether to include layer 2 (Ethernet) header data * buffers */ int fd; }; int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr) { os_memcpy(addr, l2->own_addr, ETH_ALEN); return 0; } int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto, const u8 *buf, size_t len) { if (l2 == NULL) return -1; /* * TODO: Send frame (may need different implementation depending on * whether l2->l2_hdr is set). */ return 0; } static void l2_packet_receive(int sock, void *eloop_ctx, void *sock_ctx) { struct l2_packet_data *l2 = eloop_ctx; u8 buf[2300]; int res; /* TODO: receive frame (e.g., recv() using sock */ buf[0] = 0; res = 0; l2->rx_callback(l2->rx_callback_ctx, NULL /* TODO: src addr */, buf, res); } struct l2_packet_data * l2_packet_init( const char *ifname, const u8 *own_addr, unsigned short protocol, void (*rx_callback)(void *ctx, const u8 *src_addr, const u8 *buf, size_t len), void *rx_callback_ctx, int l2_hdr) { struct l2_packet_data *l2; l2 = os_zalloc(sizeof(struct l2_packet_data)); if (l2 == NULL) return NULL; os_strlcpy(l2->ifname, ifname, sizeof(l2->ifname)); l2->rx_callback = rx_callback; l2->rx_callback_ctx = rx_callback_ctx; l2->l2_hdr = l2_hdr; /* * TODO: open connection for receiving frames */ l2->fd = -1; eloop_register_read_sock(l2->fd, l2_packet_receive, l2, NULL); return l2; } void l2_packet_deinit(struct l2_packet_data *l2) { if (l2 == NULL) return; if (l2->fd >= 0) { eloop_unregister_read_sock(l2->fd); /* TODO: close connection */ } os_free(l2); } int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len) { /* TODO: get interface IP address */ return -1; } void l2_packet_notify_auth_start(struct l2_packet_data *l2) { /* This function can be left empty */ } wpa-2.1/src/l2_packet/l2_packet_pcap.c000066400000000000000000000223271227414666700176360ustar00rootroot00000000000000/* * WPA Supplicant - Layer2 packet handling with libpcap/libdnet and WinPcap * Copyright (c) 2003-2006, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "includes.h" #ifndef CONFIG_NATIVE_WINDOWS #include #endif /* CONFIG_NATIVE_WINDOWS */ #include #ifndef CONFIG_WINPCAP #include #endif /* CONFIG_WINPCAP */ #include "common.h" #include "eloop.h" #include "l2_packet.h" static const u8 pae_group_addr[ETH_ALEN] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 }; struct l2_packet_data { pcap_t *pcap; #ifdef CONFIG_WINPCAP unsigned int num_fast_poll; #else /* CONFIG_WINPCAP */ eth_t *eth; #endif /* CONFIG_WINPCAP */ char ifname[100]; u8 own_addr[ETH_ALEN]; void (*rx_callback)(void *ctx, const u8 *src_addr, const u8 *buf, size_t len); void *rx_callback_ctx; int l2_hdr; /* whether to include layer 2 (Ethernet) header in calls * to rx_callback */ }; int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr) { os_memcpy(addr, l2->own_addr, ETH_ALEN); return 0; } #ifndef CONFIG_WINPCAP static int l2_packet_init_libdnet(struct l2_packet_data *l2) { eth_addr_t own_addr; l2->eth = eth_open(l2->ifname); if (!l2->eth) { printf("Failed to open interface '%s'.\n", l2->ifname); perror("eth_open"); return -1; } if (eth_get(l2->eth, &own_addr) < 0) { printf("Failed to get own hw address from interface '%s'.\n", l2->ifname); perror("eth_get"); eth_close(l2->eth); l2->eth = NULL; return -1; } os_memcpy(l2->own_addr, own_addr.data, ETH_ALEN); return 0; } #endif /* CONFIG_WINPCAP */ int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto, const u8 *buf, size_t len) { int ret; struct l2_ethhdr *eth; if (l2 == NULL) return -1; if (l2->l2_hdr) { #ifdef CONFIG_WINPCAP ret = pcap_sendpacket(l2->pcap, buf, len); #else /* CONFIG_WINPCAP */ ret = eth_send(l2->eth, buf, len); #endif /* CONFIG_WINPCAP */ } else { size_t mlen = sizeof(*eth) + len; eth = os_malloc(mlen); if (eth == NULL) return -1; os_memcpy(eth->h_dest, dst_addr, ETH_ALEN); os_memcpy(eth->h_source, l2->own_addr, ETH_ALEN); eth->h_proto = htons(proto); os_memcpy(eth + 1, buf, len); #ifdef CONFIG_WINPCAP ret = pcap_sendpacket(l2->pcap, (u8 *) eth, mlen); #else /* CONFIG_WINPCAP */ ret = eth_send(l2->eth, (u8 *) eth, mlen); #endif /* CONFIG_WINPCAP */ os_free(eth); } return ret; } #ifndef CONFIG_WINPCAP static void l2_packet_receive(int sock, void *eloop_ctx, void *sock_ctx) { struct l2_packet_data *l2 = eloop_ctx; pcap_t *pcap = sock_ctx; struct pcap_pkthdr hdr; const u_char *packet; struct l2_ethhdr *ethhdr; unsigned char *buf; size_t len; packet = pcap_next(pcap, &hdr); if (packet == NULL || hdr.caplen < sizeof(*ethhdr)) return; ethhdr = (struct l2_ethhdr *) packet; if (l2->l2_hdr) { buf = (unsigned char *) ethhdr; len = hdr.caplen; } else { buf = (unsigned char *) (ethhdr + 1); len = hdr.caplen - sizeof(*ethhdr); } l2->rx_callback(l2->rx_callback_ctx, ethhdr->h_source, buf, len); } #endif /* CONFIG_WINPCAP */ #ifdef CONFIG_WINPCAP static void l2_packet_receive_cb(u_char *user, const struct pcap_pkthdr *hdr, const u_char *pkt_data) { struct l2_packet_data *l2 = (struct l2_packet_data *) user; struct l2_ethhdr *ethhdr; unsigned char *buf; size_t len; if (pkt_data == NULL || hdr->caplen < sizeof(*ethhdr)) return; ethhdr = (struct l2_ethhdr *) pkt_data; if (l2->l2_hdr) { buf = (unsigned char *) ethhdr; len = hdr->caplen; } else { buf = (unsigned char *) (ethhdr + 1); len = hdr->caplen - sizeof(*ethhdr); } l2->rx_callback(l2->rx_callback_ctx, ethhdr->h_source, buf, len); /* * Use shorter poll interval for 3 seconds to reduce latency during key * handshake. */ l2->num_fast_poll = 3 * 50; } static void l2_packet_receive_timeout(void *eloop_ctx, void *timeout_ctx) { struct l2_packet_data *l2 = eloop_ctx; pcap_t *pcap = timeout_ctx; int timeout; if (l2->num_fast_poll > 0) { timeout = 20000; l2->num_fast_poll--; } else timeout = 100000; /* Register new timeout before calling l2_packet_receive() since * receive handler may free this l2_packet instance (which will * cancel this timeout). */ eloop_register_timeout(0, timeout, l2_packet_receive_timeout, l2, pcap); pcap_dispatch(pcap, 10, l2_packet_receive_cb, (u_char *) l2); } #endif /* CONFIG_WINPCAP */ static int l2_packet_init_libpcap(struct l2_packet_data *l2, unsigned short protocol) { bpf_u_int32 pcap_maskp, pcap_netp; char pcap_filter[200], pcap_err[PCAP_ERRBUF_SIZE]; struct bpf_program pcap_fp; #ifdef CONFIG_WINPCAP char ifname[128]; os_snprintf(ifname, sizeof(ifname), "\\Device\\NPF_%s", l2->ifname); pcap_lookupnet(ifname, &pcap_netp, &pcap_maskp, pcap_err); l2->pcap = pcap_open_live(ifname, 2500, 0, 10, pcap_err); if (l2->pcap == NULL) { fprintf(stderr, "pcap_open_live: %s\n", pcap_err); fprintf(stderr, "ifname='%s'\n", ifname); return -1; } if (pcap_setnonblock(l2->pcap, 1, pcap_err) < 0) fprintf(stderr, "pcap_setnonblock: %s\n", pcap_geterr(l2->pcap)); #else /* CONFIG_WINPCAP */ pcap_lookupnet(l2->ifname, &pcap_netp, &pcap_maskp, pcap_err); l2->pcap = pcap_open_live(l2->ifname, 2500, 0, 10, pcap_err); if (l2->pcap == NULL) { fprintf(stderr, "pcap_open_live: %s\n", pcap_err); fprintf(stderr, "ifname='%s'\n", l2->ifname); return -1; } if (pcap_datalink(l2->pcap) != DLT_EN10MB && pcap_set_datalink(l2->pcap, DLT_EN10MB) < 0) { fprintf(stderr, "pcap_set_datalink(DLT_EN10MB): %s\n", pcap_geterr(l2->pcap)); return -1; } #endif /* CONFIG_WINPCAP */ os_snprintf(pcap_filter, sizeof(pcap_filter), "not ether src " MACSTR " and " "( ether dst " MACSTR " or ether dst " MACSTR " ) and " "ether proto 0x%x", MAC2STR(l2->own_addr), /* do not receive own packets */ MAC2STR(l2->own_addr), MAC2STR(pae_group_addr), protocol); if (pcap_compile(l2->pcap, &pcap_fp, pcap_filter, 1, pcap_netp) < 0) { fprintf(stderr, "pcap_compile: %s\n", pcap_geterr(l2->pcap)); return -1; } if (pcap_setfilter(l2->pcap, &pcap_fp) < 0) { fprintf(stderr, "pcap_setfilter: %s\n", pcap_geterr(l2->pcap)); return -1; } pcap_freecode(&pcap_fp); #ifdef BIOCIMMEDIATE /* * When libpcap uses BPF we must enable "immediate mode" to * receive frames right away; otherwise the system may * buffer them for us. */ { unsigned int on = 1; if (ioctl(pcap_fileno(l2->pcap), BIOCIMMEDIATE, &on) < 0) { fprintf(stderr, "%s: cannot enable immediate mode on " "interface %s: %s\n", __func__, l2->ifname, strerror(errno)); /* XXX should we fail? */ } } #endif /* BIOCIMMEDIATE */ #ifdef CONFIG_WINPCAP eloop_register_timeout(0, 100000, l2_packet_receive_timeout, l2, l2->pcap); #else /* CONFIG_WINPCAP */ eloop_register_read_sock(pcap_get_selectable_fd(l2->pcap), l2_packet_receive, l2, l2->pcap); #endif /* CONFIG_WINPCAP */ return 0; } struct l2_packet_data * l2_packet_init( const char *ifname, const u8 *own_addr, unsigned short protocol, void (*rx_callback)(void *ctx, const u8 *src_addr, const u8 *buf, size_t len), void *rx_callback_ctx, int l2_hdr) { struct l2_packet_data *l2; l2 = os_zalloc(sizeof(struct l2_packet_data)); if (l2 == NULL) return NULL; os_strlcpy(l2->ifname, ifname, sizeof(l2->ifname)); l2->rx_callback = rx_callback; l2->rx_callback_ctx = rx_callback_ctx; l2->l2_hdr = l2_hdr; #ifdef CONFIG_WINPCAP if (own_addr) os_memcpy(l2->own_addr, own_addr, ETH_ALEN); #else /* CONFIG_WINPCAP */ if (l2_packet_init_libdnet(l2)) return NULL; #endif /* CONFIG_WINPCAP */ if (l2_packet_init_libpcap(l2, protocol)) { #ifndef CONFIG_WINPCAP eth_close(l2->eth); #endif /* CONFIG_WINPCAP */ os_free(l2); return NULL; } return l2; } void l2_packet_deinit(struct l2_packet_data *l2) { if (l2 == NULL) return; #ifdef CONFIG_WINPCAP eloop_cancel_timeout(l2_packet_receive_timeout, l2, l2->pcap); #else /* CONFIG_WINPCAP */ if (l2->eth) eth_close(l2->eth); eloop_unregister_read_sock(pcap_get_selectable_fd(l2->pcap)); #endif /* CONFIG_WINPCAP */ if (l2->pcap) pcap_close(l2->pcap); os_free(l2); } int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len) { pcap_if_t *devs, *dev; struct pcap_addr *addr; struct sockaddr_in *saddr; int found = 0; char err[PCAP_ERRBUF_SIZE + 1]; if (pcap_findalldevs(&devs, err) < 0) { wpa_printf(MSG_DEBUG, "pcap_findalldevs: %s\n", err); return -1; } for (dev = devs; dev && !found; dev = dev->next) { if (os_strcmp(dev->name, l2->ifname) != 0) continue; addr = dev->addresses; while (addr) { saddr = (struct sockaddr_in *) addr->addr; if (saddr && saddr->sin_family == AF_INET) { os_strlcpy(buf, inet_ntoa(saddr->sin_addr), len); found = 1; break; } addr = addr->next; } } pcap_freealldevs(devs); return found ? 0 : -1; } void l2_packet_notify_auth_start(struct l2_packet_data *l2) { #ifdef CONFIG_WINPCAP /* * Use shorter poll interval for 3 seconds to reduce latency during key * handshake. */ l2->num_fast_poll = 3 * 50; eloop_cancel_timeout(l2_packet_receive_timeout, l2, l2->pcap); eloop_register_timeout(0, 10000, l2_packet_receive_timeout, l2, l2->pcap); #endif /* CONFIG_WINPCAP */ } wpa-2.1/src/l2_packet/l2_packet_privsep.c000066400000000000000000000132741227414666700204040ustar00rootroot00000000000000/* * WPA Supplicant - Layer2 packet handling with privilege separation * Copyright (c) 2007, 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.h" #include "eloop.h" #include "l2_packet.h" #include "common/privsep_commands.h" struct l2_packet_data { int fd; /* UNIX domain socket for privsep access */ void (*rx_callback)(void *ctx, const u8 *src_addr, const u8 *buf, size_t len); void *rx_callback_ctx; u8 own_addr[ETH_ALEN]; char *own_socket_path; struct sockaddr_un priv_addr; }; static int wpa_priv_cmd(struct l2_packet_data *l2, int cmd, const void *data, size_t data_len) { struct msghdr msg; struct iovec io[2]; io[0].iov_base = &cmd; io[0].iov_len = sizeof(cmd); io[1].iov_base = (u8 *) data; io[1].iov_len = data_len; os_memset(&msg, 0, sizeof(msg)); msg.msg_iov = io; msg.msg_iovlen = data ? 2 : 1; msg.msg_name = &l2->priv_addr; msg.msg_namelen = sizeof(l2->priv_addr); if (sendmsg(l2->fd, &msg, 0) < 0) { perror("L2: sendmsg(cmd)"); return -1; } return 0; } int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr) { os_memcpy(addr, l2->own_addr, ETH_ALEN); return 0; } int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto, const u8 *buf, size_t len) { struct msghdr msg; struct iovec io[4]; int cmd = PRIVSEP_CMD_L2_SEND; io[0].iov_base = &cmd; io[0].iov_len = sizeof(cmd); io[1].iov_base = &dst_addr; io[1].iov_len = ETH_ALEN; io[2].iov_base = &proto; io[2].iov_len = 2; io[3].iov_base = (u8 *) buf; io[3].iov_len = len; os_memset(&msg, 0, sizeof(msg)); msg.msg_iov = io; msg.msg_iovlen = 4; msg.msg_name = &l2->priv_addr; msg.msg_namelen = sizeof(l2->priv_addr); if (sendmsg(l2->fd, &msg, 0) < 0) { perror("L2: sendmsg(packet_send)"); return -1; } return 0; } static void l2_packet_receive(int sock, void *eloop_ctx, void *sock_ctx) { struct l2_packet_data *l2 = eloop_ctx; u8 buf[2300]; int res; struct sockaddr_un from; socklen_t fromlen = sizeof(from); os_memset(&from, 0, sizeof(from)); res = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *) &from, &fromlen); if (res < 0) { perror("l2_packet_receive - recvfrom"); return; } if (res < ETH_ALEN) { wpa_printf(MSG_DEBUG, "L2: Too show packet received"); return; } if (from.sun_family != AF_UNIX || os_strncmp(from.sun_path, l2->priv_addr.sun_path, sizeof(from.sun_path)) != 0) { wpa_printf(MSG_DEBUG, "L2: Received message from unexpected " "source"); return; } l2->rx_callback(l2->rx_callback_ctx, buf, buf + ETH_ALEN, res - ETH_ALEN); } struct l2_packet_data * l2_packet_init( const char *ifname, const u8 *own_addr, unsigned short protocol, void (*rx_callback)(void *ctx, const u8 *src_addr, const u8 *buf, size_t len), void *rx_callback_ctx, int l2_hdr) { struct l2_packet_data *l2; char *own_dir = "/tmp"; char *priv_dir = "/var/run/wpa_priv"; size_t len; static unsigned int counter = 0; struct sockaddr_un addr; fd_set rfds; struct timeval tv; int res; u8 reply[ETH_ALEN + 1]; int reg_cmd[2]; l2 = os_zalloc(sizeof(struct l2_packet_data)); if (l2 == NULL) return NULL; l2->rx_callback = rx_callback; l2->rx_callback_ctx = rx_callback_ctx; len = os_strlen(own_dir) + 50; l2->own_socket_path = os_malloc(len); if (l2->own_socket_path == NULL) { os_free(l2); return NULL; } os_snprintf(l2->own_socket_path, len, "%s/wpa_privsep-l2-%d-%d", own_dir, getpid(), counter++); l2->priv_addr.sun_family = AF_UNIX; os_snprintf(l2->priv_addr.sun_path, sizeof(l2->priv_addr.sun_path), "%s/%s", priv_dir, ifname); l2->fd = socket(PF_UNIX, SOCK_DGRAM, 0); if (l2->fd < 0) { perror("socket(PF_UNIX)"); os_free(l2->own_socket_path); l2->own_socket_path = NULL; os_free(l2); return NULL; } os_memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; os_strlcpy(addr.sun_path, l2->own_socket_path, sizeof(addr.sun_path)); if (bind(l2->fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { perror("l2-pkt-privsep: bind(PF_UNIX)"); goto fail; } reg_cmd[0] = protocol; reg_cmd[1] = l2_hdr; if (wpa_priv_cmd(l2, PRIVSEP_CMD_L2_REGISTER, reg_cmd, sizeof(reg_cmd)) < 0) { wpa_printf(MSG_ERROR, "L2: Failed to register with wpa_priv"); goto fail; } FD_ZERO(&rfds); FD_SET(l2->fd, &rfds); tv.tv_sec = 5; tv.tv_usec = 0; res = select(l2->fd + 1, &rfds, NULL, NULL, &tv); if (res < 0 && errno != EINTR) { perror("select"); goto fail; } if (FD_ISSET(l2->fd, &rfds)) { res = recv(l2->fd, reply, sizeof(reply), 0); if (res < 0) { perror("recv"); goto fail; } } else { wpa_printf(MSG_DEBUG, "L2: Timeout while waiting for " "registration reply"); goto fail; } if (res != ETH_ALEN) { wpa_printf(MSG_DEBUG, "L2: Unexpected registration reply " "(len=%d)", res); } os_memcpy(l2->own_addr, reply, ETH_ALEN); eloop_register_read_sock(l2->fd, l2_packet_receive, l2, NULL); return l2; fail: close(l2->fd); l2->fd = -1; unlink(l2->own_socket_path); os_free(l2->own_socket_path); l2->own_socket_path = NULL; os_free(l2); return NULL; } void l2_packet_deinit(struct l2_packet_data *l2) { if (l2 == NULL) return; if (l2->fd >= 0) { wpa_priv_cmd(l2, PRIVSEP_CMD_L2_UNREGISTER, NULL, 0); eloop_unregister_read_sock(l2->fd); close(l2->fd); } if (l2->own_socket_path) { unlink(l2->own_socket_path); os_free(l2->own_socket_path); } os_free(l2); } int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len) { /* TODO */ return -1; } void l2_packet_notify_auth_start(struct l2_packet_data *l2) { wpa_priv_cmd(l2, PRIVSEP_CMD_L2_NOTIFY_AUTH_START, NULL, 0); } wpa-2.1/src/l2_packet/l2_packet_winpcap.c000066400000000000000000000220111227414666700203420ustar00rootroot00000000000000/* * WPA Supplicant - Layer2 packet handling with WinPcap RX thread * Copyright (c) 2003-2006, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. * * This l2_packet implementation is explicitly for WinPcap and Windows events. * l2_packet_pcap.c has support for WinPcap, but it requires polling to receive * frames which means relatively long latency for EAPOL RX processing. The * implementation here uses a separate thread to allow WinPcap to be receiving * all the time to reduce latency for EAPOL receiving from about 100 ms to 3 ms * when comparing l2_packet_pcap.c to l2_packet_winpcap.c. Extra sleep of 50 ms * is added in to receive thread whenever no EAPOL frames has been received for * a while. Whenever an EAPOL handshake is expected, this sleep is removed. * * The RX thread receives a frame and signals main thread through Windows event * about the availability of a new frame. Processing the received frame is * synchronized with pair of Windows events so that no extra buffer or queuing * mechanism is needed. This implementation requires Windows specific event * loop implementation, i.e., eloop_win.c. * * WinPcap has pcap_getevent() that could, in theory at least, be used to * implement this kind of waiting with a simpler single-thread design. However, * that event handle is not really signaled immediately when receiving each * frame, so it does not really work for this kind of use. */ #include "includes.h" #include #include "common.h" #include "eloop.h" #include "l2_packet.h" static const u8 pae_group_addr[ETH_ALEN] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 }; /* * Number of pcap_dispatch() iterations to do without extra wait after each * received EAPOL packet or authentication notification. This is used to reduce * latency for EAPOL receive. */ static const size_t no_wait_count = 750; struct l2_packet_data { pcap_t *pcap; unsigned int num_fast_poll; char ifname[100]; u8 own_addr[ETH_ALEN]; void (*rx_callback)(void *ctx, const u8 *src_addr, const u8 *buf, size_t len); void *rx_callback_ctx; int l2_hdr; /* whether to include layer 2 (Ethernet) header in calls to * rx_callback and l2_packet_send() */ int running; HANDLE rx_avail, rx_done, rx_thread, rx_thread_done, rx_notify; u8 *rx_buf, *rx_src; size_t rx_len; size_t rx_no_wait; }; int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr) { os_memcpy(addr, l2->own_addr, ETH_ALEN); return 0; } int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto, const u8 *buf, size_t len) { int ret; struct l2_ethhdr *eth; if (l2 == NULL) return -1; if (l2->l2_hdr) { ret = pcap_sendpacket(l2->pcap, buf, len); } else { size_t mlen = sizeof(*eth) + len; eth = os_malloc(mlen); if (eth == NULL) return -1; os_memcpy(eth->h_dest, dst_addr, ETH_ALEN); os_memcpy(eth->h_source, l2->own_addr, ETH_ALEN); eth->h_proto = htons(proto); os_memcpy(eth + 1, buf, len); ret = pcap_sendpacket(l2->pcap, (u8 *) eth, mlen); os_free(eth); } return ret; } /* pcap_dispatch() callback for the RX thread */ static void l2_packet_receive_cb(u_char *user, const struct pcap_pkthdr *hdr, const u_char *pkt_data) { struct l2_packet_data *l2 = (struct l2_packet_data *) user; struct l2_ethhdr *ethhdr; if (pkt_data == NULL || hdr->caplen < sizeof(*ethhdr)) return; ethhdr = (struct l2_ethhdr *) pkt_data; if (l2->l2_hdr) { l2->rx_buf = (u8 *) ethhdr; l2->rx_len = hdr->caplen; } else { l2->rx_buf = (u8 *) (ethhdr + 1); l2->rx_len = hdr->caplen - sizeof(*ethhdr); } l2->rx_src = ethhdr->h_source; SetEvent(l2->rx_avail); WaitForSingleObject(l2->rx_done, INFINITE); ResetEvent(l2->rx_done); l2->rx_no_wait = no_wait_count; } /* main RX loop that is running in a separate thread */ static DWORD WINAPI l2_packet_receive_thread(LPVOID arg) { struct l2_packet_data *l2 = arg; while (l2->running) { pcap_dispatch(l2->pcap, 1, l2_packet_receive_cb, (u_char *) l2); if (l2->rx_no_wait > 0) l2->rx_no_wait--; if (WaitForSingleObject(l2->rx_notify, l2->rx_no_wait ? 0 : 50) == WAIT_OBJECT_0) { l2->rx_no_wait = no_wait_count; ResetEvent(l2->rx_notify); } } SetEvent(l2->rx_thread_done); ExitThread(0); return 0; } /* main thread RX event handler */ static void l2_packet_rx_event(void *eloop_data, void *user_data) { struct l2_packet_data *l2 = eloop_data; l2->rx_callback(l2->rx_callback_ctx, l2->rx_src, l2->rx_buf, l2->rx_len); ResetEvent(l2->rx_avail); SetEvent(l2->rx_done); } static int l2_packet_init_libpcap(struct l2_packet_data *l2, unsigned short protocol) { bpf_u_int32 pcap_maskp, pcap_netp; char pcap_filter[200], pcap_err[PCAP_ERRBUF_SIZE]; struct bpf_program pcap_fp; pcap_lookupnet(l2->ifname, &pcap_netp, &pcap_maskp, pcap_err); l2->pcap = pcap_open_live(l2->ifname, 2500, 0, 1, pcap_err); if (l2->pcap == NULL) { fprintf(stderr, "pcap_open_live: %s\n", pcap_err); fprintf(stderr, "ifname='%s'\n", l2->ifname); return -1; } os_snprintf(pcap_filter, sizeof(pcap_filter), "not ether src " MACSTR " and " "( ether dst " MACSTR " or ether dst " MACSTR " ) and " "ether proto 0x%x", MAC2STR(l2->own_addr), /* do not receive own packets */ MAC2STR(l2->own_addr), MAC2STR(pae_group_addr), protocol); if (pcap_compile(l2->pcap, &pcap_fp, pcap_filter, 1, pcap_netp) < 0) { fprintf(stderr, "pcap_compile: %s\n", pcap_geterr(l2->pcap)); return -1; } if (pcap_setfilter(l2->pcap, &pcap_fp) < 0) { fprintf(stderr, "pcap_setfilter: %s\n", pcap_geterr(l2->pcap)); return -1; } pcap_freecode(&pcap_fp); return 0; } struct l2_packet_data * l2_packet_init( const char *ifname, const u8 *own_addr, unsigned short protocol, void (*rx_callback)(void *ctx, const u8 *src_addr, const u8 *buf, size_t len), void *rx_callback_ctx, int l2_hdr) { struct l2_packet_data *l2; DWORD thread_id; l2 = os_zalloc(sizeof(struct l2_packet_data)); if (l2 == NULL) return NULL; if (os_strncmp(ifname, "\\Device\\NPF_", 12) == 0) os_strlcpy(l2->ifname, ifname, sizeof(l2->ifname)); else os_snprintf(l2->ifname, sizeof(l2->ifname), "\\Device\\NPF_%s", ifname); l2->rx_callback = rx_callback; l2->rx_callback_ctx = rx_callback_ctx; l2->l2_hdr = l2_hdr; if (own_addr) os_memcpy(l2->own_addr, own_addr, ETH_ALEN); if (l2_packet_init_libpcap(l2, protocol)) { os_free(l2); return NULL; } l2->rx_avail = CreateEvent(NULL, TRUE, FALSE, NULL); l2->rx_done = CreateEvent(NULL, TRUE, FALSE, NULL); l2->rx_notify = CreateEvent(NULL, TRUE, FALSE, NULL); if (l2->rx_avail == NULL || l2->rx_done == NULL || l2->rx_notify == NULL) { CloseHandle(l2->rx_avail); CloseHandle(l2->rx_done); CloseHandle(l2->rx_notify); pcap_close(l2->pcap); os_free(l2); return NULL; } eloop_register_event(l2->rx_avail, sizeof(l2->rx_avail), l2_packet_rx_event, l2, NULL); l2->running = 1; l2->rx_thread = CreateThread(NULL, 0, l2_packet_receive_thread, l2, 0, &thread_id); return l2; } static void l2_packet_deinit_timeout(void *eloop_ctx, void *timeout_ctx) { struct l2_packet_data *l2 = eloop_ctx; if (l2->rx_thread_done && WaitForSingleObject(l2->rx_thread_done, 2000) != WAIT_OBJECT_0) { wpa_printf(MSG_DEBUG, "l2_packet_winpcap: RX thread did not " "exit - kill it\n"); TerminateThread(l2->rx_thread, 0); } CloseHandle(l2->rx_thread_done); CloseHandle(l2->rx_thread); if (l2->pcap) pcap_close(l2->pcap); eloop_unregister_event(l2->rx_avail, sizeof(l2->rx_avail)); CloseHandle(l2->rx_avail); CloseHandle(l2->rx_done); CloseHandle(l2->rx_notify); os_free(l2); } void l2_packet_deinit(struct l2_packet_data *l2) { if (l2 == NULL) return; l2->rx_thread_done = CreateEvent(NULL, TRUE, FALSE, NULL); l2->running = 0; pcap_breakloop(l2->pcap); /* * RX thread may be waiting in l2_packet_receive_cb() for l2->rx_done * event and this event is set in l2_packet_rx_event(). However, * l2_packet_deinit() may end up being called from l2->rx_callback(), * so we need to return from here and complete deinitialization in * a registered timeout to avoid having to forcefully kill the RX * thread. */ eloop_register_timeout(0, 0, l2_packet_deinit_timeout, l2, NULL); } int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len) { pcap_if_t *devs, *dev; struct pcap_addr *addr; struct sockaddr_in *saddr; int found = 0; char err[PCAP_ERRBUF_SIZE + 1]; if (pcap_findalldevs(&devs, err) < 0) { wpa_printf(MSG_DEBUG, "pcap_findalldevs: %s\n", err); return -1; } for (dev = devs; dev && !found; dev = dev->next) { if (os_strcmp(dev->name, l2->ifname) != 0) continue; addr = dev->addresses; while (addr) { saddr = (struct sockaddr_in *) addr->addr; if (saddr && saddr->sin_family == AF_INET) { os_strlcpy(buf, inet_ntoa(saddr->sin_addr), len); found = 1; break; } addr = addr->next; } } pcap_freealldevs(devs); return found ? 0 : -1; } void l2_packet_notify_auth_start(struct l2_packet_data *l2) { if (l2) SetEvent(l2->rx_notify); } wpa-2.1/src/lib.rules000066400000000000000000000003131227414666700145700ustar00rootroot00000000000000ifndef CC CC=gcc endif ifndef CFLAGS CFLAGS = -MMD -O2 -Wall -g endif CFLAGS += -I.. -I../utils Q=@ E=echo ifeq ($(V), 1) Q= E=true endif %.o: %.c $(Q)$(CC) -c -o $@ $(CFLAGS) $< @$(E) " CC " $< wpa-2.1/src/p2p/000077500000000000000000000000001227414666700134525ustar00rootroot00000000000000wpa-2.1/src/p2p/Makefile000066400000000000000000000001641227414666700151130ustar00rootroot00000000000000all: @echo Nothing to be made. clean: rm -f *~ *.o *.d *.gcno *.gcda *.gcov install: @echo Nothing to be made. wpa-2.1/src/p2p/p2p.c000066400000000000000000003556461227414666700143420ustar00rootroot00000000000000/* * Wi-Fi Direct - P2P module * Copyright (c) 2009-2010, Atheros Communications * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "includes.h" #include "common.h" #include "eloop.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" #include "wps/wps_i.h" #include "p2p_i.h" #include "p2p.h" static void p2p_state_timeout(void *eloop_ctx, void *timeout_ctx); static void p2p_device_free(struct p2p_data *p2p, struct p2p_device *dev); static void p2p_process_presence_req(struct p2p_data *p2p, const u8 *da, const u8 *sa, const u8 *data, size_t len, int rx_freq); static void p2p_process_presence_resp(struct p2p_data *p2p, const u8 *da, const u8 *sa, const u8 *data, size_t len); static void p2p_ext_listen_timeout(void *eloop_ctx, void *timeout_ctx); static void p2p_scan_timeout(void *eloop_ctx, void *timeout_ctx); /* * p2p_scan recovery timeout * * Many drivers are using 30 second timeout on scan results. Allow a bit larger * timeout for this to avoid hitting P2P timeout unnecessarily. */ #define P2P_SCAN_TIMEOUT 35 /** * P2P_PEER_EXPIRATION_AGE - Number of seconds after which inactive peer * entries will be removed */ #ifndef P2P_PEER_EXPIRATION_AGE #define P2P_PEER_EXPIRATION_AGE 60 #endif /* P2P_PEER_EXPIRATION_AGE */ #define P2P_PEER_EXPIRATION_INTERVAL (P2P_PEER_EXPIRATION_AGE / 2) static void p2p_expire_peers(struct p2p_data *p2p) { struct p2p_device *dev, *n; struct os_reltime now; size_t i; os_get_reltime(&now); dl_list_for_each_safe(dev, n, &p2p->devices, struct p2p_device, list) { if (dev->last_seen.sec + P2P_PEER_EXPIRATION_AGE >= now.sec) continue; if (dev == p2p->go_neg_peer) { /* * GO Negotiation is in progress with the peer, so * don't expire the peer entry until GO Negotiation * fails or times out. */ continue; } if (p2p->cfg->go_connected && p2p->cfg->go_connected(p2p->cfg->cb_ctx, dev->info.p2p_device_addr)) { /* * We are connected as a client to a group in which the * peer is the GO, so do not expire the peer entry. */ os_get_reltime(&dev->last_seen); continue; } for (i = 0; i < p2p->num_groups; i++) { if (p2p_group_is_client_connected( p2p->groups[i], dev->info.p2p_device_addr)) break; } if (i < p2p->num_groups) { /* * The peer is connected as a client in a group where * we are the GO, so do not expire the peer entry. */ os_get_reltime(&dev->last_seen); continue; } p2p_dbg(p2p, "Expiring old peer entry " MACSTR, MAC2STR(dev->info.p2p_device_addr)); dl_list_del(&dev->list); p2p_device_free(p2p, dev); } } static void p2p_expiration_timeout(void *eloop_ctx, void *timeout_ctx) { struct p2p_data *p2p = eloop_ctx; p2p_expire_peers(p2p); eloop_register_timeout(P2P_PEER_EXPIRATION_INTERVAL, 0, p2p_expiration_timeout, p2p, NULL); } static const char * p2p_state_txt(int state) { switch (state) { case P2P_IDLE: return "IDLE"; case P2P_SEARCH: return "SEARCH"; case P2P_CONNECT: return "CONNECT"; case P2P_CONNECT_LISTEN: return "CONNECT_LISTEN"; case P2P_GO_NEG: return "GO_NEG"; case P2P_LISTEN_ONLY: return "LISTEN_ONLY"; case P2P_WAIT_PEER_CONNECT: return "WAIT_PEER_CONNECT"; case P2P_WAIT_PEER_IDLE: return "WAIT_PEER_IDLE"; case P2P_SD_DURING_FIND: return "SD_DURING_FIND"; case P2P_PROVISIONING: return "PROVISIONING"; case P2P_PD_DURING_FIND: return "PD_DURING_FIND"; case P2P_INVITE: return "INVITE"; case P2P_INVITE_LISTEN: return "INVITE_LISTEN"; default: return "?"; } } const char * p2p_get_state_txt(struct p2p_data *p2p) { return p2p_state_txt(p2p->state); } u16 p2p_get_provisioning_info(struct p2p_data *p2p, const u8 *addr) { struct p2p_device *dev = NULL; if (!addr || !p2p) return 0; dev = p2p_get_device(p2p, addr); if (dev) return dev->wps_prov_info; else return 0; } void p2p_clear_provisioning_info(struct p2p_data *p2p, const u8 *addr) { struct p2p_device *dev = NULL; if (!addr || !p2p) return; dev = p2p_get_device(p2p, addr); if (dev) dev->wps_prov_info = 0; } void p2p_set_state(struct p2p_data *p2p, int new_state) { p2p_dbg(p2p, "State %s -> %s", p2p_state_txt(p2p->state), p2p_state_txt(new_state)); p2p->state = new_state; } void p2p_set_timeout(struct p2p_data *p2p, unsigned int sec, unsigned int usec) { p2p_dbg(p2p, "Set timeout (state=%s): %u.%06u sec", p2p_state_txt(p2p->state), sec, usec); eloop_cancel_timeout(p2p_state_timeout, p2p, NULL); eloop_register_timeout(sec, usec, p2p_state_timeout, p2p, NULL); } void p2p_clear_timeout(struct p2p_data *p2p) { p2p_dbg(p2p, "Clear timeout (state=%s)", p2p_state_txt(p2p->state)); eloop_cancel_timeout(p2p_state_timeout, p2p, NULL); } void p2p_go_neg_failed(struct p2p_data *p2p, struct p2p_device *peer, int status) { struct p2p_go_neg_results res; p2p_clear_timeout(p2p); p2p_set_state(p2p, P2P_IDLE); if (p2p->go_neg_peer) { p2p->go_neg_peer->flags &= ~P2P_DEV_PEER_WAITING_RESPONSE; p2p->go_neg_peer->wps_method = WPS_NOT_READY; p2p->go_neg_peer->oob_pw_id = 0; } p2p->go_neg_peer = NULL; os_memset(&res, 0, sizeof(res)); res.status = status; if (peer) { os_memcpy(res.peer_device_addr, peer->info.p2p_device_addr, ETH_ALEN); os_memcpy(res.peer_interface_addr, peer->intended_addr, ETH_ALEN); } p2p->cfg->go_neg_completed(p2p->cfg->cb_ctx, &res); } static void p2p_listen_in_find(struct p2p_data *p2p, int dev_disc) { unsigned int r, tu; int freq; struct wpabuf *ies; p2p_dbg(p2p, "Starting short listen state (state=%s)", p2p_state_txt(p2p->state)); freq = p2p_channel_to_freq(p2p->cfg->reg_class, p2p->cfg->channel); if (freq < 0) { p2p_dbg(p2p, "Unknown regulatory class/channel"); return; } os_get_random((u8 *) &r, sizeof(r)); tu = (r % ((p2p->max_disc_int - p2p->min_disc_int) + 1) + p2p->min_disc_int) * 100; if (p2p->max_disc_tu >= 0 && tu > (unsigned int) p2p->max_disc_tu) tu = p2p->max_disc_tu; if (!dev_disc && tu < 100) tu = 100; /* Need to wait in non-device discovery use cases */ if (p2p->cfg->max_listen && 1024 * tu / 1000 > p2p->cfg->max_listen) tu = p2p->cfg->max_listen * 1000 / 1024; if (tu == 0) { p2p_dbg(p2p, "Skip listen state since duration was 0 TU"); p2p_set_timeout(p2p, 0, 0); return; } p2p->pending_listen_freq = freq; p2p->pending_listen_sec = 0; p2p->pending_listen_usec = 1024 * tu; ies = p2p_build_probe_resp_ies(p2p); if (ies == NULL) return; if (p2p->cfg->start_listen(p2p->cfg->cb_ctx, freq, 1024 * tu / 1000, ies) < 0) { p2p_dbg(p2p, "Failed to start listen mode"); p2p->pending_listen_freq = 0; } wpabuf_free(ies); } int p2p_listen(struct p2p_data *p2p, unsigned int timeout) { int freq; struct wpabuf *ies; p2p_dbg(p2p, "Going to listen(only) state"); freq = p2p_channel_to_freq(p2p->cfg->reg_class, p2p->cfg->channel); if (freq < 0) { p2p_dbg(p2p, "Unknown regulatory class/channel"); return -1; } p2p->pending_listen_freq = freq; p2p->pending_listen_sec = timeout / 1000; p2p->pending_listen_usec = (timeout % 1000) * 1000; if (p2p->p2p_scan_running) { if (p2p->start_after_scan == P2P_AFTER_SCAN_CONNECT) { p2p_dbg(p2p, "p2p_scan running - connect is already pending - skip listen"); return 0; } p2p_dbg(p2p, "p2p_scan running - delay start of listen state"); p2p->start_after_scan = P2P_AFTER_SCAN_LISTEN; return 0; } ies = p2p_build_probe_resp_ies(p2p); if (ies == NULL) return -1; if (p2p->cfg->start_listen(p2p->cfg->cb_ctx, freq, timeout, ies) < 0) { p2p_dbg(p2p, "Failed to start listen mode"); p2p->pending_listen_freq = 0; wpabuf_free(ies); return -1; } wpabuf_free(ies); p2p_set_state(p2p, P2P_LISTEN_ONLY); return 0; } static void p2p_device_clear_reported(struct p2p_data *p2p) { struct p2p_device *dev; dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) dev->flags &= ~P2P_DEV_REPORTED; } /** * p2p_get_device - Fetch a peer entry * @p2p: P2P module context from p2p_init() * @addr: P2P Device Address of the peer * Returns: Pointer to the device entry or %NULL if not found */ struct p2p_device * p2p_get_device(struct p2p_data *p2p, const u8 *addr) { struct p2p_device *dev; dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) { if (os_memcmp(dev->info.p2p_device_addr, addr, ETH_ALEN) == 0) return dev; } return NULL; } /** * p2p_get_device_interface - Fetch a peer entry based on P2P Interface Address * @p2p: P2P module context from p2p_init() * @addr: P2P Interface Address of the peer * Returns: Pointer to the device entry or %NULL if not found */ struct p2p_device * p2p_get_device_interface(struct p2p_data *p2p, const u8 *addr) { struct p2p_device *dev; dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) { if (os_memcmp(dev->interface_addr, addr, ETH_ALEN) == 0) return dev; } return NULL; } /** * p2p_create_device - Create a peer entry * @p2p: P2P module context from p2p_init() * @addr: P2P Device Address of the peer * Returns: Pointer to the device entry or %NULL on failure * * If there is already an entry for the peer, it will be returned instead of * creating a new one. */ static struct p2p_device * p2p_create_device(struct p2p_data *p2p, const u8 *addr) { struct p2p_device *dev, *oldest = NULL; size_t count = 0; dev = p2p_get_device(p2p, addr); if (dev) return dev; dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) { count++; if (oldest == NULL || os_reltime_before(&dev->last_seen, &oldest->last_seen)) oldest = dev; } if (count + 1 > p2p->cfg->max_peers && oldest) { p2p_dbg(p2p, "Remove oldest peer entry to make room for a new peer"); dl_list_del(&oldest->list); p2p_device_free(p2p, oldest); } dev = os_zalloc(sizeof(*dev)); if (dev == NULL) return NULL; dl_list_add(&p2p->devices, &dev->list); os_memcpy(dev->info.p2p_device_addr, addr, ETH_ALEN); return dev; } static void p2p_copy_client_info(struct p2p_device *dev, struct p2p_client_info *cli) { os_memcpy(dev->info.device_name, cli->dev_name, cli->dev_name_len); dev->info.device_name[cli->dev_name_len] = '\0'; dev->info.dev_capab = cli->dev_capab; dev->info.config_methods = cli->config_methods; os_memcpy(dev->info.pri_dev_type, cli->pri_dev_type, 8); dev->info.wps_sec_dev_type_list_len = 8 * cli->num_sec_dev_types; os_memcpy(dev->info.wps_sec_dev_type_list, cli->sec_dev_types, dev->info.wps_sec_dev_type_list_len); } static int p2p_add_group_clients(struct p2p_data *p2p, const u8 *go_dev_addr, const u8 *go_interface_addr, int freq, const u8 *gi, size_t gi_len) { struct p2p_group_info info; size_t c; struct p2p_device *dev; if (gi == NULL) return 0; if (p2p_group_info_parse(gi, gi_len, &info) < 0) return -1; /* * Clear old data for this group; if the devices are still in the * group, the information will be restored in the loop following this. */ dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) { if (os_memcmp(dev->member_in_go_iface, go_interface_addr, ETH_ALEN) == 0) { os_memset(dev->member_in_go_iface, 0, ETH_ALEN); os_memset(dev->member_in_go_dev, 0, ETH_ALEN); } } for (c = 0; c < info.num_clients; c++) { struct p2p_client_info *cli = &info.client[c]; if (os_memcmp(cli->p2p_device_addr, p2p->cfg->dev_addr, ETH_ALEN) == 0) continue; /* ignore our own entry */ dev = p2p_get_device(p2p, cli->p2p_device_addr); if (dev) { if (dev->flags & (P2P_DEV_GROUP_CLIENT_ONLY | P2P_DEV_PROBE_REQ_ONLY)) { /* * Update information since we have not * received this directly from the client. */ p2p_copy_client_info(dev, cli); } else { /* * Need to update P2P Client Discoverability * flag since it is valid only in P2P Group * Info attribute. */ dev->info.dev_capab &= ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY; dev->info.dev_capab |= cli->dev_capab & P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY; } if (dev->flags & P2P_DEV_PROBE_REQ_ONLY) { dev->flags &= ~P2P_DEV_PROBE_REQ_ONLY; } } else { dev = p2p_create_device(p2p, cli->p2p_device_addr); if (dev == NULL) continue; dev->flags |= P2P_DEV_GROUP_CLIENT_ONLY; p2p_copy_client_info(dev, cli); dev->oper_freq = freq; p2p->cfg->dev_found(p2p->cfg->cb_ctx, dev->info.p2p_device_addr, &dev->info, 1); dev->flags |= P2P_DEV_REPORTED | P2P_DEV_REPORTED_ONCE; } os_memcpy(dev->interface_addr, cli->p2p_interface_addr, ETH_ALEN); os_get_reltime(&dev->last_seen); os_memcpy(dev->member_in_go_dev, go_dev_addr, ETH_ALEN); os_memcpy(dev->member_in_go_iface, go_interface_addr, ETH_ALEN); } return 0; } static void p2p_copy_wps_info(struct p2p_data *p2p, struct p2p_device *dev, int probe_req, const struct p2p_message *msg) { os_memcpy(dev->info.device_name, msg->device_name, sizeof(dev->info.device_name)); if (msg->manufacturer && msg->manufacturer_len < sizeof(dev->info.manufacturer)) { os_memset(dev->info.manufacturer, 0, sizeof(dev->info.manufacturer)); os_memcpy(dev->info.manufacturer, msg->manufacturer, msg->manufacturer_len); } if (msg->model_name && msg->model_name_len < sizeof(dev->info.model_name)) { os_memset(dev->info.model_name, 0, sizeof(dev->info.model_name)); os_memcpy(dev->info.model_name, msg->model_name, msg->model_name_len); } if (msg->model_number && msg->model_number_len < sizeof(dev->info.model_number)) { os_memset(dev->info.model_number, 0, sizeof(dev->info.model_number)); os_memcpy(dev->info.model_number, msg->model_number, msg->model_number_len); } if (msg->serial_number && msg->serial_number_len < sizeof(dev->info.serial_number)) { os_memset(dev->info.serial_number, 0, sizeof(dev->info.serial_number)); os_memcpy(dev->info.serial_number, msg->serial_number, msg->serial_number_len); } if (msg->pri_dev_type) os_memcpy(dev->info.pri_dev_type, msg->pri_dev_type, sizeof(dev->info.pri_dev_type)); else if (msg->wps_pri_dev_type) os_memcpy(dev->info.pri_dev_type, msg->wps_pri_dev_type, sizeof(dev->info.pri_dev_type)); if (msg->wps_sec_dev_type_list) { os_memcpy(dev->info.wps_sec_dev_type_list, msg->wps_sec_dev_type_list, msg->wps_sec_dev_type_list_len); dev->info.wps_sec_dev_type_list_len = msg->wps_sec_dev_type_list_len; } if (msg->capability) { /* * P2P Client Discoverability bit is reserved in all frames * that use this function, so do not change its value here. */ dev->info.dev_capab &= P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY; dev->info.dev_capab |= msg->capability[0] & ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY; dev->info.group_capab = msg->capability[1]; } if (msg->ext_listen_timing) { dev->ext_listen_period = WPA_GET_LE16(msg->ext_listen_timing); dev->ext_listen_interval = WPA_GET_LE16(msg->ext_listen_timing + 2); } if (!probe_req) { u16 new_config_methods; new_config_methods = msg->config_methods ? msg->config_methods : msg->wps_config_methods; if (new_config_methods && dev->info.config_methods != new_config_methods) { p2p_dbg(p2p, "Update peer " MACSTR " config_methods 0x%x -> 0x%x", MAC2STR(dev->info.p2p_device_addr), dev->info.config_methods, new_config_methods); dev->info.config_methods = new_config_methods; } } } /** * p2p_add_device - Add peer entries based on scan results or P2P frames * @p2p: P2P module context from p2p_init() * @addr: Source address of Beacon or Probe Response frame (may be either * P2P Device Address or P2P Interface Address) * @level: Signal level (signal strength of the received frame from the peer) * @freq: Frequency on which the Beacon or Probe Response frame was received * @rx_time: Time when the result was received * @ies: IEs from the Beacon or Probe Response frame * @ies_len: Length of ies buffer in octets * @scan_res: Whether this was based on scan results * Returns: 0 on success, -1 on failure * * If the scan result is for a GO, the clients in the group will also be added * to the peer table. This function can also be used with some other frames * like Provision Discovery Request that contains P2P Capability and P2P Device * Info attributes. */ int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, struct os_reltime *rx_time, int level, const u8 *ies, size_t ies_len, int scan_res) { struct p2p_device *dev; struct p2p_message msg; const u8 *p2p_dev_addr; int i; struct os_reltime time_now; os_memset(&msg, 0, sizeof(msg)); if (p2p_parse_ies(ies, ies_len, &msg)) { p2p_dbg(p2p, "Failed to parse P2P IE for a device entry"); p2p_parse_free(&msg); return -1; } if (msg.p2p_device_addr) p2p_dev_addr = msg.p2p_device_addr; else if (msg.device_id) p2p_dev_addr = msg.device_id; else { p2p_dbg(p2p, "Ignore scan data without P2P Device Info or P2P Device Id"); p2p_parse_free(&msg); return -1; } if (!is_zero_ether_addr(p2p->peer_filter) && os_memcmp(p2p_dev_addr, p2p->peer_filter, ETH_ALEN) != 0) { p2p_dbg(p2p, "Do not add peer filter for " MACSTR " due to peer filter", MAC2STR(p2p_dev_addr)); p2p_parse_free(&msg); return 0; } dev = p2p_create_device(p2p, p2p_dev_addr); if (dev == NULL) { p2p_parse_free(&msg); return -1; } if (rx_time == NULL) { os_get_reltime(&time_now); rx_time = &time_now; } /* * Update the device entry only if the new peer * entry is newer than the one previously stored. */ if (dev->last_seen.sec > 0 && os_reltime_before(rx_time, &dev->last_seen)) { p2p_dbg(p2p, "Do not update peer entry based on old frame (rx_time=%u.%06u last_seen=%u.%06u)", (unsigned int) rx_time->sec, (unsigned int) rx_time->usec, (unsigned int) dev->last_seen.sec, (unsigned int) dev->last_seen.usec); p2p_parse_free(&msg); return -1; } os_memcpy(&dev->last_seen, rx_time, sizeof(struct os_reltime)); dev->flags &= ~(P2P_DEV_PROBE_REQ_ONLY | P2P_DEV_GROUP_CLIENT_ONLY); if (os_memcmp(addr, p2p_dev_addr, ETH_ALEN) != 0) os_memcpy(dev->interface_addr, addr, ETH_ALEN); if (msg.ssid && (msg.ssid[1] != P2P_WILDCARD_SSID_LEN || os_memcmp(msg.ssid + 2, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN) != 0)) { os_memcpy(dev->oper_ssid, msg.ssid + 2, msg.ssid[1]); dev->oper_ssid_len = msg.ssid[1]; } if (freq >= 2412 && freq <= 2484 && msg.ds_params && *msg.ds_params >= 1 && *msg.ds_params <= 14) { int ds_freq; if (*msg.ds_params == 14) ds_freq = 2484; else ds_freq = 2407 + *msg.ds_params * 5; if (freq != ds_freq) { p2p_dbg(p2p, "Update Listen frequency based on DS Parameter Set IE: %d -> %d MHz", freq, ds_freq); freq = ds_freq; } } if (dev->listen_freq && dev->listen_freq != freq && scan_res) { p2p_dbg(p2p, "Update Listen frequency based on scan results (" MACSTR " %d -> %d MHz (DS param %d)", MAC2STR(dev->info.p2p_device_addr), dev->listen_freq, freq, msg.ds_params ? *msg.ds_params : -1); } if (scan_res) { dev->listen_freq = freq; if (msg.group_info) dev->oper_freq = freq; } dev->info.level = level; p2p_copy_wps_info(p2p, dev, 0, &msg); for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) { wpabuf_free(dev->info.wps_vendor_ext[i]); dev->info.wps_vendor_ext[i] = NULL; } for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) { if (msg.wps_vendor_ext[i] == NULL) break; dev->info.wps_vendor_ext[i] = wpabuf_alloc_copy( msg.wps_vendor_ext[i], msg.wps_vendor_ext_len[i]); if (dev->info.wps_vendor_ext[i] == NULL) break; } if (msg.wfd_subelems) { wpabuf_free(dev->info.wfd_subelems); dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems); } if (scan_res) { p2p_add_group_clients(p2p, p2p_dev_addr, addr, freq, msg.group_info, msg.group_info_len); } p2p_parse_free(&msg); if (p2p_pending_sd_req(p2p, dev)) dev->flags |= P2P_DEV_SD_SCHEDULE; if (dev->flags & P2P_DEV_REPORTED) return 0; p2p_dbg(p2p, "Peer found with Listen frequency %d MHz (rx_time=%u.%06u)", freq, (unsigned int) rx_time->sec, (unsigned int) rx_time->usec); if (dev->flags & P2P_DEV_USER_REJECTED) { p2p_dbg(p2p, "Do not report rejected device"); return 0; } if (dev->info.config_methods == 0 && (freq == 2412 || freq == 2437 || freq == 2462)) { /* * If we have only seen a Beacon frame from a GO, we do not yet * know what WPS config methods it supports. Since some * applications use config_methods value from P2P-DEVICE-FOUND * events, postpone reporting this peer until we've fully * discovered its capabilities. * * At least for now, do this only if the peer was detected on * one of the social channels since that peer can be easily be * found again and there are no limitations of having to use * passive scan on this channels, so this can be done through * Probe Response frame that includes the config_methods * information. */ p2p_dbg(p2p, "Do not report peer " MACSTR " with unknown config methods", MAC2STR(addr)); return 0; } p2p->cfg->dev_found(p2p->cfg->cb_ctx, addr, &dev->info, !(dev->flags & P2P_DEV_REPORTED_ONCE)); dev->flags |= P2P_DEV_REPORTED | P2P_DEV_REPORTED_ONCE; return 0; } static void p2p_device_free(struct p2p_data *p2p, struct p2p_device *dev) { int i; if (p2p->go_neg_peer == dev) { /* * If GO Negotiation is in progress, report that it has failed. */ p2p_go_neg_failed(p2p, dev, -1); p2p->go_neg_peer = NULL; } if (p2p->invite_peer == dev) p2p->invite_peer = NULL; if (p2p->sd_peer == dev) p2p->sd_peer = NULL; if (p2p->pending_client_disc_go == dev) p2p->pending_client_disc_go = NULL; /* dev_lost() device, but only if it was previously dev_found() */ if (dev->flags & P2P_DEV_REPORTED_ONCE) p2p->cfg->dev_lost(p2p->cfg->cb_ctx, dev->info.p2p_device_addr); for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) { wpabuf_free(dev->info.wps_vendor_ext[i]); dev->info.wps_vendor_ext[i] = NULL; } wpabuf_free(dev->info.wfd_subelems); os_free(dev); } static int p2p_get_next_prog_freq(struct p2p_data *p2p) { struct p2p_channels *c; struct p2p_reg_class *cla; size_t cl, ch; int found = 0; u8 reg_class; u8 channel; int freq; c = &p2p->cfg->channels; for (cl = 0; cl < c->reg_classes; cl++) { cla = &c->reg_class[cl]; if (cla->reg_class != p2p->last_prog_scan_class) continue; for (ch = 0; ch < cla->channels; ch++) { if (cla->channel[ch] == p2p->last_prog_scan_chan) { found = 1; break; } } if (found) break; } if (!found) { /* Start from beginning */ reg_class = c->reg_class[0].reg_class; channel = c->reg_class[0].channel[0]; } else { /* Pick the next channel */ ch++; if (ch == cla->channels) { cl++; if (cl == c->reg_classes) cl = 0; ch = 0; } reg_class = c->reg_class[cl].reg_class; channel = c->reg_class[cl].channel[ch]; } freq = p2p_channel_to_freq(reg_class, channel); p2p_dbg(p2p, "Next progressive search channel: reg_class %u channel %u -> %d MHz", reg_class, channel, freq); p2p->last_prog_scan_class = reg_class; p2p->last_prog_scan_chan = channel; if (freq == 2412 || freq == 2437 || freq == 2462) return 0; /* No need to add social channels */ return freq; } static void p2p_search(struct p2p_data *p2p) { int freq = 0; enum p2p_scan_type type; u16 pw_id = DEV_PW_DEFAULT; int res; if (p2p->drv_in_listen) { p2p_dbg(p2p, "Driver is still in Listen state - wait for it to end before continuing"); return; } p2p->cfg->stop_listen(p2p->cfg->cb_ctx); if (p2p->find_type == P2P_FIND_PROGRESSIVE && (freq = p2p_get_next_prog_freq(p2p)) > 0) { type = P2P_SCAN_SOCIAL_PLUS_ONE; p2p_dbg(p2p, "Starting search (+ freq %u)", freq); } else { type = P2P_SCAN_SOCIAL; p2p_dbg(p2p, "Starting search"); } res = p2p->cfg->p2p_scan(p2p->cfg->cb_ctx, type, freq, p2p->num_req_dev_types, p2p->req_dev_types, p2p->find_dev_id, pw_id); if (res < 0) { p2p_dbg(p2p, "Scan request failed"); p2p_continue_find(p2p); } else { p2p_dbg(p2p, "Running p2p_scan"); p2p->p2p_scan_running = 1; eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL); eloop_register_timeout(P2P_SCAN_TIMEOUT, 0, p2p_scan_timeout, p2p, NULL); } } static void p2p_find_timeout(void *eloop_ctx, void *timeout_ctx) { struct p2p_data *p2p = eloop_ctx; p2p_dbg(p2p, "Find timeout -> stop"); p2p_stop_find(p2p); } static int p2p_run_after_scan(struct p2p_data *p2p) { struct p2p_device *dev; enum p2p_after_scan op; if (p2p->after_scan_tx) { p2p->after_scan_tx_in_progress = 1; p2p_dbg(p2p, "Send pending Action frame at p2p_scan completion"); p2p->cfg->send_action(p2p->cfg->cb_ctx, p2p->after_scan_tx->freq, p2p->after_scan_tx->dst, p2p->after_scan_tx->src, p2p->after_scan_tx->bssid, (u8 *) (p2p->after_scan_tx + 1), p2p->after_scan_tx->len, p2p->after_scan_tx->wait_time); os_free(p2p->after_scan_tx); p2p->after_scan_tx = NULL; return 1; } op = p2p->start_after_scan; p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING; switch (op) { case P2P_AFTER_SCAN_NOTHING: break; case P2P_AFTER_SCAN_LISTEN: p2p_dbg(p2p, "Start previously requested Listen state"); p2p_listen(p2p, p2p->pending_listen_sec * 1000 + p2p->pending_listen_usec / 1000); return 1; case P2P_AFTER_SCAN_CONNECT: p2p_dbg(p2p, "Start previously requested connect with " MACSTR, MAC2STR(p2p->after_scan_peer)); dev = p2p_get_device(p2p, p2p->after_scan_peer); if (dev == NULL) { p2p_dbg(p2p, "Peer not known anymore"); break; } p2p_connect_send(p2p, dev); return 1; } return 0; } static void p2p_scan_timeout(void *eloop_ctx, void *timeout_ctx) { struct p2p_data *p2p = eloop_ctx; int running; p2p_dbg(p2p, "p2p_scan timeout (running=%d)", p2p->p2p_scan_running); running = p2p->p2p_scan_running; /* Make sure we recover from missed scan results callback */ p2p->p2p_scan_running = 0; if (running) p2p_run_after_scan(p2p); } static void p2p_free_req_dev_types(struct p2p_data *p2p) { p2p->num_req_dev_types = 0; os_free(p2p->req_dev_types); p2p->req_dev_types = NULL; } int p2p_find(struct p2p_data *p2p, unsigned int timeout, enum p2p_discovery_type type, unsigned int num_req_dev_types, const u8 *req_dev_types, const u8 *dev_id, unsigned int search_delay) { int res; p2p_dbg(p2p, "Starting find (type=%d)", type); os_get_reltime(&p2p->find_start); if (p2p->p2p_scan_running) { p2p_dbg(p2p, "p2p_scan is already running"); } p2p_free_req_dev_types(p2p); if (req_dev_types && num_req_dev_types) { p2p->req_dev_types = os_malloc(num_req_dev_types * WPS_DEV_TYPE_LEN); if (p2p->req_dev_types == NULL) return -1; os_memcpy(p2p->req_dev_types, req_dev_types, num_req_dev_types * WPS_DEV_TYPE_LEN); p2p->num_req_dev_types = num_req_dev_types; } if (dev_id) { os_memcpy(p2p->find_dev_id_buf, dev_id, ETH_ALEN); p2p->find_dev_id = p2p->find_dev_id_buf; } else p2p->find_dev_id = NULL; p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING; p2p_clear_timeout(p2p); p2p->cfg->stop_listen(p2p->cfg->cb_ctx); p2p->find_type = type; p2p_device_clear_reported(p2p); p2p_set_state(p2p, P2P_SEARCH); p2p->search_delay = search_delay; p2p->in_search_delay = 0; eloop_cancel_timeout(p2p_find_timeout, p2p, NULL); p2p->last_p2p_find_timeout = timeout; if (timeout) eloop_register_timeout(timeout, 0, p2p_find_timeout, p2p, NULL); switch (type) { case P2P_FIND_START_WITH_FULL: case P2P_FIND_PROGRESSIVE: res = p2p->cfg->p2p_scan(p2p->cfg->cb_ctx, P2P_SCAN_FULL, 0, p2p->num_req_dev_types, p2p->req_dev_types, dev_id, DEV_PW_DEFAULT); break; case P2P_FIND_ONLY_SOCIAL: res = p2p->cfg->p2p_scan(p2p->cfg->cb_ctx, P2P_SCAN_SOCIAL, 0, p2p->num_req_dev_types, p2p->req_dev_types, dev_id, DEV_PW_DEFAULT); break; default: return -1; } if (res == 0) { p2p_dbg(p2p, "Running p2p_scan"); p2p->p2p_scan_running = 1; eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL); eloop_register_timeout(P2P_SCAN_TIMEOUT, 0, p2p_scan_timeout, p2p, NULL); } else if (p2p->p2p_scan_running) { p2p_dbg(p2p, "Failed to start p2p_scan - another p2p_scan was already running"); /* wait for the previous p2p_scan to complete */ } else { p2p_dbg(p2p, "Failed to start p2p_scan"); p2p_set_state(p2p, P2P_IDLE); eloop_cancel_timeout(p2p_find_timeout, p2p, NULL); } return res; } void p2p_stop_find_for_freq(struct p2p_data *p2p, int freq) { p2p_dbg(p2p, "Stopping find"); eloop_cancel_timeout(p2p_find_timeout, p2p, NULL); p2p_clear_timeout(p2p); if (p2p->state == P2P_SEARCH) p2p->cfg->find_stopped(p2p->cfg->cb_ctx); p2p_set_state(p2p, P2P_IDLE); p2p_free_req_dev_types(p2p); p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING; if (p2p->go_neg_peer) p2p->go_neg_peer->flags &= ~P2P_DEV_PEER_WAITING_RESPONSE; p2p->go_neg_peer = NULL; p2p->sd_peer = NULL; p2p->invite_peer = NULL; p2p_stop_listen_for_freq(p2p, freq); } void p2p_stop_listen_for_freq(struct p2p_data *p2p, int freq) { if (freq > 0 && p2p->drv_in_listen == freq && p2p->in_listen) { p2p_dbg(p2p, "Skip stop_listen since we are on correct channel for response"); return; } if (p2p->in_listen) { p2p->in_listen = 0; p2p_clear_timeout(p2p); } if (p2p->drv_in_listen) { /* * The driver may not deliver callback to p2p_listen_end() * when the operation gets canceled, so clear the internal * variable that is tracking driver state. */ p2p_dbg(p2p, "Clear drv_in_listen (%d)", p2p->drv_in_listen); p2p->drv_in_listen = 0; } p2p->cfg->stop_listen(p2p->cfg->cb_ctx); } void p2p_stop_listen(struct p2p_data *p2p) { if (p2p->state != P2P_LISTEN_ONLY) { p2p_dbg(p2p, "Skip stop_listen since not in listen_only state."); return; } p2p_stop_listen_for_freq(p2p, 0); p2p_set_state(p2p, P2P_IDLE); } void p2p_stop_find(struct p2p_data *p2p) { p2p_stop_find_for_freq(p2p, 0); } static int p2p_prepare_channel_pref(struct p2p_data *p2p, unsigned int force_freq, unsigned int pref_freq, int go) { u8 op_class, op_channel; unsigned int freq = force_freq ? force_freq : pref_freq; p2p_dbg(p2p, "Prepare channel pref - force_freq=%u pref_freq=%u go=%d", force_freq, pref_freq, go); if (p2p_freq_to_channel(freq, &op_class, &op_channel) < 0) { p2p_dbg(p2p, "Unsupported frequency %u MHz", freq); return -1; } if (!p2p_channels_includes(&p2p->cfg->channels, op_class, op_channel) && (go || !p2p_channels_includes(&p2p->cfg->cli_channels, op_class, op_channel))) { p2p_dbg(p2p, "Frequency %u MHz (oper_class %u channel %u) not allowed for P2P", freq, op_class, op_channel); return -1; } p2p->op_reg_class = op_class; p2p->op_channel = op_channel; if (force_freq) { p2p->channels.reg_classes = 1; p2p->channels.reg_class[0].channels = 1; p2p->channels.reg_class[0].reg_class = p2p->op_reg_class; p2p->channels.reg_class[0].channel[0] = p2p->op_channel; } else { os_memcpy(&p2p->channels, &p2p->cfg->channels, sizeof(struct p2p_channels)); } return 0; } static void p2p_prepare_channel_best(struct p2p_data *p2p) { u8 op_class, op_channel; const int op_classes_5ghz[] = { 124, 115, 0 }; const int op_classes_ht40[] = { 126, 127, 116, 117, 0 }; const int op_classes_vht[] = { 128, 0 }; p2p_dbg(p2p, "Prepare channel best"); if (!p2p->cfg->cfg_op_channel && p2p->best_freq_overall > 0 && p2p_supported_freq(p2p, p2p->best_freq_overall) && p2p_freq_to_channel(p2p->best_freq_overall, &op_class, &op_channel) == 0) { p2p_dbg(p2p, "Select best overall channel as operating channel preference"); p2p->op_reg_class = op_class; p2p->op_channel = op_channel; } else if (!p2p->cfg->cfg_op_channel && p2p->best_freq_5 > 0 && p2p_supported_freq(p2p, p2p->best_freq_5) && p2p_freq_to_channel(p2p->best_freq_5, &op_class, &op_channel) == 0) { p2p_dbg(p2p, "Select best 5 GHz channel as operating channel preference"); p2p->op_reg_class = op_class; p2p->op_channel = op_channel; } else if (!p2p->cfg->cfg_op_channel && p2p->best_freq_24 > 0 && p2p_supported_freq(p2p, p2p->best_freq_24) && p2p_freq_to_channel(p2p->best_freq_24, &op_class, &op_channel) == 0) { p2p_dbg(p2p, "Select best 2.4 GHz channel as operating channel preference"); p2p->op_reg_class = op_class; p2p->op_channel = op_channel; } else if (p2p->cfg->num_pref_chan > 0 && p2p_channels_includes(&p2p->cfg->channels, p2p->cfg->pref_chan[0].op_class, p2p->cfg->pref_chan[0].chan)) { p2p_dbg(p2p, "Select first pref_chan entry as operating channel preference"); p2p->op_reg_class = p2p->cfg->pref_chan[0].op_class; p2p->op_channel = p2p->cfg->pref_chan[0].chan; } else if (p2p_channel_select(&p2p->cfg->channels, op_classes_vht, &p2p->op_reg_class, &p2p->op_channel) == 0) { p2p_dbg(p2p, "Select possible VHT channel (op_class %u channel %u) as operating channel preference", p2p->op_reg_class, p2p->op_channel); } else if (p2p_channel_select(&p2p->cfg->channels, op_classes_ht40, &p2p->op_reg_class, &p2p->op_channel) == 0) { p2p_dbg(p2p, "Select possible HT40 channel (op_class %u channel %u) as operating channel preference", p2p->op_reg_class, p2p->op_channel); } else if (p2p_channel_select(&p2p->cfg->channels, op_classes_5ghz, &p2p->op_reg_class, &p2p->op_channel) == 0) { p2p_dbg(p2p, "Select possible 5 GHz channel (op_class %u channel %u) as operating channel preference", p2p->op_reg_class, p2p->op_channel); } else { p2p_dbg(p2p, "Select pre-configured channel as operating channel preference"); p2p->op_reg_class = p2p->cfg->op_reg_class; p2p->op_channel = p2p->cfg->op_channel; } os_memcpy(&p2p->channels, &p2p->cfg->channels, sizeof(struct p2p_channels)); } /** * p2p_prepare_channel - Select operating channel for GO Negotiation * @p2p: P2P module context from p2p_init() * @dev: Selected peer device * @force_freq: Forced frequency in MHz or 0 if not forced * @pref_freq: Preferred frequency in MHz or 0 if no preference * @go: Whether the local end will be forced to be GO * Returns: 0 on success, -1 on failure (channel not supported for P2P) * * This function is used to do initial operating channel selection for GO * Negotiation prior to having received peer information. The selected channel * may be further optimized in p2p_reselect_channel() once the peer information * is available. */ int p2p_prepare_channel(struct p2p_data *p2p, struct p2p_device *dev, unsigned int force_freq, unsigned int pref_freq, int go) { p2p_dbg(p2p, "Prepare channel - force_freq=%u pref_freq=%u go=%d", force_freq, pref_freq, go); if (force_freq || pref_freq) { if (p2p_prepare_channel_pref(p2p, force_freq, pref_freq, go) < 0) return -1; } else { p2p_prepare_channel_best(p2p); } p2p_channels_dump(p2p, "prepared channels", &p2p->channels); if (go) p2p_channels_remove_freqs(&p2p->channels, &p2p->no_go_freq); else if (!force_freq) p2p_channels_union(&p2p->channels, &p2p->cfg->cli_channels, &p2p->channels); p2p_channels_dump(p2p, "after go/cli filter/add", &p2p->channels); p2p_dbg(p2p, "Own preference for operation channel: Operating Class %u Channel %u%s", p2p->op_reg_class, p2p->op_channel, force_freq ? " (forced)" : ""); if (force_freq) dev->flags |= P2P_DEV_FORCE_FREQ; else dev->flags &= ~P2P_DEV_FORCE_FREQ; return 0; } static void p2p_set_dev_persistent(struct p2p_device *dev, int persistent_group) { switch (persistent_group) { case 0: dev->flags &= ~(P2P_DEV_PREFER_PERSISTENT_GROUP | P2P_DEV_PREFER_PERSISTENT_RECONN); break; case 1: dev->flags |= P2P_DEV_PREFER_PERSISTENT_GROUP; dev->flags &= ~P2P_DEV_PREFER_PERSISTENT_RECONN; break; case 2: dev->flags |= P2P_DEV_PREFER_PERSISTENT_GROUP | P2P_DEV_PREFER_PERSISTENT_RECONN; break; } } int p2p_connect(struct p2p_data *p2p, const u8 *peer_addr, enum p2p_wps_method wps_method, int go_intent, const u8 *own_interface_addr, unsigned int force_freq, int persistent_group, const u8 *force_ssid, size_t force_ssid_len, int pd_before_go_neg, unsigned int pref_freq, u16 oob_pw_id) { struct p2p_device *dev; p2p_dbg(p2p, "Request to start group negotiation - peer=" MACSTR " GO Intent=%d Intended Interface Address=" MACSTR " wps_method=%d persistent_group=%d pd_before_go_neg=%d " "oob_pw_id=%u", MAC2STR(peer_addr), go_intent, MAC2STR(own_interface_addr), wps_method, persistent_group, pd_before_go_neg, oob_pw_id); dev = p2p_get_device(p2p, peer_addr); if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) { p2p_dbg(p2p, "Cannot connect to unknown P2P Device " MACSTR, MAC2STR(peer_addr)); return -1; } if (p2p_prepare_channel(p2p, dev, force_freq, pref_freq, go_intent == 15) < 0) return -1; if (dev->flags & P2P_DEV_GROUP_CLIENT_ONLY) { if (!(dev->info.dev_capab & P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY)) { p2p_dbg(p2p, "Cannot connect to P2P Device " MACSTR " that is in a group and is not discoverable", MAC2STR(peer_addr)); return -1; } if (dev->oper_freq <= 0) { p2p_dbg(p2p, "Cannot connect to P2P Device " MACSTR " with incomplete information", MAC2STR(peer_addr)); return -1; } /* * First, try to connect directly. If the peer does not * acknowledge frames, assume it is sleeping and use device * discoverability via the GO at that point. */ } p2p->ssid_set = 0; if (force_ssid) { wpa_hexdump_ascii(MSG_DEBUG, "P2P: Forced SSID", force_ssid, force_ssid_len); os_memcpy(p2p->ssid, force_ssid, force_ssid_len); p2p->ssid_len = force_ssid_len; p2p->ssid_set = 1; } dev->flags &= ~P2P_DEV_NOT_YET_READY; dev->flags &= ~P2P_DEV_USER_REJECTED; dev->flags &= ~P2P_DEV_WAIT_GO_NEG_RESPONSE; dev->flags &= ~P2P_DEV_WAIT_GO_NEG_CONFIRM; if (pd_before_go_neg) dev->flags |= P2P_DEV_PD_BEFORE_GO_NEG; else { dev->flags &= ~P2P_DEV_PD_BEFORE_GO_NEG; /* * Assign dialog token and tie breaker here to use the same * values in each retry within the same GO Negotiation exchange. */ dev->dialog_token++; if (dev->dialog_token == 0) dev->dialog_token = 1; dev->tie_breaker = p2p->next_tie_breaker; p2p->next_tie_breaker = !p2p->next_tie_breaker; } dev->connect_reqs = 0; dev->go_neg_req_sent = 0; dev->go_state = UNKNOWN_GO; p2p_set_dev_persistent(dev, persistent_group); p2p->go_intent = go_intent; os_memcpy(p2p->intended_addr, own_interface_addr, ETH_ALEN); if (p2p->state != P2P_IDLE) p2p_stop_find(p2p); if (p2p->after_scan_tx) { /* * We need to drop the pending frame to avoid issues with the * new GO Negotiation, e.g., when the pending frame was from a * previous attempt at starting a GO Negotiation. */ p2p_dbg(p2p, "Dropped previous pending Action frame TX that was waiting for p2p_scan completion"); os_free(p2p->after_scan_tx); p2p->after_scan_tx = NULL; } dev->wps_method = wps_method; dev->oob_pw_id = oob_pw_id; dev->status = P2P_SC_SUCCESS; if (p2p->p2p_scan_running) { p2p_dbg(p2p, "p2p_scan running - delay connect send"); p2p->start_after_scan = P2P_AFTER_SCAN_CONNECT; os_memcpy(p2p->after_scan_peer, peer_addr, ETH_ALEN); return 0; } p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING; return p2p_connect_send(p2p, dev); } int p2p_authorize(struct p2p_data *p2p, const u8 *peer_addr, enum p2p_wps_method wps_method, int go_intent, const u8 *own_interface_addr, unsigned int force_freq, int persistent_group, const u8 *force_ssid, size_t force_ssid_len, unsigned int pref_freq, u16 oob_pw_id) { struct p2p_device *dev; p2p_dbg(p2p, "Request to authorize group negotiation - peer=" MACSTR " GO Intent=%d Intended Interface Address=" MACSTR " wps_method=%d persistent_group=%d oob_pw_id=%u", MAC2STR(peer_addr), go_intent, MAC2STR(own_interface_addr), wps_method, persistent_group, oob_pw_id); dev = p2p_get_device(p2p, peer_addr); if (dev == NULL) { p2p_dbg(p2p, "Cannot authorize unknown P2P Device " MACSTR, MAC2STR(peer_addr)); return -1; } if (p2p_prepare_channel(p2p, dev, force_freq, pref_freq, go_intent == 15) < 0) return -1; p2p->ssid_set = 0; if (force_ssid) { wpa_hexdump_ascii(MSG_DEBUG, "P2P: Forced SSID", force_ssid, force_ssid_len); os_memcpy(p2p->ssid, force_ssid, force_ssid_len); p2p->ssid_len = force_ssid_len; p2p->ssid_set = 1; } dev->flags &= ~P2P_DEV_NOT_YET_READY; dev->flags &= ~P2P_DEV_USER_REJECTED; dev->go_neg_req_sent = 0; dev->go_state = UNKNOWN_GO; p2p_set_dev_persistent(dev, persistent_group); p2p->go_intent = go_intent; os_memcpy(p2p->intended_addr, own_interface_addr, ETH_ALEN); dev->wps_method = wps_method; dev->oob_pw_id = oob_pw_id; dev->status = P2P_SC_SUCCESS; return 0; } void p2p_add_dev_info(struct p2p_data *p2p, const u8 *addr, struct p2p_device *dev, struct p2p_message *msg) { os_get_reltime(&dev->last_seen); p2p_copy_wps_info(p2p, dev, 0, msg); if (msg->listen_channel) { int freq; freq = p2p_channel_to_freq(msg->listen_channel[3], msg->listen_channel[4]); if (freq < 0) { p2p_dbg(p2p, "Unknown peer Listen channel: " "country=%c%c(0x%02x) reg_class=%u channel=%u", msg->listen_channel[0], msg->listen_channel[1], msg->listen_channel[2], msg->listen_channel[3], msg->listen_channel[4]); } else { p2p_dbg(p2p, "Update peer " MACSTR " Listen channel: %u -> %u MHz", MAC2STR(dev->info.p2p_device_addr), dev->listen_freq, freq); dev->listen_freq = freq; } } if (msg->wfd_subelems) { wpabuf_free(dev->info.wfd_subelems); dev->info.wfd_subelems = wpabuf_dup(msg->wfd_subelems); } if (dev->flags & P2P_DEV_PROBE_REQ_ONLY) { dev->flags &= ~P2P_DEV_PROBE_REQ_ONLY; p2p_dbg(p2p, "Completed device entry based on data from GO Negotiation Request"); } else { p2p_dbg(p2p, "Created device entry based on GO Neg Req: " MACSTR " dev_capab=0x%x group_capab=0x%x name='%s' " "listen_freq=%d", MAC2STR(dev->info.p2p_device_addr), dev->info.dev_capab, dev->info.group_capab, dev->info.device_name, dev->listen_freq); } dev->flags &= ~P2P_DEV_GROUP_CLIENT_ONLY; if (dev->flags & P2P_DEV_USER_REJECTED) { p2p_dbg(p2p, "Do not report rejected device"); return; } p2p->cfg->dev_found(p2p->cfg->cb_ctx, addr, &dev->info, !(dev->flags & P2P_DEV_REPORTED_ONCE)); dev->flags |= P2P_DEV_REPORTED | P2P_DEV_REPORTED_ONCE; } void p2p_build_ssid(struct p2p_data *p2p, u8 *ssid, size_t *ssid_len) { os_memcpy(ssid, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN); p2p_random((char *) &ssid[P2P_WILDCARD_SSID_LEN], 2); os_memcpy(&ssid[P2P_WILDCARD_SSID_LEN + 2], p2p->cfg->ssid_postfix, p2p->cfg->ssid_postfix_len); *ssid_len = P2P_WILDCARD_SSID_LEN + 2 + p2p->cfg->ssid_postfix_len; } int p2p_go_params(struct p2p_data *p2p, struct p2p_go_neg_results *params) { p2p_build_ssid(p2p, params->ssid, ¶ms->ssid_len); p2p_random(params->passphrase, 8); return 0; } void p2p_go_complete(struct p2p_data *p2p, struct p2p_device *peer) { struct p2p_go_neg_results res; int go = peer->go_state == LOCAL_GO; struct p2p_channels intersection; int freqs; size_t i, j; p2p_dbg(p2p, "GO Negotiation with " MACSTR " completed (%s will be GO)", MAC2STR(peer->info.p2p_device_addr), go ? "local end" : "peer"); os_memset(&res, 0, sizeof(res)); res.role_go = go; os_memcpy(res.peer_device_addr, peer->info.p2p_device_addr, ETH_ALEN); os_memcpy(res.peer_interface_addr, peer->intended_addr, ETH_ALEN); res.wps_method = peer->wps_method; if (peer->flags & P2P_DEV_PREFER_PERSISTENT_GROUP) { if (peer->flags & P2P_DEV_PREFER_PERSISTENT_RECONN) res.persistent_group = 2; else res.persistent_group = 1; } if (go) { /* Setup AP mode for WPS provisioning */ res.freq = p2p_channel_to_freq(p2p->op_reg_class, p2p->op_channel); os_memcpy(res.ssid, p2p->ssid, p2p->ssid_len); res.ssid_len = p2p->ssid_len; p2p_random(res.passphrase, 8); } else { res.freq = peer->oper_freq; if (p2p->ssid_len) { os_memcpy(res.ssid, p2p->ssid, p2p->ssid_len); res.ssid_len = p2p->ssid_len; } } p2p_channels_dump(p2p, "own channels", &p2p->channels); p2p_channels_dump(p2p, "peer channels", &peer->channels); p2p_channels_intersect(&p2p->channels, &peer->channels, &intersection); if (go) { p2p_channels_remove_freqs(&intersection, &p2p->no_go_freq); p2p_channels_dump(p2p, "intersection after no-GO removal", &intersection); } freqs = 0; for (i = 0; i < intersection.reg_classes; i++) { struct p2p_reg_class *c = &intersection.reg_class[i]; if (freqs + 1 == P2P_MAX_CHANNELS) break; for (j = 0; j < c->channels; j++) { int freq; if (freqs + 1 == P2P_MAX_CHANNELS) break; freq = p2p_channel_to_freq(c->reg_class, c->channel[j]); if (freq < 0) continue; res.freq_list[freqs++] = freq; } } res.peer_config_timeout = go ? peer->client_timeout : peer->go_timeout; p2p_clear_timeout(p2p); p2p->ssid_set = 0; peer->go_neg_req_sent = 0; peer->wps_method = WPS_NOT_READY; peer->oob_pw_id = 0; p2p_set_state(p2p, P2P_PROVISIONING); p2p->cfg->go_neg_completed(p2p->cfg->cb_ctx, &res); } static void p2p_rx_p2p_action(struct p2p_data *p2p, const u8 *sa, const u8 *data, size_t len, int rx_freq) { p2p_dbg(p2p, "RX P2P Public Action from " MACSTR, MAC2STR(sa)); wpa_hexdump(MSG_MSGDUMP, "P2P: P2P Public Action contents", data, len); if (len < 1) return; switch (data[0]) { case P2P_GO_NEG_REQ: p2p_process_go_neg_req(p2p, sa, data + 1, len - 1, rx_freq); break; case P2P_GO_NEG_RESP: p2p_process_go_neg_resp(p2p, sa, data + 1, len - 1, rx_freq); break; case P2P_GO_NEG_CONF: p2p_process_go_neg_conf(p2p, sa, data + 1, len - 1); break; case P2P_INVITATION_REQ: p2p_process_invitation_req(p2p, sa, data + 1, len - 1, rx_freq); break; case P2P_INVITATION_RESP: p2p->cfg->send_action_done(p2p->cfg->cb_ctx); p2p_process_invitation_resp(p2p, sa, data + 1, len - 1); break; case P2P_PROV_DISC_REQ: p2p_process_prov_disc_req(p2p, sa, data + 1, len - 1, rx_freq); break; case P2P_PROV_DISC_RESP: p2p_process_prov_disc_resp(p2p, sa, data + 1, len - 1); break; case P2P_DEV_DISC_REQ: p2p_process_dev_disc_req(p2p, sa, data + 1, len - 1, rx_freq); break; case P2P_DEV_DISC_RESP: p2p_process_dev_disc_resp(p2p, sa, data + 1, len - 1); break; default: p2p_dbg(p2p, "Unsupported P2P Public Action frame type %d", data[0]); break; } } static void p2p_rx_action_public(struct p2p_data *p2p, const u8 *da, const u8 *sa, const u8 *bssid, const u8 *data, size_t len, int freq) { if (len < 1) return; switch (data[0]) { case WLAN_PA_VENDOR_SPECIFIC: data++; len--; if (len < 3) return; if (WPA_GET_BE24(data) != OUI_WFA) return; data += 3; len -= 3; if (len < 1) return; if (*data != P2P_OUI_TYPE) return; p2p_rx_p2p_action(p2p, sa, data + 1, len - 1, freq); break; case WLAN_PA_GAS_INITIAL_REQ: p2p_rx_gas_initial_req(p2p, sa, data + 1, len - 1, freq); break; case WLAN_PA_GAS_INITIAL_RESP: p2p_rx_gas_initial_resp(p2p, sa, data + 1, len - 1, freq); break; case WLAN_PA_GAS_COMEBACK_REQ: p2p_rx_gas_comeback_req(p2p, sa, data + 1, len - 1, freq); break; case WLAN_PA_GAS_COMEBACK_RESP: p2p_rx_gas_comeback_resp(p2p, sa, data + 1, len - 1, freq); break; } } void p2p_rx_action(struct p2p_data *p2p, const u8 *da, const u8 *sa, const u8 *bssid, u8 category, const u8 *data, size_t len, int freq) { if (category == WLAN_ACTION_PUBLIC) { p2p_rx_action_public(p2p, da, sa, bssid, data, len, freq); return; } if (category != WLAN_ACTION_VENDOR_SPECIFIC) return; if (len < 4) return; if (WPA_GET_BE24(data) != OUI_WFA) return; data += 3; len -= 3; if (*data != P2P_OUI_TYPE) return; data++; len--; /* P2P action frame */ p2p_dbg(p2p, "RX P2P Action from " MACSTR, MAC2STR(sa)); wpa_hexdump(MSG_MSGDUMP, "P2P: P2P Action contents", data, len); if (len < 1) return; switch (data[0]) { case P2P_NOA: p2p_dbg(p2p, "Received P2P Action - Notice of Absence"); /* TODO */ break; case P2P_PRESENCE_REQ: p2p_process_presence_req(p2p, da, sa, data + 1, len - 1, freq); break; case P2P_PRESENCE_RESP: p2p_process_presence_resp(p2p, da, sa, data + 1, len - 1); break; case P2P_GO_DISC_REQ: p2p_process_go_disc_req(p2p, da, sa, data + 1, len - 1, freq); break; default: p2p_dbg(p2p, "Received P2P Action - unknown type %u", data[0]); break; } } static void p2p_go_neg_start(void *eloop_ctx, void *timeout_ctx) { struct p2p_data *p2p = eloop_ctx; if (p2p->go_neg_peer == NULL) return; p2p->cfg->stop_listen(p2p->cfg->cb_ctx); p2p->go_neg_peer->status = P2P_SC_SUCCESS; p2p_connect_send(p2p, p2p->go_neg_peer); } static void p2p_invite_start(void *eloop_ctx, void *timeout_ctx) { struct p2p_data *p2p = eloop_ctx; if (p2p->invite_peer == NULL) return; p2p->cfg->stop_listen(p2p->cfg->cb_ctx); p2p_invite_send(p2p, p2p->invite_peer, p2p->invite_go_dev_addr, p2p->invite_dev_pw_id); } static void p2p_add_dev_from_probe_req(struct p2p_data *p2p, const u8 *addr, const u8 *ie, size_t ie_len) { struct p2p_message msg; struct p2p_device *dev; os_memset(&msg, 0, sizeof(msg)); if (p2p_parse_ies(ie, ie_len, &msg) < 0 || msg.p2p_attributes == NULL) { p2p_parse_free(&msg); return; /* not a P2P probe */ } if (msg.ssid == NULL || msg.ssid[1] != P2P_WILDCARD_SSID_LEN || os_memcmp(msg.ssid + 2, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN) != 0) { /* The Probe Request is not part of P2P Device Discovery. It is * not known whether the source address of the frame is the P2P * Device Address or P2P Interface Address. Do not add a new * peer entry based on this frames. */ p2p_parse_free(&msg); return; } dev = p2p_get_device(p2p, addr); if (dev) { if (dev->country[0] == 0 && msg.listen_channel) os_memcpy(dev->country, msg.listen_channel, 3); os_get_reltime(&dev->last_seen); p2p_parse_free(&msg); return; /* already known */ } dev = p2p_create_device(p2p, addr); if (dev == NULL) { p2p_parse_free(&msg); return; } os_get_reltime(&dev->last_seen); dev->flags |= P2P_DEV_PROBE_REQ_ONLY; if (msg.listen_channel) { os_memcpy(dev->country, msg.listen_channel, 3); dev->listen_freq = p2p_channel_to_freq(msg.listen_channel[3], msg.listen_channel[4]); } p2p_copy_wps_info(p2p, dev, 1, &msg); if (msg.wfd_subelems) { wpabuf_free(dev->info.wfd_subelems); dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems); } p2p_parse_free(&msg); p2p_dbg(p2p, "Created device entry based on Probe Req: " MACSTR " dev_capab=0x%x group_capab=0x%x name='%s' listen_freq=%d", MAC2STR(dev->info.p2p_device_addr), dev->info.dev_capab, dev->info.group_capab, dev->info.device_name, dev->listen_freq); } struct p2p_device * p2p_add_dev_from_go_neg_req(struct p2p_data *p2p, const u8 *addr, struct p2p_message *msg) { struct p2p_device *dev; dev = p2p_get_device(p2p, addr); if (dev) { os_get_reltime(&dev->last_seen); return dev; /* already known */ } dev = p2p_create_device(p2p, addr); if (dev == NULL) return NULL; p2p_add_dev_info(p2p, addr, dev, msg); return dev; } static int dev_type_match(const u8 *dev_type, const u8 *req_dev_type) { if (os_memcmp(dev_type, req_dev_type, WPS_DEV_TYPE_LEN) == 0) return 1; if (os_memcmp(dev_type, req_dev_type, 2) == 0 && WPA_GET_BE32(&req_dev_type[2]) == 0 && WPA_GET_BE16(&req_dev_type[6]) == 0) return 1; /* Category match with wildcard OUI/sub-category */ return 0; } int dev_type_list_match(const u8 *dev_type, const u8 *req_dev_type[], size_t num_req_dev_type) { size_t i; for (i = 0; i < num_req_dev_type; i++) { if (dev_type_match(dev_type, req_dev_type[i])) return 1; } return 0; } /** * p2p_match_dev_type - Match local device type with requested type * @p2p: P2P module context from p2p_init() * @wps: WPS TLVs from Probe Request frame (concatenated WPS IEs) * Returns: 1 on match, 0 on mismatch * * This function can be used to match the Requested Device Type attribute in * WPS IE with the local device types for deciding whether to reply to a Probe * Request frame. */ int p2p_match_dev_type(struct p2p_data *p2p, struct wpabuf *wps) { struct wps_parse_attr attr; size_t i; if (wps_parse_msg(wps, &attr)) return 1; /* assume no Requested Device Type attributes */ if (attr.num_req_dev_type == 0) return 1; /* no Requested Device Type attributes -> match */ if (dev_type_list_match(p2p->cfg->pri_dev_type, attr.req_dev_type, attr.num_req_dev_type)) return 1; /* Own Primary Device Type matches */ for (i = 0; i < p2p->cfg->num_sec_dev_types; i++) if (dev_type_list_match(p2p->cfg->sec_dev_type[i], attr.req_dev_type, attr.num_req_dev_type)) return 1; /* Own Secondary Device Type matches */ /* No matching device type found */ return 0; } struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p) { struct wpabuf *buf; u8 *len; int pw_id = -1; size_t extra = 0; #ifdef CONFIG_WIFI_DISPLAY if (p2p->wfd_ie_probe_resp) extra = wpabuf_len(p2p->wfd_ie_probe_resp); #endif /* CONFIG_WIFI_DISPLAY */ buf = wpabuf_alloc(1000 + extra); if (buf == NULL) return NULL; if (p2p->go_neg_peer) { /* Advertise immediate availability of WPS credential */ pw_id = p2p_wps_method_pw_id(p2p->go_neg_peer->wps_method); } if (p2p_build_wps_ie(p2p, buf, pw_id, 1) < 0) { p2p_dbg(p2p, "Failed to build WPS IE for Probe Response"); wpabuf_free(buf); return NULL; } #ifdef CONFIG_WIFI_DISPLAY if (p2p->wfd_ie_probe_resp) wpabuf_put_buf(buf, p2p->wfd_ie_probe_resp); #endif /* CONFIG_WIFI_DISPLAY */ /* P2P IE */ len = p2p_buf_add_ie_hdr(buf); p2p_buf_add_capability(buf, p2p->dev_capab & ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, 0); if (p2p->ext_listen_interval) p2p_buf_add_ext_listen_timing(buf, p2p->ext_listen_period, p2p->ext_listen_interval); p2p_buf_add_device_info(buf, p2p, NULL); p2p_buf_update_ie_hdr(buf, len); return buf; } static enum p2p_probe_req_status p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *dst, const u8 *bssid, const u8 *ie, size_t ie_len) { struct ieee802_11_elems elems; struct wpabuf *buf; struct ieee80211_mgmt *resp; struct p2p_message msg; struct wpabuf *ies; if (!p2p->in_listen || !p2p->drv_in_listen) { /* not in Listen state - ignore Probe Request */ return P2P_PREQ_NOT_LISTEN; } if (ieee802_11_parse_elems((u8 *) ie, ie_len, &elems, 0) == ParseFailed) { /* Ignore invalid Probe Request frames */ return P2P_PREQ_MALFORMED; } if (elems.p2p == NULL) { /* not a P2P probe - ignore it */ return P2P_PREQ_NOT_P2P; } if (dst && !is_broadcast_ether_addr(dst) && os_memcmp(dst, p2p->cfg->dev_addr, ETH_ALEN) != 0) { /* Not sent to the broadcast address or our P2P Device Address */ return P2P_PREQ_NOT_PROCESSED; } if (bssid && !is_broadcast_ether_addr(bssid)) { /* Not sent to the Wildcard BSSID */ return P2P_PREQ_NOT_PROCESSED; } if (elems.ssid == NULL || elems.ssid_len != P2P_WILDCARD_SSID_LEN || os_memcmp(elems.ssid, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN) != 0) { /* not using P2P Wildcard SSID - ignore */ return P2P_PREQ_NOT_PROCESSED; } if (supp_rates_11b_only(&elems)) { /* Indicates support for 11b rates only */ return P2P_PREQ_NOT_P2P; } os_memset(&msg, 0, sizeof(msg)); if (p2p_parse_ies(ie, ie_len, &msg) < 0) { /* Could not parse P2P attributes */ return P2P_PREQ_NOT_P2P; } if (msg.device_id && os_memcmp(msg.device_id, p2p->cfg->dev_addr, ETH_ALEN) != 0) { /* Device ID did not match */ p2p_parse_free(&msg); return P2P_PREQ_NOT_PROCESSED; } /* Check Requested Device Type match */ if (msg.wps_attributes && !p2p_match_dev_type(p2p, msg.wps_attributes)) { /* No match with Requested Device Type */ p2p_parse_free(&msg); return P2P_PREQ_NOT_PROCESSED; } p2p_parse_free(&msg); if (!p2p->cfg->send_probe_resp) { /* Response generated elsewhere */ return P2P_PREQ_NOT_PROCESSED; } p2p_dbg(p2p, "Reply to P2P Probe Request in Listen state"); /* * We do not really have a specific BSS that this frame is advertising, * so build a frame that has some information in valid format. This is * really only used for discovery purposes, not to learn exact BSS * parameters. */ ies = p2p_build_probe_resp_ies(p2p); if (ies == NULL) return P2P_PREQ_NOT_PROCESSED; buf = wpabuf_alloc(200 + wpabuf_len(ies)); if (buf == NULL) { wpabuf_free(ies); return P2P_PREQ_NOT_PROCESSED; } resp = NULL; resp = wpabuf_put(buf, resp->u.probe_resp.variable - (u8 *) resp); resp->frame_control = host_to_le16((WLAN_FC_TYPE_MGMT << 2) | (WLAN_FC_STYPE_PROBE_RESP << 4)); os_memcpy(resp->da, addr, ETH_ALEN); os_memcpy(resp->sa, p2p->cfg->dev_addr, ETH_ALEN); os_memcpy(resp->bssid, p2p->cfg->dev_addr, ETH_ALEN); resp->u.probe_resp.beacon_int = host_to_le16(100); /* hardware or low-level driver will setup seq_ctrl and timestamp */ resp->u.probe_resp.capab_info = host_to_le16(WLAN_CAPABILITY_SHORT_PREAMBLE | WLAN_CAPABILITY_PRIVACY | WLAN_CAPABILITY_SHORT_SLOT_TIME); wpabuf_put_u8(buf, WLAN_EID_SSID); wpabuf_put_u8(buf, P2P_WILDCARD_SSID_LEN); wpabuf_put_data(buf, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN); wpabuf_put_u8(buf, WLAN_EID_SUPP_RATES); wpabuf_put_u8(buf, 8); wpabuf_put_u8(buf, (60 / 5) | 0x80); wpabuf_put_u8(buf, 90 / 5); wpabuf_put_u8(buf, (120 / 5) | 0x80); wpabuf_put_u8(buf, 180 / 5); wpabuf_put_u8(buf, (240 / 5) | 0x80); wpabuf_put_u8(buf, 360 / 5); wpabuf_put_u8(buf, 480 / 5); wpabuf_put_u8(buf, 540 / 5); wpabuf_put_u8(buf, WLAN_EID_DS_PARAMS); wpabuf_put_u8(buf, 1); wpabuf_put_u8(buf, p2p->cfg->channel); wpabuf_put_buf(buf, ies); wpabuf_free(ies); p2p->cfg->send_probe_resp(p2p->cfg->cb_ctx, buf); wpabuf_free(buf); return P2P_PREQ_NOT_PROCESSED; } enum p2p_probe_req_status p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *dst, const u8 *bssid, const u8 *ie, size_t ie_len) { enum p2p_probe_req_status res; p2p_add_dev_from_probe_req(p2p, addr, ie, ie_len); res = p2p_reply_probe(p2p, addr, dst, bssid, ie, ie_len); if ((p2p->state == P2P_CONNECT || p2p->state == P2P_CONNECT_LISTEN) && p2p->go_neg_peer && os_memcmp(addr, p2p->go_neg_peer->info.p2p_device_addr, ETH_ALEN) == 0 && !(p2p->go_neg_peer->flags & P2P_DEV_WAIT_GO_NEG_CONFIRM)) { /* Received a Probe Request from GO Negotiation peer */ p2p_dbg(p2p, "Found GO Negotiation peer - try to start GO negotiation from timeout"); eloop_cancel_timeout(p2p_go_neg_start, p2p, NULL); eloop_register_timeout(0, 0, p2p_go_neg_start, p2p, NULL); return P2P_PREQ_PROCESSED; } if ((p2p->state == P2P_INVITE || p2p->state == P2P_INVITE_LISTEN) && p2p->invite_peer && os_memcmp(addr, p2p->invite_peer->info.p2p_device_addr, ETH_ALEN) == 0) { /* Received a Probe Request from Invite peer */ p2p_dbg(p2p, "Found Invite peer - try to start Invite from timeout"); eloop_register_timeout(0, 0, p2p_invite_start, p2p, NULL); return P2P_PREQ_PROCESSED; } return res; } static int p2p_assoc_req_ie_wlan_ap(struct p2p_data *p2p, const u8 *bssid, u8 *buf, size_t len, struct wpabuf *p2p_ie) { struct wpabuf *tmp; u8 *lpos; size_t tmplen; int res; u8 group_capab; if (p2p_ie == NULL) return 0; /* WLAN AP is not a P2P manager */ /* * (Re)Association Request - P2P IE * P2P Capability attribute (shall be present) * P2P Interface attribute (present if concurrent device and * P2P Management is enabled) */ tmp = wpabuf_alloc(200); if (tmp == NULL) return -1; lpos = p2p_buf_add_ie_hdr(tmp); group_capab = 0; if (p2p->num_groups > 0) { group_capab |= P2P_GROUP_CAPAB_GROUP_OWNER; if ((p2p->dev_capab & P2P_DEV_CAPAB_CONCURRENT_OPER) && (p2p->dev_capab & P2P_DEV_CAPAB_INFRA_MANAGED) && p2p->cross_connect) group_capab |= P2P_GROUP_CAPAB_CROSS_CONN; } p2p_buf_add_capability(tmp, p2p->dev_capab, group_capab); if ((p2p->dev_capab & P2P_DEV_CAPAB_CONCURRENT_OPER) && (p2p->dev_capab & P2P_DEV_CAPAB_INFRA_MANAGED)) p2p_buf_add_p2p_interface(tmp, p2p); p2p_buf_update_ie_hdr(tmp, lpos); tmplen = wpabuf_len(tmp); if (tmplen > len) res = -1; else { os_memcpy(buf, wpabuf_head(tmp), tmplen); res = tmplen; } wpabuf_free(tmp); return res; } int p2p_assoc_req_ie(struct p2p_data *p2p, const u8 *bssid, u8 *buf, size_t len, int p2p_group, struct wpabuf *p2p_ie) { struct wpabuf *tmp; u8 *lpos; struct p2p_device *peer; size_t tmplen; int res; size_t extra = 0; if (!p2p_group) return p2p_assoc_req_ie_wlan_ap(p2p, bssid, buf, len, p2p_ie); #ifdef CONFIG_WIFI_DISPLAY if (p2p->wfd_ie_assoc_req) extra = wpabuf_len(p2p->wfd_ie_assoc_req); #endif /* CONFIG_WIFI_DISPLAY */ /* * (Re)Association Request - P2P IE * P2P Capability attribute (shall be present) * Extended Listen Timing (may be present) * P2P Device Info attribute (shall be present) */ tmp = wpabuf_alloc(200 + extra); if (tmp == NULL) return -1; #ifdef CONFIG_WIFI_DISPLAY if (p2p->wfd_ie_assoc_req) wpabuf_put_buf(tmp, p2p->wfd_ie_assoc_req); #endif /* CONFIG_WIFI_DISPLAY */ peer = bssid ? p2p_get_device(p2p, bssid) : NULL; lpos = p2p_buf_add_ie_hdr(tmp); p2p_buf_add_capability(tmp, p2p->dev_capab, 0); if (p2p->ext_listen_interval) p2p_buf_add_ext_listen_timing(tmp, p2p->ext_listen_period, p2p->ext_listen_interval); p2p_buf_add_device_info(tmp, p2p, peer); p2p_buf_update_ie_hdr(tmp, lpos); tmplen = wpabuf_len(tmp); if (tmplen > len) res = -1; else { os_memcpy(buf, wpabuf_head(tmp), tmplen); res = tmplen; } wpabuf_free(tmp); return res; } int p2p_scan_result_text(const u8 *ies, size_t ies_len, char *buf, char *end) { struct wpabuf *p2p_ie; int ret; p2p_ie = ieee802_11_vendor_ie_concat(ies, ies_len, P2P_IE_VENDOR_TYPE); if (p2p_ie == NULL) return 0; ret = p2p_attr_text(p2p_ie, buf, end); wpabuf_free(p2p_ie); return ret; } int p2p_parse_dev_addr_in_p2p_ie(struct wpabuf *p2p_ie, u8 *dev_addr) { struct p2p_message msg; os_memset(&msg, 0, sizeof(msg)); if (p2p_parse_p2p_ie(p2p_ie, &msg)) return -1; if (msg.p2p_device_addr) { os_memcpy(dev_addr, msg.p2p_device_addr, ETH_ALEN); return 0; } else if (msg.device_id) { os_memcpy(dev_addr, msg.device_id, ETH_ALEN); return 0; } return -1; } int p2p_parse_dev_addr(const u8 *ies, size_t ies_len, u8 *dev_addr) { struct wpabuf *p2p_ie; int ret; p2p_ie = ieee802_11_vendor_ie_concat(ies, ies_len, P2P_IE_VENDOR_TYPE); if (p2p_ie == NULL) return -1; ret = p2p_parse_dev_addr_in_p2p_ie(p2p_ie, dev_addr); wpabuf_free(p2p_ie); return ret; } static void p2p_clear_go_neg(struct p2p_data *p2p) { p2p->go_neg_peer = NULL; p2p_clear_timeout(p2p); p2p_set_state(p2p, P2P_IDLE); } void p2p_wps_success_cb(struct p2p_data *p2p, const u8 *mac_addr) { if (p2p->go_neg_peer == NULL) { p2p_dbg(p2p, "No pending Group Formation - ignore WPS registration success notification"); return; /* No pending Group Formation */ } if (os_memcmp(mac_addr, p2p->go_neg_peer->intended_addr, ETH_ALEN) != 0) { p2p_dbg(p2p, "Ignore WPS registration success notification for " MACSTR " (GO Negotiation peer " MACSTR ")", MAC2STR(mac_addr), MAC2STR(p2p->go_neg_peer->intended_addr)); return; /* Ignore unexpected peer address */ } p2p_dbg(p2p, "Group Formation completed successfully with " MACSTR, MAC2STR(mac_addr)); p2p_clear_go_neg(p2p); } void p2p_group_formation_failed(struct p2p_data *p2p) { if (p2p->go_neg_peer == NULL) { p2p_dbg(p2p, "No pending Group Formation - ignore group formation failure notification"); return; /* No pending Group Formation */ } p2p_dbg(p2p, "Group Formation failed with " MACSTR, MAC2STR(p2p->go_neg_peer->intended_addr)); p2p_clear_go_neg(p2p); } struct p2p_data * p2p_init(const struct p2p_config *cfg) { struct p2p_data *p2p; if (cfg->max_peers < 1) return NULL; p2p = os_zalloc(sizeof(*p2p) + sizeof(*cfg)); if (p2p == NULL) return NULL; p2p->cfg = (struct p2p_config *) (p2p + 1); os_memcpy(p2p->cfg, cfg, sizeof(*cfg)); if (cfg->dev_name) p2p->cfg->dev_name = os_strdup(cfg->dev_name); if (cfg->manufacturer) p2p->cfg->manufacturer = os_strdup(cfg->manufacturer); if (cfg->model_name) p2p->cfg->model_name = os_strdup(cfg->model_name); if (cfg->model_number) p2p->cfg->model_number = os_strdup(cfg->model_number); if (cfg->serial_number) p2p->cfg->serial_number = os_strdup(cfg->serial_number); if (cfg->pref_chan) { p2p->cfg->pref_chan = os_malloc(cfg->num_pref_chan * sizeof(struct p2p_channel)); if (p2p->cfg->pref_chan) { os_memcpy(p2p->cfg->pref_chan, cfg->pref_chan, cfg->num_pref_chan * sizeof(struct p2p_channel)); } else p2p->cfg->num_pref_chan = 0; } p2p->min_disc_int = 1; p2p->max_disc_int = 3; p2p->max_disc_tu = -1; os_get_random(&p2p->next_tie_breaker, 1); p2p->next_tie_breaker &= 0x01; if (cfg->sd_request) p2p->dev_capab |= P2P_DEV_CAPAB_SERVICE_DISCOVERY; p2p->dev_capab |= P2P_DEV_CAPAB_INVITATION_PROCEDURE; if (cfg->concurrent_operations) p2p->dev_capab |= P2P_DEV_CAPAB_CONCURRENT_OPER; p2p->dev_capab |= P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY; dl_list_init(&p2p->devices); eloop_register_timeout(P2P_PEER_EXPIRATION_INTERVAL, 0, p2p_expiration_timeout, p2p, NULL); p2p->go_timeout = 100; p2p->client_timeout = 20; p2p_dbg(p2p, "initialized"); p2p_channels_dump(p2p, "channels", &p2p->cfg->channels); p2p_channels_dump(p2p, "cli_channels", &p2p->cfg->cli_channels); return p2p; } void p2p_deinit(struct p2p_data *p2p) { #ifdef CONFIG_WIFI_DISPLAY wpabuf_free(p2p->wfd_ie_beacon); wpabuf_free(p2p->wfd_ie_probe_req); wpabuf_free(p2p->wfd_ie_probe_resp); wpabuf_free(p2p->wfd_ie_assoc_req); wpabuf_free(p2p->wfd_ie_invitation); wpabuf_free(p2p->wfd_ie_prov_disc_req); wpabuf_free(p2p->wfd_ie_prov_disc_resp); wpabuf_free(p2p->wfd_ie_go_neg); wpabuf_free(p2p->wfd_dev_info); wpabuf_free(p2p->wfd_assoc_bssid); wpabuf_free(p2p->wfd_coupled_sink_info); #endif /* CONFIG_WIFI_DISPLAY */ eloop_cancel_timeout(p2p_expiration_timeout, p2p, NULL); eloop_cancel_timeout(p2p_ext_listen_timeout, p2p, NULL); eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL); eloop_cancel_timeout(p2p_go_neg_start, p2p, NULL); p2p_flush(p2p); p2p_free_req_dev_types(p2p); os_free(p2p->cfg->dev_name); os_free(p2p->cfg->manufacturer); os_free(p2p->cfg->model_name); os_free(p2p->cfg->model_number); os_free(p2p->cfg->serial_number); os_free(p2p->cfg->pref_chan); os_free(p2p->groups); wpabuf_free(p2p->sd_resp); os_free(p2p->after_scan_tx); p2p_remove_wps_vendor_extensions(p2p); os_free(p2p->no_go_freq.range); os_free(p2p); } void p2p_flush(struct p2p_data *p2p) { struct p2p_device *dev, *prev; p2p_stop_find(p2p); dl_list_for_each_safe(dev, prev, &p2p->devices, struct p2p_device, list) { dl_list_del(&dev->list); p2p_device_free(p2p, dev); } p2p_free_sd_queries(p2p); os_free(p2p->after_scan_tx); p2p->after_scan_tx = NULL; } int p2p_unauthorize(struct p2p_data *p2p, const u8 *addr) { struct p2p_device *dev; dev = p2p_get_device(p2p, addr); if (dev == NULL) return -1; p2p_dbg(p2p, "Unauthorizing " MACSTR, MAC2STR(addr)); if (p2p->go_neg_peer == dev) p2p->go_neg_peer = NULL; dev->wps_method = WPS_NOT_READY; dev->oob_pw_id = 0; dev->flags &= ~P2P_DEV_WAIT_GO_NEG_RESPONSE; dev->flags &= ~P2P_DEV_WAIT_GO_NEG_CONFIRM; /* Check if after_scan_tx is for this peer. If so free it */ if (p2p->after_scan_tx && os_memcmp(addr, p2p->after_scan_tx->dst, ETH_ALEN) == 0) { os_free(p2p->after_scan_tx); p2p->after_scan_tx = NULL; } return 0; } int p2p_set_dev_name(struct p2p_data *p2p, const char *dev_name) { os_free(p2p->cfg->dev_name); if (dev_name) { p2p->cfg->dev_name = os_strdup(dev_name); if (p2p->cfg->dev_name == NULL) return -1; } else p2p->cfg->dev_name = NULL; return 0; } int p2p_set_manufacturer(struct p2p_data *p2p, const char *manufacturer) { os_free(p2p->cfg->manufacturer); p2p->cfg->manufacturer = NULL; if (manufacturer) { p2p->cfg->manufacturer = os_strdup(manufacturer); if (p2p->cfg->manufacturer == NULL) return -1; } return 0; } int p2p_set_model_name(struct p2p_data *p2p, const char *model_name) { os_free(p2p->cfg->model_name); p2p->cfg->model_name = NULL; if (model_name) { p2p->cfg->model_name = os_strdup(model_name); if (p2p->cfg->model_name == NULL) return -1; } return 0; } int p2p_set_model_number(struct p2p_data *p2p, const char *model_number) { os_free(p2p->cfg->model_number); p2p->cfg->model_number = NULL; if (model_number) { p2p->cfg->model_number = os_strdup(model_number); if (p2p->cfg->model_number == NULL) return -1; } return 0; } int p2p_set_serial_number(struct p2p_data *p2p, const char *serial_number) { os_free(p2p->cfg->serial_number); p2p->cfg->serial_number = NULL; if (serial_number) { p2p->cfg->serial_number = os_strdup(serial_number); if (p2p->cfg->serial_number == NULL) return -1; } return 0; } void p2p_set_config_methods(struct p2p_data *p2p, u16 config_methods) { p2p->cfg->config_methods = config_methods; } void p2p_set_uuid(struct p2p_data *p2p, const u8 *uuid) { os_memcpy(p2p->cfg->uuid, uuid, 16); } int p2p_set_pri_dev_type(struct p2p_data *p2p, const u8 *pri_dev_type) { os_memcpy(p2p->cfg->pri_dev_type, pri_dev_type, 8); return 0; } int p2p_set_sec_dev_types(struct p2p_data *p2p, const u8 dev_types[][8], size_t num_dev_types) { if (num_dev_types > P2P_SEC_DEVICE_TYPES) num_dev_types = P2P_SEC_DEVICE_TYPES; p2p->cfg->num_sec_dev_types = num_dev_types; os_memcpy(p2p->cfg->sec_dev_type, dev_types, num_dev_types * 8); return 0; } void p2p_remove_wps_vendor_extensions(struct p2p_data *p2p) { int i; for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) { wpabuf_free(p2p->wps_vendor_ext[i]); p2p->wps_vendor_ext[i] = NULL; } } int p2p_add_wps_vendor_extension(struct p2p_data *p2p, const struct wpabuf *vendor_ext) { int i; if (vendor_ext == NULL) return -1; for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) { if (p2p->wps_vendor_ext[i] == NULL) break; } if (i >= P2P_MAX_WPS_VENDOR_EXT) return -1; p2p->wps_vendor_ext[i] = wpabuf_dup(vendor_ext); if (p2p->wps_vendor_ext[i] == NULL) return -1; return 0; } int p2p_set_country(struct p2p_data *p2p, const char *country) { os_memcpy(p2p->cfg->country, country, 3); return 0; } void p2p_continue_find(struct p2p_data *p2p) { struct p2p_device *dev; p2p_set_state(p2p, P2P_SEARCH); dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) { if (dev->flags & P2P_DEV_SD_SCHEDULE) { if (p2p_start_sd(p2p, dev) == 0) return; else break; } else if (dev->req_config_methods && !(dev->flags & P2P_DEV_PD_FOR_JOIN)) { p2p_dbg(p2p, "Send pending Provision Discovery Request to " MACSTR " (config methods 0x%x)", MAC2STR(dev->info.p2p_device_addr), dev->req_config_methods); if (p2p_send_prov_disc_req(p2p, dev, 0, 0) == 0) return; } } p2p_listen_in_find(p2p, 1); } static void p2p_sd_cb(struct p2p_data *p2p, int success) { p2p_dbg(p2p, "Service Discovery Query TX callback: success=%d", success); p2p->pending_action_state = P2P_NO_PENDING_ACTION; if (!success) { if (p2p->sd_peer) { p2p->sd_peer->flags &= ~P2P_DEV_SD_SCHEDULE; p2p->sd_peer = NULL; } p2p_continue_find(p2p); return; } if (p2p->sd_peer == NULL) { p2p_dbg(p2p, "No SD peer entry known"); p2p_continue_find(p2p); return; } /* Wait for response from the peer */ p2p_set_state(p2p, P2P_SD_DURING_FIND); p2p_set_timeout(p2p, 0, 200000); } /** * p2p_retry_pd - Retry any pending provision disc requests in IDLE state * @p2p: P2P module context from p2p_init() */ static void p2p_retry_pd(struct p2p_data *p2p) { struct p2p_device *dev; if (p2p->state != P2P_IDLE) return; /* * Retry the prov disc req attempt only for the peer that the user had * requested. */ dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) { if (os_memcmp(p2p->pending_pd_devaddr, dev->info.p2p_device_addr, ETH_ALEN) != 0) continue; if (!dev->req_config_methods) continue; p2p_dbg(p2p, "Send pending Provision Discovery Request to " MACSTR " (config methods 0x%x)", MAC2STR(dev->info.p2p_device_addr), dev->req_config_methods); p2p_send_prov_disc_req(p2p, dev, dev->flags & P2P_DEV_PD_FOR_JOIN, p2p->pd_force_freq); return; } } static void p2p_prov_disc_cb(struct p2p_data *p2p, int success) { p2p_dbg(p2p, "Provision Discovery Request TX callback: success=%d", success); /* * Postpone resetting the pending action state till after we actually * time out. This allows us to take some action like notifying any * interested parties about no response to the request. * * When the timer (below) goes off we check in IDLE, SEARCH, or * LISTEN_ONLY state, which are the only allowed states to issue a PD * requests in, if this was still pending and then raise notification. */ if (!success) { p2p->pending_action_state = P2P_NO_PENDING_ACTION; if (p2p->user_initiated_pd && (p2p->state == P2P_SEARCH || p2p->state == P2P_LISTEN_ONLY)) { /* Retry request from timeout to avoid busy loops */ p2p->pending_action_state = P2P_PENDING_PD; p2p_set_timeout(p2p, 0, 50000); } else if (p2p->state != P2P_IDLE) p2p_continue_find(p2p); else if (p2p->user_initiated_pd) { p2p->pending_action_state = P2P_PENDING_PD; p2p_set_timeout(p2p, 0, 300000); } return; } /* * This postponing, of resetting pending_action_state, needs to be * done only for user initiated PD requests and not internal ones. */ if (p2p->user_initiated_pd) p2p->pending_action_state = P2P_PENDING_PD; else p2p->pending_action_state = P2P_NO_PENDING_ACTION; /* Wait for response from the peer */ if (p2p->state == P2P_SEARCH) p2p_set_state(p2p, P2P_PD_DURING_FIND); p2p_set_timeout(p2p, 0, 200000); } int p2p_scan_res_handler(struct p2p_data *p2p, const u8 *bssid, int freq, struct os_reltime *rx_time, int level, const u8 *ies, size_t ies_len) { if (os_reltime_before(rx_time, &p2p->find_start)) { /* * The driver may have cached (e.g., in cfg80211 BSS table) the * scan results for relatively long time. To avoid reporting * stale information, update P2P peers only based on results * that have based on frames received after the last p2p_find * operation was started. */ p2p_dbg(p2p, "Ignore old scan result for " MACSTR " (rx_time=%u.%06u)", MAC2STR(bssid), (unsigned int) rx_time->sec, (unsigned int) rx_time->usec); return 0; } p2p_add_device(p2p, bssid, freq, rx_time, level, ies, ies_len, 1); return 0; } void p2p_scan_res_handled(struct p2p_data *p2p) { if (!p2p->p2p_scan_running) { p2p_dbg(p2p, "p2p_scan was not running, but scan results received"); } p2p->p2p_scan_running = 0; eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL); if (p2p_run_after_scan(p2p)) return; if (p2p->state == P2P_SEARCH) p2p_continue_find(p2p); } void p2p_scan_ie(struct p2p_data *p2p, struct wpabuf *ies, const u8 *dev_id) { u8 *len; #ifdef CONFIG_WIFI_DISPLAY if (p2p->wfd_ie_probe_req) wpabuf_put_buf(ies, p2p->wfd_ie_probe_req); #endif /* CONFIG_WIFI_DISPLAY */ len = p2p_buf_add_ie_hdr(ies); p2p_buf_add_capability(ies, p2p->dev_capab & ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, 0); if (dev_id) p2p_buf_add_device_id(ies, dev_id); if (p2p->cfg->reg_class && p2p->cfg->channel) p2p_buf_add_listen_channel(ies, p2p->cfg->country, p2p->cfg->reg_class, p2p->cfg->channel); if (p2p->ext_listen_interval) p2p_buf_add_ext_listen_timing(ies, p2p->ext_listen_period, p2p->ext_listen_interval); /* TODO: p2p_buf_add_operating_channel() if GO */ p2p_buf_update_ie_hdr(ies, len); } size_t p2p_scan_ie_buf_len(struct p2p_data *p2p) { size_t len = 100; #ifdef CONFIG_WIFI_DISPLAY if (p2p && p2p->wfd_ie_probe_req) len += wpabuf_len(p2p->wfd_ie_probe_req); #endif /* CONFIG_WIFI_DISPLAY */ return len; } int p2p_ie_text(struct wpabuf *p2p_ie, char *buf, char *end) { return p2p_attr_text(p2p_ie, buf, end); } static void p2p_go_neg_req_cb(struct p2p_data *p2p, int success) { struct p2p_device *dev = p2p->go_neg_peer; int timeout; p2p_dbg(p2p, "GO Negotiation Request TX callback: success=%d", success); if (dev == NULL) { p2p_dbg(p2p, "No pending GO Negotiation"); return; } if (success) { if (dev->flags & P2P_DEV_USER_REJECTED) { p2p_set_state(p2p, P2P_IDLE); return; } } else if (dev->go_neg_req_sent) { /* Cancel the increment from p2p_connect_send() on failure */ dev->go_neg_req_sent--; } if (!success && (dev->info.dev_capab & P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY) && !is_zero_ether_addr(dev->member_in_go_dev)) { p2p_dbg(p2p, "Peer " MACSTR " did not acknowledge request - try to use device discoverability through its GO", MAC2STR(dev->info.p2p_device_addr)); p2p->cfg->send_action_done(p2p->cfg->cb_ctx); p2p_send_dev_disc_req(p2p, dev); return; } /* * Use P2P find, if needed, to find the other device from its listen * channel. */ p2p_set_state(p2p, P2P_CONNECT); timeout = success ? 500000 : 100000; if (!success && p2p->go_neg_peer && (p2p->go_neg_peer->flags & P2P_DEV_PEER_WAITING_RESPONSE)) { unsigned int r; /* * Peer is expected to wait our response and we will skip the * listen phase. Add some randomness to the wait time here to * make it less likely to hit cases where we could end up in * sync with peer not listening. */ os_get_random((u8 *) &r, sizeof(r)); timeout += r % 100000; } p2p_set_timeout(p2p, 0, timeout); } static void p2p_go_neg_resp_cb(struct p2p_data *p2p, int success) { p2p_dbg(p2p, "GO Negotiation Response TX callback: success=%d", success); if (!p2p->go_neg_peer && p2p->state == P2P_PROVISIONING) { p2p_dbg(p2p, "Ignore TX callback event - GO Negotiation is not running anymore"); return; } p2p_set_state(p2p, P2P_CONNECT); p2p_set_timeout(p2p, 0, 500000); } static void p2p_go_neg_resp_failure_cb(struct p2p_data *p2p, int success, const u8 *addr) { p2p_dbg(p2p, "GO Negotiation Response (failure) TX callback: success=%d", success); if (p2p->go_neg_peer && p2p->go_neg_peer->status != P2P_SC_SUCCESS) { p2p_go_neg_failed(p2p, p2p->go_neg_peer, p2p->go_neg_peer->status); } else if (success) { struct p2p_device *dev; dev = p2p_get_device(p2p, addr); if (dev && dev->status == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) dev->flags |= P2P_DEV_PEER_WAITING_RESPONSE; } } static void p2p_go_neg_conf_cb(struct p2p_data *p2p, enum p2p_send_action_result result) { struct p2p_device *dev; p2p_dbg(p2p, "GO Negotiation Confirm TX callback: result=%d", result); p2p->cfg->send_action_done(p2p->cfg->cb_ctx); if (result == P2P_SEND_ACTION_FAILED) { p2p_go_neg_failed(p2p, p2p->go_neg_peer, -1); return; } if (result == P2P_SEND_ACTION_NO_ACK) { /* * It looks like the TX status for GO Negotiation Confirm is * often showing failure even when the peer has actually * received the frame. Since the peer may change channels * immediately after having received the frame, we may not see * an Ack for retries, so just dropping a single frame may * trigger this. To allow the group formation to succeed if the * peer did indeed receive the frame, continue regardless of * the TX status. */ p2p_dbg(p2p, "Assume GO Negotiation Confirm TX was actually received by the peer even though Ack was not reported"); } dev = p2p->go_neg_peer; if (dev == NULL) return; p2p_go_complete(p2p, dev); } void p2p_send_action_cb(struct p2p_data *p2p, unsigned int freq, const u8 *dst, const u8 *src, const u8 *bssid, enum p2p_send_action_result result) { enum p2p_pending_action_state state; int success; p2p_dbg(p2p, "Action frame TX callback (state=%d freq=%u dst=" MACSTR " src=" MACSTR " bssid=" MACSTR " result=%d", p2p->pending_action_state, freq, MAC2STR(dst), MAC2STR(src), MAC2STR(bssid), result); success = result == P2P_SEND_ACTION_SUCCESS; state = p2p->pending_action_state; p2p->pending_action_state = P2P_NO_PENDING_ACTION; switch (state) { case P2P_NO_PENDING_ACTION: if (p2p->after_scan_tx_in_progress) { p2p->after_scan_tx_in_progress = 0; if (p2p->start_after_scan != P2P_AFTER_SCAN_NOTHING && p2p_run_after_scan(p2p)) break; if (p2p->state == P2P_SEARCH) { p2p_dbg(p2p, "Continue find after after_scan_tx completion"); p2p_continue_find(p2p); } } break; case P2P_PENDING_GO_NEG_REQUEST: p2p_go_neg_req_cb(p2p, success); break; case P2P_PENDING_GO_NEG_RESPONSE: p2p_go_neg_resp_cb(p2p, success); break; case P2P_PENDING_GO_NEG_RESPONSE_FAILURE: p2p_go_neg_resp_failure_cb(p2p, success, dst); break; case P2P_PENDING_GO_NEG_CONFIRM: p2p_go_neg_conf_cb(p2p, result); break; case P2P_PENDING_SD: p2p_sd_cb(p2p, success); break; case P2P_PENDING_PD: p2p_prov_disc_cb(p2p, success); break; case P2P_PENDING_INVITATION_REQUEST: p2p_invitation_req_cb(p2p, success); break; case P2P_PENDING_INVITATION_RESPONSE: p2p_invitation_resp_cb(p2p, success); break; case P2P_PENDING_DEV_DISC_REQUEST: p2p_dev_disc_req_cb(p2p, success); break; case P2P_PENDING_DEV_DISC_RESPONSE: p2p_dev_disc_resp_cb(p2p, success); break; case P2P_PENDING_GO_DISC_REQ: p2p_go_disc_req_cb(p2p, success); break; } p2p->after_scan_tx_in_progress = 0; } void p2p_listen_cb(struct p2p_data *p2p, unsigned int freq, unsigned int duration) { if (freq == p2p->pending_client_disc_freq) { p2p_dbg(p2p, "Client discoverability remain-awake completed"); p2p->pending_client_disc_freq = 0; return; } if (freq != p2p->pending_listen_freq) { p2p_dbg(p2p, "Unexpected listen callback for freq=%u duration=%u (pending_listen_freq=%u)", freq, duration, p2p->pending_listen_freq); return; } p2p_dbg(p2p, "Starting Listen timeout(%u,%u) on freq=%u based on callback", p2p->pending_listen_sec, p2p->pending_listen_usec, p2p->pending_listen_freq); p2p->in_listen = 1; p2p->drv_in_listen = freq; if (p2p->pending_listen_sec || p2p->pending_listen_usec) { /* * Add 20 msec extra wait to avoid race condition with driver * remain-on-channel end event, i.e., give driver more time to * complete the operation before our timeout expires. */ p2p_set_timeout(p2p, p2p->pending_listen_sec, p2p->pending_listen_usec + 20000); } p2p->pending_listen_freq = 0; } int p2p_listen_end(struct p2p_data *p2p, unsigned int freq) { p2p_dbg(p2p, "Driver ended Listen state (freq=%u)", freq); p2p->drv_in_listen = 0; if (p2p->in_listen) return 0; /* Internal timeout will trigger the next step */ if (p2p->state == P2P_CONNECT_LISTEN && p2p->go_neg_peer) { if (p2p->go_neg_peer->connect_reqs >= 120) { p2p_dbg(p2p, "Timeout on sending GO Negotiation Request without getting response"); p2p_go_neg_failed(p2p, p2p->go_neg_peer, -1); return 0; } p2p_set_state(p2p, P2P_CONNECT); p2p_connect_send(p2p, p2p->go_neg_peer); return 1; } else if (p2p->state == P2P_SEARCH) { if (p2p->p2p_scan_running) { /* * Search is already in progress. This can happen if * an Action frame RX is reported immediately after * the end of a remain-on-channel operation and the * response frame to that is sent using an offchannel * operation while in p2p_find. Avoid an attempt to * restart a scan here. */ p2p_dbg(p2p, "p2p_scan already in progress - do not try to start a new one"); return 1; } if (p2p->pending_listen_freq) { /* * Better wait a bit if the driver is unable to start * offchannel operation for some reason. p2p_search() * will be started from internal timeout. */ p2p_dbg(p2p, "Listen operation did not seem to start - delay search phase to avoid busy loop"); p2p_set_timeout(p2p, 0, 100000); return 1; } if (p2p->search_delay) { p2p_dbg(p2p, "Delay search operation by %u ms", p2p->search_delay); p2p_set_timeout(p2p, p2p->search_delay / 1000, (p2p->search_delay % 1000) * 1000); return 1; } p2p_search(p2p); return 1; } return 0; } static void p2p_timeout_connect(struct p2p_data *p2p) { p2p->cfg->send_action_done(p2p->cfg->cb_ctx); if (p2p->go_neg_peer && (p2p->go_neg_peer->flags & P2P_DEV_WAIT_GO_NEG_CONFIRM)) { p2p_dbg(p2p, "Wait for GO Negotiation Confirm timed out - assume GO Negotiation failed"); p2p_go_neg_failed(p2p, p2p->go_neg_peer, -1); return; } if (p2p->go_neg_peer && (p2p->go_neg_peer->flags & P2P_DEV_PEER_WAITING_RESPONSE) && p2p->go_neg_peer->connect_reqs < 120) { p2p_dbg(p2p, "Peer expected to wait our response - skip listen"); p2p_connect_send(p2p, p2p->go_neg_peer); return; } if (p2p->go_neg_peer && p2p->go_neg_peer->oob_go_neg_freq > 0) { p2p_dbg(p2p, "Skip connect-listen since GO Neg channel known (OOB)"); p2p_set_state(p2p, P2P_CONNECT_LISTEN); p2p_set_timeout(p2p, 0, 30000); return; } p2p_set_state(p2p, P2P_CONNECT_LISTEN); p2p_listen_in_find(p2p, 0); } static void p2p_timeout_connect_listen(struct p2p_data *p2p) { if (p2p->go_neg_peer) { if (p2p->drv_in_listen) { p2p_dbg(p2p, "Driver is still in Listen state; wait for it to complete"); return; } if (p2p->go_neg_peer->connect_reqs >= 120) { p2p_dbg(p2p, "Timeout on sending GO Negotiation Request without getting response"); p2p_go_neg_failed(p2p, p2p->go_neg_peer, -1); return; } p2p_set_state(p2p, P2P_CONNECT); p2p_connect_send(p2p, p2p->go_neg_peer); } else p2p_set_state(p2p, P2P_IDLE); } static void p2p_timeout_wait_peer_connect(struct p2p_data *p2p) { p2p_set_state(p2p, P2P_WAIT_PEER_IDLE); if (p2p->cfg->is_concurrent_session_active && p2p->cfg->is_concurrent_session_active(p2p->cfg->cb_ctx)) p2p_set_timeout(p2p, 0, 500000); else p2p_set_timeout(p2p, 0, 200000); } static void p2p_timeout_wait_peer_idle(struct p2p_data *p2p) { struct p2p_device *dev = p2p->go_neg_peer; if (dev == NULL) { p2p_dbg(p2p, "Unknown GO Neg peer - stop GO Neg wait"); return; } dev->wait_count++; if (dev->wait_count >= 120) { p2p_dbg(p2p, "Timeout on waiting peer to become ready for GO Negotiation"); p2p_go_neg_failed(p2p, dev, -1); return; } p2p_dbg(p2p, "Go to Listen state while waiting for the peer to become ready for GO Negotiation"); p2p_set_state(p2p, P2P_WAIT_PEER_CONNECT); p2p_listen_in_find(p2p, 0); } static void p2p_timeout_sd_during_find(struct p2p_data *p2p) { p2p_dbg(p2p, "Service Discovery Query timeout"); if (p2p->sd_peer) { p2p->cfg->send_action_done(p2p->cfg->cb_ctx); p2p->sd_peer->flags &= ~P2P_DEV_SD_SCHEDULE; p2p->sd_peer = NULL; } p2p_continue_find(p2p); } static void p2p_timeout_prov_disc_during_find(struct p2p_data *p2p) { p2p_dbg(p2p, "Provision Discovery Request timeout"); p2p->cfg->send_action_done(p2p->cfg->cb_ctx); p2p_continue_find(p2p); } static void p2p_timeout_prov_disc_req(struct p2p_data *p2p) { p2p->pending_action_state = P2P_NO_PENDING_ACTION; /* * For user initiated PD requests that we have not gotten any responses * for while in IDLE state, we retry them a couple of times before * giving up. */ if (!p2p->user_initiated_pd) return; p2p_dbg(p2p, "User initiated Provision Discovery Request timeout"); if (p2p->pd_retries) { p2p->pd_retries--; p2p_retry_pd(p2p); } else { struct p2p_device *dev; int for_join = 0; dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) { if (os_memcmp(p2p->pending_pd_devaddr, dev->info.p2p_device_addr, ETH_ALEN) != 0) continue; if (dev->req_config_methods && (dev->flags & P2P_DEV_PD_FOR_JOIN)) for_join = 1; } if (p2p->cfg->prov_disc_fail) p2p->cfg->prov_disc_fail(p2p->cfg->cb_ctx, p2p->pending_pd_devaddr, for_join ? P2P_PROV_DISC_TIMEOUT_JOIN : P2P_PROV_DISC_TIMEOUT); p2p_reset_pending_pd(p2p); } } static void p2p_timeout_invite(struct p2p_data *p2p) { p2p->cfg->send_action_done(p2p->cfg->cb_ctx); p2p_set_state(p2p, P2P_INVITE_LISTEN); if (p2p->inv_role == P2P_INVITE_ROLE_ACTIVE_GO) { /* * Better remain on operating channel instead of listen channel * when running a group. */ p2p_dbg(p2p, "Inviting in active GO role - wait on operating channel"); p2p_set_timeout(p2p, 0, 100000); return; } p2p_listen_in_find(p2p, 0); } static void p2p_timeout_invite_listen(struct p2p_data *p2p) { if (p2p->invite_peer && p2p->invite_peer->invitation_reqs < 100) { p2p_set_state(p2p, P2P_INVITE); p2p_invite_send(p2p, p2p->invite_peer, p2p->invite_go_dev_addr, p2p->invite_dev_pw_id); } else { if (p2p->invite_peer) { p2p_dbg(p2p, "Invitation Request retry limit reached"); if (p2p->cfg->invitation_result) p2p->cfg->invitation_result( p2p->cfg->cb_ctx, -1, NULL, NULL, p2p->invite_peer->info.p2p_device_addr, 0); } p2p_set_state(p2p, P2P_IDLE); } } static void p2p_state_timeout(void *eloop_ctx, void *timeout_ctx) { struct p2p_data *p2p = eloop_ctx; p2p_dbg(p2p, "Timeout (state=%s)", p2p_state_txt(p2p->state)); p2p->in_listen = 0; switch (p2p->state) { case P2P_IDLE: /* Check if we timed out waiting for PD req */ if (p2p->pending_action_state == P2P_PENDING_PD) p2p_timeout_prov_disc_req(p2p); break; case P2P_SEARCH: /* Check if we timed out waiting for PD req */ if (p2p->pending_action_state == P2P_PENDING_PD) p2p_timeout_prov_disc_req(p2p); if (p2p->search_delay && !p2p->in_search_delay) { p2p_dbg(p2p, "Delay search operation by %u ms", p2p->search_delay); p2p->in_search_delay = 1; p2p_set_timeout(p2p, p2p->search_delay / 1000, (p2p->search_delay % 1000) * 1000); break; } p2p->in_search_delay = 0; p2p_search(p2p); break; case P2P_CONNECT: p2p_timeout_connect(p2p); break; case P2P_CONNECT_LISTEN: p2p_timeout_connect_listen(p2p); break; case P2P_GO_NEG: break; case P2P_LISTEN_ONLY: /* Check if we timed out waiting for PD req */ if (p2p->pending_action_state == P2P_PENDING_PD) p2p_timeout_prov_disc_req(p2p); if (p2p->ext_listen_only) { p2p_dbg(p2p, "Extended Listen Timing - Listen State completed"); p2p->ext_listen_only = 0; p2p_set_state(p2p, P2P_IDLE); } break; case P2P_WAIT_PEER_CONNECT: p2p_timeout_wait_peer_connect(p2p); break; case P2P_WAIT_PEER_IDLE: p2p_timeout_wait_peer_idle(p2p); break; case P2P_SD_DURING_FIND: p2p_timeout_sd_during_find(p2p); break; case P2P_PROVISIONING: break; case P2P_PD_DURING_FIND: p2p_timeout_prov_disc_during_find(p2p); break; case P2P_INVITE: p2p_timeout_invite(p2p); break; case P2P_INVITE_LISTEN: p2p_timeout_invite_listen(p2p); break; } } int p2p_reject(struct p2p_data *p2p, const u8 *peer_addr) { struct p2p_device *dev; dev = p2p_get_device(p2p, peer_addr); p2p_dbg(p2p, "Local request to reject connection attempts by peer " MACSTR, MAC2STR(peer_addr)); if (dev == NULL) { p2p_dbg(p2p, "Peer " MACSTR " unknown", MAC2STR(peer_addr)); return -1; } dev->status = P2P_SC_FAIL_REJECTED_BY_USER; dev->flags |= P2P_DEV_USER_REJECTED; return 0; } const char * p2p_wps_method_text(enum p2p_wps_method method) { switch (method) { case WPS_NOT_READY: return "not-ready"; case WPS_PIN_DISPLAY: return "Display"; case WPS_PIN_KEYPAD: return "Keypad"; case WPS_PBC: return "PBC"; case WPS_NFC: return "NFC"; } return "??"; } static const char * p2p_go_state_text(enum p2p_go_state go_state) { switch (go_state) { case UNKNOWN_GO: return "unknown"; case LOCAL_GO: return "local"; case REMOTE_GO: return "remote"; } return "??"; } const struct p2p_peer_info * p2p_get_peer_info(struct p2p_data *p2p, const u8 *addr, int next) { struct p2p_device *dev; if (addr) dev = p2p_get_device(p2p, addr); else dev = dl_list_first(&p2p->devices, struct p2p_device, list); if (dev && next) { dev = dl_list_first(&dev->list, struct p2p_device, list); if (&dev->list == &p2p->devices) dev = NULL; } if (dev == NULL) return NULL; return &dev->info; } int p2p_get_peer_info_txt(const struct p2p_peer_info *info, char *buf, size_t buflen) { struct p2p_device *dev; int res; char *pos, *end; struct os_reltime now; if (info == NULL) return -1; dev = (struct p2p_device *) (((u8 *) info) - offsetof(struct p2p_device, info)); pos = buf; end = buf + buflen; os_get_reltime(&now); res = os_snprintf(pos, end - pos, "age=%d\n" "listen_freq=%d\n" "wps_method=%s\n" "interface_addr=" MACSTR "\n" "member_in_go_dev=" MACSTR "\n" "member_in_go_iface=" MACSTR "\n" "go_neg_req_sent=%d\n" "go_state=%s\n" "dialog_token=%u\n" "intended_addr=" MACSTR "\n" "country=%c%c\n" "oper_freq=%d\n" "req_config_methods=0x%x\n" "flags=%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n" "status=%d\n" "wait_count=%u\n" "invitation_reqs=%u\n", (int) (now.sec - dev->last_seen.sec), dev->listen_freq, p2p_wps_method_text(dev->wps_method), MAC2STR(dev->interface_addr), MAC2STR(dev->member_in_go_dev), MAC2STR(dev->member_in_go_iface), dev->go_neg_req_sent, p2p_go_state_text(dev->go_state), dev->dialog_token, MAC2STR(dev->intended_addr), dev->country[0] ? dev->country[0] : '_', dev->country[1] ? dev->country[1] : '_', dev->oper_freq, dev->req_config_methods, dev->flags & P2P_DEV_PROBE_REQ_ONLY ? "[PROBE_REQ_ONLY]" : "", dev->flags & P2P_DEV_REPORTED ? "[REPORTED]" : "", dev->flags & P2P_DEV_NOT_YET_READY ? "[NOT_YET_READY]" : "", dev->flags & P2P_DEV_SD_INFO ? "[SD_INFO]" : "", dev->flags & P2P_DEV_SD_SCHEDULE ? "[SD_SCHEDULE]" : "", dev->flags & P2P_DEV_PD_PEER_DISPLAY ? "[PD_PEER_DISPLAY]" : "", dev->flags & P2P_DEV_PD_PEER_KEYPAD ? "[PD_PEER_KEYPAD]" : "", dev->flags & P2P_DEV_USER_REJECTED ? "[USER_REJECTED]" : "", dev->flags & P2P_DEV_PEER_WAITING_RESPONSE ? "[PEER_WAITING_RESPONSE]" : "", dev->flags & P2P_DEV_PREFER_PERSISTENT_GROUP ? "[PREFER_PERSISTENT_GROUP]" : "", dev->flags & P2P_DEV_WAIT_GO_NEG_RESPONSE ? "[WAIT_GO_NEG_RESPONSE]" : "", dev->flags & P2P_DEV_WAIT_GO_NEG_CONFIRM ? "[WAIT_GO_NEG_CONFIRM]" : "", dev->flags & P2P_DEV_GROUP_CLIENT_ONLY ? "[GROUP_CLIENT_ONLY]" : "", dev->flags & P2P_DEV_FORCE_FREQ ? "[FORCE_FREQ]" : "", dev->flags & P2P_DEV_PD_FOR_JOIN ? "[PD_FOR_JOIN]" : "", dev->status, dev->wait_count, dev->invitation_reqs); if (res < 0 || res >= end - pos) return pos - buf; pos += res; if (dev->ext_listen_period) { res = os_snprintf(pos, end - pos, "ext_listen_period=%u\n" "ext_listen_interval=%u\n", dev->ext_listen_period, dev->ext_listen_interval); if (res < 0 || res >= end - pos) return pos - buf; pos += res; } if (dev->oper_ssid_len) { res = os_snprintf(pos, end - pos, "oper_ssid=%s\n", wpa_ssid_txt(dev->oper_ssid, dev->oper_ssid_len)); if (res < 0 || res >= end - pos) return pos - buf; pos += res; } #ifdef CONFIG_WIFI_DISPLAY if (dev->info.wfd_subelems) { res = os_snprintf(pos, end - pos, "wfd_subelems="); if (res < 0 || res >= end - pos) return pos - buf; pos += res; pos += wpa_snprintf_hex(pos, end - pos, wpabuf_head(dev->info.wfd_subelems), wpabuf_len(dev->info.wfd_subelems)); res = os_snprintf(pos, end - pos, "\n"); if (res < 0 || res >= end - pos) return pos - buf; pos += res; } #endif /* CONFIG_WIFI_DISPLAY */ return pos - buf; } int p2p_peer_known(struct p2p_data *p2p, const u8 *addr) { return p2p_get_device(p2p, addr) != NULL; } void p2p_set_client_discoverability(struct p2p_data *p2p, int enabled) { if (enabled) { p2p_dbg(p2p, "Client discoverability enabled"); p2p->dev_capab |= P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY; } else { p2p_dbg(p2p, "Client discoverability disabled"); p2p->dev_capab &= ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY; } } static struct wpabuf * p2p_build_presence_req(u32 duration1, u32 interval1, u32 duration2, u32 interval2) { struct wpabuf *req; struct p2p_noa_desc desc1, desc2, *ptr1 = NULL, *ptr2 = NULL; u8 *len; req = wpabuf_alloc(100); if (req == NULL) return NULL; if (duration1 || interval1) { os_memset(&desc1, 0, sizeof(desc1)); desc1.count_type = 1; desc1.duration = duration1; desc1.interval = interval1; ptr1 = &desc1; if (duration2 || interval2) { os_memset(&desc2, 0, sizeof(desc2)); desc2.count_type = 2; desc2.duration = duration2; desc2.interval = interval2; ptr2 = &desc2; } } p2p_buf_add_action_hdr(req, P2P_PRESENCE_REQ, 1); len = p2p_buf_add_ie_hdr(req); p2p_buf_add_noa(req, 0, 0, 0, ptr1, ptr2); p2p_buf_update_ie_hdr(req, len); return req; } int p2p_presence_req(struct p2p_data *p2p, const u8 *go_interface_addr, const u8 *own_interface_addr, unsigned int freq, u32 duration1, u32 interval1, u32 duration2, u32 interval2) { struct wpabuf *req; p2p_dbg(p2p, "Send Presence Request to GO " MACSTR " (own interface " MACSTR ") freq=%u dur1=%u int1=%u " "dur2=%u int2=%u", MAC2STR(go_interface_addr), MAC2STR(own_interface_addr), freq, duration1, interval1, duration2, interval2); req = p2p_build_presence_req(duration1, interval1, duration2, interval2); if (req == NULL) return -1; p2p->pending_action_state = P2P_NO_PENDING_ACTION; if (p2p_send_action(p2p, freq, go_interface_addr, own_interface_addr, go_interface_addr, wpabuf_head(req), wpabuf_len(req), 200) < 0) { p2p_dbg(p2p, "Failed to send Action frame"); } wpabuf_free(req); return 0; } static struct wpabuf * p2p_build_presence_resp(u8 status, const u8 *noa, size_t noa_len, u8 dialog_token) { struct wpabuf *resp; u8 *len; resp = wpabuf_alloc(100 + noa_len); if (resp == NULL) return NULL; p2p_buf_add_action_hdr(resp, P2P_PRESENCE_RESP, dialog_token); len = p2p_buf_add_ie_hdr(resp); p2p_buf_add_status(resp, status); if (noa) { wpabuf_put_u8(resp, P2P_ATTR_NOTICE_OF_ABSENCE); wpabuf_put_le16(resp, noa_len); wpabuf_put_data(resp, noa, noa_len); } else p2p_buf_add_noa(resp, 0, 0, 0, NULL, NULL); p2p_buf_update_ie_hdr(resp, len); return resp; } static void p2p_process_presence_req(struct p2p_data *p2p, const u8 *da, const u8 *sa, const u8 *data, size_t len, int rx_freq) { struct p2p_message msg; u8 status; struct wpabuf *resp; size_t g; struct p2p_group *group = NULL; int parsed = 0; u8 noa[50]; int noa_len; p2p_dbg(p2p, "Received P2P Action - P2P Presence Request"); for (g = 0; g < p2p->num_groups; g++) { if (os_memcmp(da, p2p_group_get_interface_addr(p2p->groups[g]), ETH_ALEN) == 0) { group = p2p->groups[g]; break; } } if (group == NULL) { p2p_dbg(p2p, "Ignore P2P Presence Request for unknown group " MACSTR, MAC2STR(da)); return; } if (p2p_parse(data, len, &msg) < 0) { p2p_dbg(p2p, "Failed to parse P2P Presence Request"); status = P2P_SC_FAIL_INVALID_PARAMS; goto fail; } parsed = 1; if (msg.noa == NULL) { p2p_dbg(p2p, "No NoA attribute in P2P Presence Request"); status = P2P_SC_FAIL_INVALID_PARAMS; goto fail; } status = p2p_group_presence_req(group, sa, msg.noa, msg.noa_len); fail: if (p2p->cfg->get_noa) noa_len = p2p->cfg->get_noa(p2p->cfg->cb_ctx, da, noa, sizeof(noa)); else noa_len = -1; resp = p2p_build_presence_resp(status, noa_len > 0 ? noa : NULL, noa_len > 0 ? noa_len : 0, msg.dialog_token); if (parsed) p2p_parse_free(&msg); if (resp == NULL) return; p2p->pending_action_state = P2P_NO_PENDING_ACTION; if (p2p_send_action(p2p, rx_freq, sa, da, da, wpabuf_head(resp), wpabuf_len(resp), 200) < 0) { p2p_dbg(p2p, "Failed to send Action frame"); } wpabuf_free(resp); } static void p2p_process_presence_resp(struct p2p_data *p2p, const u8 *da, const u8 *sa, const u8 *data, size_t len) { struct p2p_message msg; p2p_dbg(p2p, "Received P2P Action - P2P Presence Response"); if (p2p_parse(data, len, &msg) < 0) { p2p_dbg(p2p, "Failed to parse P2P Presence Response"); return; } if (msg.status == NULL || msg.noa == NULL) { p2p_dbg(p2p, "No Status or NoA attribute in P2P Presence Response"); p2p_parse_free(&msg); return; } if (p2p->cfg->presence_resp) { p2p->cfg->presence_resp(p2p->cfg->cb_ctx, sa, *msg.status, msg.noa, msg.noa_len); } if (*msg.status) { p2p_dbg(p2p, "P2P Presence Request was rejected: status %u", *msg.status); p2p_parse_free(&msg); return; } p2p_dbg(p2p, "P2P Presence Request was accepted"); wpa_hexdump(MSG_DEBUG, "P2P: P2P Presence Response - NoA", msg.noa, msg.noa_len); /* TODO: process NoA */ p2p_parse_free(&msg); } static void p2p_ext_listen_timeout(void *eloop_ctx, void *timeout_ctx) { struct p2p_data *p2p = eloop_ctx; if (p2p->ext_listen_interval) { /* Schedule next extended listen timeout */ eloop_register_timeout(p2p->ext_listen_interval_sec, p2p->ext_listen_interval_usec, p2p_ext_listen_timeout, p2p, NULL); } if (p2p->state == P2P_LISTEN_ONLY && p2p->ext_listen_only) { /* * This should not really happen, but it looks like the Listen * command may fail is something else (e.g., a scan) was * running at an inconvenient time. As a workaround, allow new * Extended Listen operation to be started. */ p2p_dbg(p2p, "Previous Extended Listen operation had not been completed - try again"); p2p->ext_listen_only = 0; p2p_set_state(p2p, P2P_IDLE); } if (p2p->state != P2P_IDLE) { p2p_dbg(p2p, "Skip Extended Listen timeout in active state (%s)", p2p_state_txt(p2p->state)); return; } p2p_dbg(p2p, "Extended Listen timeout"); p2p->ext_listen_only = 1; if (p2p_listen(p2p, p2p->ext_listen_period) < 0) { p2p_dbg(p2p, "Failed to start Listen state for Extended Listen Timing"); p2p->ext_listen_only = 0; } } int p2p_ext_listen(struct p2p_data *p2p, unsigned int period, unsigned int interval) { if (period > 65535 || interval > 65535 || period > interval || (period == 0 && interval > 0) || (period > 0 && interval == 0)) { p2p_dbg(p2p, "Invalid Extended Listen Timing request: period=%u interval=%u", period, interval); return -1; } eloop_cancel_timeout(p2p_ext_listen_timeout, p2p, NULL); if (interval == 0) { p2p_dbg(p2p, "Disabling Extended Listen Timing"); p2p->ext_listen_period = 0; p2p->ext_listen_interval = 0; return 0; } p2p_dbg(p2p, "Enabling Extended Listen Timing: period %u msec, interval %u msec", period, interval); p2p->ext_listen_period = period; p2p->ext_listen_interval = interval; p2p->ext_listen_interval_sec = interval / 1000; p2p->ext_listen_interval_usec = (interval % 1000) * 1000; eloop_register_timeout(p2p->ext_listen_interval_sec, p2p->ext_listen_interval_usec, p2p_ext_listen_timeout, p2p, NULL); return 0; } void p2p_deauth_notif(struct p2p_data *p2p, const u8 *bssid, u16 reason_code, const u8 *ie, size_t ie_len) { struct p2p_message msg; if (bssid == NULL || ie == NULL) return; os_memset(&msg, 0, sizeof(msg)); if (p2p_parse_ies(ie, ie_len, &msg)) return; if (msg.minor_reason_code == NULL) { p2p_parse_free(&msg); return; } p2p_dbg(p2p, "Deauthentication notification BSSID " MACSTR " reason_code=%u minor_reason_code=%u", MAC2STR(bssid), reason_code, *msg.minor_reason_code); p2p_parse_free(&msg); } void p2p_disassoc_notif(struct p2p_data *p2p, const u8 *bssid, u16 reason_code, const u8 *ie, size_t ie_len) { struct p2p_message msg; if (bssid == NULL || ie == NULL) return; os_memset(&msg, 0, sizeof(msg)); if (p2p_parse_ies(ie, ie_len, &msg)) return; if (msg.minor_reason_code == NULL) { p2p_parse_free(&msg); return; } p2p_dbg(p2p, "Disassociation notification BSSID " MACSTR " reason_code=%u minor_reason_code=%u", MAC2STR(bssid), reason_code, *msg.minor_reason_code); p2p_parse_free(&msg); } void p2p_set_managed_oper(struct p2p_data *p2p, int enabled) { if (enabled) { p2p_dbg(p2p, "Managed P2P Device operations enabled"); p2p->dev_capab |= P2P_DEV_CAPAB_INFRA_MANAGED; } else { p2p_dbg(p2p, "Managed P2P Device operations disabled"); p2p->dev_capab &= ~P2P_DEV_CAPAB_INFRA_MANAGED; } } int p2p_set_listen_channel(struct p2p_data *p2p, u8 reg_class, u8 channel) { if (p2p_channel_to_freq(reg_class, channel) < 0) return -1; p2p_dbg(p2p, "Set Listen channel: reg_class %u channel %u", reg_class, channel); p2p->cfg->reg_class = reg_class; p2p->cfg->channel = channel; return 0; } int p2p_set_ssid_postfix(struct p2p_data *p2p, const u8 *postfix, size_t len) { p2p_dbg(p2p, "New SSID postfix: %s", wpa_ssid_txt(postfix, len)); if (postfix == NULL) { p2p->cfg->ssid_postfix_len = 0; return 0; } if (len > sizeof(p2p->cfg->ssid_postfix)) return -1; os_memcpy(p2p->cfg->ssid_postfix, postfix, len); p2p->cfg->ssid_postfix_len = len; return 0; } int p2p_set_oper_channel(struct p2p_data *p2p, u8 op_reg_class, u8 op_channel, int cfg_op_channel) { if (p2p_channel_to_freq(op_reg_class, op_channel) < 0) return -1; p2p_dbg(p2p, "Set Operating channel: reg_class %u channel %u", op_reg_class, op_channel); p2p->cfg->op_reg_class = op_reg_class; p2p->cfg->op_channel = op_channel; p2p->cfg->cfg_op_channel = cfg_op_channel; return 0; } int p2p_set_pref_chan(struct p2p_data *p2p, unsigned int num_pref_chan, const struct p2p_channel *pref_chan) { struct p2p_channel *n; if (pref_chan) { n = os_malloc(num_pref_chan * sizeof(struct p2p_channel)); if (n == NULL) return -1; os_memcpy(n, pref_chan, num_pref_chan * sizeof(struct p2p_channel)); } else n = NULL; os_free(p2p->cfg->pref_chan); p2p->cfg->pref_chan = n; p2p->cfg->num_pref_chan = num_pref_chan; return 0; } int p2p_set_no_go_freq(struct p2p_data *p2p, const struct wpa_freq_range_list *list) { struct wpa_freq_range *tmp; if (list == NULL || list->num == 0) { os_free(p2p->no_go_freq.range); p2p->no_go_freq.range = NULL; p2p->no_go_freq.num = 0; return 0; } tmp = os_calloc(list->num, sizeof(struct wpa_freq_range)); if (tmp == NULL) return -1; os_memcpy(tmp, list->range, list->num * sizeof(struct wpa_freq_range)); os_free(p2p->no_go_freq.range); p2p->no_go_freq.range = tmp; p2p->no_go_freq.num = list->num; p2p_dbg(p2p, "Updated no GO chan list"); return 0; } int p2p_get_interface_addr(struct p2p_data *p2p, const u8 *dev_addr, u8 *iface_addr) { struct p2p_device *dev = p2p_get_device(p2p, dev_addr); if (dev == NULL || is_zero_ether_addr(dev->interface_addr)) return -1; os_memcpy(iface_addr, dev->interface_addr, ETH_ALEN); return 0; } int p2p_get_dev_addr(struct p2p_data *p2p, const u8 *iface_addr, u8 *dev_addr) { struct p2p_device *dev = p2p_get_device_interface(p2p, iface_addr); if (dev == NULL) return -1; os_memcpy(dev_addr, dev->info.p2p_device_addr, ETH_ALEN); return 0; } void p2p_set_peer_filter(struct p2p_data *p2p, const u8 *addr) { os_memcpy(p2p->peer_filter, addr, ETH_ALEN); if (is_zero_ether_addr(p2p->peer_filter)) p2p_dbg(p2p, "Disable peer filter"); else p2p_dbg(p2p, "Enable peer filter for " MACSTR, MAC2STR(p2p->peer_filter)); } void p2p_set_cross_connect(struct p2p_data *p2p, int enabled) { p2p_dbg(p2p, "Cross connection %s", enabled ? "enabled" : "disabled"); if (p2p->cross_connect == enabled) return; p2p->cross_connect = enabled; /* TODO: may need to tear down any action group where we are GO(?) */ } int p2p_get_oper_freq(struct p2p_data *p2p, const u8 *iface_addr) { struct p2p_device *dev = p2p_get_device_interface(p2p, iface_addr); if (dev == NULL) return -1; if (dev->oper_freq <= 0) return -1; return dev->oper_freq; } void p2p_set_intra_bss_dist(struct p2p_data *p2p, int enabled) { p2p_dbg(p2p, "Intra BSS distribution %s", enabled ? "enabled" : "disabled"); p2p->cfg->p2p_intra_bss = enabled; } void p2p_update_channel_list(struct p2p_data *p2p, const struct p2p_channels *chan, const struct p2p_channels *cli_chan) { p2p_dbg(p2p, "Update channel list"); os_memcpy(&p2p->cfg->channels, chan, sizeof(struct p2p_channels)); p2p_channels_dump(p2p, "channels", &p2p->cfg->channels); os_memcpy(&p2p->cfg->cli_channels, cli_chan, sizeof(struct p2p_channels)); p2p_channels_dump(p2p, "cli_channels", &p2p->cfg->cli_channels); } int p2p_send_action(struct p2p_data *p2p, unsigned int freq, const u8 *dst, const u8 *src, const u8 *bssid, const u8 *buf, size_t len, unsigned int wait_time) { if (p2p->p2p_scan_running) { p2p_dbg(p2p, "Delay Action frame TX until p2p_scan completes"); if (p2p->after_scan_tx) { p2p_dbg(p2p, "Dropped previous pending Action frame TX"); os_free(p2p->after_scan_tx); } p2p->after_scan_tx = os_malloc(sizeof(*p2p->after_scan_tx) + len); if (p2p->after_scan_tx == NULL) return -1; p2p->after_scan_tx->freq = freq; os_memcpy(p2p->after_scan_tx->dst, dst, ETH_ALEN); os_memcpy(p2p->after_scan_tx->src, src, ETH_ALEN); os_memcpy(p2p->after_scan_tx->bssid, bssid, ETH_ALEN); p2p->after_scan_tx->len = len; p2p->after_scan_tx->wait_time = wait_time; os_memcpy(p2p->after_scan_tx + 1, buf, len); return 0; } return p2p->cfg->send_action(p2p->cfg->cb_ctx, freq, dst, src, bssid, buf, len, wait_time); } void p2p_set_best_channels(struct p2p_data *p2p, int freq_24, int freq_5, int freq_overall) { p2p_dbg(p2p, "Best channel: 2.4 GHz: %d, 5 GHz: %d, overall: %d", freq_24, freq_5, freq_overall); p2p->best_freq_24 = freq_24; p2p->best_freq_5 = freq_5; p2p->best_freq_overall = freq_overall; } void p2p_set_own_freq_preference(struct p2p_data *p2p, int freq) { p2p_dbg(p2p, "Own frequency preference: %d MHz", freq); p2p->own_freq_preference = freq; } const u8 * p2p_get_go_neg_peer(struct p2p_data *p2p) { if (p2p == NULL || p2p->go_neg_peer == NULL) return NULL; return p2p->go_neg_peer->info.p2p_device_addr; } const struct p2p_peer_info * p2p_get_peer_found(struct p2p_data *p2p, const u8 *addr, int next) { struct p2p_device *dev; if (addr) { dev = p2p_get_device(p2p, addr); if (!dev) return NULL; if (!next) { if (dev->flags & P2P_DEV_PROBE_REQ_ONLY) return NULL; return &dev->info; } else { do { dev = dl_list_first(&dev->list, struct p2p_device, list); if (&dev->list == &p2p->devices) return NULL; } while (dev->flags & P2P_DEV_PROBE_REQ_ONLY); } } else { dev = dl_list_first(&p2p->devices, struct p2p_device, list); if (!dev) return NULL; while (dev->flags & P2P_DEV_PROBE_REQ_ONLY) { dev = dl_list_first(&dev->list, struct p2p_device, list); if (&dev->list == &p2p->devices) return NULL; } } return &dev->info; } int p2p_in_progress(struct p2p_data *p2p) { if (p2p == NULL) return 0; if (p2p->state == P2P_SEARCH) return 2; return p2p->state != P2P_IDLE && p2p->state != P2P_PROVISIONING; } void p2p_set_config_timeout(struct p2p_data *p2p, u8 go_timeout, u8 client_timeout) { if (p2p) { p2p->go_timeout = go_timeout; p2p->client_timeout = client_timeout; } } #ifdef CONFIG_WIFI_DISPLAY static void p2p_update_wfd_ie_groups(struct p2p_data *p2p) { size_t g; struct p2p_group *group; for (g = 0; g < p2p->num_groups; g++) { group = p2p->groups[g]; p2p_group_force_beacon_update_ies(group); } } int p2p_set_wfd_ie_beacon(struct p2p_data *p2p, struct wpabuf *ie) { wpabuf_free(p2p->wfd_ie_beacon); p2p->wfd_ie_beacon = ie; p2p_update_wfd_ie_groups(p2p); return 0; } int p2p_set_wfd_ie_probe_req(struct p2p_data *p2p, struct wpabuf *ie) { wpabuf_free(p2p->wfd_ie_probe_req); p2p->wfd_ie_probe_req = ie; return 0; } int p2p_set_wfd_ie_probe_resp(struct p2p_data *p2p, struct wpabuf *ie) { wpabuf_free(p2p->wfd_ie_probe_resp); p2p->wfd_ie_probe_resp = ie; p2p_update_wfd_ie_groups(p2p); return 0; } int p2p_set_wfd_ie_assoc_req(struct p2p_data *p2p, struct wpabuf *ie) { wpabuf_free(p2p->wfd_ie_assoc_req); p2p->wfd_ie_assoc_req = ie; return 0; } int p2p_set_wfd_ie_invitation(struct p2p_data *p2p, struct wpabuf *ie) { wpabuf_free(p2p->wfd_ie_invitation); p2p->wfd_ie_invitation = ie; return 0; } int p2p_set_wfd_ie_prov_disc_req(struct p2p_data *p2p, struct wpabuf *ie) { wpabuf_free(p2p->wfd_ie_prov_disc_req); p2p->wfd_ie_prov_disc_req = ie; return 0; } int p2p_set_wfd_ie_prov_disc_resp(struct p2p_data *p2p, struct wpabuf *ie) { wpabuf_free(p2p->wfd_ie_prov_disc_resp); p2p->wfd_ie_prov_disc_resp = ie; return 0; } int p2p_set_wfd_ie_go_neg(struct p2p_data *p2p, struct wpabuf *ie) { wpabuf_free(p2p->wfd_ie_go_neg); p2p->wfd_ie_go_neg = ie; return 0; } int p2p_set_wfd_dev_info(struct p2p_data *p2p, const struct wpabuf *elem) { wpabuf_free(p2p->wfd_dev_info); if (elem) { p2p->wfd_dev_info = wpabuf_dup(elem); if (p2p->wfd_dev_info == NULL) return -1; } else p2p->wfd_dev_info = NULL; return 0; } int p2p_set_wfd_assoc_bssid(struct p2p_data *p2p, const struct wpabuf *elem) { wpabuf_free(p2p->wfd_assoc_bssid); if (elem) { p2p->wfd_assoc_bssid = wpabuf_dup(elem); if (p2p->wfd_assoc_bssid == NULL) return -1; } else p2p->wfd_assoc_bssid = NULL; return 0; } int p2p_set_wfd_coupled_sink_info(struct p2p_data *p2p, const struct wpabuf *elem) { wpabuf_free(p2p->wfd_coupled_sink_info); if (elem) { p2p->wfd_coupled_sink_info = wpabuf_dup(elem); if (p2p->wfd_coupled_sink_info == NULL) return -1; } else p2p->wfd_coupled_sink_info = NULL; return 0; } #endif /* CONFIG_WIFI_DISPLAY */ int p2p_set_disc_int(struct p2p_data *p2p, int min_disc_int, int max_disc_int, int max_disc_tu) { if (min_disc_int > max_disc_int || min_disc_int < 0 || max_disc_int < 0) return -1; p2p->min_disc_int = min_disc_int; p2p->max_disc_int = max_disc_int; p2p->max_disc_tu = max_disc_tu; p2p_dbg(p2p, "Set discoverable interval: min=%d max=%d max_tu=%d", min_disc_int, max_disc_int, max_disc_tu); return 0; } void p2p_dbg(struct p2p_data *p2p, const char *fmt, ...) { va_list ap; char buf[500]; if (!p2p->cfg->debug_print) return; va_start(ap, fmt); vsnprintf(buf, sizeof(buf), fmt, ap); buf[sizeof(buf) - 1] = '\0'; va_end(ap); p2p->cfg->debug_print(p2p->cfg->cb_ctx, MSG_DEBUG, buf); } void p2p_info(struct p2p_data *p2p, const char *fmt, ...) { va_list ap; char buf[500]; if (!p2p->cfg->debug_print) return; va_start(ap, fmt); vsnprintf(buf, sizeof(buf), fmt, ap); buf[sizeof(buf) - 1] = '\0'; va_end(ap); p2p->cfg->debug_print(p2p->cfg->cb_ctx, MSG_INFO, buf); } void p2p_err(struct p2p_data *p2p, const char *fmt, ...) { va_list ap; char buf[500]; if (!p2p->cfg->debug_print) return; va_start(ap, fmt); vsnprintf(buf, sizeof(buf), fmt, ap); buf[sizeof(buf) - 1] = '\0'; va_end(ap); p2p->cfg->debug_print(p2p->cfg->cb_ctx, MSG_ERROR, buf); } #ifdef CONFIG_WPS_NFC static struct wpabuf * p2p_build_nfc_handover(struct p2p_data *p2p, int client_freq, const u8 *go_dev_addr, const u8 *ssid, size_t ssid_len) { struct wpabuf *buf; u8 op_class, channel; enum p2p_role_indication role = P2P_DEVICE_NOT_IN_GROUP; buf = wpabuf_alloc(1000); if (buf == NULL) return NULL; op_class = p2p->cfg->reg_class; channel = p2p->cfg->channel; p2p_buf_add_capability(buf, p2p->dev_capab & ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, 0); p2p_buf_add_device_info(buf, p2p, NULL); if (p2p->num_groups > 0) { role = P2P_GO_IN_A_GROUP; p2p_freq_to_channel(p2p_group_get_freq(p2p->groups[0]), &op_class, &channel); } else if (client_freq > 0) { role = P2P_CLIENT_IN_A_GROUP; p2p_freq_to_channel(client_freq, &op_class, &channel); } p2p_buf_add_oob_go_neg_channel(buf, p2p->cfg->country, op_class, channel, role); if (p2p->num_groups > 0) { /* Limit number of clients to avoid very long message */ p2p_buf_add_group_info(p2p->groups[0], buf, 5); p2p_group_buf_add_id(p2p->groups[0], buf); } else if (client_freq > 0 && go_dev_addr && !is_zero_ether_addr(go_dev_addr) && ssid && ssid_len > 0) { /* * Add the optional P2P Group ID to indicate in which group this * device is a P2P Client. */ p2p_buf_add_group_id(buf, go_dev_addr, ssid, ssid_len); } return buf; } struct wpabuf * p2p_build_nfc_handover_req(struct p2p_data *p2p, int client_freq, const u8 *go_dev_addr, const u8 *ssid, size_t ssid_len) { return p2p_build_nfc_handover(p2p, client_freq, go_dev_addr, ssid, ssid_len); } struct wpabuf * p2p_build_nfc_handover_sel(struct p2p_data *p2p, int client_freq, const u8 *go_dev_addr, const u8 *ssid, size_t ssid_len) { return p2p_build_nfc_handover(p2p, client_freq, go_dev_addr, ssid, ssid_len); } int p2p_process_nfc_connection_handover(struct p2p_data *p2p, struct p2p_nfc_params *params) { struct p2p_message msg; struct p2p_device *dev; const u8 *p2p_dev_addr; int freq; enum p2p_role_indication role; params->next_step = NO_ACTION; if (p2p_parse_ies_separate(params->wsc_attr, params->wsc_len, params->p2p_attr, params->p2p_len, &msg)) { p2p_dbg(p2p, "Failed to parse WSC/P2P attributes from NFC"); p2p_parse_free(&msg); return -1; } if (msg.p2p_device_addr) p2p_dev_addr = msg.p2p_device_addr; else if (msg.device_id) p2p_dev_addr = msg.device_id; else { p2p_dbg(p2p, "Ignore scan data without P2P Device Info or P2P Device Id"); p2p_parse_free(&msg); return -1; } if (msg.oob_dev_password) { os_memcpy(params->oob_dev_pw, msg.oob_dev_password, msg.oob_dev_password_len); params->oob_dev_pw_len = msg.oob_dev_password_len; } dev = p2p_create_device(p2p, p2p_dev_addr); if (dev == NULL) { p2p_parse_free(&msg); return -1; } params->peer = &dev->info; os_get_reltime(&dev->last_seen); dev->flags &= ~(P2P_DEV_PROBE_REQ_ONLY | P2P_DEV_GROUP_CLIENT_ONLY); p2p_copy_wps_info(p2p, dev, 0, &msg); if (!msg.oob_go_neg_channel) { p2p_dbg(p2p, "OOB GO Negotiation Channel attribute not included"); return -1; } if (msg.oob_go_neg_channel[3] == 0 && msg.oob_go_neg_channel[4] == 0) freq = 0; else freq = p2p_channel_to_freq(msg.oob_go_neg_channel[3], msg.oob_go_neg_channel[4]); if (freq < 0) { p2p_dbg(p2p, "Unknown peer OOB GO Neg channel"); return -1; } role = msg.oob_go_neg_channel[5]; if (role == P2P_GO_IN_A_GROUP) { p2p_dbg(p2p, "Peer OOB GO operating channel: %u MHz", freq); params->go_freq = freq; } else if (role == P2P_CLIENT_IN_A_GROUP) { p2p_dbg(p2p, "Peer (client) OOB GO operating channel: %u MHz", freq); params->go_freq = freq; } else p2p_dbg(p2p, "Peer OOB GO Neg channel: %u MHz", freq); dev->oob_go_neg_freq = freq; if (!params->sel && role != P2P_GO_IN_A_GROUP) { freq = p2p_channel_to_freq(p2p->cfg->reg_class, p2p->cfg->channel); if (freq < 0) { p2p_dbg(p2p, "Own listen channel not known"); return -1; } p2p_dbg(p2p, "Use own Listen channel as OOB GO Neg channel: %u MHz", freq); dev->oob_go_neg_freq = freq; } if (msg.group_id) { os_memcpy(params->go_dev_addr, msg.group_id, ETH_ALEN); params->go_ssid_len = msg.group_id_len - ETH_ALEN; os_memcpy(params->go_ssid, msg.group_id + ETH_ALEN, params->go_ssid_len); } p2p_parse_free(&msg); if (dev->flags & P2P_DEV_USER_REJECTED) { p2p_dbg(p2p, "Do not report rejected device"); return 0; } if (!(dev->flags & P2P_DEV_REPORTED)) { p2p->cfg->dev_found(p2p->cfg->cb_ctx, p2p_dev_addr, &dev->info, !(dev->flags & P2P_DEV_REPORTED_ONCE)); dev->flags |= P2P_DEV_REPORTED | P2P_DEV_REPORTED_ONCE; } if (role == P2P_GO_IN_A_GROUP && p2p->num_groups > 0) params->next_step = BOTH_GO; else if (role == P2P_GO_IN_A_GROUP) params->next_step = JOIN_GROUP; else if (role == P2P_CLIENT_IN_A_GROUP) { dev->flags |= P2P_DEV_GROUP_CLIENT_ONLY; params->next_step = PEER_CLIENT; } else if (p2p->num_groups > 0) params->next_step = AUTH_JOIN; else if (params->sel) params->next_step = INIT_GO_NEG; else params->next_step = RESP_GO_NEG; return 0; } void p2p_set_authorized_oob_dev_pw_id(struct p2p_data *p2p, u16 dev_pw_id, int go_intent, const u8 *own_interface_addr) { p2p->authorized_oob_dev_pw_id = dev_pw_id; if (dev_pw_id == 0) { p2p_dbg(p2p, "NFC OOB Password unauthorized for static handover"); return; } p2p_dbg(p2p, "NFC OOB Password (id=%u) authorized for static handover", dev_pw_id); p2p->go_intent = go_intent; os_memcpy(p2p->intended_addr, own_interface_addr, ETH_ALEN); } #endif /* CONFIG_WPS_NFC */ wpa-2.1/src/p2p/p2p.h000066400000000000000000002033301227414666700143250ustar00rootroot00000000000000/* * Wi-Fi Direct - P2P module * Copyright (c) 2009-2010, Atheros Communications * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef P2P_H #define P2P_H #include "wps/wps_defs.h" /** * P2P_MAX_REG_CLASSES - Maximum number of regulatory classes */ #define P2P_MAX_REG_CLASSES 10 /** * P2P_MAX_REG_CLASS_CHANNELS - Maximum number of channels per regulatory class */ #define P2P_MAX_REG_CLASS_CHANNELS 20 /** * struct p2p_channels - List of supported channels */ struct p2p_channels { /** * struct p2p_reg_class - Supported regulatory class */ struct p2p_reg_class { /** * reg_class - Regulatory class (IEEE 802.11-2007, Annex J) */ u8 reg_class; /** * channel - Supported channels */ u8 channel[P2P_MAX_REG_CLASS_CHANNELS]; /** * channels - Number of channel entries in use */ size_t channels; } reg_class[P2P_MAX_REG_CLASSES]; /** * reg_classes - Number of reg_class entries in use */ size_t reg_classes; }; enum p2p_wps_method { WPS_NOT_READY, WPS_PIN_DISPLAY, WPS_PIN_KEYPAD, WPS_PBC, WPS_NFC }; /** * struct p2p_go_neg_results - P2P Group Owner Negotiation results */ struct p2p_go_neg_results { /** * status - Negotiation result (Status Code) * * 0 (P2P_SC_SUCCESS) indicates success. Non-zero values indicate * failed negotiation. */ int status; /** * role_go - Whether local end is Group Owner */ int role_go; /** * freq - Frequency of the group operational channel in MHz */ int freq; int ht40; int vht; /** * ssid - SSID of the group */ u8 ssid[32]; /** * ssid_len - Length of SSID in octets */ size_t ssid_len; /** * psk - WPA pre-shared key (256 bits) (GO only) */ u8 psk[32]; /** * psk_set - Whether PSK field is configured (GO only) */ int psk_set; /** * passphrase - WPA2-Personal passphrase for the group (GO only) */ char passphrase[64]; /** * peer_device_addr - P2P Device Address of the peer */ u8 peer_device_addr[ETH_ALEN]; /** * peer_interface_addr - P2P Interface Address of the peer */ u8 peer_interface_addr[ETH_ALEN]; /** * wps_method - WPS method to be used during provisioning */ enum p2p_wps_method wps_method; #define P2P_MAX_CHANNELS 50 /** * freq_list - Zero-terminated list of possible operational channels */ int freq_list[P2P_MAX_CHANNELS]; /** * persistent_group - Whether the group should be made persistent * 0 = not persistent * 1 = persistent group without persistent reconnect * 2 = persistent group with persistent reconnect */ int persistent_group; /** * peer_config_timeout - Peer configuration timeout (in 10 msec units) */ unsigned int peer_config_timeout; }; struct p2p_data; enum p2p_scan_type { P2P_SCAN_SOCIAL, P2P_SCAN_FULL, P2P_SCAN_SOCIAL_PLUS_ONE }; #define P2P_MAX_WPS_VENDOR_EXT 10 /** * struct p2p_peer_info - P2P peer information */ struct p2p_peer_info { /** * p2p_device_addr - P2P Device Address of the peer */ u8 p2p_device_addr[ETH_ALEN]; /** * pri_dev_type - Primary Device Type */ u8 pri_dev_type[8]; /** * device_name - Device Name (0..32 octets encoded in UTF-8) */ char device_name[33]; /** * manufacturer - Manufacturer (0..64 octets encoded in UTF-8) */ char manufacturer[65]; /** * model_name - Model Name (0..32 octets encoded in UTF-8) */ char model_name[33]; /** * model_number - Model Number (0..32 octets encoded in UTF-8) */ char model_number[33]; /** * serial_number - Serial Number (0..32 octets encoded in UTF-8) */ char serial_number[33]; /** * level - Signal level */ int level; /** * config_methods - WPS Configuration Methods */ u16 config_methods; /** * dev_capab - Device Capabilities */ u8 dev_capab; /** * group_capab - Group Capabilities */ u8 group_capab; /** * wps_sec_dev_type_list - WPS secondary device type list * * This list includes from 0 to 16 Secondary Device Types as indicated * by wps_sec_dev_type_list_len (8 * number of types). */ u8 wps_sec_dev_type_list[128]; /** * wps_sec_dev_type_list_len - Length of secondary device type list */ size_t wps_sec_dev_type_list_len; struct wpabuf *wps_vendor_ext[P2P_MAX_WPS_VENDOR_EXT]; /** * wfd_subelems - Wi-Fi Display subelements from WFD IE(s) */ struct wpabuf *wfd_subelems; }; enum p2p_prov_disc_status { P2P_PROV_DISC_SUCCESS, P2P_PROV_DISC_TIMEOUT, P2P_PROV_DISC_REJECTED, P2P_PROV_DISC_TIMEOUT_JOIN, }; struct p2p_channel { u8 op_class; u8 chan; }; /** * struct p2p_config - P2P configuration * * This configuration is provided to the P2P module during initialization with * p2p_init(). */ struct p2p_config { /** * country - Country code to use in P2P operations */ char country[3]; /** * reg_class - Regulatory class for own listen channel */ u8 reg_class; /** * channel - Own listen channel */ u8 channel; /** * Regulatory class for own operational channel */ u8 op_reg_class; /** * op_channel - Own operational channel */ u8 op_channel; /** * cfg_op_channel - Whether op_channel is hardcoded in configuration */ u8 cfg_op_channel; /** * channels - Own supported regulatory classes and channels * * List of supposerted channels per regulatory class. The regulatory * classes are defined in IEEE Std 802.11-2007 Annex J and the * numbering of the clases depends on the configured country code. */ struct p2p_channels channels; /** * cli_channels - Additional client channels * * This list of channels (if any) will be used when advertising local * channels during GO Negotiation or Invitation for the cases where the * local end may become the client. This may allow the peer to become a * GO on additional channels if it supports these options. The main use * case for this is to include passive-scan channels on devices that may * not know their current location and have configured most channels to * not allow initiation of radition (i.e., another device needs to take * master responsibilities). */ struct p2p_channels cli_channels; /** * num_pref_chan - Number of pref_chan entries */ unsigned int num_pref_chan; /** * pref_chan - Preferred channels for GO Negotiation */ struct p2p_channel *pref_chan; /** * pri_dev_type - Primary Device Type (see WPS) */ u8 pri_dev_type[8]; /** * P2P_SEC_DEVICE_TYPES - Maximum number of secondary device types */ #define P2P_SEC_DEVICE_TYPES 5 /** * sec_dev_type - Optional secondary device types */ u8 sec_dev_type[P2P_SEC_DEVICE_TYPES][8]; /** * num_sec_dev_types - Number of sec_dev_type entries */ size_t num_sec_dev_types; /** * dev_addr - P2P Device Address */ u8 dev_addr[ETH_ALEN]; /** * dev_name - Device Name */ char *dev_name; char *manufacturer; char *model_name; char *model_number; char *serial_number; u8 uuid[16]; u16 config_methods; /** * concurrent_operations - Whether concurrent operations are supported */ int concurrent_operations; /** * max_peers - Maximum number of discovered peers to remember * * If more peers are discovered, older entries will be removed to make * room for the new ones. */ size_t max_peers; /** * p2p_intra_bss - Intra BSS communication is supported */ int p2p_intra_bss; /** * ssid_postfix - Postfix data to add to the SSID * * This data will be added to the end of the SSID after the * DIRECT- prefix. */ u8 ssid_postfix[32 - 9]; /** * ssid_postfix_len - Length of the ssid_postfix data */ size_t ssid_postfix_len; /** * max_listen - Maximum listen duration in ms */ unsigned int max_listen; /** * cb_ctx - Context to use with callback functions */ void *cb_ctx; /** * debug_print - Debug print * @ctx: Callback context from cb_ctx * @level: Debug verbosity level (MSG_*) * @msg: Debug message */ void (*debug_print)(void *ctx, int level, const char *msg); /* Callbacks to request lower layer driver operations */ /** * p2p_scan - Request a P2P scan/search * @ctx: Callback context from cb_ctx * @type: Scan type * @freq: Specific frequency (MHz) to scan or 0 for no restriction * @num_req_dev_types: Number of requested device types * @req_dev_types: Array containing requested device types * @dev_id: Device ID to search for or %NULL to find all devices * @pw_id: Device Password ID * Returns: 0 on success, -1 on failure * * This callback function is used to request a P2P scan or search * operation to be completed. Type type argument specifies which type * of scan is to be done. @P2P_SCAN_SOCIAL indicates that only the * social channels (1, 6, 11) should be scanned. @P2P_SCAN_FULL * indicates that all channels are to be scanned. * @P2P_SCAN_SOCIAL_PLUS_ONE request scan of all the social channels * plus one extra channel specified by freq. * * The full scan is used for the initial scan to find group owners from * all. The other types are used during search phase scan of the social * channels (with potential variation if the Listen channel of the * target peer is known or if other channels are scanned in steps). * * The scan results are returned after this call by calling * p2p_scan_res_handler() for each scan result that has a P2P IE and * then calling p2p_scan_res_handled() to indicate that all scan * results have been indicated. */ int (*p2p_scan)(void *ctx, enum p2p_scan_type type, int freq, unsigned int num_req_dev_types, const u8 *req_dev_types, const u8 *dev_id, u16 pw_id); /** * send_probe_resp - Transmit a Probe Response frame * @ctx: Callback context from cb_ctx * @buf: Probe Response frame (including the header and body) * Returns: 0 on success, -1 on failure * * This function is used to reply to Probe Request frames that were * indicated with a call to p2p_probe_req_rx(). The response is to be * sent on the same channel or to be dropped if the driver is not * anymore listening to Probe Request frames. * * Alternatively, the responsibility for building the Probe Response * frames in Listen state may be in another system component in which * case this function need to be implemented (i.e., the function * pointer can be %NULL). The WPS and P2P IEs to be added for Probe * Response frames in such a case are available from the * start_listen() callback. It should be noted that the received Probe * Request frames must be indicated by calling p2p_probe_req_rx() even * if this send_probe_resp() is not used. */ int (*send_probe_resp)(void *ctx, const struct wpabuf *buf); /** * send_action - Transmit an Action frame * @ctx: Callback context from cb_ctx * @freq: Frequency in MHz for the channel on which to transmit * @dst: Destination MAC address (Address 1) * @src: Source MAC address (Address 2) * @bssid: BSSID (Address 3) * @buf: Frame body (starting from Category field) * @len: Length of buf in octets * @wait_time: How many msec to wait for a response frame * Returns: 0 on success, -1 on failure * * The Action frame may not be transmitted immediately and the status * of the transmission must be reported by calling * p2p_send_action_cb() once the frame has either been transmitted or * it has been dropped due to excessive retries or other failure to * transmit. */ int (*send_action)(void *ctx, unsigned int freq, const u8 *dst, const u8 *src, const u8 *bssid, const u8 *buf, size_t len, unsigned int wait_time); /** * send_action_done - Notify that Action frame sequence was completed * @ctx: Callback context from cb_ctx * * This function is called when the Action frame sequence that was * started with send_action() has been completed, i.e., when there is * no need to wait for a response from the destination peer anymore. */ void (*send_action_done)(void *ctx); /** * start_listen - Start Listen state * @ctx: Callback context from cb_ctx * @freq: Frequency of the listen channel in MHz * @duration: Duration for the Listen state in milliseconds * @probe_resp_ie: IE(s) to be added to Probe Response frames * Returns: 0 on success, -1 on failure * * This Listen state may not start immediately since the driver may * have other pending operations to complete first. Once the Listen * state has started, p2p_listen_cb() must be called to notify the P2P * module. Once the Listen state is stopped, p2p_listen_end() must be * called to notify the P2P module that the driver is not in the Listen * state anymore. * * If the send_probe_resp() is not used for generating the response, * the IEs from probe_resp_ie need to be added to the end of the Probe * Response frame body. If send_probe_resp() is used, the probe_resp_ie * information can be ignored. */ int (*start_listen)(void *ctx, unsigned int freq, unsigned int duration, const struct wpabuf *probe_resp_ie); /** * stop_listen - Stop Listen state * @ctx: Callback context from cb_ctx * * This callback can be used to stop a Listen state operation that was * previously requested with start_listen(). */ void (*stop_listen)(void *ctx); /** * get_noa - Get current Notice of Absence attribute payload * @ctx: Callback context from cb_ctx * @interface_addr: P2P Interface Address of the GO * @buf: Buffer for returning NoA * @buf_len: Buffer length in octets * Returns: Number of octets used in buf, 0 to indicate no NoA is being * advertized, or -1 on failure * * This function is used to fetch the current Notice of Absence * attribute value from GO. */ int (*get_noa)(void *ctx, const u8 *interface_addr, u8 *buf, size_t buf_len); /* Callbacks to notify events to upper layer management entity */ /** * dev_found - Notification of a found P2P Device * @ctx: Callback context from cb_ctx * @addr: Source address of the message triggering this notification * @info: P2P peer information * @new_device: Inform if the peer is newly found * * This callback is used to notify that a new P2P Device has been * found. This may happen, e.g., during Search state based on scan * results or during Listen state based on receive Probe Request and * Group Owner Negotiation Request. */ void (*dev_found)(void *ctx, const u8 *addr, const struct p2p_peer_info *info, int new_device); /** * dev_lost - Notification of a lost P2P Device * @ctx: Callback context from cb_ctx * @dev_addr: P2P Device Address of the lost P2P Device * * This callback is used to notify that a P2P Device has been deleted. */ void (*dev_lost)(void *ctx, const u8 *dev_addr); /** * find_stopped - Notification of a p2p_find operation stopping * @ctx: Callback context from cb_ctx */ void (*find_stopped)(void *ctx); /** * go_neg_req_rx - Notification of a receive GO Negotiation Request * @ctx: Callback context from cb_ctx * @src: Source address of the message triggering this notification * @dev_passwd_id: WPS Device Password ID * * This callback is used to notify that a P2P Device is requesting * group owner negotiation with us, but we do not have all the * necessary information to start GO Negotiation. This indicates that * the local user has not authorized the connection yet by providing a * PIN or PBC button press. This information can be provided with a * call to p2p_connect(). */ void (*go_neg_req_rx)(void *ctx, const u8 *src, u16 dev_passwd_id); /** * go_neg_completed - Notification of GO Negotiation results * @ctx: Callback context from cb_ctx * @res: GO Negotiation results * * This callback is used to notify that Group Owner Negotiation has * been completed. Non-zero struct p2p_go_neg_results::status indicates * failed negotiation. In case of success, this function is responsible * for creating a new group interface (or using the existing interface * depending on driver features), setting up the group interface in * proper mode based on struct p2p_go_neg_results::role_go and * initializing WPS provisioning either as a Registrar (if GO) or as an * Enrollee. Successful WPS provisioning must be indicated by calling * p2p_wps_success_cb(). The callee is responsible for timing out group * formation if WPS provisioning cannot be completed successfully * within 15 seconds. */ void (*go_neg_completed)(void *ctx, struct p2p_go_neg_results *res); /** * sd_request - Callback on Service Discovery Request * @ctx: Callback context from cb_ctx * @freq: Frequency (in MHz) of the channel * @sa: Source address of the request * @dialog_token: Dialog token * @update_indic: Service Update Indicator from the source of request * @tlvs: P2P Service Request TLV(s) * @tlvs_len: Length of tlvs buffer in octets * * This callback is used to indicate reception of a service discovery * request. Response to the query must be indicated by calling * p2p_sd_response() with the context information from the arguments to * this callback function. * * This callback handler can be set to %NULL to indicate that service * discovery is not supported. */ void (*sd_request)(void *ctx, int freq, const u8 *sa, u8 dialog_token, u16 update_indic, const u8 *tlvs, size_t tlvs_len); /** * sd_response - Callback on Service Discovery Response * @ctx: Callback context from cb_ctx * @sa: Source address of the request * @update_indic: Service Update Indicator from the source of response * @tlvs: P2P Service Response TLV(s) * @tlvs_len: Length of tlvs buffer in octets * * This callback is used to indicate reception of a service discovery * response. This callback handler can be set to %NULL if no service * discovery requests are used. The information provided with this call * is replies to the queries scheduled with p2p_sd_request(). */ void (*sd_response)(void *ctx, const u8 *sa, u16 update_indic, const u8 *tlvs, size_t tlvs_len); /** * prov_disc_req - Callback on Provisiong Discovery Request * @ctx: Callback context from cb_ctx * @peer: Source address of the request * @config_methods: Requested WPS Config Method * @dev_addr: P2P Device Address of the found P2P Device * @pri_dev_type: Primary Device Type * @dev_name: Device Name * @supp_config_methods: Supported configuration Methods * @dev_capab: Device Capabilities * @group_capab: Group Capabilities * @group_id: P2P Group ID (or %NULL if not included) * @group_id_len: Length of P2P Group ID * * This callback is used to indicate reception of a Provision Discovery * Request frame that the P2P module accepted. */ void (*prov_disc_req)(void *ctx, const u8 *peer, u16 config_methods, const u8 *dev_addr, const u8 *pri_dev_type, const char *dev_name, u16 supp_config_methods, u8 dev_capab, u8 group_capab, const u8 *group_id, size_t group_id_len); /** * prov_disc_resp - Callback on Provisiong Discovery Response * @ctx: Callback context from cb_ctx * @peer: Source address of the response * @config_methods: Value from p2p_prov_disc_req() or 0 on failure * * This callback is used to indicate reception of a Provision Discovery * Response frame for a pending request scheduled with * p2p_prov_disc_req(). This callback handler can be set to %NULL if * provision discovery is not used. */ void (*prov_disc_resp)(void *ctx, const u8 *peer, u16 config_methods); /** * prov_disc_fail - Callback on Provision Discovery failure * @ctx: Callback context from cb_ctx * @peer: Source address of the response * @status: Cause of failure, will not be %P2P_PROV_DISC_SUCCESS * * This callback is used to indicate either a failure or no response * to an earlier provision discovery request. * * This callback handler can be set to %NULL if provision discovery * is not used or failures do not need to be indicated. */ void (*prov_disc_fail)(void *ctx, const u8 *peer, enum p2p_prov_disc_status status); /** * invitation_process - Optional callback for processing Invitations * @ctx: Callback context from cb_ctx * @sa: Source address of the Invitation Request * @bssid: P2P Group BSSID from the request or %NULL if not included * @go_dev_addr: GO Device Address from P2P Group ID * @ssid: SSID from P2P Group ID * @ssid_len: Length of ssid buffer in octets * @go: Variable for returning whether the local end is GO in the group * @group_bssid: Buffer for returning P2P Group BSSID (if local end GO) * @force_freq: Variable for returning forced frequency for the group * @persistent_group: Whether this is an invitation to reinvoke a * persistent group (instead of invitation to join an active * group) * @channels: Available operating channels for the group * @dev_pw_id: Device Password ID for NFC static handover or -1 if not * used * Returns: Status code (P2P_SC_*) * * This optional callback can be used to implement persistent reconnect * by allowing automatic restarting of persistent groups without user * interaction. If this callback is not implemented (i.e., is %NULL), * the received Invitation Request frames are replied with * %P2P_SC_REQ_RECEIVED status and indicated to upper layer with the * invitation_result() callback. * * If the requested parameters are acceptable and the group is known, * %P2P_SC_SUCCESS may be returned. If the requested group is unknown, * %P2P_SC_FAIL_UNKNOWN_GROUP should be returned. %P2P_SC_REQ_RECEIVED * can be returned if there is not enough data to provide immediate * response, i.e., if some sort of user interaction is needed. The * invitation_received() callback will be called in that case * immediately after this call. */ u8 (*invitation_process)(void *ctx, const u8 *sa, const u8 *bssid, const u8 *go_dev_addr, const u8 *ssid, size_t ssid_len, int *go, u8 *group_bssid, int *force_freq, int persistent_group, const struct p2p_channels *channels, int dev_pw_id); /** * invitation_received - Callback on Invitation Request RX * @ctx: Callback context from cb_ctx * @sa: Source address of the Invitation Request * @bssid: P2P Group BSSID or %NULL if not received * @ssid: SSID of the group * @ssid_len: Length of ssid in octets * @go_dev_addr: GO Device Address * @status: Response Status * @op_freq: Operational frequency for the group * * This callback is used to indicate sending of an Invitation Response * for a received Invitation Request. If status == 0 (success), the * upper layer code is responsible for starting the group. status == 1 * indicates need to get user authorization for the group. Other status * values indicate that the invitation request was rejected. */ void (*invitation_received)(void *ctx, const u8 *sa, const u8 *bssid, const u8 *ssid, size_t ssid_len, const u8 *go_dev_addr, u8 status, int op_freq); /** * invitation_result - Callback on Invitation result * @ctx: Callback context from cb_ctx * @status: Negotiation result (Status Code) * @bssid: P2P Group BSSID or %NULL if not received * @channels: Available operating channels for the group * @addr: Peer address * @freq: Frequency (in MHz) indicated during invitation or 0 * * This callback is used to indicate result of an Invitation procedure * started with a call to p2p_invite(). The indicated status code is * the value received from the peer in Invitation Response with 0 * (P2P_SC_SUCCESS) indicating success or -1 to indicate a timeout or a * local failure in transmitting the Invitation Request. */ void (*invitation_result)(void *ctx, int status, const u8 *bssid, const struct p2p_channels *channels, const u8 *addr, int freq); /** * go_connected - Check whether we are connected to a GO * @ctx: Callback context from cb_ctx * @dev_addr: P2P Device Address of a GO * Returns: 1 if we are connected as a P2P client to the specified GO * or 0 if not. */ int (*go_connected)(void *ctx, const u8 *dev_addr); /** * presence_resp - Callback on Presence Response * @ctx: Callback context from cb_ctx * @src: Source address (GO's P2P Interface Address) * @status: Result of the request (P2P_SC_*) * @noa: Returned NoA value * @noa_len: Length of the NoA buffer in octets */ void (*presence_resp)(void *ctx, const u8 *src, u8 status, const u8 *noa, size_t noa_len); /** * is_concurrent_session_active - Check whether concurrent session is * active on other virtual interfaces * @ctx: Callback context from cb_ctx * Returns: 1 if concurrent session is active on other virtual interface * or 0 if not. */ int (*is_concurrent_session_active)(void *ctx); }; /* P2P module initialization/deinitialization */ /** * p2p_init - Initialize P2P module * @cfg: P2P module configuration * Returns: Pointer to private data or %NULL on failure * * This function is used to initialize global P2P module context (one per * device). The P2P module will keep a copy of the configuration data, so the * caller does not need to maintain this structure. However, the callback * functions and the context parameters to them must be kept available until * the P2P module is deinitialized with p2p_deinit(). */ struct p2p_data * p2p_init(const struct p2p_config *cfg); /** * p2p_deinit - Deinitialize P2P module * @p2p: P2P module context from p2p_init() */ void p2p_deinit(struct p2p_data *p2p); /** * p2p_flush - Flush P2P module state * @p2p: P2P module context from p2p_init() * * This command removes the P2P module state like peer device entries. */ void p2p_flush(struct p2p_data *p2p); /** * p2p_unauthorize - Unauthorize the specified peer device * @p2p: P2P module context from p2p_init() * @addr: P2P peer entry to be unauthorized * Returns: 0 on success, -1 on failure * * This command removes any connection authorization from the specified P2P * peer device address. This can be used, e.g., to cancel effect of a previous * p2p_authorize() or p2p_connect() call that has not yet resulted in completed * GO Negotiation. */ int p2p_unauthorize(struct p2p_data *p2p, const u8 *addr); /** * p2p_set_dev_name - Set device name * @p2p: P2P module context from p2p_init() * Returns: 0 on success, -1 on failure * * This function can be used to update the P2P module configuration with * information that was not available at the time of the p2p_init() call. */ int p2p_set_dev_name(struct p2p_data *p2p, const char *dev_name); int p2p_set_manufacturer(struct p2p_data *p2p, const char *manufacturer); int p2p_set_model_name(struct p2p_data *p2p, const char *model_name); int p2p_set_model_number(struct p2p_data *p2p, const char *model_number); int p2p_set_serial_number(struct p2p_data *p2p, const char *serial_number); void p2p_set_config_methods(struct p2p_data *p2p, u16 config_methods); void p2p_set_uuid(struct p2p_data *p2p, const u8 *uuid); /** * p2p_set_pri_dev_type - Set primary device type * @p2p: P2P module context from p2p_init() * Returns: 0 on success, -1 on failure * * This function can be used to update the P2P module configuration with * information that was not available at the time of the p2p_init() call. */ int p2p_set_pri_dev_type(struct p2p_data *p2p, const u8 *pri_dev_type); /** * p2p_set_sec_dev_types - Set secondary device types * @p2p: P2P module context from p2p_init() * Returns: 0 on success, -1 on failure * * This function can be used to update the P2P module configuration with * information that was not available at the time of the p2p_init() call. */ int p2p_set_sec_dev_types(struct p2p_data *p2p, const u8 dev_types[][8], size_t num_dev_types); int p2p_set_country(struct p2p_data *p2p, const char *country); /* Commands from upper layer management entity */ enum p2p_discovery_type { P2P_FIND_START_WITH_FULL, P2P_FIND_ONLY_SOCIAL, P2P_FIND_PROGRESSIVE }; /** * p2p_find - Start P2P Find (Device Discovery) * @p2p: P2P module context from p2p_init() * @timeout: Timeout for find operation in seconds or 0 for no timeout * @type: Device Discovery type * @num_req_dev_types: Number of requested device types * @req_dev_types: Requested device types array, must be an array * containing num_req_dev_types * WPS_DEV_TYPE_LEN bytes; %NULL if no * requested device types. * @dev_id: Device ID to search for or %NULL to find all devices * @search_delay: Extra delay in milliseconds between search iterations * Returns: 0 on success, -1 on failure */ int p2p_find(struct p2p_data *p2p, unsigned int timeout, enum p2p_discovery_type type, unsigned int num_req_dev_types, const u8 *req_dev_types, const u8 *dev_id, unsigned int search_delay); /** * p2p_stop_find - Stop P2P Find (Device Discovery) * @p2p: P2P module context from p2p_init() */ void p2p_stop_find(struct p2p_data *p2p); /** * p2p_stop_find_for_freq - Stop P2P Find for next oper on specific freq * @p2p: P2P module context from p2p_init() * @freq: Frequency in MHz for next operation * * This is like p2p_stop_find(), but Listen state is not stopped if we are * already on the same frequency. */ void p2p_stop_find_for_freq(struct p2p_data *p2p, int freq); /** * p2p_listen - Start P2P Listen state for specified duration * @p2p: P2P module context from p2p_init() * @timeout: Listen state duration in milliseconds * Returns: 0 on success, -1 on failure * * This function can be used to request the P2P module to keep the device * discoverable on the listen channel for an extended set of time. At least in * its current form, this is mainly used for testing purposes and may not be of * much use for normal P2P operations. */ int p2p_listen(struct p2p_data *p2p, unsigned int timeout); /** * p2p_stop_listen - Stop P2P Listen * @p2p: P2P module context from p2p_init() */ void p2p_stop_listen(struct p2p_data *p2p); /** * p2p_connect - Start P2P group formation (GO negotiation) * @p2p: P2P module context from p2p_init() * @peer_addr: MAC address of the peer P2P client * @wps_method: WPS method to be used in provisioning * @go_intent: Local GO intent value (1..15) * @own_interface_addr: Intended interface address to use with the group * @force_freq: The only allowed channel frequency in MHz or 0 * @persistent_group: Whether to create a persistent group (0 = no, 1 = * persistent group without persistent reconnect, 2 = persistent group with * persistent reconnect) * @force_ssid: Forced SSID for the group if we become GO or %NULL to generate * a new SSID * @force_ssid_len: Length of $force_ssid buffer * @pd_before_go_neg: Whether to send Provision Discovery prior to GO * Negotiation as an interoperability workaround when initiating group * formation * @pref_freq: Preferred operating frequency in MHz or 0 (this is only used if * force_freq == 0) * Returns: 0 on success, -1 on failure */ int p2p_connect(struct p2p_data *p2p, const u8 *peer_addr, enum p2p_wps_method wps_method, int go_intent, const u8 *own_interface_addr, unsigned int force_freq, int persistent_group, const u8 *force_ssid, size_t force_ssid_len, int pd_before_go_neg, unsigned int pref_freq, u16 oob_pw_id); /** * p2p_authorize - Authorize P2P group formation (GO negotiation) * @p2p: P2P module context from p2p_init() * @peer_addr: MAC address of the peer P2P client * @wps_method: WPS method to be used in provisioning * @go_intent: Local GO intent value (1..15) * @own_interface_addr: Intended interface address to use with the group * @force_freq: The only allowed channel frequency in MHz or 0 * @persistent_group: Whether to create a persistent group (0 = no, 1 = * persistent group without persistent reconnect, 2 = persistent group with * persistent reconnect) * @force_ssid: Forced SSID for the group if we become GO or %NULL to generate * a new SSID * @force_ssid_len: Length of $force_ssid buffer * @pref_freq: Preferred operating frequency in MHz or 0 (this is only used if * force_freq == 0) * Returns: 0 on success, -1 on failure * * This is like p2p_connect(), but the actual group negotiation is not * initiated automatically, i.e., the other end is expected to do that. */ int p2p_authorize(struct p2p_data *p2p, const u8 *peer_addr, enum p2p_wps_method wps_method, int go_intent, const u8 *own_interface_addr, unsigned int force_freq, int persistent_group, const u8 *force_ssid, size_t force_ssid_len, unsigned int pref_freq, u16 oob_pw_id); /** * p2p_reject - Reject peer device (explicitly block connection attempts) * @p2p: P2P module context from p2p_init() * @peer_addr: MAC address of the peer P2P client * Returns: 0 on success, -1 on failure */ int p2p_reject(struct p2p_data *p2p, const u8 *peer_addr); /** * p2p_prov_disc_req - Send Provision Discovery Request * @p2p: P2P module context from p2p_init() * @peer_addr: MAC address of the peer P2P client * @config_methods: WPS Config Methods value (only one bit set) * @join: Whether this is used by a client joining an active group * @force_freq: Forced TX frequency for the frame (mainly for the join case) * @user_initiated_pd: Flag to indicate if initiated by user or not * Returns: 0 on success, -1 on failure * * This function can be used to request a discovered P2P peer to display a PIN * (config_methods = WPS_CONFIG_DISPLAY) or be prepared to enter a PIN from us * (config_methods = WPS_CONFIG_KEYPAD). The Provision Discovery Request frame * is transmitted once immediately and if no response is received, the frame * will be sent again whenever the target device is discovered during device * dsicovery (start with a p2p_find() call). Response from the peer is * indicated with the p2p_config::prov_disc_resp() callback. */ int p2p_prov_disc_req(struct p2p_data *p2p, const u8 *peer_addr, u16 config_methods, int join, int force_freq, int user_initiated_pd); /** * p2p_sd_request - Schedule a service discovery query * @p2p: P2P module context from p2p_init() * @dst: Destination peer or %NULL to apply for all peers * @tlvs: P2P Service Query TLV(s) * Returns: Reference to the query or %NULL on failure * * Response to the query is indicated with the p2p_config::sd_response() * callback. */ void * p2p_sd_request(struct p2p_data *p2p, const u8 *dst, const struct wpabuf *tlvs); #ifdef CONFIG_WIFI_DISPLAY void * p2p_sd_request_wfd(struct p2p_data *p2p, const u8 *dst, const struct wpabuf *tlvs); #endif /* CONFIG_WIFI_DISPLAY */ /** * p2p_sd_cancel_request - Cancel a pending service discovery query * @p2p: P2P module context from p2p_init() * @req: Query reference from p2p_sd_request() * Returns: 0 if request for cancelled; -1 if not found */ int p2p_sd_cancel_request(struct p2p_data *p2p, void *req); /** * p2p_sd_response - Send response to a service discovery query * @p2p: P2P module context from p2p_init() * @freq: Frequency from p2p_config::sd_request() callback * @dst: Destination address from p2p_config::sd_request() callback * @dialog_token: Dialog token from p2p_config::sd_request() callback * @resp_tlvs: P2P Service Response TLV(s) * * This function is called as a response to the request indicated with * p2p_config::sd_request() callback. */ void p2p_sd_response(struct p2p_data *p2p, int freq, const u8 *dst, u8 dialog_token, const struct wpabuf *resp_tlvs); /** * p2p_sd_service_update - Indicate a change in local services * @p2p: P2P module context from p2p_init() * * This function needs to be called whenever there is a change in availability * of the local services. This will increment the Service Update Indicator * value which will be used in SD Request and Response frames. */ void p2p_sd_service_update(struct p2p_data *p2p); enum p2p_invite_role { P2P_INVITE_ROLE_GO, P2P_INVITE_ROLE_ACTIVE_GO, P2P_INVITE_ROLE_CLIENT }; /** * p2p_invite - Invite a P2P Device into a group * @p2p: P2P module context from p2p_init() * @peer: Device Address of the peer P2P Device * @role: Local role in the group * @bssid: Group BSSID or %NULL if not known * @ssid: Group SSID * @ssid_len: Length of ssid in octets * @force_freq: The only allowed channel frequency in MHz or 0 * @go_dev_addr: Forced GO Device Address or %NULL if none * @persistent_group: Whether this is to reinvoke a persistent group * @pref_freq: Preferred operating frequency in MHz or 0 (this is only used if * force_freq == 0) * @dev_pw_id: Device Password ID from OOB Device Password (NFC) static handover * case or -1 if not used * Returns: 0 on success, -1 on failure */ int p2p_invite(struct p2p_data *p2p, const u8 *peer, enum p2p_invite_role role, const u8 *bssid, const u8 *ssid, size_t ssid_len, unsigned int force_freq, const u8 *go_dev_addr, int persistent_group, unsigned int pref_freq, int dev_pw_id); /** * p2p_presence_req - Request GO presence * @p2p: P2P module context from p2p_init() * @go_interface_addr: GO P2P Interface Address * @own_interface_addr: Own P2P Interface Address for this group * @freq: Group operating frequence (in MHz) * @duration1: Preferred presence duration in microseconds * @interval1: Preferred presence interval in microseconds * @duration2: Acceptable presence duration in microseconds * @interval2: Acceptable presence interval in microseconds * Returns: 0 on success, -1 on failure * * If both duration and interval values are zero, the parameter pair is not * specified (i.e., to remove Presence Request, use duration1 = interval1 = 0). */ int p2p_presence_req(struct p2p_data *p2p, const u8 *go_interface_addr, const u8 *own_interface_addr, unsigned int freq, u32 duration1, u32 interval1, u32 duration2, u32 interval2); /** * p2p_ext_listen - Set Extended Listen Timing * @p2p: P2P module context from p2p_init() * @freq: Group operating frequence (in MHz) * @period: Availability period in milliseconds (1-65535; 0 to disable) * @interval: Availability interval in milliseconds (1-65535; 0 to disable) * Returns: 0 on success, -1 on failure * * This function can be used to enable or disable (period = interval = 0) * Extended Listen Timing. When enabled, the P2P Device will become * discoverable (go into Listen State) every @interval milliseconds for at * least @period milliseconds. */ int p2p_ext_listen(struct p2p_data *p2p, unsigned int period, unsigned int interval); /* Event notifications from upper layer management operations */ /** * p2p_wps_success_cb - Report successfully completed WPS provisioning * @p2p: P2P module context from p2p_init() * @mac_addr: Peer address * * This function is used to report successfully completed WPS provisioning * during group formation in both GO/Registrar and client/Enrollee roles. */ void p2p_wps_success_cb(struct p2p_data *p2p, const u8 *mac_addr); /** * p2p_group_formation_failed - Report failed WPS provisioning * @p2p: P2P module context from p2p_init() * * This function is used to report failed group formation. This can happen * either due to failed WPS provisioning or due to 15 second timeout during * the provisioning phase. */ void p2p_group_formation_failed(struct p2p_data *p2p); /** * p2p_get_provisioning_info - Get any stored provisioning info * @p2p: P2P module context from p2p_init() * @addr: Peer P2P Device Address * Returns: WPS provisioning information (WPS config method) or 0 if no * information is available * * This function is used to retrieve stored WPS provisioning info for the given * peer. */ u16 p2p_get_provisioning_info(struct p2p_data *p2p, const u8 *addr); /** * p2p_clear_provisioning_info - Clear any stored provisioning info * @p2p: P2P module context from p2p_init() * @iface_addr: Peer P2P Device Address * * This function is used to clear stored WPS provisioning info for the given * peer. */ void p2p_clear_provisioning_info(struct p2p_data *p2p, const u8 *addr); /* Event notifications from lower layer driver operations */ /** * enum p2p_probe_req_status * * @P2P_PREQ_MALFORMED: frame was not well-formed * @P2P_PREQ_NOT_LISTEN: device isn't in listen state, frame ignored * @P2P_PREQ_NOT_P2P: frame was not a P2P probe request * @P2P_PREQ_P2P_NOT_PROCESSED: frame was P2P but wasn't processed * @P2P_PREQ_P2P_PROCESSED: frame has been processed by P2P */ enum p2p_probe_req_status { P2P_PREQ_MALFORMED, P2P_PREQ_NOT_LISTEN, P2P_PREQ_NOT_P2P, P2P_PREQ_NOT_PROCESSED, P2P_PREQ_PROCESSED }; /** * p2p_probe_req_rx - Report reception of a Probe Request frame * @p2p: P2P module context from p2p_init() * @addr: Source MAC address * @dst: Destination MAC address if available or %NULL * @bssid: BSSID if available or %NULL * @ie: Information elements from the Probe Request frame body * @ie_len: Length of ie buffer in octets * Returns: value indicating the type and status of the probe request */ enum p2p_probe_req_status p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *dst, const u8 *bssid, const u8 *ie, size_t ie_len); /** * p2p_rx_action - Report received Action frame * @p2p: P2P module context from p2p_init() * @da: Destination address of the received Action frame * @sa: Source address of the received Action frame * @bssid: Address 3 of the received Action frame * @category: Category of the received Action frame * @data: Action frame body after the Category field * @len: Length of the data buffer in octets * @freq: Frequency (in MHz) on which the frame was received */ void p2p_rx_action(struct p2p_data *p2p, const u8 *da, const u8 *sa, const u8 *bssid, u8 category, const u8 *data, size_t len, int freq); /** * p2p_scan_res_handler - Indicate a P2P scan results * @p2p: P2P module context from p2p_init() * @bssid: BSSID of the scan result * @freq: Frequency of the channel on which the device was found in MHz * @rx_time: Time when the result was received * @level: Signal level (signal strength of the received Beacon/Probe Response * frame) * @ies: Pointer to IEs from the scan result * @ies_len: Length of the ies buffer * Returns: 0 to continue or 1 to stop scan result indication * * This function is called to indicate a scan result entry with P2P IE from a * scan requested with struct p2p_config::p2p_scan(). This can be called during * the actual scan process (i.e., whenever a new device is found) or as a * sequence of calls after the full scan has been completed. The former option * can result in optimized operations, but may not be supported by all * driver/firmware designs. The ies buffer need to include at least the P2P IE, * but it is recommended to include all IEs received from the device. The * caller does not need to check that the IEs contain a P2P IE before calling * this function since frames will be filtered internally if needed. * * This function will return 1 if it wants to stop scan result iteration (and * scan in general if it is still in progress). This is used to allow faster * start of a pending operation, e.g., to start a pending GO negotiation. */ int p2p_scan_res_handler(struct p2p_data *p2p, const u8 *bssid, int freq, struct os_reltime *rx_time, int level, const u8 *ies, size_t ies_len); /** * p2p_scan_res_handled - Indicate end of scan results * @p2p: P2P module context from p2p_init() * * This function is called to indicate that all P2P scan results from a scan * have been reported with zero or more calls to p2p_scan_res_handler(). This * function must be called as a response to successful * struct p2p_config::p2p_scan() call if none of the p2p_scan_res_handler() * calls stopped iteration. */ void p2p_scan_res_handled(struct p2p_data *p2p); enum p2p_send_action_result { P2P_SEND_ACTION_SUCCESS /* Frame was send and acknowledged */, P2P_SEND_ACTION_NO_ACK /* Frame was sent, but not acknowledged */, P2P_SEND_ACTION_FAILED /* Frame was not sent due to a failure */ }; /** * p2p_send_action_cb - Notify TX status of an Action frame * @p2p: P2P module context from p2p_init() * @freq: Channel frequency in MHz * @dst: Destination MAC address (Address 1) * @src: Source MAC address (Address 2) * @bssid: BSSID (Address 3) * @result: Result of the transmission attempt * * This function is used to indicate the result of an Action frame transmission * that was requested with struct p2p_config::send_action() callback. */ void p2p_send_action_cb(struct p2p_data *p2p, unsigned int freq, const u8 *dst, const u8 *src, const u8 *bssid, enum p2p_send_action_result result); /** * p2p_listen_cb - Indicate the start of a requested Listen state * @p2p: P2P module context from p2p_init() * @freq: Listen channel frequency in MHz * @duration: Duration for the Listen state in milliseconds * * This function is used to indicate that a Listen state requested with * struct p2p_config::start_listen() callback has started. */ void p2p_listen_cb(struct p2p_data *p2p, unsigned int freq, unsigned int duration); /** * p2p_listen_end - Indicate the end of a requested Listen state * @p2p: P2P module context from p2p_init() * @freq: Listen channel frequency in MHz * Returns: 0 if no operations were started, 1 if an operation was started * * This function is used to indicate that a Listen state requested with * struct p2p_config::start_listen() callback has ended. */ int p2p_listen_end(struct p2p_data *p2p, unsigned int freq); void p2p_deauth_notif(struct p2p_data *p2p, const u8 *bssid, u16 reason_code, const u8 *ie, size_t ie_len); void p2p_disassoc_notif(struct p2p_data *p2p, const u8 *bssid, u16 reason_code, const u8 *ie, size_t ie_len); /* Per-group P2P state for GO */ struct p2p_group; /** * struct p2p_group_config - P2P group configuration * * This configuration is provided to the P2P module during initialization of * the per-group information with p2p_group_init(). */ struct p2p_group_config { /** * persistent_group - Whether the group is persistent * 0 = not a persistent group * 1 = persistent group without persistent reconnect * 2 = persistent group with persistent reconnect */ int persistent_group; /** * interface_addr - P2P Interface Address of the group */ u8 interface_addr[ETH_ALEN]; /** * max_clients - Maximum number of clients in the group */ unsigned int max_clients; /** * ssid - Group SSID */ u8 ssid[32]; /** * ssid_len - Length of SSID */ size_t ssid_len; /** * freq - Operating channel of the group */ int freq; /** * cb_ctx - Context to use with callback functions */ void *cb_ctx; /** * ie_update - Notification of IE update * @ctx: Callback context from cb_ctx * @beacon_ies: P2P IE for Beacon frames or %NULL if no change * @proberesp_ies: P2P Ie for Probe Response frames * * P2P module uses this callback function to notify whenever the P2P IE * in Beacon or Probe Response frames should be updated based on group * events. * * The callee is responsible for freeing the returned buffer(s) with * wpabuf_free(). */ void (*ie_update)(void *ctx, struct wpabuf *beacon_ies, struct wpabuf *proberesp_ies); /** * idle_update - Notification of changes in group idle state * @ctx: Callback context from cb_ctx * @idle: Whether the group is idle (no associated stations) */ void (*idle_update)(void *ctx, int idle); }; /** * p2p_group_init - Initialize P2P group * @p2p: P2P module context from p2p_init() * @config: P2P group configuration (will be freed by p2p_group_deinit()) * Returns: Pointer to private data or %NULL on failure * * This function is used to initialize per-group P2P module context. Currently, * this is only used to manage GO functionality and P2P clients do not need to * create an instance of this per-group information. */ struct p2p_group * p2p_group_init(struct p2p_data *p2p, struct p2p_group_config *config); /** * p2p_group_deinit - Deinitialize P2P group * @group: P2P group context from p2p_group_init() */ void p2p_group_deinit(struct p2p_group *group); /** * p2p_group_notif_assoc - Notification of P2P client association with GO * @group: P2P group context from p2p_group_init() * @addr: Interface address of the P2P client * @ie: IEs from the (Re)association Request frame * @len: Length of the ie buffer in octets * Returns: 0 on success, -1 on failure */ int p2p_group_notif_assoc(struct p2p_group *group, const u8 *addr, const u8 *ie, size_t len); /** * p2p_group_assoc_resp_ie - Build P2P IE for (re)association response * @group: P2P group context from p2p_group_init() * @status: Status value (P2P_SC_SUCCESS if association succeeded) * Returns: P2P IE for (Re)association Response or %NULL on failure * * The caller is responsible for freeing the returned buffer with * wpabuf_free(). */ struct wpabuf * p2p_group_assoc_resp_ie(struct p2p_group *group, u8 status); /** * p2p_group_notif_disassoc - Notification of P2P client disassociation from GO * @group: P2P group context from p2p_group_init() * @addr: Interface address of the P2P client */ void p2p_group_notif_disassoc(struct p2p_group *group, const u8 *addr); /** * p2p_group_notif_formation_done - Notification of completed group formation * @group: P2P group context from p2p_group_init() */ void p2p_group_notif_formation_done(struct p2p_group *group); /** * p2p_group_notif_noa - Notification of NoA change * @group: P2P group context from p2p_group_init() * @noa: Notice of Absence attribute payload, %NULL if none * @noa_len: Length of noa buffer in octets * Returns: 0 on success, -1 on failure * * Notify the P2P group management about a new NoA contents. This will be * inserted into the P2P IEs in Beacon and Probe Response frames with rest of * the group information. */ int p2p_group_notif_noa(struct p2p_group *group, const u8 *noa, size_t noa_len); /** * p2p_group_match_dev_type - Match device types in group with requested type * @group: P2P group context from p2p_group_init() * @wps: WPS TLVs from Probe Request frame (concatenated WPS IEs) * Returns: 1 on match, 0 on mismatch * * This function can be used to match the Requested Device Type attribute in * WPS IE with the device types of a group member for deciding whether a GO * should reply to a Probe Request frame. Match will be reported if the WPS IE * is not requested any specific device type. */ int p2p_group_match_dev_type(struct p2p_group *group, struct wpabuf *wps); /** * p2p_group_match_dev_id - Match P2P Device Address in group with requested device id */ int p2p_group_match_dev_id(struct p2p_group *group, struct wpabuf *p2p); /** * p2p_group_go_discover - Send GO Discoverability Request to a group client * @group: P2P group context from p2p_group_init() * Returns: 0 on success (frame scheduled); -1 if client was not found */ int p2p_group_go_discover(struct p2p_group *group, const u8 *dev_id, const u8 *searching_dev, int rx_freq); /* Generic helper functions */ /** * p2p_ie_text - Build text format description of P2P IE * @p2p_ie: P2P IE * @buf: Buffer for returning text * @end: Pointer to the end of the buf area * Returns: Number of octets written to the buffer or -1 on failure * * This function can be used to parse P2P IE contents into text format * field=value lines. */ int p2p_ie_text(struct wpabuf *p2p_ie, char *buf, char *end); /** * p2p_scan_result_text - Build text format description of P2P IE * @ies: Information elements from scan results * @ies_len: ies buffer length in octets * @buf: Buffer for returning text * @end: Pointer to the end of the buf area * Returns: Number of octets written to the buffer or -1 on failure * * This function can be used to parse P2P IE contents into text format * field=value lines. */ int p2p_scan_result_text(const u8 *ies, size_t ies_len, char *buf, char *end); /** * p2p_parse_dev_addr_in_p2p_ie - Parse P2P Device Address from a concatenated * P2P IE * @p2p_ie: P2P IE * @dev_addr: Buffer for returning P2P Device Address * Returns: 0 on success or -1 if P2P Device Address could not be parsed */ int p2p_parse_dev_addr_in_p2p_ie(struct wpabuf *p2p_ie, u8 *dev_addr); /** * p2p_parse_dev_addr - Parse P2P Device Address from P2P IE(s) * @ies: Information elements from scan results * @ies_len: ies buffer length in octets * @dev_addr: Buffer for returning P2P Device Address * Returns: 0 on success or -1 if P2P Device Address could not be parsed */ int p2p_parse_dev_addr(const u8 *ies, size_t ies_len, u8 *dev_addr); /** * p2p_assoc_req_ie - Build P2P IE for (Re)Association Request frame * @p2p: P2P module context from p2p_init() * @bssid: BSSID * @buf: Buffer for writing the P2P IE * @len: Maximum buf length in octets * @p2p_group: Whether this is for association with a P2P GO * @p2p_ie: Reassembled P2P IE data from scan results or %NULL if none * Returns: Number of octets written into buf or -1 on failure */ int p2p_assoc_req_ie(struct p2p_data *p2p, const u8 *bssid, u8 *buf, size_t len, int p2p_group, struct wpabuf *p2p_ie); /** * p2p_scan_ie - Build P2P IE for Probe Request * @p2p: P2P module context from p2p_init() * @ies: Buffer for writing P2P IE * @dev_id: Device ID to search for or %NULL for any */ void p2p_scan_ie(struct p2p_data *p2p, struct wpabuf *ies, const u8 *dev_id); /** * p2p_scan_ie_buf_len - Get maximum buffer length needed for p2p_scan_ie * @p2p: P2P module context from p2p_init() * Returns: Number of octets that p2p_scan_ie() may add to the buffer */ size_t p2p_scan_ie_buf_len(struct p2p_data *p2p); /** * p2p_go_params - Generate random P2P group parameters * @p2p: P2P module context from p2p_init() * @params: Buffer for parameters * Returns: 0 on success, -1 on failure */ int p2p_go_params(struct p2p_data *p2p, struct p2p_go_neg_results *params); /** * p2p_get_group_capab - Get Group Capability from P2P IE data * @p2p_ie: P2P IE(s) contents * Returns: Group Capability */ u8 p2p_get_group_capab(const struct wpabuf *p2p_ie); /** * p2p_get_cross_connect_disallowed - Does WLAN AP disallows cross connection * @p2p_ie: P2P IE(s) contents * Returns: 0 if cross connection is allow, 1 if not */ int p2p_get_cross_connect_disallowed(const struct wpabuf *p2p_ie); /** * p2p_get_go_dev_addr - Get P2P Device Address from P2P IE data * @p2p_ie: P2P IE(s) contents * Returns: Pointer to P2P Device Address or %NULL if not included */ const u8 * p2p_get_go_dev_addr(const struct wpabuf *p2p_ie); /** * p2p_get_peer_info - Get P2P peer information * @p2p: P2P module context from p2p_init() * @addr: P2P Device Address of the peer or %NULL to indicate the first peer * @next: Whether to select the peer entry following the one indicated by addr * Returns: Pointer to peer info or %NULL if not found */ const struct p2p_peer_info * p2p_get_peer_info(struct p2p_data *p2p, const u8 *addr, int next); /** * p2p_get_peer_info_txt - Get internal P2P peer information in text format * @info: Pointer to P2P peer info from p2p_get_peer_info() * @buf: Buffer for returning text * @buflen: Maximum buffer length * Returns: Number of octets written to the buffer or -1 on failure * * Note: This information is internal to the P2P module and subject to change. * As such, this should not really be used by external programs for purposes * other than debugging. */ int p2p_get_peer_info_txt(const struct p2p_peer_info *info, char *buf, size_t buflen); /** * p2p_peer_known - Check whether P2P peer is known * @p2p: P2P module context from p2p_init() * @addr: P2P Device Address of the peer * Returns: 1 if the specified device is in the P2P peer table or 0 if not */ int p2p_peer_known(struct p2p_data *p2p, const u8 *addr); /** * p2p_set_client_discoverability - Set client discoverability capability * @p2p: P2P module context from p2p_init() * @enabled: Whether client discoverability will be enabled * * This function can be used to disable (and re-enable) client discoverability. * This capability is enabled by default and should not be disabled in normal * use cases, i.e., this is mainly for testing purposes. */ void p2p_set_client_discoverability(struct p2p_data *p2p, int enabled); /** * p2p_set_managed_oper - Set managed P2P Device operations capability * @p2p: P2P module context from p2p_init() * @enabled: Whether managed P2P Device operations will be enabled */ void p2p_set_managed_oper(struct p2p_data *p2p, int enabled); int p2p_set_listen_channel(struct p2p_data *p2p, u8 reg_class, u8 channel); int p2p_set_ssid_postfix(struct p2p_data *p2p, const u8 *postfix, size_t len); int p2p_get_interface_addr(struct p2p_data *p2p, const u8 *dev_addr, u8 *iface_addr); int p2p_get_dev_addr(struct p2p_data *p2p, const u8 *iface_addr, u8 *dev_addr); void p2p_set_peer_filter(struct p2p_data *p2p, const u8 *addr); /** * p2p_set_cross_connect - Set cross connection capability * @p2p: P2P module context from p2p_init() * @enabled: Whether cross connection will be enabled */ void p2p_set_cross_connect(struct p2p_data *p2p, int enabled); int p2p_get_oper_freq(struct p2p_data *p2p, const u8 *iface_addr); /** * p2p_set_intra_bss_dist - Set intra BSS distribution * @p2p: P2P module context from p2p_init() * @enabled: Whether intra BSS distribution will be enabled */ void p2p_set_intra_bss_dist(struct p2p_data *p2p, int enabled); int p2p_channels_includes_freq(const struct p2p_channels *channels, unsigned int freq); /** * p2p_supported_freq - Check whether channel is supported for P2P * @p2p: P2P module context from p2p_init() * @freq: Channel frequency in MHz * Returns: 0 if channel not usable for P2P, 1 if usable for P2P */ int p2p_supported_freq(struct p2p_data *p2p, unsigned int freq); /** * p2p_supported_freq_go - Check whether channel is supported for P2P GO operation * @p2p: P2P module context from p2p_init() * @freq: Channel frequency in MHz * Returns: 0 if channel not usable for P2P, 1 if usable for P2P */ int p2p_supported_freq_go(struct p2p_data *p2p, unsigned int freq); /** * p2p_supported_freq_cli - Check whether channel is supported for P2P client operation * @p2p: P2P module context from p2p_init() * @freq: Channel frequency in MHz * Returns: 0 if channel not usable for P2P, 1 if usable for P2P */ int p2p_supported_freq_cli(struct p2p_data *p2p, unsigned int freq); /** * p2p_get_pref_freq - Get channel from preferred channel list * @p2p: P2P module context from p2p_init() * @channels: List of channels * Returns: Preferred channel */ unsigned int p2p_get_pref_freq(struct p2p_data *p2p, const struct p2p_channels *channels); void p2p_update_channel_list(struct p2p_data *p2p, const struct p2p_channels *chan, const struct p2p_channels *cli_chan); /** * p2p_set_best_channels - Update best channel information * @p2p: P2P module context from p2p_init() * @freq_24: Frequency (MHz) of best channel in 2.4 GHz band * @freq_5: Frequency (MHz) of best channel in 5 GHz band * @freq_overall: Frequency (MHz) of best channel overall */ void p2p_set_best_channels(struct p2p_data *p2p, int freq_24, int freq_5, int freq_overall); /** * p2p_set_own_freq_preference - Set own preference for channel * @p2p: P2P module context from p2p_init() * @freq: Frequency (MHz) of the preferred channel or 0 if no preference * * This function can be used to set a preference on the operating channel based * on frequencies used on the other virtual interfaces that share the same * radio. If non-zero, this is used to try to avoid multi-channel concurrency. */ void p2p_set_own_freq_preference(struct p2p_data *p2p, int freq); const u8 * p2p_get_go_neg_peer(struct p2p_data *p2p); /** * p2p_get_group_num_members - Get number of members in group * @group: P2P group context from p2p_group_init() * Returns: Number of members in the group */ unsigned int p2p_get_group_num_members(struct p2p_group *group); /** * p2p_iterate_group_members - Iterate group members * @group: P2P group context from p2p_group_init() * @next: iteration pointer, must be a pointer to a void * that is set to %NULL * on the first call and not modified later * Returns: A P2P Interface Address for each call and %NULL for no more members */ const u8 * p2p_iterate_group_members(struct p2p_group *group, void **next); /** * p2p_group_get_dev_addr - Get a P2P Device Address of a client in a group * @group: P2P group context from p2p_group_init() * @addr: P2P Interface Address of the client * Returns: P2P Device Address of the client if found or %NULL if no match * found */ const u8 * p2p_group_get_dev_addr(struct p2p_group *group, const u8 *addr); /** * p2p_group_is_client_connected - Check whether a specific client is connected * @group: P2P group context from p2p_group_init() * @addr: P2P Device Address of the client * Returns: 1 if client is connected or 0 if not */ int p2p_group_is_client_connected(struct p2p_group *group, const u8 *dev_addr); /** * p2p_get_peer_found - Get P2P peer info structure of a found peer * @p2p: P2P module context from p2p_init() * @addr: P2P Device Address of the peer or %NULL to indicate the first peer * @next: Whether to select the peer entry following the one indicated by addr * Returns: The first P2P peer info available or %NULL if no such peer exists */ const struct p2p_peer_info * p2p_get_peer_found(struct p2p_data *p2p, const u8 *addr, int next); /** * p2p_remove_wps_vendor_extensions - Remove WPS vendor extensions * @p2p: P2P module context from p2p_init() */ void p2p_remove_wps_vendor_extensions(struct p2p_data *p2p); /** * p2p_add_wps_vendor_extension - Add a WPS vendor extension * @p2p: P2P module context from p2p_init() * @vendor_ext: The vendor extensions to add * Returns: 0 on success, -1 on failure * * The wpabuf structures in the array are owned by the P2P * module after this call. */ int p2p_add_wps_vendor_extension(struct p2p_data *p2p, const struct wpabuf *vendor_ext); /** * p2p_set_oper_channel - Set the P2P operating channel * @p2p: P2P module context from p2p_init() * @op_reg_class: Operating regulatory class to set * @op_channel: operating channel to set * @cfg_op_channel : Whether op_channel is hardcoded in configuration * Returns: 0 on success, -1 on failure */ int p2p_set_oper_channel(struct p2p_data *p2p, u8 op_reg_class, u8 op_channel, int cfg_op_channel); /** * p2p_set_pref_chan - Set P2P preferred channel list * @p2p: P2P module context from p2p_init() * @num_pref_chan: Number of entries in pref_chan list * @pref_chan: Preferred channels or %NULL to remove preferences * Returns: 0 on success, -1 on failure */ int p2p_set_pref_chan(struct p2p_data *p2p, unsigned int num_pref_chan, const struct p2p_channel *pref_chan); /** * p2p_set_no_go_freq - Set no GO channel ranges * @p2p: P2P module context from p2p_init() * @list: Channel ranges or %NULL to remove restriction * Returns: 0 on success, -1 on failure */ int p2p_set_no_go_freq(struct p2p_data *p2p, const struct wpa_freq_range_list *list); /** * p2p_in_progress - Check whether a P2P operation is progress * @p2p: P2P module context from p2p_init() * Returns: 0 if P2P module is idle or 1 if an operation is in progress */ int p2p_in_progress(struct p2p_data *p2p); const char * p2p_wps_method_text(enum p2p_wps_method method); /** * p2p_set_config_timeout - Set local config timeouts * @p2p: P2P module context from p2p_init() * @go_timeout: Time in 10 ms units it takes to start the GO mode * @client_timeout: Time in 10 ms units it takes to start the client mode */ void p2p_set_config_timeout(struct p2p_data *p2p, u8 go_timeout, u8 client_timeout); int p2p_set_wfd_ie_beacon(struct p2p_data *p2p, struct wpabuf *ie); int p2p_set_wfd_ie_probe_req(struct p2p_data *p2p, struct wpabuf *ie); int p2p_set_wfd_ie_probe_resp(struct p2p_data *p2p, struct wpabuf *ie); int p2p_set_wfd_ie_assoc_req(struct p2p_data *p2p, struct wpabuf *ie); int p2p_set_wfd_ie_invitation(struct p2p_data *p2p, struct wpabuf *ie); int p2p_set_wfd_ie_prov_disc_req(struct p2p_data *p2p, struct wpabuf *ie); int p2p_set_wfd_ie_prov_disc_resp(struct p2p_data *p2p, struct wpabuf *ie); int p2p_set_wfd_ie_go_neg(struct p2p_data *p2p, struct wpabuf *ie); int p2p_set_wfd_dev_info(struct p2p_data *p2p, const struct wpabuf *elem); int p2p_set_wfd_assoc_bssid(struct p2p_data *p2p, const struct wpabuf *elem); int p2p_set_wfd_coupled_sink_info(struct p2p_data *p2p, const struct wpabuf *elem); struct wpabuf * wifi_display_encaps(struct wpabuf *subelems); /** * p2p_set_disc_int - Set min/max discoverable interval for p2p_find * @p2p: P2P module context from p2p_init() * @min_disc_int: minDiscoverableInterval (in units of 100 TU); default 1 * @max_disc_int: maxDiscoverableInterval (in units of 100 TU); default 3 * @max_disc_tu: Maximum number of TUs (1.024 ms) for discoverable interval; or * -1 not to limit * Returns: 0 on success, or -1 on failure * * This function can be used to configure minDiscoverableInterval and * maxDiscoverableInterval parameters for the Listen state during device * discovery (p2p_find). A random number of 100 TU units is picked for each * Listen state iteration from [min_disc_int,max_disc_int] range. * * max_disc_tu can be used to futher limit the discoverable duration. However, * it should be noted that use of this parameter is not recommended since it * would not be compliant with the P2P specification. */ int p2p_set_disc_int(struct p2p_data *p2p, int min_disc_int, int max_disc_int, int max_disc_tu); /** * p2p_get_state_txt - Get current P2P state for debug purposes * @p2p: P2P module context from p2p_init() * Returns: Name of the current P2P module state * * It should be noted that the P2P module state names are internal information * and subject to change at any point, i.e., this information should be used * mainly for debugging purposes. */ const char * p2p_get_state_txt(struct p2p_data *p2p); struct wpabuf * p2p_build_nfc_handover_req(struct p2p_data *p2p, int client_freq, const u8 *go_dev_addr, const u8 *ssid, size_t ssid_len); struct wpabuf * p2p_build_nfc_handover_sel(struct p2p_data *p2p, int client_freq, const u8 *go_dev_addr, const u8 *ssid, size_t ssid_len); struct p2p_nfc_params { int sel; const u8 *wsc_attr; size_t wsc_len; const u8 *p2p_attr; size_t p2p_len; enum { NO_ACTION, JOIN_GROUP, AUTH_JOIN, INIT_GO_NEG, RESP_GO_NEG, BOTH_GO, PEER_CLIENT } next_step; struct p2p_peer_info *peer; u8 oob_dev_pw[WPS_OOB_PUBKEY_HASH_LEN + 2 + WPS_OOB_DEVICE_PASSWORD_LEN]; size_t oob_dev_pw_len; int go_freq; u8 go_dev_addr[ETH_ALEN]; u8 go_ssid[32]; size_t go_ssid_len; }; int p2p_process_nfc_connection_handover(struct p2p_data *p2p, struct p2p_nfc_params *params); void p2p_set_authorized_oob_dev_pw_id(struct p2p_data *p2p, u16 dev_pw_id, int go_intent, const u8 *own_interface_addr); #endif /* P2P_H */ wpa-2.1/src/p2p/p2p_build.c000066400000000000000000000322251227414666700155020ustar00rootroot00000000000000/* * P2P - IE builder * Copyright (c) 2009-2010, Atheros Communications * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "includes.h" #include "common.h" #include "common/ieee802_11_defs.h" #include "wps/wps_i.h" #include "p2p_i.h" void p2p_buf_add_action_hdr(struct wpabuf *buf, u8 subtype, u8 dialog_token) { wpabuf_put_u8(buf, WLAN_ACTION_VENDOR_SPECIFIC); wpabuf_put_be24(buf, OUI_WFA); wpabuf_put_u8(buf, P2P_OUI_TYPE); wpabuf_put_u8(buf, subtype); /* OUI Subtype */ wpabuf_put_u8(buf, dialog_token); wpa_printf(MSG_DEBUG, "P2P: * Dialog Token: %d", dialog_token); } void p2p_buf_add_public_action_hdr(struct wpabuf *buf, u8 subtype, u8 dialog_token) { wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC); wpabuf_put_u8(buf, WLAN_PA_VENDOR_SPECIFIC); wpabuf_put_be24(buf, OUI_WFA); wpabuf_put_u8(buf, P2P_OUI_TYPE); wpabuf_put_u8(buf, subtype); /* OUI Subtype */ wpabuf_put_u8(buf, dialog_token); wpa_printf(MSG_DEBUG, "P2P: * Dialog Token: %d", dialog_token); } u8 * p2p_buf_add_ie_hdr(struct wpabuf *buf) { u8 *len; /* P2P IE header */ wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC); len = wpabuf_put(buf, 1); /* IE length to be filled */ wpabuf_put_be24(buf, OUI_WFA); wpabuf_put_u8(buf, P2P_OUI_TYPE); wpa_printf(MSG_DEBUG, "P2P: * P2P IE header"); return len; } void p2p_buf_update_ie_hdr(struct wpabuf *buf, u8 *len) { /* Update P2P IE Length */ *len = (u8 *) wpabuf_put(buf, 0) - len - 1; } void p2p_buf_add_capability(struct wpabuf *buf, u8 dev_capab, u8 group_capab) { /* P2P Capability */ wpabuf_put_u8(buf, P2P_ATTR_CAPABILITY); wpabuf_put_le16(buf, 2); wpabuf_put_u8(buf, dev_capab); /* Device Capabilities */ wpabuf_put_u8(buf, group_capab); /* Group Capabilities */ wpa_printf(MSG_DEBUG, "P2P: * Capability dev=%02x group=%02x", dev_capab, group_capab); } void p2p_buf_add_go_intent(struct wpabuf *buf, u8 go_intent) { /* Group Owner Intent */ wpabuf_put_u8(buf, P2P_ATTR_GROUP_OWNER_INTENT); wpabuf_put_le16(buf, 1); wpabuf_put_u8(buf, go_intent); wpa_printf(MSG_DEBUG, "P2P: * GO Intent: Intent %u Tie breaker %u", go_intent >> 1, go_intent & 0x01); } void p2p_buf_add_listen_channel(struct wpabuf *buf, const char *country, u8 reg_class, u8 channel) { /* Listen Channel */ wpabuf_put_u8(buf, P2P_ATTR_LISTEN_CHANNEL); wpabuf_put_le16(buf, 5); wpabuf_put_data(buf, country, 3); wpabuf_put_u8(buf, reg_class); /* Regulatory Class */ wpabuf_put_u8(buf, channel); /* Channel Number */ wpa_printf(MSG_DEBUG, "P2P: * Listen Channel: Regulatory Class %u " "Channel %u", reg_class, channel); } void p2p_buf_add_operating_channel(struct wpabuf *buf, const char *country, u8 reg_class, u8 channel) { /* Operating Channel */ wpabuf_put_u8(buf, P2P_ATTR_OPERATING_CHANNEL); wpabuf_put_le16(buf, 5); wpabuf_put_data(buf, country, 3); wpabuf_put_u8(buf, reg_class); /* Regulatory Class */ wpabuf_put_u8(buf, channel); /* Channel Number */ wpa_printf(MSG_DEBUG, "P2P: * Operating Channel: Regulatory Class %u " "Channel %u", reg_class, channel); } void p2p_buf_add_channel_list(struct wpabuf *buf, const char *country, struct p2p_channels *chan) { u8 *len; size_t i; /* Channel List */ wpabuf_put_u8(buf, P2P_ATTR_CHANNEL_LIST); len = wpabuf_put(buf, 2); /* IE length to be filled */ wpabuf_put_data(buf, country, 3); /* Country String */ for (i = 0; i < chan->reg_classes; i++) { struct p2p_reg_class *c = &chan->reg_class[i]; wpabuf_put_u8(buf, c->reg_class); wpabuf_put_u8(buf, c->channels); wpabuf_put_data(buf, c->channel, c->channels); } /* Update attribute length */ WPA_PUT_LE16(len, (u8 *) wpabuf_put(buf, 0) - len - 2); wpa_hexdump(MSG_DEBUG, "P2P: * Channel List", len + 2, (u8 *) wpabuf_put(buf, 0) - len - 2); } void p2p_buf_add_status(struct wpabuf *buf, u8 status) { /* Status */ wpabuf_put_u8(buf, P2P_ATTR_STATUS); wpabuf_put_le16(buf, 1); wpabuf_put_u8(buf, status); wpa_printf(MSG_DEBUG, "P2P: * Status: %d", status); } void p2p_buf_add_device_info(struct wpabuf *buf, struct p2p_data *p2p, struct p2p_device *peer) { u8 *len; u16 methods; size_t nlen, i; /* P2P Device Info */ wpabuf_put_u8(buf, P2P_ATTR_DEVICE_INFO); len = wpabuf_put(buf, 2); /* IE length to be filled */ /* P2P Device address */ wpabuf_put_data(buf, p2p->cfg->dev_addr, ETH_ALEN); /* Config Methods */ methods = 0; if (peer && peer->wps_method != WPS_NOT_READY) { if (peer->wps_method == WPS_PBC) methods |= WPS_CONFIG_PUSHBUTTON; else if (peer->wps_method == WPS_PIN_DISPLAY || peer->wps_method == WPS_PIN_KEYPAD) methods |= WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD; } else if (p2p->cfg->config_methods) { methods |= p2p->cfg->config_methods & (WPS_CONFIG_PUSHBUTTON | WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD); } else { methods |= WPS_CONFIG_PUSHBUTTON; methods |= WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD; } wpabuf_put_be16(buf, methods); /* Primary Device Type */ wpabuf_put_data(buf, p2p->cfg->pri_dev_type, sizeof(p2p->cfg->pri_dev_type)); /* Number of Secondary Device Types */ wpabuf_put_u8(buf, p2p->cfg->num_sec_dev_types); /* Secondary Device Type List */ for (i = 0; i < p2p->cfg->num_sec_dev_types; i++) wpabuf_put_data(buf, p2p->cfg->sec_dev_type[i], WPS_DEV_TYPE_LEN); /* Device Name */ nlen = p2p->cfg->dev_name ? os_strlen(p2p->cfg->dev_name) : 0; wpabuf_put_be16(buf, ATTR_DEV_NAME); wpabuf_put_be16(buf, nlen); wpabuf_put_data(buf, p2p->cfg->dev_name, nlen); /* Update attribute length */ WPA_PUT_LE16(len, (u8 *) wpabuf_put(buf, 0) - len - 2); wpa_printf(MSG_DEBUG, "P2P: * Device Info"); } void p2p_buf_add_device_id(struct wpabuf *buf, const u8 *dev_addr) { /* P2P Device ID */ wpabuf_put_u8(buf, P2P_ATTR_DEVICE_ID); wpabuf_put_le16(buf, ETH_ALEN); wpabuf_put_data(buf, dev_addr, ETH_ALEN); wpa_printf(MSG_DEBUG, "P2P: * Device ID: " MACSTR, MAC2STR(dev_addr)); } void p2p_buf_add_config_timeout(struct wpabuf *buf, u8 go_timeout, u8 client_timeout) { /* Configuration Timeout */ wpabuf_put_u8(buf, P2P_ATTR_CONFIGURATION_TIMEOUT); wpabuf_put_le16(buf, 2); wpabuf_put_u8(buf, go_timeout); wpabuf_put_u8(buf, client_timeout); wpa_printf(MSG_DEBUG, "P2P: * Configuration Timeout: GO %d (*10ms) " "client %d (*10ms)", go_timeout, client_timeout); } void p2p_buf_add_intended_addr(struct wpabuf *buf, const u8 *interface_addr) { /* Intended P2P Interface Address */ wpabuf_put_u8(buf, P2P_ATTR_INTENDED_INTERFACE_ADDR); wpabuf_put_le16(buf, ETH_ALEN); wpabuf_put_data(buf, interface_addr, ETH_ALEN); wpa_printf(MSG_DEBUG, "P2P: * Intended P2P Interface Address " MACSTR, MAC2STR(interface_addr)); } void p2p_buf_add_group_bssid(struct wpabuf *buf, const u8 *bssid) { /* P2P Group BSSID */ wpabuf_put_u8(buf, P2P_ATTR_GROUP_BSSID); wpabuf_put_le16(buf, ETH_ALEN); wpabuf_put_data(buf, bssid, ETH_ALEN); wpa_printf(MSG_DEBUG, "P2P: * P2P Group BSSID " MACSTR, MAC2STR(bssid)); } void p2p_buf_add_group_id(struct wpabuf *buf, const u8 *dev_addr, const u8 *ssid, size_t ssid_len) { /* P2P Group ID */ wpabuf_put_u8(buf, P2P_ATTR_GROUP_ID); wpabuf_put_le16(buf, ETH_ALEN + ssid_len); wpabuf_put_data(buf, dev_addr, ETH_ALEN); wpabuf_put_data(buf, ssid, ssid_len); wpa_printf(MSG_DEBUG, "P2P: * P2P Group ID " MACSTR, MAC2STR(dev_addr)); wpa_hexdump_ascii(MSG_DEBUG, "P2P: P2P Group ID SSID", ssid, ssid_len); } void p2p_buf_add_invitation_flags(struct wpabuf *buf, u8 flags) { /* Invitation Flags */ wpabuf_put_u8(buf, P2P_ATTR_INVITATION_FLAGS); wpabuf_put_le16(buf, 1); wpabuf_put_u8(buf, flags); wpa_printf(MSG_DEBUG, "P2P: * Invitation Flags: bitmap 0x%x", flags); } static void p2p_buf_add_noa_desc(struct wpabuf *buf, struct p2p_noa_desc *desc) { if (desc == NULL) return; wpabuf_put_u8(buf, desc->count_type); wpabuf_put_le32(buf, desc->duration); wpabuf_put_le32(buf, desc->interval); wpabuf_put_le32(buf, desc->start_time); } void p2p_buf_add_noa(struct wpabuf *buf, u8 noa_index, u8 opp_ps, u8 ctwindow, struct p2p_noa_desc *desc1, struct p2p_noa_desc *desc2) { /* Notice of Absence */ wpabuf_put_u8(buf, P2P_ATTR_NOTICE_OF_ABSENCE); wpabuf_put_le16(buf, 2 + (desc1 ? 13 : 0) + (desc2 ? 13 : 0)); wpabuf_put_u8(buf, noa_index); wpabuf_put_u8(buf, (opp_ps ? 0x80 : 0) | (ctwindow & 0x7f)); p2p_buf_add_noa_desc(buf, desc1); p2p_buf_add_noa_desc(buf, desc2); wpa_printf(MSG_DEBUG, "P2P: * Notice of Absence"); } void p2p_buf_add_ext_listen_timing(struct wpabuf *buf, u16 period, u16 interval) { /* Extended Listen Timing */ wpabuf_put_u8(buf, P2P_ATTR_EXT_LISTEN_TIMING); wpabuf_put_le16(buf, 4); wpabuf_put_le16(buf, period); wpabuf_put_le16(buf, interval); wpa_printf(MSG_DEBUG, "P2P: * Extended Listen Timing (period %u msec " "interval %u msec)", period, interval); } void p2p_buf_add_p2p_interface(struct wpabuf *buf, struct p2p_data *p2p) { /* P2P Interface */ wpabuf_put_u8(buf, P2P_ATTR_INTERFACE); wpabuf_put_le16(buf, ETH_ALEN + 1 + ETH_ALEN); /* P2P Device address */ wpabuf_put_data(buf, p2p->cfg->dev_addr, ETH_ALEN); /* * FIX: Fetch interface address list from driver. Do not include * the P2P Device address if it is never used as interface address. */ /* P2P Interface Address Count */ wpabuf_put_u8(buf, 1); wpabuf_put_data(buf, p2p->cfg->dev_addr, ETH_ALEN); } void p2p_buf_add_oob_go_neg_channel(struct wpabuf *buf, const char *country, u8 oper_class, u8 channel, enum p2p_role_indication role) { /* OOB Group Owner Negotiation Channel */ wpabuf_put_u8(buf, P2P_ATTR_OOB_GO_NEG_CHANNEL); wpabuf_put_le16(buf, 6); wpabuf_put_data(buf, country, 3); wpabuf_put_u8(buf, oper_class); /* Operating Class */ wpabuf_put_u8(buf, channel); /* Channel Number */ wpabuf_put_u8(buf, (u8) role); /* Role indication */ wpa_printf(MSG_DEBUG, "P2P: * OOB GO Negotiation Channel: Operating " "Class %u Channel %u Role %d", oper_class, channel, role); } static int p2p_add_wps_string(struct wpabuf *buf, enum wps_attribute attr, const char *val) { size_t len; len = val ? os_strlen(val) : 0; if (wpabuf_tailroom(buf) < 4 + len) return -1; wpabuf_put_be16(buf, attr); #ifndef CONFIG_WPS_STRICT if (len == 0) { /* * Some deployed WPS implementations fail to parse zeor-length * attributes. As a workaround, send a space character if the * device attribute string is empty. */ if (wpabuf_tailroom(buf) < 3) return -1; wpabuf_put_be16(buf, 1); wpabuf_put_u8(buf, ' '); return 0; } #endif /* CONFIG_WPS_STRICT */ wpabuf_put_be16(buf, len); if (val) wpabuf_put_data(buf, val, len); return 0; } int p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, int pw_id, int all_attr) { u8 *len; int i; if (wpabuf_tailroom(buf) < 6) return -1; wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC); len = wpabuf_put(buf, 1); wpabuf_put_be32(buf, WPS_DEV_OUI_WFA); if (wps_build_version(buf) < 0) return -1; if (all_attr) { if (wpabuf_tailroom(buf) < 5) return -1; wpabuf_put_be16(buf, ATTR_WPS_STATE); wpabuf_put_be16(buf, 1); wpabuf_put_u8(buf, WPS_STATE_NOT_CONFIGURED); } if (pw_id >= 0) { if (wpabuf_tailroom(buf) < 6) return -1; /* Device Password ID */ wpabuf_put_be16(buf, ATTR_DEV_PASSWORD_ID); wpabuf_put_be16(buf, 2); wpa_printf(MSG_DEBUG, "P2P: WPS IE Device Password ID: %d", pw_id); wpabuf_put_be16(buf, pw_id); } if (all_attr) { if (wpabuf_tailroom(buf) < 5) return -1; wpabuf_put_be16(buf, ATTR_RESPONSE_TYPE); wpabuf_put_be16(buf, 1); wpabuf_put_u8(buf, WPS_RESP_ENROLLEE_INFO); if (wps_build_uuid_e(buf, p2p->cfg->uuid) < 0 || p2p_add_wps_string(buf, ATTR_MANUFACTURER, p2p->cfg->manufacturer) < 0 || p2p_add_wps_string(buf, ATTR_MODEL_NAME, p2p->cfg->model_name) < 0 || p2p_add_wps_string(buf, ATTR_MODEL_NUMBER, p2p->cfg->model_number) < 0 || p2p_add_wps_string(buf, ATTR_SERIAL_NUMBER, p2p->cfg->serial_number) < 0) return -1; if (wpabuf_tailroom(buf) < 4 + WPS_DEV_TYPE_LEN) return -1; wpabuf_put_be16(buf, ATTR_PRIMARY_DEV_TYPE); wpabuf_put_be16(buf, WPS_DEV_TYPE_LEN); wpabuf_put_data(buf, p2p->cfg->pri_dev_type, WPS_DEV_TYPE_LEN); if (p2p_add_wps_string(buf, ATTR_DEV_NAME, p2p->cfg->dev_name) < 0) return -1; if (wpabuf_tailroom(buf) < 6) return -1; wpabuf_put_be16(buf, ATTR_CONFIG_METHODS); wpabuf_put_be16(buf, 2); wpabuf_put_be16(buf, p2p->cfg->config_methods); } if (wps_build_wfa_ext(buf, 0, NULL, 0) < 0) return -1; if (all_attr && p2p->cfg->num_sec_dev_types) { if (wpabuf_tailroom(buf) < 4 + WPS_DEV_TYPE_LEN * p2p->cfg->num_sec_dev_types) return -1; wpabuf_put_be16(buf, ATTR_SECONDARY_DEV_TYPE_LIST); wpabuf_put_be16(buf, WPS_DEV_TYPE_LEN * p2p->cfg->num_sec_dev_types); wpabuf_put_data(buf, p2p->cfg->sec_dev_type, WPS_DEV_TYPE_LEN * p2p->cfg->num_sec_dev_types); } /* Add the WPS vendor extensions */ for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) { if (p2p->wps_vendor_ext[i] == NULL) break; if (wpabuf_tailroom(buf) < 4 + wpabuf_len(p2p->wps_vendor_ext[i])) continue; wpabuf_put_be16(buf, ATTR_VENDOR_EXT); wpabuf_put_be16(buf, wpabuf_len(p2p->wps_vendor_ext[i])); wpabuf_put_buf(buf, p2p->wps_vendor_ext[i]); } p2p_buf_update_ie_hdr(buf, len); return 0; } wpa-2.1/src/p2p/p2p_dev_disc.c000066400000000000000000000207661227414666700161720ustar00rootroot00000000000000/* * Wi-Fi Direct - P2P Device Discoverability procedure * Copyright (c) 2010, Atheros Communications * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "includes.h" #include "common.h" #include "common/ieee802_11_defs.h" #include "p2p_i.h" #include "p2p.h" static struct wpabuf * p2p_build_dev_disc_req(struct p2p_data *p2p, struct p2p_device *go, const u8 *dev_id) { struct wpabuf *buf; u8 *len; buf = wpabuf_alloc(100); if (buf == NULL) return NULL; go->dialog_token++; if (go->dialog_token == 0) go->dialog_token = 1; p2p_buf_add_public_action_hdr(buf, P2P_DEV_DISC_REQ, go->dialog_token); len = p2p_buf_add_ie_hdr(buf); p2p_buf_add_device_id(buf, dev_id); p2p_buf_add_group_id(buf, go->info.p2p_device_addr, go->oper_ssid, go->oper_ssid_len); p2p_buf_update_ie_hdr(buf, len); return buf; } void p2p_dev_disc_req_cb(struct p2p_data *p2p, int success) { p2p_dbg(p2p, "Device Discoverability Request TX callback: success=%d", success); if (!success) { /* * Use P2P find, if needed, to find the other device or to * retry device discoverability. */ p2p_set_state(p2p, P2P_CONNECT); p2p_set_timeout(p2p, 0, 100000); return; } p2p_dbg(p2p, "GO acknowledged Device Discoverability Request - wait for response"); /* * TODO: is the remain-on-channel from Action frame TX long enough for * most cases or should we try to increase its duration and/or start * another remain-on-channel if needed once the previous one expires? */ } int p2p_send_dev_disc_req(struct p2p_data *p2p, struct p2p_device *dev) { struct p2p_device *go; struct wpabuf *req; go = p2p_get_device(p2p, dev->member_in_go_dev); if (go == NULL || dev->oper_freq <= 0) { p2p_dbg(p2p, "Could not find peer entry for GO and frequency to send Device Discoverability Request"); return -1; } req = p2p_build_dev_disc_req(p2p, go, dev->info.p2p_device_addr); if (req == NULL) return -1; p2p_dbg(p2p, "Sending Device Discoverability Request to GO " MACSTR " for client " MACSTR, MAC2STR(go->info.p2p_device_addr), MAC2STR(dev->info.p2p_device_addr)); p2p->pending_client_disc_go = go; os_memcpy(p2p->pending_client_disc_addr, dev->info.p2p_device_addr, ETH_ALEN); p2p->pending_action_state = P2P_PENDING_DEV_DISC_REQUEST; if (p2p_send_action(p2p, dev->oper_freq, go->info.p2p_device_addr, p2p->cfg->dev_addr, go->info.p2p_device_addr, wpabuf_head(req), wpabuf_len(req), 1000) < 0) { p2p_dbg(p2p, "Failed to send Action frame"); wpabuf_free(req); /* TODO: how to recover from failure? */ return -1; } wpabuf_free(req); return 0; } static struct wpabuf * p2p_build_dev_disc_resp(u8 dialog_token, u8 status) { struct wpabuf *buf; u8 *len; buf = wpabuf_alloc(100); if (buf == NULL) return NULL; p2p_buf_add_public_action_hdr(buf, P2P_DEV_DISC_RESP, dialog_token); len = p2p_buf_add_ie_hdr(buf); p2p_buf_add_status(buf, status); p2p_buf_update_ie_hdr(buf, len); return buf; } void p2p_dev_disc_resp_cb(struct p2p_data *p2p, int success) { p2p_dbg(p2p, "Device Discoverability Response TX callback: success=%d", success); p2p->cfg->send_action_done(p2p->cfg->cb_ctx); } static void p2p_send_dev_disc_resp(struct p2p_data *p2p, u8 dialog_token, const u8 *addr, int freq, u8 status) { struct wpabuf *resp; resp = p2p_build_dev_disc_resp(dialog_token, status); if (resp == NULL) return; p2p_dbg(p2p, "Sending Device Discoverability Response to " MACSTR " (status %u freq %d)", MAC2STR(addr), status, freq); p2p->pending_action_state = P2P_PENDING_DEV_DISC_RESPONSE; if (p2p_send_action(p2p, freq, addr, p2p->cfg->dev_addr, p2p->cfg->dev_addr, wpabuf_head(resp), wpabuf_len(resp), 200) < 0) { p2p_dbg(p2p, "Failed to send Action frame"); } wpabuf_free(resp); } void p2p_process_dev_disc_req(struct p2p_data *p2p, const u8 *sa, const u8 *data, size_t len, int rx_freq) { struct p2p_message msg; size_t g; p2p_dbg(p2p, "Received Device Discoverability Request from " MACSTR " (freq=%d)", MAC2STR(sa), rx_freq); if (p2p_parse(data, len, &msg)) return; if (msg.dialog_token == 0) { p2p_dbg(p2p, "Invalid Dialog Token 0 (must be nonzero) in Device Discoverability Request"); p2p_send_dev_disc_resp(p2p, msg.dialog_token, sa, rx_freq, P2P_SC_FAIL_INVALID_PARAMS); p2p_parse_free(&msg); return; } if (msg.device_id == NULL) { p2p_dbg(p2p, "P2P Device ID attribute missing from Device Discoverability Request"); p2p_send_dev_disc_resp(p2p, msg.dialog_token, sa, rx_freq, P2P_SC_FAIL_INVALID_PARAMS); p2p_parse_free(&msg); return; } for (g = 0; g < p2p->num_groups; g++) { if (p2p_group_go_discover(p2p->groups[g], msg.device_id, sa, rx_freq) == 0) { p2p_dbg(p2p, "Scheduled GO Discoverability Request for the target device"); /* * P2P group code will use a callback to indicate TX * status, so that we can reply to the request once the * target client has acknowledged the request or it has * timed out. */ p2p->pending_dev_disc_dialog_token = msg.dialog_token; os_memcpy(p2p->pending_dev_disc_addr, sa, ETH_ALEN); p2p->pending_dev_disc_freq = rx_freq; p2p_parse_free(&msg); return; } } p2p_dbg(p2p, "Requested client was not found in any group or did not support client discoverability"); p2p_send_dev_disc_resp(p2p, msg.dialog_token, sa, rx_freq, P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE); p2p_parse_free(&msg); } void p2p_process_dev_disc_resp(struct p2p_data *p2p, const u8 *sa, const u8 *data, size_t len) { struct p2p_message msg; struct p2p_device *go; u8 status; p2p_dbg(p2p, "Received Device Discoverability Response from " MACSTR, MAC2STR(sa)); go = p2p->pending_client_disc_go; if (go == NULL || os_memcmp(sa, go->info.p2p_device_addr, ETH_ALEN) != 0) { p2p_dbg(p2p, "Ignore unexpected Device Discoverability Response"); return; } if (p2p_parse(data, len, &msg)) return; if (msg.status == NULL) { p2p_parse_free(&msg); return; } if (msg.dialog_token != go->dialog_token) { p2p_dbg(p2p, "Ignore Device Discoverability Response with unexpected dialog token %u (expected %u)", msg.dialog_token, go->dialog_token); p2p_parse_free(&msg); return; } status = *msg.status; p2p_parse_free(&msg); p2p_dbg(p2p, "Device Discoverability Response status %u", status); if (p2p->go_neg_peer == NULL || os_memcmp(p2p->pending_client_disc_addr, p2p->go_neg_peer->info.p2p_device_addr, ETH_ALEN) != 0 || os_memcmp(p2p->go_neg_peer->member_in_go_dev, go->info.p2p_device_addr, ETH_ALEN) != 0) { p2p_dbg(p2p, "No pending operation with the client discoverability peer anymore"); return; } if (status == 0) { /* * Peer is expected to be awake for at least 100 TU; try to * connect immediately. */ p2p_dbg(p2p, "Client discoverability request succeeded"); if (p2p->state == P2P_CONNECT) { /* * Change state to force the timeout to start in * P2P_CONNECT again without going through the short * Listen state. */ p2p_set_state(p2p, P2P_CONNECT_LISTEN); p2p->cfg->send_action_done(p2p->cfg->cb_ctx); } p2p_set_timeout(p2p, 0, 0); } else { /* * Client discoverability request failed; try to connect from * timeout. */ p2p_dbg(p2p, "Client discoverability request failed"); p2p_set_timeout(p2p, 0, 500000); } } void p2p_go_disc_req_cb(struct p2p_data *p2p, int success) { p2p_dbg(p2p, "GO Discoverability Request TX callback: success=%d", success); p2p->cfg->send_action_done(p2p->cfg->cb_ctx); if (p2p->pending_dev_disc_dialog_token == 0) { p2p_dbg(p2p, "No pending Device Discoverability Request"); return; } p2p_send_dev_disc_resp(p2p, p2p->pending_dev_disc_dialog_token, p2p->pending_dev_disc_addr, p2p->pending_dev_disc_freq, success ? P2P_SC_SUCCESS : P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE); p2p->pending_dev_disc_dialog_token = 0; } void p2p_process_go_disc_req(struct p2p_data *p2p, const u8 *da, const u8 *sa, const u8 *data, size_t len, int rx_freq) { unsigned int tu; struct wpabuf *ies; p2p_dbg(p2p, "Received GO Discoverability Request - remain awake for 100 TU"); ies = p2p_build_probe_resp_ies(p2p); if (ies == NULL) return; /* Remain awake 100 TU on operating channel */ p2p->pending_client_disc_freq = rx_freq; tu = 100; if (p2p->cfg->start_listen(p2p->cfg->cb_ctx, rx_freq, 1024 * tu / 1000, ies) < 0) { p2p_dbg(p2p, "Failed to start listen mode for client discoverability"); } wpabuf_free(ies); } wpa-2.1/src/p2p/p2p_go_neg.c000066400000000000000000001076171227414666700156510ustar00rootroot00000000000000/* * Wi-Fi Direct - P2P Group Owner Negotiation * Copyright (c) 2009-2010, Atheros Communications * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "includes.h" #include "common.h" #include "common/ieee802_11_defs.h" #include "wps/wps_defs.h" #include "p2p_i.h" #include "p2p.h" static int p2p_go_det(u8 own_intent, u8 peer_value) { u8 peer_intent = peer_value >> 1; if (own_intent == peer_intent) { if (own_intent == P2P_MAX_GO_INTENT) return -1; /* both devices want to become GO */ /* Use tie breaker bit to determine GO */ return (peer_value & 0x01) ? 0 : 1; } return own_intent > peer_intent; } int p2p_peer_channels_check(struct p2p_data *p2p, struct p2p_channels *own, struct p2p_device *dev, const u8 *channel_list, size_t channel_list_len) { const u8 *pos, *end; struct p2p_channels *ch; size_t channels; struct p2p_channels intersection; ch = &dev->channels; os_memset(ch, 0, sizeof(*ch)); pos = channel_list; end = channel_list + channel_list_len; if (end - pos < 3) return -1; os_memcpy(dev->country, pos, 3); wpa_hexdump_ascii(MSG_DEBUG, "P2P: Peer country", pos, 3); if (pos[2] != 0x04 && os_memcmp(pos, p2p->cfg->country, 2) != 0) { p2p_info(p2p, "Mismatching country (ours=%c%c peer's=%c%c)", p2p->cfg->country[0], p2p->cfg->country[1], pos[0], pos[1]); return -1; } pos += 3; while (pos + 2 < end) { struct p2p_reg_class *cl = &ch->reg_class[ch->reg_classes]; cl->reg_class = *pos++; if (pos + 1 + pos[0] > end) { p2p_info(p2p, "Invalid peer Channel List"); return -1; } channels = *pos++; cl->channels = channels > P2P_MAX_REG_CLASS_CHANNELS ? P2P_MAX_REG_CLASS_CHANNELS : channels; os_memcpy(cl->channel, pos, cl->channels); pos += channels; ch->reg_classes++; if (ch->reg_classes == P2P_MAX_REG_CLASSES) break; } p2p_channels_intersect(own, &dev->channels, &intersection); p2p_dbg(p2p, "Own reg_classes %d peer reg_classes %d intersection reg_classes %d", (int) own->reg_classes, (int) dev->channels.reg_classes, (int) intersection.reg_classes); if (intersection.reg_classes == 0) { p2p_info(p2p, "No common channels found"); return -1; } return 0; } static int p2p_peer_channels(struct p2p_data *p2p, struct p2p_device *dev, const u8 *channel_list, size_t channel_list_len) { return p2p_peer_channels_check(p2p, &p2p->channels, dev, channel_list, channel_list_len); } u16 p2p_wps_method_pw_id(enum p2p_wps_method wps_method) { switch (wps_method) { case WPS_PIN_DISPLAY: return DEV_PW_REGISTRAR_SPECIFIED; case WPS_PIN_KEYPAD: return DEV_PW_USER_SPECIFIED; case WPS_PBC: return DEV_PW_PUSHBUTTON; case WPS_NFC: return DEV_PW_NFC_CONNECTION_HANDOVER; default: return DEV_PW_DEFAULT; } } static const char * p2p_wps_method_str(enum p2p_wps_method wps_method) { switch (wps_method) { case WPS_PIN_DISPLAY: return "Display"; case WPS_PIN_KEYPAD: return "Keypad"; case WPS_PBC: return "PBC"; case WPS_NFC: return "NFC"; default: return "??"; } } static struct wpabuf * p2p_build_go_neg_req(struct p2p_data *p2p, struct p2p_device *peer) { struct wpabuf *buf; u8 *len; u8 group_capab; size_t extra = 0; u16 pw_id; #ifdef CONFIG_WIFI_DISPLAY if (p2p->wfd_ie_go_neg) extra = wpabuf_len(p2p->wfd_ie_go_neg); #endif /* CONFIG_WIFI_DISPLAY */ buf = wpabuf_alloc(1000 + extra); if (buf == NULL) return NULL; p2p_buf_add_public_action_hdr(buf, P2P_GO_NEG_REQ, peer->dialog_token); len = p2p_buf_add_ie_hdr(buf); group_capab = 0; if (peer->flags & P2P_DEV_PREFER_PERSISTENT_GROUP) { group_capab |= P2P_GROUP_CAPAB_PERSISTENT_GROUP; if (peer->flags & P2P_DEV_PREFER_PERSISTENT_RECONN) group_capab |= P2P_GROUP_CAPAB_PERSISTENT_RECONN; } if (p2p->cross_connect) group_capab |= P2P_GROUP_CAPAB_CROSS_CONN; if (p2p->cfg->p2p_intra_bss) group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST; p2p_buf_add_capability(buf, p2p->dev_capab & ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, group_capab); p2p_buf_add_go_intent(buf, (p2p->go_intent << 1) | peer->tie_breaker); p2p_buf_add_config_timeout(buf, p2p->go_timeout, p2p->client_timeout); p2p_buf_add_listen_channel(buf, p2p->cfg->country, p2p->cfg->reg_class, p2p->cfg->channel); if (p2p->ext_listen_interval) p2p_buf_add_ext_listen_timing(buf, p2p->ext_listen_period, p2p->ext_listen_interval); p2p_buf_add_intended_addr(buf, p2p->intended_addr); p2p_buf_add_channel_list(buf, p2p->cfg->country, &p2p->channels); p2p_buf_add_device_info(buf, p2p, peer); p2p_buf_add_operating_channel(buf, p2p->cfg->country, p2p->op_reg_class, p2p->op_channel); p2p_buf_update_ie_hdr(buf, len); /* WPS IE with Device Password ID attribute */ pw_id = p2p_wps_method_pw_id(peer->wps_method); if (peer->oob_pw_id) pw_id = peer->oob_pw_id; if (p2p_build_wps_ie(p2p, buf, pw_id, 0) < 0) { p2p_dbg(p2p, "Failed to build WPS IE for GO Negotiation Request"); wpabuf_free(buf); return NULL; } #ifdef CONFIG_WIFI_DISPLAY if (p2p->wfd_ie_go_neg) wpabuf_put_buf(buf, p2p->wfd_ie_go_neg); #endif /* CONFIG_WIFI_DISPLAY */ return buf; } int p2p_connect_send(struct p2p_data *p2p, struct p2p_device *dev) { struct wpabuf *req; int freq; if (dev->flags & P2P_DEV_PD_BEFORE_GO_NEG) { u16 config_method; p2p_dbg(p2p, "Use PD-before-GO-Neg workaround for " MACSTR, MAC2STR(dev->info.p2p_device_addr)); if (dev->wps_method == WPS_PIN_DISPLAY) config_method = WPS_CONFIG_KEYPAD; else if (dev->wps_method == WPS_PIN_KEYPAD) config_method = WPS_CONFIG_DISPLAY; else if (dev->wps_method == WPS_PBC) config_method = WPS_CONFIG_PUSHBUTTON; else return -1; return p2p_prov_disc_req(p2p, dev->info.p2p_device_addr, config_method, 0, 0, 1); } freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq; if (dev->oob_go_neg_freq > 0) freq = dev->oob_go_neg_freq; if (freq <= 0) { p2p_dbg(p2p, "No Listen/Operating frequency known for the peer " MACSTR " to send GO Negotiation Request", MAC2STR(dev->info.p2p_device_addr)); return -1; } req = p2p_build_go_neg_req(p2p, dev); if (req == NULL) return -1; p2p_dbg(p2p, "Sending GO Negotiation Request"); p2p_set_state(p2p, P2P_CONNECT); p2p->pending_action_state = P2P_PENDING_GO_NEG_REQUEST; p2p->go_neg_peer = dev; dev->flags |= P2P_DEV_WAIT_GO_NEG_RESPONSE; dev->connect_reqs++; if (p2p_send_action(p2p, freq, dev->info.p2p_device_addr, p2p->cfg->dev_addr, dev->info.p2p_device_addr, wpabuf_head(req), wpabuf_len(req), 500) < 0) { p2p_dbg(p2p, "Failed to send Action frame"); /* Use P2P find to recover and retry */ p2p_set_timeout(p2p, 0, 0); } else dev->go_neg_req_sent++; wpabuf_free(req); return 0; } static struct wpabuf * p2p_build_go_neg_resp(struct p2p_data *p2p, struct p2p_device *peer, u8 dialog_token, u8 status, u8 tie_breaker) { struct wpabuf *buf; u8 *len; u8 group_capab; size_t extra = 0; u16 pw_id; p2p_dbg(p2p, "Building GO Negotiation Response"); #ifdef CONFIG_WIFI_DISPLAY if (p2p->wfd_ie_go_neg) extra = wpabuf_len(p2p->wfd_ie_go_neg); #endif /* CONFIG_WIFI_DISPLAY */ buf = wpabuf_alloc(1000 + extra); if (buf == NULL) return NULL; p2p_buf_add_public_action_hdr(buf, P2P_GO_NEG_RESP, dialog_token); len = p2p_buf_add_ie_hdr(buf); p2p_buf_add_status(buf, status); group_capab = 0; if (peer && peer->go_state == LOCAL_GO) { if (peer->flags & P2P_DEV_PREFER_PERSISTENT_GROUP) { group_capab |= P2P_GROUP_CAPAB_PERSISTENT_GROUP; if (peer->flags & P2P_DEV_PREFER_PERSISTENT_RECONN) group_capab |= P2P_GROUP_CAPAB_PERSISTENT_RECONN; } if (p2p->cross_connect) group_capab |= P2P_GROUP_CAPAB_CROSS_CONN; if (p2p->cfg->p2p_intra_bss) group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST; } p2p_buf_add_capability(buf, p2p->dev_capab & ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, group_capab); p2p_buf_add_go_intent(buf, (p2p->go_intent << 1) | tie_breaker); p2p_buf_add_config_timeout(buf, p2p->go_timeout, p2p->client_timeout); if (peer && peer->go_state == REMOTE_GO) { p2p_dbg(p2p, "Omit Operating Channel attribute"); } else { p2p_buf_add_operating_channel(buf, p2p->cfg->country, p2p->op_reg_class, p2p->op_channel); } p2p_buf_add_intended_addr(buf, p2p->intended_addr); if (status || peer == NULL) { p2p_buf_add_channel_list(buf, p2p->cfg->country, &p2p->channels); } else if (peer->go_state == REMOTE_GO) { p2p_buf_add_channel_list(buf, p2p->cfg->country, &p2p->channels); } else { struct p2p_channels res; p2p_channels_intersect(&p2p->channels, &peer->channels, &res); p2p_buf_add_channel_list(buf, p2p->cfg->country, &res); } p2p_buf_add_device_info(buf, p2p, peer); if (peer && peer->go_state == LOCAL_GO) { p2p_buf_add_group_id(buf, p2p->cfg->dev_addr, p2p->ssid, p2p->ssid_len); } p2p_buf_update_ie_hdr(buf, len); /* WPS IE with Device Password ID attribute */ pw_id = p2p_wps_method_pw_id(peer ? peer->wps_method : WPS_NOT_READY); if (peer && peer->oob_pw_id) pw_id = peer->oob_pw_id; if (p2p_build_wps_ie(p2p, buf, pw_id, 0) < 0) { p2p_dbg(p2p, "Failed to build WPS IE for GO Negotiation Response"); wpabuf_free(buf); return NULL; } #ifdef CONFIG_WIFI_DISPLAY if (p2p->wfd_ie_go_neg) wpabuf_put_buf(buf, p2p->wfd_ie_go_neg); #endif /* CONFIG_WIFI_DISPLAY */ return buf; } /** * p2p_reselect_channel - Re-select operating channel based on peer information * @p2p: P2P module context from p2p_init() * @intersection: Support channel list intersection from local and peer * * This function is used to re-select the best channel after having received * information from the peer to allow supported channel lists to be intersected. * This can be used to improve initial channel selection done in * p2p_prepare_channel() prior to the start of GO Negotiation. In addition, this * can be used for Invitation case. */ void p2p_reselect_channel(struct p2p_data *p2p, struct p2p_channels *intersection) { struct p2p_reg_class *cl; int freq; u8 op_reg_class, op_channel; unsigned int i; const int op_classes_5ghz[] = { 124, 115, 0 }; const int op_classes_ht40[] = { 126, 127, 116, 117, 0 }; const int op_classes_vht[] = { 128, 0 }; if (p2p->own_freq_preference > 0 && p2p_freq_to_channel(p2p->own_freq_preference, &op_reg_class, &op_channel) == 0 && p2p_channels_includes(intersection, op_reg_class, op_channel)) { p2p_dbg(p2p, "Pick own channel preference (reg_class %u channel %u) from intersection", op_reg_class, op_channel); p2p->op_reg_class = op_reg_class; p2p->op_channel = op_channel; return; } if (p2p->best_freq_overall > 0 && p2p_freq_to_channel(p2p->best_freq_overall, &op_reg_class, &op_channel) == 0 && p2p_channels_includes(intersection, op_reg_class, op_channel)) { p2p_dbg(p2p, "Pick best overall channel (reg_class %u channel %u) from intersection", op_reg_class, op_channel); p2p->op_reg_class = op_reg_class; p2p->op_channel = op_channel; return; } /* First, try to pick the best channel from another band */ freq = p2p_channel_to_freq(p2p->op_reg_class, p2p->op_channel); if (freq >= 2400 && freq < 2500 && p2p->best_freq_5 > 0 && !p2p_channels_includes(intersection, p2p->op_reg_class, p2p->op_channel) && p2p_freq_to_channel(p2p->best_freq_5, &op_reg_class, &op_channel) == 0 && p2p_channels_includes(intersection, op_reg_class, op_channel)) { p2p_dbg(p2p, "Pick best 5 GHz channel (reg_class %u channel %u) from intersection", op_reg_class, op_channel); p2p->op_reg_class = op_reg_class; p2p->op_channel = op_channel; return; } if (freq >= 4900 && freq < 6000 && p2p->best_freq_24 > 0 && !p2p_channels_includes(intersection, p2p->op_reg_class, p2p->op_channel) && p2p_freq_to_channel(p2p->best_freq_24, &op_reg_class, &op_channel) == 0 && p2p_channels_includes(intersection, op_reg_class, op_channel)) { p2p_dbg(p2p, "Pick best 2.4 GHz channel (reg_class %u channel %u) from intersection", op_reg_class, op_channel); p2p->op_reg_class = op_reg_class; p2p->op_channel = op_channel; return; } /* Select channel with highest preference if the peer supports it */ for (i = 0; p2p->cfg->pref_chan && i < p2p->cfg->num_pref_chan; i++) { if (p2p_channels_includes(intersection, p2p->cfg->pref_chan[i].op_class, p2p->cfg->pref_chan[i].chan)) { p2p->op_reg_class = p2p->cfg->pref_chan[i].op_class; p2p->op_channel = p2p->cfg->pref_chan[i].chan; p2p_dbg(p2p, "Pick highest preferred channel (op_class %u channel %u) from intersection", p2p->op_reg_class, p2p->op_channel); return; } } /* Try a channel where we might be able to use VHT */ if (p2p_channel_select(intersection, op_classes_vht, &p2p->op_reg_class, &p2p->op_channel) == 0) { p2p_dbg(p2p, "Pick possible VHT channel (op_class %u channel %u) from intersection", p2p->op_reg_class, p2p->op_channel); return; } /* Try a channel where we might be able to use HT40 */ if (p2p_channel_select(intersection, op_classes_ht40, &p2p->op_reg_class, &p2p->op_channel) == 0) { p2p_dbg(p2p, "Pick possible HT40 channel (op_class %u channel %u) from intersection", p2p->op_reg_class, p2p->op_channel); return; } /* Prefer a 5 GHz channel */ if (p2p_channel_select(intersection, op_classes_5ghz, &p2p->op_reg_class, &p2p->op_channel) == 0) { p2p_dbg(p2p, "Pick possible 5 GHz channel (op_class %u channel %u) from intersection", p2p->op_reg_class, p2p->op_channel); return; } /* * Try to see if the original channel is in the intersection. If * so, no need to change anything, as it already contains some * randomness. */ if (p2p_channels_includes(intersection, p2p->op_reg_class, p2p->op_channel)) { p2p_dbg(p2p, "Using original operating class and channel (op_class %u channel %u) from intersection", p2p->op_reg_class, p2p->op_channel); return; } /* * Fall back to whatever is included in the channel intersection since * no better options seems to be available. */ cl = &intersection->reg_class[0]; p2p_dbg(p2p, "Pick another channel (reg_class %u channel %u) from intersection", cl->reg_class, cl->channel[0]); p2p->op_reg_class = cl->reg_class; p2p->op_channel = cl->channel[0]; } static int p2p_go_select_channel(struct p2p_data *p2p, struct p2p_device *dev, u8 *status) { struct p2p_channels tmp, intersection; p2p_channels_dump(p2p, "own channels", &p2p->channels); p2p_channels_dump(p2p, "peer channels", &dev->channels); p2p_channels_intersect(&p2p->channels, &dev->channels, &tmp); p2p_channels_dump(p2p, "intersection", &tmp); p2p_channels_remove_freqs(&tmp, &p2p->no_go_freq); p2p_channels_dump(p2p, "intersection after no-GO removal", &tmp); p2p_channels_intersect(&tmp, &p2p->cfg->channels, &intersection); p2p_channels_dump(p2p, "intersection with local channel list", &intersection); if (intersection.reg_classes == 0 || intersection.reg_class[0].channels == 0) { *status = P2P_SC_FAIL_NO_COMMON_CHANNELS; p2p_dbg(p2p, "No common channels found"); return -1; } if (!p2p_channels_includes(&intersection, p2p->op_reg_class, p2p->op_channel)) { if (dev->flags & P2P_DEV_FORCE_FREQ) { *status = P2P_SC_FAIL_NO_COMMON_CHANNELS; p2p_dbg(p2p, "Peer does not support the forced channel"); return -1; } p2p_dbg(p2p, "Selected operating channel (op_class %u channel %u) not acceptable to the peer", p2p->op_reg_class, p2p->op_channel); p2p_reselect_channel(p2p, &intersection); } else if (!(dev->flags & P2P_DEV_FORCE_FREQ) && !p2p->cfg->cfg_op_channel) { p2p_dbg(p2p, "Try to optimize channel selection with peer information received; previously selected op_class %u channel %u", p2p->op_reg_class, p2p->op_channel); p2p_reselect_channel(p2p, &intersection); } if (!p2p->ssid_set) { p2p_build_ssid(p2p, p2p->ssid, &p2p->ssid_len); p2p->ssid_set = 1; } return 0; } void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa, const u8 *data, size_t len, int rx_freq) { struct p2p_device *dev = NULL; struct wpabuf *resp; struct p2p_message msg; u8 status = P2P_SC_FAIL_INVALID_PARAMS; int tie_breaker = 0; int freq; p2p_dbg(p2p, "Received GO Negotiation Request from " MACSTR "(freq=%d)", MAC2STR(sa), rx_freq); if (p2p_parse(data, len, &msg)) return; if (!msg.capability) { p2p_dbg(p2p, "Mandatory Capability attribute missing from GO Negotiation Request"); #ifdef CONFIG_P2P_STRICT goto fail; #endif /* CONFIG_P2P_STRICT */ } if (msg.go_intent) tie_breaker = *msg.go_intent & 0x01; else { p2p_dbg(p2p, "Mandatory GO Intent attribute missing from GO Negotiation Request"); #ifdef CONFIG_P2P_STRICT goto fail; #endif /* CONFIG_P2P_STRICT */ } if (!msg.config_timeout) { p2p_dbg(p2p, "Mandatory Configuration Timeout attribute missing from GO Negotiation Request"); #ifdef CONFIG_P2P_STRICT goto fail; #endif /* CONFIG_P2P_STRICT */ } if (!msg.listen_channel) { p2p_dbg(p2p, "No Listen Channel attribute received"); goto fail; } if (!msg.operating_channel) { p2p_dbg(p2p, "No Operating Channel attribute received"); goto fail; } if (!msg.channel_list) { p2p_dbg(p2p, "No Channel List attribute received"); goto fail; } if (!msg.intended_addr) { p2p_dbg(p2p, "No Intended P2P Interface Address attribute received"); goto fail; } if (!msg.p2p_device_info) { p2p_dbg(p2p, "No P2P Device Info attribute received"); goto fail; } if (os_memcmp(msg.p2p_device_addr, sa, ETH_ALEN) != 0) { p2p_dbg(p2p, "Unexpected GO Negotiation Request SA=" MACSTR " != dev_addr=" MACSTR, MAC2STR(sa), MAC2STR(msg.p2p_device_addr)); goto fail; } dev = p2p_get_device(p2p, sa); if (msg.status && *msg.status) { p2p_dbg(p2p, "Unexpected Status attribute (%d) in GO Negotiation Request", *msg.status); goto fail; } if (dev == NULL) dev = p2p_add_dev_from_go_neg_req(p2p, sa, &msg); else if (dev->flags & P2P_DEV_PROBE_REQ_ONLY) p2p_add_dev_info(p2p, sa, dev, &msg); else if (!dev->listen_freq && !dev->oper_freq) { /* * This may happen if the peer entry was added based on PD * Request and no Probe Request/Response frame has been received * from this peer (or that information has timed out). */ p2p_dbg(p2p, "Update peer " MACSTR " based on GO Neg Req since listen/oper freq not known", MAC2STR(dev->info.p2p_device_addr)); p2p_add_dev_info(p2p, sa, dev, &msg); } if (dev && dev->flags & P2P_DEV_USER_REJECTED) { p2p_dbg(p2p, "User has rejected this peer"); status = P2P_SC_FAIL_REJECTED_BY_USER; } else if (dev == NULL || (dev->wps_method == WPS_NOT_READY && (p2p->authorized_oob_dev_pw_id == 0 || p2p->authorized_oob_dev_pw_id != msg.dev_password_id))) { p2p_dbg(p2p, "Not ready for GO negotiation with " MACSTR, MAC2STR(sa)); status = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE; p2p->cfg->go_neg_req_rx(p2p->cfg->cb_ctx, sa, msg.dev_password_id); } else if (p2p->go_neg_peer && p2p->go_neg_peer != dev) { p2p_dbg(p2p, "Already in Group Formation with another peer"); status = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE; } else { int go; if (!p2p->go_neg_peer) { p2p_dbg(p2p, "Starting GO Negotiation with previously authorized peer"); if (!(dev->flags & P2P_DEV_FORCE_FREQ)) { p2p_dbg(p2p, "Use default channel settings"); p2p->op_reg_class = p2p->cfg->op_reg_class; p2p->op_channel = p2p->cfg->op_channel; os_memcpy(&p2p->channels, &p2p->cfg->channels, sizeof(struct p2p_channels)); } else { p2p_dbg(p2p, "Use previously configured forced channel settings"); } } dev->flags &= ~P2P_DEV_NOT_YET_READY; if (!msg.go_intent) { p2p_dbg(p2p, "No GO Intent attribute received"); goto fail; } if ((*msg.go_intent >> 1) > P2P_MAX_GO_INTENT) { p2p_dbg(p2p, "Invalid GO Intent value (%u) received", *msg.go_intent >> 1); goto fail; } if (dev->go_neg_req_sent && os_memcmp(sa, p2p->cfg->dev_addr, ETH_ALEN) > 0) { p2p_dbg(p2p, "Do not reply since peer has higher address and GO Neg Request already sent"); p2p_parse_free(&msg); return; } go = p2p_go_det(p2p->go_intent, *msg.go_intent); if (go < 0) { p2p_dbg(p2p, "Incompatible GO Intent"); status = P2P_SC_FAIL_BOTH_GO_INTENT_15; goto fail; } if (p2p_peer_channels(p2p, dev, msg.channel_list, msg.channel_list_len) < 0) { p2p_dbg(p2p, "No common channels found"); status = P2P_SC_FAIL_NO_COMMON_CHANNELS; goto fail; } switch (msg.dev_password_id) { case DEV_PW_REGISTRAR_SPECIFIED: p2p_dbg(p2p, "PIN from peer Display"); if (dev->wps_method != WPS_PIN_KEYPAD) { p2p_dbg(p2p, "We have wps_method=%s -> incompatible", p2p_wps_method_str(dev->wps_method)); status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD; goto fail; } break; case DEV_PW_USER_SPECIFIED: p2p_dbg(p2p, "Peer entered PIN on Keypad"); if (dev->wps_method != WPS_PIN_DISPLAY) { p2p_dbg(p2p, "We have wps_method=%s -> incompatible", p2p_wps_method_str(dev->wps_method)); status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD; goto fail; } break; case DEV_PW_PUSHBUTTON: p2p_dbg(p2p, "Peer using pushbutton"); if (dev->wps_method != WPS_PBC) { p2p_dbg(p2p, "We have wps_method=%s -> incompatible", p2p_wps_method_str(dev->wps_method)); status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD; goto fail; } break; default: if (msg.dev_password_id && msg.dev_password_id == dev->oob_pw_id) { p2p_dbg(p2p, "Peer using NFC"); if (dev->wps_method != WPS_NFC) { p2p_dbg(p2p, "We have wps_method=%s -> incompatible", p2p_wps_method_str( dev->wps_method)); status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD; goto fail; } break; } #ifdef CONFIG_WPS_NFC if (p2p->authorized_oob_dev_pw_id && msg.dev_password_id == p2p->authorized_oob_dev_pw_id) { p2p_dbg(p2p, "Using static handover with our device password from NFC Tag"); dev->wps_method = WPS_NFC; dev->oob_pw_id = p2p->authorized_oob_dev_pw_id; break; } #endif /* CONFIG_WPS_NFC */ p2p_dbg(p2p, "Unsupported Device Password ID %d", msg.dev_password_id); status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD; goto fail; } if (go && p2p_go_select_channel(p2p, dev, &status) < 0) goto fail; dev->go_state = go ? LOCAL_GO : REMOTE_GO; dev->oper_freq = p2p_channel_to_freq(msg.operating_channel[3], msg.operating_channel[4]); p2p_dbg(p2p, "Peer operating channel preference: %d MHz", dev->oper_freq); if (msg.config_timeout) { dev->go_timeout = msg.config_timeout[0]; dev->client_timeout = msg.config_timeout[1]; } p2p_dbg(p2p, "GO Negotiation with " MACSTR, MAC2STR(sa)); if (p2p->state != P2P_IDLE) p2p_stop_find_for_freq(p2p, rx_freq); p2p_set_state(p2p, P2P_GO_NEG); p2p_clear_timeout(p2p); dev->dialog_token = msg.dialog_token; os_memcpy(dev->intended_addr, msg.intended_addr, ETH_ALEN); p2p->go_neg_peer = dev; status = P2P_SC_SUCCESS; } fail: if (dev) dev->status = status; resp = p2p_build_go_neg_resp(p2p, dev, msg.dialog_token, status, !tie_breaker); p2p_parse_free(&msg); if (resp == NULL) return; p2p_dbg(p2p, "Sending GO Negotiation Response"); if (rx_freq > 0) freq = rx_freq; else freq = p2p_channel_to_freq(p2p->cfg->reg_class, p2p->cfg->channel); if (freq < 0) { p2p_dbg(p2p, "Unknown regulatory class/channel"); wpabuf_free(resp); return; } if (status == P2P_SC_SUCCESS) { p2p->pending_action_state = P2P_PENDING_GO_NEG_RESPONSE; dev->flags |= P2P_DEV_WAIT_GO_NEG_CONFIRM; if (os_memcmp(sa, p2p->cfg->dev_addr, ETH_ALEN) < 0) { /* * Peer has smaller address, so the GO Negotiation * Response from us is expected to complete * negotiation. Ignore a GO Negotiation Response from * the peer if it happens to be received after this * point due to a race condition in GO Negotiation * Request transmission and processing. */ dev->flags &= ~P2P_DEV_WAIT_GO_NEG_RESPONSE; } } else p2p->pending_action_state = P2P_PENDING_GO_NEG_RESPONSE_FAILURE; if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr, p2p->cfg->dev_addr, wpabuf_head(resp), wpabuf_len(resp), 500) < 0) { p2p_dbg(p2p, "Failed to send Action frame"); } wpabuf_free(resp); } static struct wpabuf * p2p_build_go_neg_conf(struct p2p_data *p2p, struct p2p_device *peer, u8 dialog_token, u8 status, const u8 *resp_chan, int go) { struct wpabuf *buf; u8 *len; struct p2p_channels res; u8 group_capab; size_t extra = 0; p2p_dbg(p2p, "Building GO Negotiation Confirm"); #ifdef CONFIG_WIFI_DISPLAY if (p2p->wfd_ie_go_neg) extra = wpabuf_len(p2p->wfd_ie_go_neg); #endif /* CONFIG_WIFI_DISPLAY */ buf = wpabuf_alloc(1000 + extra); if (buf == NULL) return NULL; p2p_buf_add_public_action_hdr(buf, P2P_GO_NEG_CONF, dialog_token); len = p2p_buf_add_ie_hdr(buf); p2p_buf_add_status(buf, status); group_capab = 0; if (peer->go_state == LOCAL_GO) { if (peer->flags & P2P_DEV_PREFER_PERSISTENT_GROUP) { group_capab |= P2P_GROUP_CAPAB_PERSISTENT_GROUP; if (peer->flags & P2P_DEV_PREFER_PERSISTENT_RECONN) group_capab |= P2P_GROUP_CAPAB_PERSISTENT_RECONN; } if (p2p->cross_connect) group_capab |= P2P_GROUP_CAPAB_CROSS_CONN; if (p2p->cfg->p2p_intra_bss) group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST; } p2p_buf_add_capability(buf, p2p->dev_capab & ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, group_capab); if (go || resp_chan == NULL) p2p_buf_add_operating_channel(buf, p2p->cfg->country, p2p->op_reg_class, p2p->op_channel); else p2p_buf_add_operating_channel(buf, (const char *) resp_chan, resp_chan[3], resp_chan[4]); p2p_channels_intersect(&p2p->channels, &peer->channels, &res); p2p_buf_add_channel_list(buf, p2p->cfg->country, &res); if (go) { p2p_buf_add_group_id(buf, p2p->cfg->dev_addr, p2p->ssid, p2p->ssid_len); } p2p_buf_update_ie_hdr(buf, len); #ifdef CONFIG_WIFI_DISPLAY if (p2p->wfd_ie_go_neg) wpabuf_put_buf(buf, p2p->wfd_ie_go_neg); #endif /* CONFIG_WIFI_DISPLAY */ return buf; } void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa, const u8 *data, size_t len, int rx_freq) { struct p2p_device *dev; struct wpabuf *conf; int go = -1; struct p2p_message msg; u8 status = P2P_SC_SUCCESS; int freq; p2p_dbg(p2p, "Received GO Negotiation Response from " MACSTR " (freq=%d)", MAC2STR(sa), rx_freq); dev = p2p_get_device(p2p, sa); if (dev == NULL || dev->wps_method == WPS_NOT_READY || dev != p2p->go_neg_peer) { p2p_dbg(p2p, "Not ready for GO negotiation with " MACSTR, MAC2STR(sa)); return; } if (p2p_parse(data, len, &msg)) return; if (!(dev->flags & P2P_DEV_WAIT_GO_NEG_RESPONSE)) { p2p_dbg(p2p, "Was not expecting GO Negotiation Response - ignore"); p2p_parse_free(&msg); return; } dev->flags &= ~P2P_DEV_WAIT_GO_NEG_RESPONSE; if (msg.dialog_token != dev->dialog_token) { p2p_dbg(p2p, "Unexpected Dialog Token %u (expected %u)", msg.dialog_token, dev->dialog_token); p2p_parse_free(&msg); return; } if (!msg.status) { p2p_dbg(p2p, "No Status attribute received"); status = P2P_SC_FAIL_INVALID_PARAMS; goto fail; } if (*msg.status) { p2p_dbg(p2p, "GO Negotiation rejected: status %d", *msg.status); dev->go_neg_req_sent = 0; if (*msg.status == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) { p2p_dbg(p2p, "Wait for the peer to become ready for GO Negotiation"); dev->flags |= P2P_DEV_NOT_YET_READY; dev->wait_count = 0; p2p_set_state(p2p, P2P_WAIT_PEER_IDLE); p2p_set_timeout(p2p, 0, 0); } else { p2p_dbg(p2p, "Stop GO Negotiation attempt"); p2p_go_neg_failed(p2p, dev, *msg.status); } p2p->cfg->send_action_done(p2p->cfg->cb_ctx); p2p_parse_free(&msg); return; } if (!msg.capability) { p2p_dbg(p2p, "Mandatory Capability attribute missing from GO Negotiation Response"); #ifdef CONFIG_P2P_STRICT status = P2P_SC_FAIL_INVALID_PARAMS; goto fail; #endif /* CONFIG_P2P_STRICT */ } if (!msg.p2p_device_info) { p2p_dbg(p2p, "Mandatory P2P Device Info attribute missing from GO Negotiation Response"); #ifdef CONFIG_P2P_STRICT status = P2P_SC_FAIL_INVALID_PARAMS; goto fail; #endif /* CONFIG_P2P_STRICT */ } if (!msg.intended_addr) { p2p_dbg(p2p, "No Intended P2P Interface Address attribute received"); status = P2P_SC_FAIL_INVALID_PARAMS; goto fail; } if (!msg.go_intent) { p2p_dbg(p2p, "No GO Intent attribute received"); status = P2P_SC_FAIL_INVALID_PARAMS; goto fail; } if ((*msg.go_intent >> 1) > P2P_MAX_GO_INTENT) { p2p_dbg(p2p, "Invalid GO Intent value (%u) received", *msg.go_intent >> 1); status = P2P_SC_FAIL_INVALID_PARAMS; goto fail; } go = p2p_go_det(p2p->go_intent, *msg.go_intent); if (go < 0) { p2p_dbg(p2p, "Incompatible GO Intent"); status = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; goto fail; } if (!go && msg.group_id) { /* Store SSID for Provisioning step */ p2p->ssid_len = msg.group_id_len - ETH_ALEN; os_memcpy(p2p->ssid, msg.group_id + ETH_ALEN, p2p->ssid_len); } else if (!go) { p2p_dbg(p2p, "Mandatory P2P Group ID attribute missing from GO Negotiation Response"); p2p->ssid_len = 0; status = P2P_SC_FAIL_INVALID_PARAMS; goto fail; } if (!msg.config_timeout) { p2p_dbg(p2p, "Mandatory Configuration Timeout attribute missing from GO Negotiation Response"); #ifdef CONFIG_P2P_STRICT status = P2P_SC_FAIL_INVALID_PARAMS; goto fail; #endif /* CONFIG_P2P_STRICT */ } else { dev->go_timeout = msg.config_timeout[0]; dev->client_timeout = msg.config_timeout[1]; } if (!msg.operating_channel && !go) { /* * Note: P2P Client may omit Operating Channel attribute to * indicate it does not have a preference. */ p2p_dbg(p2p, "No Operating Channel attribute received"); status = P2P_SC_FAIL_INVALID_PARAMS; goto fail; } if (!msg.channel_list) { p2p_dbg(p2p, "No Channel List attribute received"); status = P2P_SC_FAIL_INVALID_PARAMS; goto fail; } if (p2p_peer_channels(p2p, dev, msg.channel_list, msg.channel_list_len) < 0) { p2p_dbg(p2p, "No common channels found"); status = P2P_SC_FAIL_NO_COMMON_CHANNELS; goto fail; } if (msg.operating_channel) { dev->oper_freq = p2p_channel_to_freq(msg.operating_channel[3], msg.operating_channel[4]); p2p_dbg(p2p, "Peer operating channel preference: %d MHz", dev->oper_freq); } else dev->oper_freq = 0; switch (msg.dev_password_id) { case DEV_PW_REGISTRAR_SPECIFIED: p2p_dbg(p2p, "PIN from peer Display"); if (dev->wps_method != WPS_PIN_KEYPAD) { p2p_dbg(p2p, "We have wps_method=%s -> incompatible", p2p_wps_method_str(dev->wps_method)); status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD; goto fail; } break; case DEV_PW_USER_SPECIFIED: p2p_dbg(p2p, "Peer entered PIN on Keypad"); if (dev->wps_method != WPS_PIN_DISPLAY) { p2p_dbg(p2p, "We have wps_method=%s -> incompatible", p2p_wps_method_str(dev->wps_method)); status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD; goto fail; } break; case DEV_PW_PUSHBUTTON: p2p_dbg(p2p, "Peer using pushbutton"); if (dev->wps_method != WPS_PBC) { p2p_dbg(p2p, "We have wps_method=%s -> incompatible", p2p_wps_method_str(dev->wps_method)); status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD; goto fail; } break; default: if (msg.dev_password_id && msg.dev_password_id == dev->oob_pw_id) { p2p_dbg(p2p, "Peer using NFC"); if (dev->wps_method != WPS_NFC) { p2p_dbg(p2p, "We have wps_method=%s -> incompatible", p2p_wps_method_str(dev->wps_method)); status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD; goto fail; } break; } p2p_dbg(p2p, "Unsupported Device Password ID %d", msg.dev_password_id); status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD; goto fail; } if (go && p2p_go_select_channel(p2p, dev, &status) < 0) goto fail; p2p_set_state(p2p, P2P_GO_NEG); p2p_clear_timeout(p2p); p2p_dbg(p2p, "GO Negotiation with " MACSTR, MAC2STR(sa)); os_memcpy(dev->intended_addr, msg.intended_addr, ETH_ALEN); fail: conf = p2p_build_go_neg_conf(p2p, dev, msg.dialog_token, status, msg.operating_channel, go); p2p_parse_free(&msg); if (conf == NULL) return; p2p_dbg(p2p, "Sending GO Negotiation Confirm"); if (status == P2P_SC_SUCCESS) { p2p->pending_action_state = P2P_PENDING_GO_NEG_CONFIRM; dev->go_state = go ? LOCAL_GO : REMOTE_GO; } else p2p->pending_action_state = P2P_NO_PENDING_ACTION; if (rx_freq > 0) freq = rx_freq; else freq = dev->listen_freq; if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr, sa, wpabuf_head(conf), wpabuf_len(conf), 200) < 0) { p2p_dbg(p2p, "Failed to send Action frame"); p2p_go_neg_failed(p2p, dev, -1); p2p->cfg->send_action_done(p2p->cfg->cb_ctx); } wpabuf_free(conf); if (status != P2P_SC_SUCCESS) { p2p_dbg(p2p, "GO Negotiation failed"); p2p_go_neg_failed(p2p, dev, status); } } void p2p_process_go_neg_conf(struct p2p_data *p2p, const u8 *sa, const u8 *data, size_t len) { struct p2p_device *dev; struct p2p_message msg; p2p_dbg(p2p, "Received GO Negotiation Confirm from " MACSTR, MAC2STR(sa)); dev = p2p_get_device(p2p, sa); if (dev == NULL || dev->wps_method == WPS_NOT_READY || dev != p2p->go_neg_peer) { p2p_dbg(p2p, "Not ready for GO negotiation with " MACSTR, MAC2STR(sa)); return; } if (p2p->pending_action_state == P2P_PENDING_GO_NEG_RESPONSE) { p2p_dbg(p2p, "Stopped waiting for TX status on GO Negotiation Response since we already received Confirmation"); p2p->pending_action_state = P2P_NO_PENDING_ACTION; } if (p2p_parse(data, len, &msg)) return; if (!(dev->flags & P2P_DEV_WAIT_GO_NEG_CONFIRM)) { p2p_dbg(p2p, "Was not expecting GO Negotiation Confirm - ignore"); p2p_parse_free(&msg); return; } dev->flags &= ~P2P_DEV_WAIT_GO_NEG_CONFIRM; p2p->cfg->send_action_done(p2p->cfg->cb_ctx); if (msg.dialog_token != dev->dialog_token) { p2p_dbg(p2p, "Unexpected Dialog Token %u (expected %u)", msg.dialog_token, dev->dialog_token); p2p_parse_free(&msg); return; } if (!msg.status) { p2p_dbg(p2p, "No Status attribute received"); p2p_parse_free(&msg); return; } if (*msg.status) { p2p_dbg(p2p, "GO Negotiation rejected: status %d", *msg.status); p2p_go_neg_failed(p2p, dev, *msg.status); p2p_parse_free(&msg); return; } if (dev->go_state == REMOTE_GO && msg.group_id) { /* Store SSID for Provisioning step */ p2p->ssid_len = msg.group_id_len - ETH_ALEN; os_memcpy(p2p->ssid, msg.group_id + ETH_ALEN, p2p->ssid_len); } else if (dev->go_state == REMOTE_GO) { p2p_dbg(p2p, "Mandatory P2P Group ID attribute missing from GO Negotiation Confirmation"); p2p->ssid_len = 0; p2p_go_neg_failed(p2p, dev, P2P_SC_FAIL_INVALID_PARAMS); p2p_parse_free(&msg); return; } if (!msg.operating_channel) { p2p_dbg(p2p, "Mandatory Operating Channel attribute missing from GO Negotiation Confirmation"); #ifdef CONFIG_P2P_STRICT p2p_parse_free(&msg); return; #endif /* CONFIG_P2P_STRICT */ } else if (dev->go_state == REMOTE_GO) { int oper_freq = p2p_channel_to_freq(msg.operating_channel[3], msg.operating_channel[4]); if (oper_freq != dev->oper_freq) { p2p_dbg(p2p, "Updated peer (GO) operating channel preference from %d MHz to %d MHz", dev->oper_freq, oper_freq); dev->oper_freq = oper_freq; } } if (!msg.channel_list) { p2p_dbg(p2p, "Mandatory Operating Channel attribute missing from GO Negotiation Confirmation"); #ifdef CONFIG_P2P_STRICT p2p_parse_free(&msg); return; #endif /* CONFIG_P2P_STRICT */ } p2p_parse_free(&msg); if (dev->go_state == UNKNOWN_GO) { /* * This should not happen since GO negotiation has already * been completed. */ p2p_dbg(p2p, "Unexpected GO Neg state - do not know which end becomes GO"); return; } /* * The peer could have missed our ctrl::ack frame for GO Negotiation * Confirm and continue retransmitting the frame. To reduce the * likelihood of the peer not getting successful TX status for the * GO Negotiation Confirm frame, wait a short time here before starting * the group so that we will remain on the current channel to * acknowledge any possible retransmission from the peer. */ p2p_dbg(p2p, "20 ms wait on current channel before starting group"); os_sleep(0, 20000); p2p_go_complete(p2p, dev); } wpa-2.1/src/p2p/p2p_group.c000066400000000000000000000554751227414666700155530ustar00rootroot00000000000000/* * Wi-Fi Direct - P2P group operations * Copyright (c) 2009-2010, Atheros Communications * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "includes.h" #include "common.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" #include "wps/wps_defs.h" #include "wps/wps_i.h" #include "p2p_i.h" #include "p2p.h" struct p2p_group_member { struct p2p_group_member *next; u8 addr[ETH_ALEN]; /* P2P Interface Address */ u8 dev_addr[ETH_ALEN]; /* P2P Device Address */ struct wpabuf *p2p_ie; struct wpabuf *wfd_ie; struct wpabuf *client_info; u8 dev_capab; }; /** * struct p2p_group - Internal P2P module per-group data */ struct p2p_group { struct p2p_data *p2p; struct p2p_group_config *cfg; struct p2p_group_member *members; unsigned int num_members; int group_formation; int beacon_update; struct wpabuf *noa; struct wpabuf *wfd_ie; }; struct p2p_group * p2p_group_init(struct p2p_data *p2p, struct p2p_group_config *config) { struct p2p_group *group, **groups; group = os_zalloc(sizeof(*group)); if (group == NULL) return NULL; groups = os_realloc_array(p2p->groups, p2p->num_groups + 1, sizeof(struct p2p_group *)); if (groups == NULL) { os_free(group); return NULL; } groups[p2p->num_groups++] = group; p2p->groups = groups; group->p2p = p2p; group->cfg = config; group->group_formation = 1; group->beacon_update = 1; p2p_group_update_ies(group); group->cfg->idle_update(group->cfg->cb_ctx, 1); return group; } static void p2p_group_free_member(struct p2p_group_member *m) { wpabuf_free(m->wfd_ie); wpabuf_free(m->p2p_ie); wpabuf_free(m->client_info); os_free(m); } static void p2p_group_free_members(struct p2p_group *group) { struct p2p_group_member *m, *prev; m = group->members; group->members = NULL; group->num_members = 0; while (m) { prev = m; m = m->next; p2p_group_free_member(prev); } } void p2p_group_deinit(struct p2p_group *group) { size_t g; struct p2p_data *p2p; if (group == NULL) return; p2p = group->p2p; for (g = 0; g < p2p->num_groups; g++) { if (p2p->groups[g] == group) { while (g + 1 < p2p->num_groups) { p2p->groups[g] = p2p->groups[g + 1]; g++; } p2p->num_groups--; break; } } p2p_group_free_members(group); os_free(group->cfg); wpabuf_free(group->noa); wpabuf_free(group->wfd_ie); os_free(group); } static void p2p_client_info(struct wpabuf *ie, struct p2p_group_member *m) { if (m->client_info == NULL) return; if (wpabuf_tailroom(ie) < wpabuf_len(m->client_info) + 1) return; wpabuf_put_buf(ie, m->client_info); } static void p2p_group_add_common_ies(struct p2p_group *group, struct wpabuf *ie) { u8 dev_capab = group->p2p->dev_capab, group_capab = 0; /* P2P Capability */ dev_capab &= ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY; group_capab |= P2P_GROUP_CAPAB_GROUP_OWNER; if (group->cfg->persistent_group) { group_capab |= P2P_GROUP_CAPAB_PERSISTENT_GROUP; if (group->cfg->persistent_group == 2) group_capab |= P2P_GROUP_CAPAB_PERSISTENT_RECONN; } if (group->p2p->cfg->p2p_intra_bss) group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST; if (group->group_formation) group_capab |= P2P_GROUP_CAPAB_GROUP_FORMATION; if (group->p2p->cross_connect) group_capab |= P2P_GROUP_CAPAB_CROSS_CONN; if (group->num_members >= group->cfg->max_clients) group_capab |= P2P_GROUP_CAPAB_GROUP_LIMIT; group_capab |= P2P_GROUP_CAPAB_IP_ADDR_ALLOCATION; p2p_buf_add_capability(ie, dev_capab, group_capab); } static void p2p_group_add_noa(struct wpabuf *ie, struct wpabuf *noa) { if (noa == NULL) return; /* Notice of Absence */ wpabuf_put_u8(ie, P2P_ATTR_NOTICE_OF_ABSENCE); wpabuf_put_le16(ie, wpabuf_len(noa)); wpabuf_put_buf(ie, noa); } static struct wpabuf * p2p_group_encaps_probe_resp(struct wpabuf *subelems) { struct wpabuf *ie; const u8 *pos, *end; size_t len; if (subelems == NULL) return NULL; len = wpabuf_len(subelems) + 100; ie = wpabuf_alloc(len); if (ie == NULL) return NULL; pos = wpabuf_head(subelems); end = pos + wpabuf_len(subelems); while (end > pos) { size_t frag_len = end - pos; if (frag_len > 251) frag_len = 251; wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC); wpabuf_put_u8(ie, 4 + frag_len); wpabuf_put_be32(ie, P2P_IE_VENDOR_TYPE); wpabuf_put_data(ie, pos, frag_len); pos += frag_len; } return ie; } static struct wpabuf * p2p_group_build_beacon_ie(struct p2p_group *group) { struct wpabuf *ie; u8 *len; size_t extra = 0; #ifdef CONFIG_WIFI_DISPLAY if (group->p2p->wfd_ie_beacon) extra = wpabuf_len(group->p2p->wfd_ie_beacon); #endif /* CONFIG_WIFI_DISPLAY */ ie = wpabuf_alloc(257 + extra); if (ie == NULL) return NULL; #ifdef CONFIG_WIFI_DISPLAY if (group->p2p->wfd_ie_beacon) wpabuf_put_buf(ie, group->p2p->wfd_ie_beacon); #endif /* CONFIG_WIFI_DISPLAY */ len = p2p_buf_add_ie_hdr(ie); p2p_group_add_common_ies(group, ie); p2p_buf_add_device_id(ie, group->p2p->cfg->dev_addr); p2p_group_add_noa(ie, group->noa); p2p_buf_update_ie_hdr(ie, len); return ie; } #ifdef CONFIG_WIFI_DISPLAY struct wpabuf * p2p_group_get_wfd_ie(struct p2p_group *g) { return g->wfd_ie; } struct wpabuf * wifi_display_encaps(struct wpabuf *subelems) { struct wpabuf *ie; const u8 *pos, *end; if (subelems == NULL) return NULL; ie = wpabuf_alloc(wpabuf_len(subelems) + 100); if (ie == NULL) return NULL; pos = wpabuf_head(subelems); end = pos + wpabuf_len(subelems); while (end > pos) { size_t frag_len = end - pos; if (frag_len > 251) frag_len = 251; wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC); wpabuf_put_u8(ie, 4 + frag_len); wpabuf_put_be32(ie, WFD_IE_VENDOR_TYPE); wpabuf_put_data(ie, pos, frag_len); pos += frag_len; } return ie; } static int wifi_display_add_dev_info_descr(struct wpabuf *buf, struct p2p_group_member *m) { const u8 *pos, *end; const u8 *dev_info = NULL; const u8 *assoc_bssid = NULL; const u8 *coupled_sink = NULL; u8 zero_addr[ETH_ALEN]; if (m->wfd_ie == NULL) return 0; os_memset(zero_addr, 0, ETH_ALEN); pos = wpabuf_head_u8(m->wfd_ie); end = pos + wpabuf_len(m->wfd_ie); while (pos + 1 < end) { u8 id; u16 len; id = *pos++; len = WPA_GET_BE16(pos); pos += 2; if (pos + len > end) break; switch (id) { case WFD_SUBELEM_DEVICE_INFO: if (len < 6) break; dev_info = pos; break; case WFD_SUBELEM_ASSOCIATED_BSSID: if (len < ETH_ALEN) break; assoc_bssid = pos; break; case WFD_SUBELEM_COUPLED_SINK: if (len < 1 + ETH_ALEN) break; coupled_sink = pos; break; } pos += len; } if (dev_info == NULL) return 0; wpabuf_put_u8(buf, 23); wpabuf_put_data(buf, m->dev_addr, ETH_ALEN); if (assoc_bssid) wpabuf_put_data(buf, assoc_bssid, ETH_ALEN); else wpabuf_put_data(buf, zero_addr, ETH_ALEN); wpabuf_put_data(buf, dev_info, 2); /* WFD Device Info */ wpabuf_put_data(buf, dev_info + 4, 2); /* WFD Device Max Throughput */ if (coupled_sink) { wpabuf_put_data(buf, coupled_sink, 1 + ETH_ALEN); } else { wpabuf_put_u8(buf, 0); wpabuf_put_data(buf, zero_addr, ETH_ALEN); } return 1; } static struct wpabuf * wifi_display_build_go_ie(struct p2p_group *group) { struct wpabuf *wfd_subelems, *wfd_ie; struct p2p_group_member *m; u8 *len; unsigned int count = 0; if (!group->p2p->wfd_ie_probe_resp) return NULL; wfd_subelems = wpabuf_alloc(wpabuf_len(group->p2p->wfd_ie_probe_resp) + group->num_members * 24 + 100); if (wfd_subelems == NULL) return NULL; if (group->p2p->wfd_dev_info) wpabuf_put_buf(wfd_subelems, group->p2p->wfd_dev_info); if (group->p2p->wfd_assoc_bssid) wpabuf_put_buf(wfd_subelems, group->p2p->wfd_assoc_bssid); if (group->p2p->wfd_coupled_sink_info) wpabuf_put_buf(wfd_subelems, group->p2p->wfd_coupled_sink_info); /* Build WFD Session Info */ wpabuf_put_u8(wfd_subelems, WFD_SUBELEM_SESSION_INFO); len = wpabuf_put(wfd_subelems, 2); m = group->members; while (m) { if (wifi_display_add_dev_info_descr(wfd_subelems, m)) count++; m = m->next; } if (count == 0) { /* No Wi-Fi Display clients - do not include subelement */ wfd_subelems->used -= 3; } else { WPA_PUT_BE16(len, (u8 *) wpabuf_put(wfd_subelems, 0) - len - 2); p2p_dbg(group->p2p, "WFD: WFD Session Info: %u descriptors", count); } wfd_ie = wifi_display_encaps(wfd_subelems); wpabuf_free(wfd_subelems); return wfd_ie; } static void wifi_display_group_update(struct p2p_group *group) { wpabuf_free(group->wfd_ie); group->wfd_ie = wifi_display_build_go_ie(group); } #endif /* CONFIG_WIFI_DISPLAY */ void p2p_buf_add_group_info(struct p2p_group *group, struct wpabuf *buf, int max_clients) { u8 *group_info; int count = 0; struct p2p_group_member *m; p2p_dbg(group->p2p, "* P2P Group Info"); group_info = wpabuf_put(buf, 0); wpabuf_put_u8(buf, P2P_ATTR_GROUP_INFO); wpabuf_put_le16(buf, 0); /* Length to be filled */ for (m = group->members; m; m = m->next) { p2p_client_info(buf, m); count++; if (max_clients >= 0 && count >= max_clients) break; } WPA_PUT_LE16(group_info + 1, (u8 *) wpabuf_put(buf, 0) - group_info - 3); } void p2p_group_buf_add_id(struct p2p_group *group, struct wpabuf *buf) { p2p_buf_add_group_id(buf, group->p2p->cfg->dev_addr, group->cfg->ssid, group->cfg->ssid_len); } static struct wpabuf * p2p_group_build_probe_resp_ie(struct p2p_group *group) { struct wpabuf *p2p_subelems, *ie; p2p_subelems = wpabuf_alloc(500); if (p2p_subelems == NULL) return NULL; p2p_group_add_common_ies(group, p2p_subelems); p2p_group_add_noa(p2p_subelems, group->noa); /* P2P Device Info */ p2p_buf_add_device_info(p2p_subelems, group->p2p, NULL); /* P2P Group Info: Only when at least one P2P Client is connected */ if (group->members) p2p_buf_add_group_info(group, p2p_subelems, -1); ie = p2p_group_encaps_probe_resp(p2p_subelems); wpabuf_free(p2p_subelems); #ifdef CONFIG_WIFI_DISPLAY if (group->wfd_ie) { struct wpabuf *wfd = wpabuf_dup(group->wfd_ie); ie = wpabuf_concat(wfd, ie); } #endif /* CONFIG_WIFI_DISPLAY */ return ie; } void p2p_group_update_ies(struct p2p_group *group) { struct wpabuf *beacon_ie; struct wpabuf *probe_resp_ie; #ifdef CONFIG_WIFI_DISPLAY wifi_display_group_update(group); #endif /* CONFIG_WIFI_DISPLAY */ probe_resp_ie = p2p_group_build_probe_resp_ie(group); if (probe_resp_ie == NULL) return; wpa_hexdump_buf(MSG_MSGDUMP, "P2P: Update GO Probe Response P2P IE", probe_resp_ie); if (group->beacon_update) { beacon_ie = p2p_group_build_beacon_ie(group); if (beacon_ie) group->beacon_update = 0; wpa_hexdump_buf(MSG_MSGDUMP, "P2P: Update GO Beacon P2P IE", beacon_ie); } else beacon_ie = NULL; group->cfg->ie_update(group->cfg->cb_ctx, beacon_ie, probe_resp_ie); } /** * p2p_build_client_info - Build P2P Client Info Descriptor * @addr: MAC address of the peer device * @p2p_ie: P2P IE from (Re)Association Request * @dev_capab: Buffer for returning Device Capability * @dev_addr: Buffer for returning P2P Device Address * Returns: P2P Client Info Descriptor or %NULL on failure * * This function builds P2P Client Info Descriptor based on the information * available from (Re)Association Request frame. Group owner can use this to * build the P2P Group Info attribute for Probe Response frames. */ static struct wpabuf * p2p_build_client_info(const u8 *addr, struct wpabuf *p2p_ie, u8 *dev_capab, u8 *dev_addr) { const u8 *spos; struct p2p_message msg; u8 *len_pos; struct wpabuf *buf; if (p2p_ie == NULL) return NULL; os_memset(&msg, 0, sizeof(msg)); if (p2p_parse_p2p_ie(p2p_ie, &msg) || msg.capability == NULL || msg.p2p_device_info == NULL) return NULL; buf = wpabuf_alloc(ETH_ALEN + 1 + 1 + msg.p2p_device_info_len); if (buf == NULL) return NULL; *dev_capab = msg.capability[0]; os_memcpy(dev_addr, msg.p2p_device_addr, ETH_ALEN); spos = msg.p2p_device_info; /* P2P Device address */ /* P2P Client Info Descriptor */ /* Length to be set */ len_pos = wpabuf_put(buf, 1); /* P2P Device address */ wpabuf_put_data(buf, spos, ETH_ALEN); /* P2P Interface address */ wpabuf_put_data(buf, addr, ETH_ALEN); /* Device Capability Bitmap */ wpabuf_put_u8(buf, msg.capability[0]); /* * Config Methods, Primary Device Type, Number of Secondary Device * Types, Secondary Device Type List, Device Name copied from * Device Info */ wpabuf_put_data(buf, spos + ETH_ALEN, msg.p2p_device_info_len - ETH_ALEN); *len_pos = wpabuf_len(buf) - 1; return buf; } static int p2p_group_remove_member(struct p2p_group *group, const u8 *addr) { struct p2p_group_member *m, *prev; if (group == NULL) return 0; m = group->members; prev = NULL; while (m) { if (os_memcmp(m->addr, addr, ETH_ALEN) == 0) break; prev = m; m = m->next; } if (m == NULL) return 0; if (prev) prev->next = m->next; else group->members = m->next; p2p_group_free_member(m); group->num_members--; return 1; } int p2p_group_notif_assoc(struct p2p_group *group, const u8 *addr, const u8 *ie, size_t len) { struct p2p_group_member *m; if (group == NULL) return -1; p2p_add_device(group->p2p, addr, 0, NULL, 0, ie, len, 0); m = os_zalloc(sizeof(*m)); if (m == NULL) return -1; os_memcpy(m->addr, addr, ETH_ALEN); m->p2p_ie = ieee802_11_vendor_ie_concat(ie, len, P2P_IE_VENDOR_TYPE); if (m->p2p_ie) { m->client_info = p2p_build_client_info(addr, m->p2p_ie, &m->dev_capab, m->dev_addr); } #ifdef CONFIG_WIFI_DISPLAY m->wfd_ie = ieee802_11_vendor_ie_concat(ie, len, WFD_IE_VENDOR_TYPE); #endif /* CONFIG_WIFI_DISPLAY */ p2p_group_remove_member(group, addr); m->next = group->members; group->members = m; group->num_members++; p2p_dbg(group->p2p, "Add client " MACSTR " to group (p2p=%d wfd=%d client_info=%d); num_members=%u/%u", MAC2STR(addr), m->p2p_ie ? 1 : 0, m->wfd_ie ? 1 : 0, m->client_info ? 1 : 0, group->num_members, group->cfg->max_clients); if (group->num_members == group->cfg->max_clients) group->beacon_update = 1; p2p_group_update_ies(group); if (group->num_members == 1) group->cfg->idle_update(group->cfg->cb_ctx, 0); return 0; } struct wpabuf * p2p_group_assoc_resp_ie(struct p2p_group *group, u8 status) { struct wpabuf *resp; u8 *rlen; size_t extra = 0; #ifdef CONFIG_WIFI_DISPLAY if (group->wfd_ie) extra = wpabuf_len(group->wfd_ie); #endif /* CONFIG_WIFI_DISPLAY */ /* * (Re)Association Response - P2P IE * Status attribute (shall be present when association request is * denied) * Extended Listen Timing (may be present) */ resp = wpabuf_alloc(20 + extra); if (resp == NULL) return NULL; #ifdef CONFIG_WIFI_DISPLAY if (group->wfd_ie) wpabuf_put_buf(resp, group->wfd_ie); #endif /* CONFIG_WIFI_DISPLAY */ rlen = p2p_buf_add_ie_hdr(resp); if (status != P2P_SC_SUCCESS) p2p_buf_add_status(resp, status); p2p_buf_update_ie_hdr(resp, rlen); return resp; } void p2p_group_notif_disassoc(struct p2p_group *group, const u8 *addr) { if (p2p_group_remove_member(group, addr)) { p2p_dbg(group->p2p, "Remove client " MACSTR " from group; num_members=%u/%u", MAC2STR(addr), group->num_members, group->cfg->max_clients); if (group->num_members == group->cfg->max_clients - 1) group->beacon_update = 1; p2p_group_update_ies(group); if (group->num_members == 0) group->cfg->idle_update(group->cfg->cb_ctx, 1); } } /** * p2p_match_dev_type_member - Match client device type with requested type * @m: Group member * @wps: WPS TLVs from Probe Request frame (concatenated WPS IEs) * Returns: 1 on match, 0 on mismatch * * This function can be used to match the Requested Device Type attribute in * WPS IE with the device types of a group member for deciding whether a GO * should reply to a Probe Request frame. */ static int p2p_match_dev_type_member(struct p2p_group_member *m, struct wpabuf *wps) { const u8 *pos, *end; struct wps_parse_attr attr; u8 num_sec; if (m->client_info == NULL || wps == NULL) return 0; pos = wpabuf_head(m->client_info); end = pos + wpabuf_len(m->client_info); pos += 1 + 2 * ETH_ALEN + 1 + 2; if (end - pos < WPS_DEV_TYPE_LEN + 1) return 0; if (wps_parse_msg(wps, &attr)) return 1; /* assume no Requested Device Type attributes */ if (attr.num_req_dev_type == 0) return 1; /* no Requested Device Type attributes -> match */ if (dev_type_list_match(pos, attr.req_dev_type, attr.num_req_dev_type)) return 1; /* Match with client Primary Device Type */ pos += WPS_DEV_TYPE_LEN; num_sec = *pos++; if (end - pos < num_sec * WPS_DEV_TYPE_LEN) return 0; while (num_sec > 0) { num_sec--; if (dev_type_list_match(pos, attr.req_dev_type, attr.num_req_dev_type)) return 1; /* Match with client Secondary Device Type */ pos += WPS_DEV_TYPE_LEN; } /* No matching device type found */ return 0; } int p2p_group_match_dev_type(struct p2p_group *group, struct wpabuf *wps) { struct p2p_group_member *m; if (p2p_match_dev_type(group->p2p, wps)) return 1; /* Match with own device type */ for (m = group->members; m; m = m->next) { if (p2p_match_dev_type_member(m, wps)) return 1; /* Match with group client device type */ } /* No match with Requested Device Type */ return 0; } int p2p_group_match_dev_id(struct p2p_group *group, struct wpabuf *p2p) { struct p2p_group_member *m; struct p2p_message msg; os_memset(&msg, 0, sizeof(msg)); if (p2p_parse_p2p_ie(p2p, &msg)) return 1; /* Failed to parse - assume no filter on Device ID */ if (!msg.device_id) return 1; /* No filter on Device ID */ if (os_memcmp(msg.device_id, group->p2p->cfg->dev_addr, ETH_ALEN) == 0) return 1; /* Match with our P2P Device Address */ for (m = group->members; m; m = m->next) { if (os_memcmp(msg.device_id, m->dev_addr, ETH_ALEN) == 0) return 1; /* Match with group client P2P Device Address */ } /* No match with Device ID */ return 0; } void p2p_group_notif_formation_done(struct p2p_group *group) { if (group == NULL) return; group->group_formation = 0; group->beacon_update = 1; p2p_group_update_ies(group); } int p2p_group_notif_noa(struct p2p_group *group, const u8 *noa, size_t noa_len) { if (noa == NULL) { wpabuf_free(group->noa); group->noa = NULL; } else { if (group->noa) { if (wpabuf_size(group->noa) >= noa_len) { group->noa->used = 0; wpabuf_put_data(group->noa, noa, noa_len); } else { wpabuf_free(group->noa); group->noa = NULL; } } if (!group->noa) { group->noa = wpabuf_alloc_copy(noa, noa_len); if (group->noa == NULL) return -1; } } group->beacon_update = 1; p2p_group_update_ies(group); return 0; } static struct p2p_group_member * p2p_group_get_client(struct p2p_group *group, const u8 *dev_id) { struct p2p_group_member *m; for (m = group->members; m; m = m->next) { if (os_memcmp(dev_id, m->dev_addr, ETH_ALEN) == 0) return m; } return NULL; } static struct p2p_group_member * p2p_group_get_client_iface( struct p2p_group *group, const u8 *interface_addr) { struct p2p_group_member *m; for (m = group->members; m; m = m->next) { if (os_memcmp(interface_addr, m->addr, ETH_ALEN) == 0) return m; } return NULL; } const u8 * p2p_group_get_dev_addr(struct p2p_group *group, const u8 *addr) { struct p2p_group_member *m; if (group == NULL) return NULL; m = p2p_group_get_client_iface(group, addr); if (m && !is_zero_ether_addr(m->dev_addr)) return m->dev_addr; return NULL; } static struct wpabuf * p2p_build_go_disc_req(void) { struct wpabuf *buf; buf = wpabuf_alloc(100); if (buf == NULL) return NULL; p2p_buf_add_action_hdr(buf, P2P_GO_DISC_REQ, 0); return buf; } int p2p_group_go_discover(struct p2p_group *group, const u8 *dev_id, const u8 *searching_dev, int rx_freq) { struct p2p_group_member *m; struct wpabuf *req; struct p2p_data *p2p = group->p2p; int freq; m = p2p_group_get_client(group, dev_id); if (m == NULL || m->client_info == NULL) { p2p_dbg(group->p2p, "Requested client was not in this group " MACSTR, MAC2STR(group->cfg->interface_addr)); return -1; } if (!(m->dev_capab & P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY)) { p2p_dbg(group->p2p, "Requested client does not support client discoverability"); return -1; } p2p_dbg(group->p2p, "Schedule GO Discoverability Request to be sent to " MACSTR, MAC2STR(dev_id)); req = p2p_build_go_disc_req(); if (req == NULL) return -1; /* TODO: Should really use group operating frequency here */ freq = rx_freq; p2p->pending_action_state = P2P_PENDING_GO_DISC_REQ; if (p2p->cfg->send_action(p2p->cfg->cb_ctx, freq, m->addr, group->cfg->interface_addr, group->cfg->interface_addr, wpabuf_head(req), wpabuf_len(req), 200) < 0) { p2p_dbg(p2p, "Failed to send Action frame"); } wpabuf_free(req); return 0; } const u8 * p2p_group_get_interface_addr(struct p2p_group *group) { return group->cfg->interface_addr; } u8 p2p_group_presence_req(struct p2p_group *group, const u8 *client_interface_addr, const u8 *noa, size_t noa_len) { struct p2p_group_member *m; u8 curr_noa[50]; int curr_noa_len; m = p2p_group_get_client_iface(group, client_interface_addr); if (m == NULL || m->client_info == NULL) { p2p_dbg(group->p2p, "Client was not in this group"); return P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE; } wpa_hexdump(MSG_DEBUG, "P2P: Presence Request NoA", noa, noa_len); if (group->p2p->cfg->get_noa) curr_noa_len = group->p2p->cfg->get_noa( group->p2p->cfg->cb_ctx, group->cfg->interface_addr, curr_noa, sizeof(curr_noa)); else curr_noa_len = -1; if (curr_noa_len < 0) p2p_dbg(group->p2p, "Failed to fetch current NoA"); else if (curr_noa_len == 0) p2p_dbg(group->p2p, "No NoA being advertized"); else wpa_hexdump(MSG_DEBUG, "P2P: Current NoA", curr_noa, curr_noa_len); /* TODO: properly process request and store copy */ if (curr_noa_len > 0 || curr_noa_len == -1) return P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE; return P2P_SC_SUCCESS; } unsigned int p2p_get_group_num_members(struct p2p_group *group) { return group->num_members; } const u8 * p2p_iterate_group_members(struct p2p_group *group, void **next) { struct p2p_group_member *iter = *next; if (!iter) iter = group->members; else iter = iter->next; *next = iter; if (!iter) return NULL; return iter->addr; } int p2p_group_is_client_connected(struct p2p_group *group, const u8 *dev_addr) { struct p2p_group_member *m; for (m = group->members; m; m = m->next) { if (os_memcmp(m->dev_addr, dev_addr, ETH_ALEN) == 0) return 1; } return 0; } int p2p_group_is_group_id_match(struct p2p_group *group, const u8 *group_id, size_t group_id_len) { if (group_id_len != ETH_ALEN + group->cfg->ssid_len) return 0; if (os_memcmp(group_id, group->p2p->cfg->dev_addr, ETH_ALEN) != 0) return 0; return os_memcmp(group_id + ETH_ALEN, group->cfg->ssid, group->cfg->ssid_len) == 0; } void p2p_group_force_beacon_update_ies(struct p2p_group *group) { group->beacon_update = 1; p2p_group_update_ies(group); } int p2p_group_get_freq(struct p2p_group *group) { return group->cfg->freq; } wpa-2.1/src/p2p/p2p_i.h000066400000000000000000000515171227414666700146450ustar00rootroot00000000000000/* * P2P - Internal definitions for P2P module * Copyright (c) 2009-2010, Atheros Communications * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef P2P_I_H #define P2P_I_H #include "utils/list.h" #include "p2p.h" enum p2p_role_indication; enum p2p_go_state { UNKNOWN_GO, LOCAL_GO, REMOTE_GO }; /** * struct p2p_device - P2P Device data (internal to P2P module) */ struct p2p_device { struct dl_list list; struct os_reltime last_seen; int listen_freq; int oob_go_neg_freq; enum p2p_wps_method wps_method; u16 oob_pw_id; struct p2p_peer_info info; /* * If the peer was discovered based on an interface address (e.g., GO * from Beacon/Probe Response), the interface address is stored here. * p2p_device_addr must still be set in such a case to the unique * identifier for the P2P Device. */ u8 interface_addr[ETH_ALEN]; /* * P2P Device Address of the GO in whose group this P2P Device is a * client. */ u8 member_in_go_dev[ETH_ALEN]; /* * P2P Interface Address of the GO in whose group this P2P Device is a * client. */ u8 member_in_go_iface[ETH_ALEN]; int go_neg_req_sent; enum p2p_go_state go_state; u8 dialog_token; u8 tie_breaker; u8 intended_addr[ETH_ALEN]; char country[3]; struct p2p_channels channels; int oper_freq; u8 oper_ssid[32]; size_t oper_ssid_len; /** * req_config_methods - Pending provision discovery methods */ u16 req_config_methods; /** * wps_prov_info - Stored provisioning WPS config method * * This is used to store pending WPS config method between Provisioning * Discovery and connection to a running group. */ u16 wps_prov_info; #define P2P_DEV_PROBE_REQ_ONLY BIT(0) #define P2P_DEV_REPORTED BIT(1) #define P2P_DEV_NOT_YET_READY BIT(2) #define P2P_DEV_SD_INFO BIT(3) #define P2P_DEV_SD_SCHEDULE BIT(4) #define P2P_DEV_PD_PEER_DISPLAY BIT(5) #define P2P_DEV_PD_PEER_KEYPAD BIT(6) #define P2P_DEV_USER_REJECTED BIT(7) #define P2P_DEV_PEER_WAITING_RESPONSE BIT(8) #define P2P_DEV_PREFER_PERSISTENT_GROUP BIT(9) #define P2P_DEV_WAIT_GO_NEG_RESPONSE BIT(10) #define P2P_DEV_WAIT_GO_NEG_CONFIRM BIT(11) #define P2P_DEV_GROUP_CLIENT_ONLY BIT(12) #define P2P_DEV_FORCE_FREQ BIT(13) #define P2P_DEV_PD_FOR_JOIN BIT(14) #define P2P_DEV_REPORTED_ONCE BIT(15) #define P2P_DEV_PREFER_PERSISTENT_RECONN BIT(16) #define P2P_DEV_PD_BEFORE_GO_NEG BIT(17) #define P2P_DEV_NO_PREF_CHAN BIT(18) unsigned int flags; int status; /* enum p2p_status_code */ unsigned int wait_count; unsigned int connect_reqs; unsigned int invitation_reqs; u16 ext_listen_period; u16 ext_listen_interval; u8 go_timeout; u8 client_timeout; }; struct p2p_sd_query { struct p2p_sd_query *next; u8 peer[ETH_ALEN]; int for_all_peers; int wsd; /* Wi-Fi Display Service Discovery Request */ struct wpabuf *tlvs; }; struct p2p_pending_action_tx { unsigned int freq; u8 dst[ETH_ALEN]; u8 src[ETH_ALEN]; u8 bssid[ETH_ALEN]; size_t len; unsigned int wait_time; /* Followed by len octets of the frame */ }; /** * struct p2p_data - P2P module data (internal to P2P module) */ struct p2p_data { /** * cfg - P2P module configuration * * This is included in the same memory allocation with the * struct p2p_data and as such, must not be freed separately. */ struct p2p_config *cfg; /** * state - The current P2P state */ enum p2p_state { /** * P2P_IDLE - Idle */ P2P_IDLE, /** * P2P_SEARCH - Search (Device Discovery) */ P2P_SEARCH, /** * P2P_CONNECT - Trying to start GO Negotiation */ P2P_CONNECT, /** * P2P_CONNECT_LISTEN - Listen during GO Negotiation start */ P2P_CONNECT_LISTEN, /** * P2P_GO_NEG - In GO Negotiation */ P2P_GO_NEG, /** * P2P_LISTEN_ONLY - Listen only */ P2P_LISTEN_ONLY, /** * P2P_WAIT_PEER_CONNECT - Waiting peer in List for GO Neg */ P2P_WAIT_PEER_CONNECT, /** * P2P_WAIT_PEER_IDLE - Waiting peer idle for GO Neg */ P2P_WAIT_PEER_IDLE, /** * P2P_SD_DURING_FIND - Service Discovery during find */ P2P_SD_DURING_FIND, /** * P2P_PROVISIONING - Provisioning (during group formation) */ P2P_PROVISIONING, /** * P2P_PD_DURING_FIND - Provision Discovery during find */ P2P_PD_DURING_FIND, /** * P2P_INVITE - Trying to start Invite */ P2P_INVITE, /** * P2P_INVITE_LISTEN - Listen during Invite */ P2P_INVITE_LISTEN, } state; /** * min_disc_int - minDiscoverableInterval */ int min_disc_int; /** * max_disc_int - maxDiscoverableInterval */ int max_disc_int; /** * max_disc_tu - Maximum number of TUs for discoverable interval */ int max_disc_tu; /** * devices - List of known P2P Device peers */ struct dl_list devices; /** * go_neg_peer - Pointer to GO Negotiation peer */ struct p2p_device *go_neg_peer; /** * invite_peer - Pointer to Invite peer */ struct p2p_device *invite_peer; const u8 *invite_go_dev_addr; u8 invite_go_dev_addr_buf[ETH_ALEN]; int invite_dev_pw_id; /** * sd_peer - Pointer to Service Discovery peer */ struct p2p_device *sd_peer; /** * sd_query - Pointer to Service Discovery query */ struct p2p_sd_query *sd_query; /* GO Negotiation data */ /** * intended_addr - Local Intended P2P Interface Address * * This address is used during group owner negotiation as the Intended * P2P Interface Address and the group interface will be created with * address as the local address in case of successfully completed * negotiation. */ u8 intended_addr[ETH_ALEN]; /** * go_intent - Local GO Intent to be used during GO Negotiation */ u8 go_intent; /** * next_tie_breaker - Next tie-breaker value to use in GO Negotiation */ u8 next_tie_breaker; /** * ssid - Selected SSID for GO Negotiation (if local end will be GO) */ u8 ssid[32]; /** * ssid_len - ssid length in octets */ size_t ssid_len; /** * ssid_set - Whether SSID is already set for GO Negotiation */ int ssid_set; /** * Regulatory class for own operational channel */ u8 op_reg_class; /** * op_channel - Own operational channel */ u8 op_channel; /** * channels - Own supported regulatory classes and channels * * List of supposerted channels per regulatory class. The regulatory * classes are defined in IEEE Std 802.11-2007 Annex J and the * numbering of the clases depends on the configured country code. */ struct p2p_channels channels; struct wpa_freq_range_list no_go_freq; enum p2p_pending_action_state { P2P_NO_PENDING_ACTION, P2P_PENDING_GO_NEG_REQUEST, P2P_PENDING_GO_NEG_RESPONSE, P2P_PENDING_GO_NEG_RESPONSE_FAILURE, P2P_PENDING_GO_NEG_CONFIRM, P2P_PENDING_SD, P2P_PENDING_PD, P2P_PENDING_INVITATION_REQUEST, P2P_PENDING_INVITATION_RESPONSE, P2P_PENDING_DEV_DISC_REQUEST, P2P_PENDING_DEV_DISC_RESPONSE, P2P_PENDING_GO_DISC_REQ } pending_action_state; unsigned int pending_listen_freq; unsigned int pending_listen_sec; unsigned int pending_listen_usec; u8 dev_capab; int in_listen; int drv_in_listen; /** * sd_queries - Pending service discovery queries */ struct p2p_sd_query *sd_queries; /** * srv_update_indic - Service Update Indicator for local services */ u16 srv_update_indic; struct wpabuf *sd_resp; /* Fragmented SD response */ u8 sd_resp_addr[ETH_ALEN]; u8 sd_resp_dialog_token; size_t sd_resp_pos; /* Offset in sd_resp */ u8 sd_frag_id; struct wpabuf *sd_rx_resp; /* Reassembled SD response */ u16 sd_rx_update_indic; /* P2P Invitation data */ enum p2p_invite_role inv_role; u8 inv_bssid[ETH_ALEN]; int inv_bssid_set; u8 inv_ssid[32]; size_t inv_ssid_len; u8 inv_sa[ETH_ALEN]; u8 inv_group_bssid[ETH_ALEN]; u8 *inv_group_bssid_ptr; u8 inv_go_dev_addr[ETH_ALEN]; u8 inv_status; int inv_op_freq; int inv_persistent; enum p2p_discovery_type find_type; unsigned int last_p2p_find_timeout; u8 last_prog_scan_class; u8 last_prog_scan_chan; int p2p_scan_running; enum p2p_after_scan { P2P_AFTER_SCAN_NOTHING, P2P_AFTER_SCAN_LISTEN, P2P_AFTER_SCAN_CONNECT } start_after_scan; u8 after_scan_peer[ETH_ALEN]; struct p2p_pending_action_tx *after_scan_tx; unsigned int after_scan_tx_in_progress:1; /* Requested device types for find/search */ unsigned int num_req_dev_types; u8 *req_dev_types; u8 *find_dev_id; u8 find_dev_id_buf[ETH_ALEN]; struct os_reltime find_start; /* time of last p2p_find start */ struct p2p_group **groups; size_t num_groups; struct p2p_device *pending_client_disc_go; u8 pending_client_disc_addr[ETH_ALEN]; u8 pending_dev_disc_dialog_token; u8 pending_dev_disc_addr[ETH_ALEN]; int pending_dev_disc_freq; unsigned int pending_client_disc_freq; int ext_listen_only; unsigned int ext_listen_period; unsigned int ext_listen_interval; unsigned int ext_listen_interval_sec; unsigned int ext_listen_interval_usec; u8 peer_filter[ETH_ALEN]; int cross_connect; int best_freq_24; int best_freq_5; int best_freq_overall; int own_freq_preference; /** * wps_vendor_ext - WPS Vendor Extensions to add */ struct wpabuf *wps_vendor_ext[P2P_MAX_WPS_VENDOR_EXT]; /* * user_initiated_pd - Whether a PD request is user initiated or not. */ u8 user_initiated_pd; /* * Keep track of which peer a given PD request was sent to. * Used to raise a timeout alert in case there is no response. */ u8 pending_pd_devaddr[ETH_ALEN]; /* * Retry counter for provision discovery requests when issued * in IDLE state. */ int pd_retries; /** * pd_force_freq - Forced frequency for PD retries or 0 to auto-select * * This is is used during PD retries for join-a-group case to use the * correct operating frequency determined from a BSS entry for the GO. */ int pd_force_freq; u8 go_timeout; u8 client_timeout; /* Extra delay in milliseconds between search iterations */ unsigned int search_delay; int in_search_delay; #ifdef CONFIG_WIFI_DISPLAY struct wpabuf *wfd_ie_beacon; struct wpabuf *wfd_ie_probe_req; struct wpabuf *wfd_ie_probe_resp; struct wpabuf *wfd_ie_assoc_req; struct wpabuf *wfd_ie_invitation; struct wpabuf *wfd_ie_prov_disc_req; struct wpabuf *wfd_ie_prov_disc_resp; struct wpabuf *wfd_ie_go_neg; struct wpabuf *wfd_dev_info; struct wpabuf *wfd_assoc_bssid; struct wpabuf *wfd_coupled_sink_info; #endif /* CONFIG_WIFI_DISPLAY */ u16 authorized_oob_dev_pw_id; }; /** * struct p2p_message - Parsed P2P message (or P2P IE) */ struct p2p_message { struct wpabuf *p2p_attributes; struct wpabuf *wps_attributes; struct wpabuf *wfd_subelems; u8 dialog_token; const u8 *capability; const u8 *go_intent; const u8 *status; const u8 *listen_channel; const u8 *operating_channel; const u8 *channel_list; u8 channel_list_len; const u8 *config_timeout; const u8 *intended_addr; const u8 *group_bssid; const u8 *invitation_flags; const u8 *group_info; size_t group_info_len; const u8 *group_id; size_t group_id_len; const u8 *device_id; const u8 *manageability; const u8 *noa; size_t noa_len; const u8 *ext_listen_timing; const u8 *minor_reason_code; const u8 *oob_go_neg_channel; /* P2P Device Info */ const u8 *p2p_device_info; size_t p2p_device_info_len; const u8 *p2p_device_addr; const u8 *pri_dev_type; u8 num_sec_dev_types; char device_name[33]; u16 config_methods; /* WPS IE */ u16 dev_password_id; int dev_password_id_present; u16 wps_config_methods; const u8 *wps_pri_dev_type; const u8 *wps_sec_dev_type_list; size_t wps_sec_dev_type_list_len; const u8 *wps_vendor_ext[P2P_MAX_WPS_VENDOR_EXT]; size_t wps_vendor_ext_len[P2P_MAX_WPS_VENDOR_EXT]; const u8 *manufacturer; size_t manufacturer_len; const u8 *model_name; size_t model_name_len; const u8 *model_number; size_t model_number_len; const u8 *serial_number; size_t serial_number_len; const u8 *oob_dev_password; size_t oob_dev_password_len; /* DS Parameter Set IE */ const u8 *ds_params; /* SSID IE */ const u8 *ssid; }; #define P2P_MAX_GROUP_ENTRIES 50 struct p2p_group_info { unsigned int num_clients; struct p2p_client_info { const u8 *p2p_device_addr; const u8 *p2p_interface_addr; u8 dev_capab; u16 config_methods; const u8 *pri_dev_type; u8 num_sec_dev_types; const u8 *sec_dev_types; const char *dev_name; size_t dev_name_len; } client[P2P_MAX_GROUP_ENTRIES]; }; /* p2p_utils.c */ int p2p_random(char *buf, size_t len); int p2p_channel_to_freq(int op_class, int channel); int p2p_freq_to_channel(unsigned int freq, u8 *op_class, u8 *channel); void p2p_channels_intersect(const struct p2p_channels *a, const struct p2p_channels *b, struct p2p_channels *res); void p2p_channels_union(const struct p2p_channels *a, const struct p2p_channels *b, struct p2p_channels *res); void p2p_channels_remove_freqs(struct p2p_channels *chan, const struct wpa_freq_range_list *list); int p2p_channels_includes(const struct p2p_channels *channels, u8 reg_class, u8 channel); void p2p_channels_dump(struct p2p_data *p2p, const char *title, const struct p2p_channels *chan); int p2p_channel_select(struct p2p_channels *chans, const int *classes, u8 *op_class, u8 *op_channel); /* p2p_parse.c */ int p2p_parse_p2p_ie(const struct wpabuf *buf, struct p2p_message *msg); int p2p_parse_ies(const u8 *data, size_t len, struct p2p_message *msg); int p2p_parse(const u8 *data, size_t len, struct p2p_message *msg); int p2p_parse_ies_separate(const u8 *wsc, size_t wsc_len, const u8 *p2p, size_t p2p_len, struct p2p_message *msg); void p2p_parse_free(struct p2p_message *msg); int p2p_attr_text(struct wpabuf *data, char *buf, char *end); int p2p_group_info_parse(const u8 *gi, size_t gi_len, struct p2p_group_info *info); /* p2p_build.c */ struct p2p_noa_desc { u8 count_type; u32 duration; u32 interval; u32 start_time; }; /* p2p_group.c */ const u8 * p2p_group_get_interface_addr(struct p2p_group *group); u8 p2p_group_presence_req(struct p2p_group *group, const u8 *client_interface_addr, const u8 *noa, size_t noa_len); int p2p_group_is_group_id_match(struct p2p_group *group, const u8 *group_id, size_t group_id_len); void p2p_group_update_ies(struct p2p_group *group); void p2p_group_force_beacon_update_ies(struct p2p_group *group); struct wpabuf * p2p_group_get_wfd_ie(struct p2p_group *g); void p2p_buf_add_group_info(struct p2p_group *group, struct wpabuf *buf, int max_clients); void p2p_group_buf_add_id(struct p2p_group *group, struct wpabuf *buf); int p2p_group_get_freq(struct p2p_group *group); void p2p_buf_add_action_hdr(struct wpabuf *buf, u8 subtype, u8 dialog_token); void p2p_buf_add_public_action_hdr(struct wpabuf *buf, u8 subtype, u8 dialog_token); u8 * p2p_buf_add_ie_hdr(struct wpabuf *buf); void p2p_buf_add_status(struct wpabuf *buf, u8 status); void p2p_buf_add_device_info(struct wpabuf *buf, struct p2p_data *p2p, struct p2p_device *peer); void p2p_buf_add_device_id(struct wpabuf *buf, const u8 *dev_addr); void p2p_buf_update_ie_hdr(struct wpabuf *buf, u8 *len); void p2p_buf_add_capability(struct wpabuf *buf, u8 dev_capab, u8 group_capab); void p2p_buf_add_go_intent(struct wpabuf *buf, u8 go_intent); void p2p_buf_add_listen_channel(struct wpabuf *buf, const char *country, u8 reg_class, u8 channel); void p2p_buf_add_operating_channel(struct wpabuf *buf, const char *country, u8 reg_class, u8 channel); void p2p_buf_add_channel_list(struct wpabuf *buf, const char *country, struct p2p_channels *chan); void p2p_buf_add_config_timeout(struct wpabuf *buf, u8 go_timeout, u8 client_timeout); void p2p_buf_add_intended_addr(struct wpabuf *buf, const u8 *interface_addr); void p2p_buf_add_group_bssid(struct wpabuf *buf, const u8 *bssid); void p2p_buf_add_group_id(struct wpabuf *buf, const u8 *dev_addr, const u8 *ssid, size_t ssid_len); void p2p_buf_add_invitation_flags(struct wpabuf *buf, u8 flags); void p2p_buf_add_noa(struct wpabuf *buf, u8 noa_index, u8 opp_ps, u8 ctwindow, struct p2p_noa_desc *desc1, struct p2p_noa_desc *desc2); void p2p_buf_add_ext_listen_timing(struct wpabuf *buf, u16 period, u16 interval); void p2p_buf_add_p2p_interface(struct wpabuf *buf, struct p2p_data *p2p); void p2p_buf_add_oob_go_neg_channel(struct wpabuf *buf, const char *country, u8 oper_class, u8 channel, enum p2p_role_indication role); int p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, int pw_id, int all_attr); /* p2p_sd.c */ struct p2p_sd_query * p2p_pending_sd_req(struct p2p_data *p2p, struct p2p_device *dev); void p2p_free_sd_queries(struct p2p_data *p2p); void p2p_rx_gas_initial_req(struct p2p_data *p2p, const u8 *sa, const u8 *data, size_t len, int rx_freq); void p2p_rx_gas_initial_resp(struct p2p_data *p2p, const u8 *sa, const u8 *data, size_t len, int rx_freq); void p2p_rx_gas_comeback_req(struct p2p_data *p2p, const u8 *sa, const u8 *data, size_t len, int rx_freq); void p2p_rx_gas_comeback_resp(struct p2p_data *p2p, const u8 *sa, const u8 *data, size_t len, int rx_freq); int p2p_start_sd(struct p2p_data *p2p, struct p2p_device *dev); /* p2p_go_neg.c */ int p2p_peer_channels_check(struct p2p_data *p2p, struct p2p_channels *own, struct p2p_device *dev, const u8 *channel_list, size_t channel_list_len); void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa, const u8 *data, size_t len, int rx_freq); void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa, const u8 *data, size_t len, int rx_freq); void p2p_process_go_neg_conf(struct p2p_data *p2p, const u8 *sa, const u8 *data, size_t len); int p2p_connect_send(struct p2p_data *p2p, struct p2p_device *dev); u16 p2p_wps_method_pw_id(enum p2p_wps_method wps_method); void p2p_reselect_channel(struct p2p_data *p2p, struct p2p_channels *intersection); /* p2p_pd.c */ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, const u8 *data, size_t len, int rx_freq); void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, const u8 *data, size_t len); int p2p_send_prov_disc_req(struct p2p_data *p2p, struct p2p_device *dev, int join, int force_freq); void p2p_reset_pending_pd(struct p2p_data *p2p); /* p2p_invitation.c */ void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa, const u8 *data, size_t len, int rx_freq); void p2p_process_invitation_resp(struct p2p_data *p2p, const u8 *sa, const u8 *data, size_t len); int p2p_invite_send(struct p2p_data *p2p, struct p2p_device *dev, const u8 *go_dev_addr, int dev_pw_id); void p2p_invitation_req_cb(struct p2p_data *p2p, int success); void p2p_invitation_resp_cb(struct p2p_data *p2p, int success); /* p2p_dev_disc.c */ void p2p_process_dev_disc_req(struct p2p_data *p2p, const u8 *sa, const u8 *data, size_t len, int rx_freq); void p2p_dev_disc_req_cb(struct p2p_data *p2p, int success); int p2p_send_dev_disc_req(struct p2p_data *p2p, struct p2p_device *dev); void p2p_dev_disc_resp_cb(struct p2p_data *p2p, int success); void p2p_process_dev_disc_resp(struct p2p_data *p2p, const u8 *sa, const u8 *data, size_t len); void p2p_go_disc_req_cb(struct p2p_data *p2p, int success); void p2p_process_go_disc_req(struct p2p_data *p2p, const u8 *da, const u8 *sa, const u8 *data, size_t len, int rx_freq); /* p2p.c */ void p2p_set_state(struct p2p_data *p2p, int new_state); void p2p_set_timeout(struct p2p_data *p2p, unsigned int sec, unsigned int usec); void p2p_clear_timeout(struct p2p_data *p2p); void p2p_continue_find(struct p2p_data *p2p); struct p2p_device * p2p_add_dev_from_go_neg_req(struct p2p_data *p2p, const u8 *addr, struct p2p_message *msg); void p2p_add_dev_info(struct p2p_data *p2p, const u8 *addr, struct p2p_device *dev, struct p2p_message *msg); int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, struct os_reltime *rx_time, int level, const u8 *ies, size_t ies_len, int scan_res); struct p2p_device * p2p_get_device(struct p2p_data *p2p, const u8 *addr); struct p2p_device * p2p_get_device_interface(struct p2p_data *p2p, const u8 *addr); void p2p_go_neg_failed(struct p2p_data *p2p, struct p2p_device *peer, int status); void p2p_go_complete(struct p2p_data *p2p, struct p2p_device *peer); int p2p_match_dev_type(struct p2p_data *p2p, struct wpabuf *wps); int dev_type_list_match(const u8 *dev_type, const u8 *req_dev_type[], size_t num_req_dev_type); struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p); void p2p_build_ssid(struct p2p_data *p2p, u8 *ssid, size_t *ssid_len); int p2p_send_action(struct p2p_data *p2p, unsigned int freq, const u8 *dst, const u8 *src, const u8 *bssid, const u8 *buf, size_t len, unsigned int wait_time); void p2p_stop_listen_for_freq(struct p2p_data *p2p, int freq); int p2p_prepare_channel(struct p2p_data *p2p, struct p2p_device *dev, unsigned int force_freq, unsigned int pref_freq, int go); void p2p_dbg(struct p2p_data *p2p, const char *fmt, ...) PRINTF_FORMAT(2, 3); void p2p_info(struct p2p_data *p2p, const char *fmt, ...) PRINTF_FORMAT(2, 3); void p2p_err(struct p2p_data *p2p, const char *fmt, ...) PRINTF_FORMAT(2, 3); #endif /* P2P_I_H */ wpa-2.1/src/p2p/p2p_invitation.c000066400000000000000000000417551227414666700165770ustar00rootroot00000000000000/* * Wi-Fi Direct - P2P Invitation procedure * Copyright (c) 2010, Atheros Communications * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "includes.h" #include "common.h" #include "common/ieee802_11_defs.h" #include "p2p_i.h" #include "p2p.h" static struct wpabuf * p2p_build_invitation_req(struct p2p_data *p2p, struct p2p_device *peer, const u8 *go_dev_addr, int dev_pw_id) { struct wpabuf *buf; u8 *len; const u8 *dev_addr; size_t extra = 0; #ifdef CONFIG_WIFI_DISPLAY struct wpabuf *wfd_ie = p2p->wfd_ie_invitation; if (wfd_ie && p2p->inv_role == P2P_INVITE_ROLE_ACTIVE_GO) { size_t i; for (i = 0; i < p2p->num_groups; i++) { struct p2p_group *g = p2p->groups[i]; struct wpabuf *ie; if (os_memcmp(p2p_group_get_interface_addr(g), p2p->inv_bssid, ETH_ALEN) != 0) continue; ie = p2p_group_get_wfd_ie(g); if (ie) { wfd_ie = ie; break; } } } if (wfd_ie) extra = wpabuf_len(wfd_ie); #endif /* CONFIG_WIFI_DISPLAY */ buf = wpabuf_alloc(1000 + extra); if (buf == NULL) return NULL; peer->dialog_token++; if (peer->dialog_token == 0) peer->dialog_token = 1; p2p_buf_add_public_action_hdr(buf, P2P_INVITATION_REQ, peer->dialog_token); len = p2p_buf_add_ie_hdr(buf); if (p2p->inv_role == P2P_INVITE_ROLE_ACTIVE_GO || !p2p->inv_persistent) p2p_buf_add_config_timeout(buf, 0, 0); else p2p_buf_add_config_timeout(buf, p2p->go_timeout, p2p->client_timeout); p2p_buf_add_invitation_flags(buf, p2p->inv_persistent ? P2P_INVITATION_FLAGS_TYPE : 0); if (p2p->inv_role != P2P_INVITE_ROLE_CLIENT || !(peer->flags & P2P_DEV_NO_PREF_CHAN)) p2p_buf_add_operating_channel(buf, p2p->cfg->country, p2p->op_reg_class, p2p->op_channel); if (p2p->inv_bssid_set) p2p_buf_add_group_bssid(buf, p2p->inv_bssid); p2p_buf_add_channel_list(buf, p2p->cfg->country, &p2p->channels); if (go_dev_addr) dev_addr = go_dev_addr; else if (p2p->inv_role == P2P_INVITE_ROLE_CLIENT) dev_addr = peer->info.p2p_device_addr; else dev_addr = p2p->cfg->dev_addr; p2p_buf_add_group_id(buf, dev_addr, p2p->inv_ssid, p2p->inv_ssid_len); p2p_buf_add_device_info(buf, p2p, peer); p2p_buf_update_ie_hdr(buf, len); #ifdef CONFIG_WIFI_DISPLAY if (wfd_ie) wpabuf_put_buf(buf, wfd_ie); #endif /* CONFIG_WIFI_DISPLAY */ if (dev_pw_id >= 0) { /* WSC IE in Invitation Request for NFC static handover */ p2p_build_wps_ie(p2p, buf, dev_pw_id, 0); } return buf; } static struct wpabuf * p2p_build_invitation_resp(struct p2p_data *p2p, struct p2p_device *peer, u8 dialog_token, u8 status, const u8 *group_bssid, u8 reg_class, u8 channel, struct p2p_channels *channels) { struct wpabuf *buf; u8 *len; size_t extra = 0; #ifdef CONFIG_WIFI_DISPLAY struct wpabuf *wfd_ie = p2p->wfd_ie_invitation; if (wfd_ie && group_bssid) { size_t i; for (i = 0; i < p2p->num_groups; i++) { struct p2p_group *g = p2p->groups[i]; struct wpabuf *ie; if (os_memcmp(p2p_group_get_interface_addr(g), group_bssid, ETH_ALEN) != 0) continue; ie = p2p_group_get_wfd_ie(g); if (ie) { wfd_ie = ie; break; } } } if (wfd_ie) extra = wpabuf_len(wfd_ie); #endif /* CONFIG_WIFI_DISPLAY */ buf = wpabuf_alloc(1000 + extra); if (buf == NULL) return NULL; p2p_buf_add_public_action_hdr(buf, P2P_INVITATION_RESP, dialog_token); len = p2p_buf_add_ie_hdr(buf); p2p_buf_add_status(buf, status); p2p_buf_add_config_timeout(buf, 0, 0); /* FIX */ if (reg_class && channel) p2p_buf_add_operating_channel(buf, p2p->cfg->country, reg_class, channel); if (group_bssid) p2p_buf_add_group_bssid(buf, group_bssid); if (channels) p2p_buf_add_channel_list(buf, p2p->cfg->country, channels); p2p_buf_update_ie_hdr(buf, len); #ifdef CONFIG_WIFI_DISPLAY if (wfd_ie) wpabuf_put_buf(buf, wfd_ie); #endif /* CONFIG_WIFI_DISPLAY */ return buf; } void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa, const u8 *data, size_t len, int rx_freq) { struct p2p_device *dev; struct p2p_message msg; struct wpabuf *resp = NULL; u8 status = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE; int freq; int go = 0; u8 group_bssid[ETH_ALEN], *bssid; int op_freq = 0; u8 reg_class = 0, channel = 0; struct p2p_channels intersection, *channels = NULL; int persistent; os_memset(group_bssid, 0, sizeof(group_bssid)); p2p_dbg(p2p, "Received Invitation Request from " MACSTR " (freq=%d)", MAC2STR(sa), rx_freq); if (p2p_parse(data, len, &msg)) return; dev = p2p_get_device(p2p, sa); if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) { p2p_dbg(p2p, "Invitation Request from unknown peer " MACSTR, MAC2STR(sa)); if (p2p_add_device(p2p, sa, rx_freq, NULL, 0, data + 1, len - 1, 0)) { p2p_dbg(p2p, "Invitation Request add device failed " MACSTR, MAC2STR(sa)); status = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE; goto fail; } dev = p2p_get_device(p2p, sa); if (dev == NULL) { p2p_dbg(p2p, "Reject Invitation Request from unknown peer " MACSTR, MAC2STR(sa)); status = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE; goto fail; } } if (!msg.group_id || !msg.channel_list) { p2p_dbg(p2p, "Mandatory attribute missing in Invitation Request from " MACSTR, MAC2STR(sa)); status = P2P_SC_FAIL_INVALID_PARAMS; goto fail; } if (msg.invitation_flags) persistent = *msg.invitation_flags & P2P_INVITATION_FLAGS_TYPE; else { /* Invitation Flags is a mandatory attribute starting from P2P * spec 1.06. As a backwards compatibility mechanism, assume * the request was for a persistent group if the attribute is * missing. */ p2p_dbg(p2p, "Mandatory Invitation Flags attribute missing from Invitation Request"); persistent = 1; } if (p2p_peer_channels_check(p2p, &p2p->cfg->channels, dev, msg.channel_list, msg.channel_list_len) < 0) { p2p_dbg(p2p, "No common channels found"); status = P2P_SC_FAIL_NO_COMMON_CHANNELS; goto fail; } p2p_channels_intersect(&p2p->cfg->channels, &dev->channels, &intersection); if (p2p->cfg->invitation_process) { status = p2p->cfg->invitation_process( p2p->cfg->cb_ctx, sa, msg.group_bssid, msg.group_id, msg.group_id + ETH_ALEN, msg.group_id_len - ETH_ALEN, &go, group_bssid, &op_freq, persistent, &intersection, msg.dev_password_id_present ? msg.dev_password_id : -1); } if (op_freq) { p2p_dbg(p2p, "Invitation processing forced frequency %d MHz", op_freq); if (p2p_freq_to_channel(op_freq, ®_class, &channel) < 0) { p2p_dbg(p2p, "Unknown forced freq %d MHz from invitation_process()", op_freq); status = P2P_SC_FAIL_NO_COMMON_CHANNELS; goto fail; } if (!p2p_channels_includes(&intersection, reg_class, channel)) { p2p_dbg(p2p, "forced freq %d MHz not in the supported channels interaction", op_freq); status = P2P_SC_FAIL_NO_COMMON_CHANNELS; goto fail; } if (status == P2P_SC_SUCCESS) channels = &intersection; } else { p2p_dbg(p2p, "No forced channel from invitation processing - figure out best one to use"); /* Default to own configuration as a starting point */ p2p->op_reg_class = p2p->cfg->op_reg_class; p2p->op_channel = p2p->cfg->op_channel; p2p_dbg(p2p, "Own default op_class %d channel %d", p2p->op_reg_class, p2p->op_channel); /* Use peer preference if specified and compatible */ if (msg.operating_channel) { int req_freq; req_freq = p2p_channel_to_freq( msg.operating_channel[3], msg.operating_channel[4]); p2p_dbg(p2p, "Peer operating channel preference: %d MHz", req_freq); if (req_freq > 0 && p2p_channels_includes(&intersection, msg.operating_channel[3], msg.operating_channel[4])) { p2p->op_reg_class = msg.operating_channel[3]; p2p->op_channel = msg.operating_channel[4]; p2p_dbg(p2p, "Use peer preference op_class %d channel %d", p2p->op_reg_class, p2p->op_channel); } else { p2p_dbg(p2p, "Cannot use peer channel preference"); } } if (!p2p_channels_includes(&intersection, p2p->op_reg_class, p2p->op_channel)) { p2p_dbg(p2p, "Initially selected channel (op_class %d channel %d) not in channel intersection - try to reselect", p2p->op_reg_class, p2p->op_channel); p2p_reselect_channel(p2p, &intersection); p2p_dbg(p2p, "Re-selection result: op_class %d channel %d", p2p->op_reg_class, p2p->op_channel); if (!p2p_channels_includes(&intersection, p2p->op_reg_class, p2p->op_channel)) { p2p_dbg(p2p, "Peer does not support selected operating channel (reg_class=%u channel=%u)", p2p->op_reg_class, p2p->op_channel); status = P2P_SC_FAIL_NO_COMMON_CHANNELS; goto fail; } } else if (!(dev->flags & P2P_DEV_FORCE_FREQ) && !p2p->cfg->cfg_op_channel) { p2p_dbg(p2p, "Try to reselect channel selection with peer information received; previously selected op_class %u channel %u", p2p->op_reg_class, p2p->op_channel); p2p_reselect_channel(p2p, &intersection); } op_freq = p2p_channel_to_freq(p2p->op_reg_class, p2p->op_channel); if (op_freq < 0) { p2p_dbg(p2p, "Unknown operational channel (country=%c%c reg_class=%u channel=%u)", p2p->cfg->country[0], p2p->cfg->country[1], p2p->op_reg_class, p2p->op_channel); status = P2P_SC_FAIL_NO_COMMON_CHANNELS; goto fail; } p2p_dbg(p2p, "Selected operating channel - %d MHz", op_freq); if (status == P2P_SC_SUCCESS) { reg_class = p2p->op_reg_class; channel = p2p->op_channel; channels = &intersection; } } fail: if (go && status == P2P_SC_SUCCESS && !is_zero_ether_addr(group_bssid)) bssid = group_bssid; else bssid = NULL; resp = p2p_build_invitation_resp(p2p, dev, msg.dialog_token, status, bssid, reg_class, channel, channels); if (resp == NULL) goto out; if (rx_freq > 0) freq = rx_freq; else freq = p2p_channel_to_freq(p2p->cfg->reg_class, p2p->cfg->channel); if (freq < 0) { p2p_dbg(p2p, "Unknown regulatory class/channel"); goto out; } /* * Store copy of invitation data to be used when processing TX status * callback for the Acton frame. */ os_memcpy(p2p->inv_sa, sa, ETH_ALEN); if (msg.group_bssid) { os_memcpy(p2p->inv_group_bssid, msg.group_bssid, ETH_ALEN); p2p->inv_group_bssid_ptr = p2p->inv_group_bssid; } else p2p->inv_group_bssid_ptr = NULL; if (msg.group_id_len - ETH_ALEN <= 32) { os_memcpy(p2p->inv_ssid, msg.group_id + ETH_ALEN, msg.group_id_len - ETH_ALEN); p2p->inv_ssid_len = msg.group_id_len - ETH_ALEN; } os_memcpy(p2p->inv_go_dev_addr, msg.group_id, ETH_ALEN); p2p->inv_status = status; p2p->inv_op_freq = op_freq; p2p->pending_action_state = P2P_PENDING_INVITATION_RESPONSE; if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr, p2p->cfg->dev_addr, wpabuf_head(resp), wpabuf_len(resp), 200) < 0) { p2p_dbg(p2p, "Failed to send Action frame"); } out: wpabuf_free(resp); p2p_parse_free(&msg); } void p2p_process_invitation_resp(struct p2p_data *p2p, const u8 *sa, const u8 *data, size_t len) { struct p2p_device *dev; struct p2p_message msg; struct p2p_channels intersection, *channels = NULL; p2p_dbg(p2p, "Received Invitation Response from " MACSTR, MAC2STR(sa)); dev = p2p_get_device(p2p, sa); if (dev == NULL) { p2p_dbg(p2p, "Ignore Invitation Response from unknown peer " MACSTR, MAC2STR(sa)); return; } if (dev != p2p->invite_peer) { p2p_dbg(p2p, "Ignore unexpected Invitation Response from peer " MACSTR, MAC2STR(sa)); return; } if (p2p_parse(data, len, &msg)) return; if (!msg.status) { p2p_dbg(p2p, "Mandatory Status attribute missing in Invitation Response from " MACSTR, MAC2STR(sa)); p2p_parse_free(&msg); return; } if (!msg.channel_list && *msg.status == P2P_SC_SUCCESS) { p2p_dbg(p2p, "Mandatory Channel List attribute missing in Invitation Response from " MACSTR, MAC2STR(sa)); #ifdef CONFIG_P2P_STRICT p2p_parse_free(&msg); return; #endif /* CONFIG_P2P_STRICT */ /* Try to survive without peer channel list */ channels = &p2p->channels; } else if (!msg.channel_list) { /* Non-success cases are not required to include Channel List */ channels = &p2p->channels; } else if (p2p_peer_channels_check(p2p, &p2p->channels, dev, msg.channel_list, msg.channel_list_len) < 0) { p2p_dbg(p2p, "No common channels found"); p2p_parse_free(&msg); return; } else { p2p_channels_intersect(&p2p->channels, &dev->channels, &intersection); channels = &intersection; } if (p2p->cfg->invitation_result) { int freq = p2p_channel_to_freq(p2p->op_reg_class, p2p->op_channel); if (freq < 0) freq = 0; p2p->cfg->invitation_result(p2p->cfg->cb_ctx, *msg.status, msg.group_bssid, channels, sa, freq); } p2p_parse_free(&msg); p2p_clear_timeout(p2p); p2p_set_state(p2p, P2P_IDLE); p2p->invite_peer = NULL; } int p2p_invite_send(struct p2p_data *p2p, struct p2p_device *dev, const u8 *go_dev_addr, int dev_pw_id) { struct wpabuf *req; int freq; freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq; if (freq <= 0) freq = dev->oob_go_neg_freq; if (freq <= 0) { p2p_dbg(p2p, "No Listen/Operating frequency known for the peer " MACSTR " to send Invitation Request", MAC2STR(dev->info.p2p_device_addr)); return -1; } req = p2p_build_invitation_req(p2p, dev, go_dev_addr, dev_pw_id); if (req == NULL) return -1; if (p2p->state != P2P_IDLE) p2p_stop_listen_for_freq(p2p, freq); p2p_dbg(p2p, "Sending Invitation Request"); p2p_set_state(p2p, P2P_INVITE); p2p->pending_action_state = P2P_PENDING_INVITATION_REQUEST; p2p->invite_peer = dev; dev->invitation_reqs++; if (p2p_send_action(p2p, freq, dev->info.p2p_device_addr, p2p->cfg->dev_addr, dev->info.p2p_device_addr, wpabuf_head(req), wpabuf_len(req), 500) < 0) { p2p_dbg(p2p, "Failed to send Action frame"); /* Use P2P find to recover and retry */ p2p_set_timeout(p2p, 0, 0); } wpabuf_free(req); return 0; } void p2p_invitation_req_cb(struct p2p_data *p2p, int success) { p2p_dbg(p2p, "Invitation Request TX callback: success=%d", success); if (p2p->invite_peer == NULL) { p2p_dbg(p2p, "No pending Invite"); return; } /* * Use P2P find, if needed, to find the other device from its listen * channel. */ p2p_set_state(p2p, P2P_INVITE); p2p_set_timeout(p2p, 0, success ? 500000 : 100000); } void p2p_invitation_resp_cb(struct p2p_data *p2p, int success) { p2p_dbg(p2p, "Invitation Response TX callback: success=%d", success); p2p->cfg->send_action_done(p2p->cfg->cb_ctx); if (!success) p2p_dbg(p2p, "Assume Invitation Response was actually received by the peer even though Ack was not reported"); if (p2p->cfg->invitation_received) { p2p->cfg->invitation_received(p2p->cfg->cb_ctx, p2p->inv_sa, p2p->inv_group_bssid_ptr, p2p->inv_ssid, p2p->inv_ssid_len, p2p->inv_go_dev_addr, p2p->inv_status, p2p->inv_op_freq); } } int p2p_invite(struct p2p_data *p2p, const u8 *peer, enum p2p_invite_role role, const u8 *bssid, const u8 *ssid, size_t ssid_len, unsigned int force_freq, const u8 *go_dev_addr, int persistent_group, unsigned int pref_freq, int dev_pw_id) { struct p2p_device *dev; p2p_dbg(p2p, "Request to invite peer " MACSTR " role=%d persistent=%d " "force_freq=%u", MAC2STR(peer), role, persistent_group, force_freq); if (bssid) p2p_dbg(p2p, "Invitation for BSSID " MACSTR, MAC2STR(bssid)); if (go_dev_addr) { p2p_dbg(p2p, "Invitation for GO Device Address " MACSTR, MAC2STR(go_dev_addr)); os_memcpy(p2p->invite_go_dev_addr_buf, go_dev_addr, ETH_ALEN); p2p->invite_go_dev_addr = p2p->invite_go_dev_addr_buf; } else p2p->invite_go_dev_addr = NULL; wpa_hexdump_ascii(MSG_DEBUG, "Invitation for SSID", ssid, ssid_len); if (dev_pw_id >= 0) { p2p_dbg(p2p, "Invitation to use Device Password ID %d", dev_pw_id); } p2p->invite_dev_pw_id = dev_pw_id; dev = p2p_get_device(p2p, peer); if (dev == NULL || (dev->listen_freq <= 0 && dev->oper_freq <= 0 && dev->oob_go_neg_freq <= 0)) { p2p_dbg(p2p, "Cannot invite unknown P2P Device " MACSTR, MAC2STR(peer)); return -1; } if (p2p_prepare_channel(p2p, dev, force_freq, pref_freq, role != P2P_INVITE_ROLE_CLIENT) < 0) return -1; if (persistent_group && role == P2P_INVITE_ROLE_CLIENT && !force_freq && !pref_freq) dev->flags |= P2P_DEV_NO_PREF_CHAN; else dev->flags &= ~P2P_DEV_NO_PREF_CHAN; if (dev->flags & P2P_DEV_GROUP_CLIENT_ONLY) { if (!(dev->info.dev_capab & P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY)) { p2p_dbg(p2p, "Cannot invite a P2P Device " MACSTR " that is in a group and is not discoverable", MAC2STR(peer)); } /* TODO: use device discoverability request through GO */ } dev->invitation_reqs = 0; if (p2p->state != P2P_IDLE) p2p_stop_find(p2p); p2p->inv_role = role; p2p->inv_bssid_set = bssid != NULL; if (bssid) os_memcpy(p2p->inv_bssid, bssid, ETH_ALEN); os_memcpy(p2p->inv_ssid, ssid, ssid_len); p2p->inv_ssid_len = ssid_len; p2p->inv_persistent = persistent_group; return p2p_invite_send(p2p, dev, go_dev_addr, dev_pw_id); } wpa-2.1/src/p2p/p2p_parse.c000066400000000000000000000500141227414666700155110ustar00rootroot00000000000000/* * P2P - IE parser * Copyright (c) 2009-2010, Atheros Communications * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "includes.h" #include "common.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" #include "wps/wps_i.h" #include "p2p_i.h" static int p2p_parse_attribute(u8 id, const u8 *data, u16 len, struct p2p_message *msg) { const u8 *pos; size_t i, nlen; char devtype[WPS_DEV_TYPE_BUFSIZE]; switch (id) { case P2P_ATTR_CAPABILITY: if (len < 2) { wpa_printf(MSG_DEBUG, "P2P: Too short Capability " "attribute (length %d)", len); return -1; } msg->capability = data; wpa_printf(MSG_DEBUG, "P2P: * Device Capability %02x " "Group Capability %02x", data[0], data[1]); break; case P2P_ATTR_DEVICE_ID: if (len < ETH_ALEN) { wpa_printf(MSG_DEBUG, "P2P: Too short Device ID " "attribute (length %d)", len); return -1; } msg->device_id = data; wpa_printf(MSG_DEBUG, "P2P: * Device ID " MACSTR, MAC2STR(msg->device_id)); break; case P2P_ATTR_GROUP_OWNER_INTENT: if (len < 1) { wpa_printf(MSG_DEBUG, "P2P: Too short GO Intent " "attribute (length %d)", len); return -1; } msg->go_intent = data; wpa_printf(MSG_DEBUG, "P2P: * GO Intent: Intent %u " "Tie breaker %u", data[0] >> 1, data[0] & 0x01); break; case P2P_ATTR_STATUS: if (len < 1) { wpa_printf(MSG_DEBUG, "P2P: Too short Status " "attribute (length %d)", len); return -1; } msg->status = data; wpa_printf(MSG_DEBUG, "P2P: * Status: %d", data[0]); break; case P2P_ATTR_LISTEN_CHANNEL: if (len == 0) { wpa_printf(MSG_DEBUG, "P2P: * Listen Channel: Ignore " "null channel"); break; } if (len < 5) { wpa_printf(MSG_DEBUG, "P2P: Too short Listen Channel " "attribute (length %d)", len); return -1; } msg->listen_channel = data; wpa_printf(MSG_DEBUG, "P2P: * Listen Channel: " "Country %c%c(0x%02x) Regulatory " "Class %d Channel Number %d", data[0], data[1], data[2], data[3], data[4]); break; case P2P_ATTR_OPERATING_CHANNEL: if (len == 0) { wpa_printf(MSG_DEBUG, "P2P: * Operating Channel: " "Ignore null channel"); break; } if (len < 5) { wpa_printf(MSG_DEBUG, "P2P: Too short Operating " "Channel attribute (length %d)", len); return -1; } msg->operating_channel = data; wpa_printf(MSG_DEBUG, "P2P: * Operating Channel: " "Country %c%c(0x%02x) Regulatory " "Class %d Channel Number %d", data[0], data[1], data[2], data[3], data[4]); break; case P2P_ATTR_CHANNEL_LIST: if (len < 3) { wpa_printf(MSG_DEBUG, "P2P: Too short Channel List " "attribute (length %d)", len); return -1; } msg->channel_list = data; msg->channel_list_len = len; wpa_printf(MSG_DEBUG, "P2P: * Channel List: Country String " "'%c%c(0x%02x)'", data[0], data[1], data[2]); wpa_hexdump(MSG_MSGDUMP, "P2P: Channel List", msg->channel_list, msg->channel_list_len); break; case P2P_ATTR_GROUP_INFO: msg->group_info = data; msg->group_info_len = len; wpa_printf(MSG_DEBUG, "P2P: * Group Info"); break; case P2P_ATTR_DEVICE_INFO: if (len < ETH_ALEN + 2 + 8 + 1) { wpa_printf(MSG_DEBUG, "P2P: Too short Device Info " "attribute (length %d)", len); return -1; } msg->p2p_device_info = data; msg->p2p_device_info_len = len; pos = data; msg->p2p_device_addr = pos; pos += ETH_ALEN; msg->config_methods = WPA_GET_BE16(pos); pos += 2; msg->pri_dev_type = pos; pos += 8; msg->num_sec_dev_types = *pos++; if (msg->num_sec_dev_types * 8 > data + len - pos) { wpa_printf(MSG_DEBUG, "P2P: Device Info underflow"); return -1; } pos += msg->num_sec_dev_types * 8; if (data + len - pos < 4) { wpa_printf(MSG_DEBUG, "P2P: Invalid Device Name " "length %d", (int) (data + len - pos)); return -1; } if (WPA_GET_BE16(pos) != ATTR_DEV_NAME) { wpa_hexdump(MSG_DEBUG, "P2P: Unexpected Device Name " "header", pos, 4); return -1; } pos += 2; nlen = WPA_GET_BE16(pos); pos += 2; if (data + len - pos < (int) nlen || nlen > 32) { wpa_printf(MSG_DEBUG, "P2P: Invalid Device Name " "length %d (buf len %d)", (int) nlen, (int) (data + len - pos)); return -1; } os_memcpy(msg->device_name, pos, nlen); msg->device_name[nlen] = '\0'; for (i = 0; i < nlen; i++) { if (msg->device_name[i] == '\0') break; if (msg->device_name[i] > 0 && msg->device_name[i] < 32) msg->device_name[i] = '_'; } wpa_printf(MSG_DEBUG, "P2P: * Device Info: addr " MACSTR " primary device type %s device name '%s' " "config methods 0x%x", MAC2STR(msg->p2p_device_addr), wps_dev_type_bin2str(msg->pri_dev_type, devtype, sizeof(devtype)), msg->device_name, msg->config_methods); break; case P2P_ATTR_CONFIGURATION_TIMEOUT: if (len < 2) { wpa_printf(MSG_DEBUG, "P2P: Too short Configuration " "Timeout attribute (length %d)", len); return -1; } msg->config_timeout = data; wpa_printf(MSG_DEBUG, "P2P: * Configuration Timeout"); break; case P2P_ATTR_INTENDED_INTERFACE_ADDR: if (len < ETH_ALEN) { wpa_printf(MSG_DEBUG, "P2P: Too short Intended P2P " "Interface Address attribute (length %d)", len); return -1; } msg->intended_addr = data; wpa_printf(MSG_DEBUG, "P2P: * Intended P2P Interface Address: " MACSTR, MAC2STR(msg->intended_addr)); break; case P2P_ATTR_GROUP_BSSID: if (len < ETH_ALEN) { wpa_printf(MSG_DEBUG, "P2P: Too short P2P Group BSSID " "attribute (length %d)", len); return -1; } msg->group_bssid = data; wpa_printf(MSG_DEBUG, "P2P: * P2P Group BSSID: " MACSTR, MAC2STR(msg->group_bssid)); break; case P2P_ATTR_GROUP_ID: if (len < ETH_ALEN || len > ETH_ALEN + 32) { wpa_printf(MSG_DEBUG, "P2P: Invalid P2P Group ID " "attribute length %d", len); return -1; } msg->group_id = data; msg->group_id_len = len; wpa_printf(MSG_DEBUG, "P2P: * P2P Group ID: Device Address " MACSTR, MAC2STR(msg->group_id)); wpa_hexdump_ascii(MSG_DEBUG, "P2P: * P2P Group ID: SSID", msg->group_id + ETH_ALEN, msg->group_id_len - ETH_ALEN); break; case P2P_ATTR_INVITATION_FLAGS: if (len < 1) { wpa_printf(MSG_DEBUG, "P2P: Too short Invitation " "Flag attribute (length %d)", len); return -1; } msg->invitation_flags = data; wpa_printf(MSG_DEBUG, "P2P: * Invitation Flags: bitmap 0x%x", data[0]); break; case P2P_ATTR_MANAGEABILITY: if (len < 1) { wpa_printf(MSG_DEBUG, "P2P: Too short Manageability " "attribute (length %d)", len); return -1; } msg->manageability = data; wpa_printf(MSG_DEBUG, "P2P: * Manageability: bitmap 0x%x", data[0]); break; case P2P_ATTR_NOTICE_OF_ABSENCE: if (len < 2) { wpa_printf(MSG_DEBUG, "P2P: Too short Notice of " "Absence attribute (length %d)", len); return -1; } msg->noa = data; msg->noa_len = len; wpa_printf(MSG_DEBUG, "P2P: * Notice of Absence"); break; case P2P_ATTR_EXT_LISTEN_TIMING: if (len < 4) { wpa_printf(MSG_DEBUG, "P2P: Too short Extended Listen " "Timing attribute (length %d)", len); return -1; } msg->ext_listen_timing = data; wpa_printf(MSG_DEBUG, "P2P: * Extended Listen Timing " "(period %u msec interval %u msec)", WPA_GET_LE16(msg->ext_listen_timing), WPA_GET_LE16(msg->ext_listen_timing + 2)); break; case P2P_ATTR_MINOR_REASON_CODE: if (len < 1) { wpa_printf(MSG_DEBUG, "P2P: Too short Minor Reason " "Code attribute (length %d)", len); return -1; } msg->minor_reason_code = data; wpa_printf(MSG_DEBUG, "P2P: * Minor Reason Code: %u", *msg->minor_reason_code); break; case P2P_ATTR_OOB_GO_NEG_CHANNEL: if (len < 6) { wpa_printf(MSG_DEBUG, "P2P: Too short OOB GO Neg " "Channel attribute (length %d)", len); return -1; } msg->oob_go_neg_channel = data; wpa_printf(MSG_DEBUG, "P2P: * OOB GO Neg Channel: " "Country %c%c(0x%02x) Operating Class %d " "Channel Number %d Role %d", data[0], data[1], data[2], data[3], data[4], data[5]); break; default: wpa_printf(MSG_DEBUG, "P2P: Skipped unknown attribute %d " "(length %d)", id, len); break; } return 0; } /** * p2p_parse_p2p_ie - Parse P2P IE * @buf: Concatenated P2P IE(s) payload * @msg: Buffer for returning parsed attributes * Returns: 0 on success, -1 on failure * * Note: Caller is responsible for clearing the msg data structure before * calling this function. */ int p2p_parse_p2p_ie(const struct wpabuf *buf, struct p2p_message *msg) { const u8 *pos = wpabuf_head_u8(buf); const u8 *end = pos + wpabuf_len(buf); wpa_printf(MSG_DEBUG, "P2P: Parsing P2P IE"); while (pos < end) { u16 attr_len; if (pos + 2 >= end) { wpa_printf(MSG_DEBUG, "P2P: Invalid P2P attribute"); return -1; } attr_len = WPA_GET_LE16(pos + 1); wpa_printf(MSG_DEBUG, "P2P: Attribute %d length %u", pos[0], attr_len); if (pos + 3 + attr_len > end) { wpa_printf(MSG_DEBUG, "P2P: Attribute underflow " "(len=%u left=%d)", attr_len, (int) (end - pos - 3)); wpa_hexdump(MSG_MSGDUMP, "P2P: Data", pos, end - pos); return -1; } if (p2p_parse_attribute(pos[0], pos + 3, attr_len, msg)) return -1; pos += 3 + attr_len; } return 0; } static int p2p_parse_wps_ie(const struct wpabuf *buf, struct p2p_message *msg) { struct wps_parse_attr attr; int i; wpa_printf(MSG_DEBUG, "P2P: Parsing WPS IE"); if (wps_parse_msg(buf, &attr)) return -1; if (attr.dev_name && attr.dev_name_len < sizeof(msg->device_name) && !msg->device_name[0]) os_memcpy(msg->device_name, attr.dev_name, attr.dev_name_len); if (attr.config_methods) { msg->wps_config_methods = WPA_GET_BE16(attr.config_methods); wpa_printf(MSG_DEBUG, "P2P: Config Methods (WPS): 0x%x", msg->wps_config_methods); } if (attr.dev_password_id) { msg->dev_password_id = WPA_GET_BE16(attr.dev_password_id); wpa_printf(MSG_DEBUG, "P2P: Device Password ID: %d", msg->dev_password_id); msg->dev_password_id_present = 1; } if (attr.primary_dev_type) { char devtype[WPS_DEV_TYPE_BUFSIZE]; msg->wps_pri_dev_type = attr.primary_dev_type; wpa_printf(MSG_DEBUG, "P2P: Primary Device Type (WPS): %s", wps_dev_type_bin2str(msg->wps_pri_dev_type, devtype, sizeof(devtype))); } if (attr.sec_dev_type_list) { msg->wps_sec_dev_type_list = attr.sec_dev_type_list; msg->wps_sec_dev_type_list_len = attr.sec_dev_type_list_len; } for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) { msg->wps_vendor_ext[i] = attr.vendor_ext[i]; msg->wps_vendor_ext_len[i] = attr.vendor_ext_len[i]; } msg->manufacturer = attr.manufacturer; msg->manufacturer_len = attr.manufacturer_len; msg->model_name = attr.model_name; msg->model_name_len = attr.model_name_len; msg->model_number = attr.model_number; msg->model_number_len = attr.model_number_len; msg->serial_number = attr.serial_number; msg->serial_number_len = attr.serial_number_len; msg->oob_dev_password = attr.oob_dev_password; msg->oob_dev_password_len = attr.oob_dev_password_len; return 0; } /** * p2p_parse_ies - Parse P2P message IEs (both WPS and P2P IE) * @data: IEs from the message * @len: Length of data buffer in octets * @msg: Buffer for returning parsed attributes * Returns: 0 on success, -1 on failure * * Note: Caller is responsible for clearing the msg data structure before * calling this function. * * Note: Caller must free temporary memory allocations by calling * p2p_parse_free() when the parsed data is not needed anymore. */ int p2p_parse_ies(const u8 *data, size_t len, struct p2p_message *msg) { struct ieee802_11_elems elems; ieee802_11_parse_elems(data, len, &elems, 0); if (elems.ds_params && elems.ds_params_len >= 1) msg->ds_params = elems.ds_params; if (elems.ssid) msg->ssid = elems.ssid - 2; msg->wps_attributes = ieee802_11_vendor_ie_concat(data, len, WPS_DEV_OUI_WFA); if (msg->wps_attributes && p2p_parse_wps_ie(msg->wps_attributes, msg)) { p2p_parse_free(msg); return -1; } msg->p2p_attributes = ieee802_11_vendor_ie_concat(data, len, P2P_IE_VENDOR_TYPE); if (msg->p2p_attributes && p2p_parse_p2p_ie(msg->p2p_attributes, msg)) { wpa_printf(MSG_DEBUG, "P2P: Failed to parse P2P IE data"); if (msg->p2p_attributes) wpa_hexdump_buf(MSG_MSGDUMP, "P2P: P2P IE data", msg->p2p_attributes); p2p_parse_free(msg); return -1; } #ifdef CONFIG_WIFI_DISPLAY if (elems.wfd) { msg->wfd_subelems = ieee802_11_vendor_ie_concat( data, len, WFD_IE_VENDOR_TYPE); } #endif /* CONFIG_WIFI_DISPLAY */ return 0; } /** * p2p_parse - Parse a P2P Action frame contents * @data: Action frame payload after Category and Code fields * @len: Length of data buffer in octets * @msg: Buffer for returning parsed attributes * Returns: 0 on success, -1 on failure * * Note: Caller must free temporary memory allocations by calling * p2p_parse_free() when the parsed data is not needed anymore. */ int p2p_parse(const u8 *data, size_t len, struct p2p_message *msg) { os_memset(msg, 0, sizeof(*msg)); wpa_printf(MSG_DEBUG, "P2P: Parsing the received message"); if (len < 1) { wpa_printf(MSG_DEBUG, "P2P: No Dialog Token in the message"); return -1; } msg->dialog_token = data[0]; wpa_printf(MSG_DEBUG, "P2P: * Dialog Token: %d", msg->dialog_token); return p2p_parse_ies(data + 1, len - 1, msg); } int p2p_parse_ies_separate(const u8 *wsc, size_t wsc_len, const u8 *p2p, size_t p2p_len, struct p2p_message *msg) { os_memset(msg, 0, sizeof(*msg)); msg->wps_attributes = wpabuf_alloc_copy(wsc, wsc_len); if (msg->wps_attributes && p2p_parse_wps_ie(msg->wps_attributes, msg)) { p2p_parse_free(msg); return -1; } msg->p2p_attributes = wpabuf_alloc_copy(p2p, p2p_len); if (msg->p2p_attributes && p2p_parse_p2p_ie(msg->p2p_attributes, msg)) { wpa_printf(MSG_DEBUG, "P2P: Failed to parse P2P IE data"); if (msg->p2p_attributes) wpa_hexdump_buf(MSG_MSGDUMP, "P2P: P2P IE data", msg->p2p_attributes); p2p_parse_free(msg); return -1; } return 0; } /** * p2p_parse_free - Free temporary data from P2P parsing * @msg: Parsed attributes */ void p2p_parse_free(struct p2p_message *msg) { wpabuf_free(msg->p2p_attributes); msg->p2p_attributes = NULL; wpabuf_free(msg->wps_attributes); msg->wps_attributes = NULL; #ifdef CONFIG_WIFI_DISPLAY wpabuf_free(msg->wfd_subelems); msg->wfd_subelems = NULL; #endif /* CONFIG_WIFI_DISPLAY */ } int p2p_group_info_parse(const u8 *gi, size_t gi_len, struct p2p_group_info *info) { const u8 *g, *gend; os_memset(info, 0, sizeof(*info)); if (gi == NULL) return 0; g = gi; gend = gi + gi_len; while (g < gend) { struct p2p_client_info *cli; const u8 *t, *cend; int count; cli = &info->client[info->num_clients]; cend = g + 1 + g[0]; if (cend > gend) return -1; /* invalid data */ /* g at start of P2P Client Info Descriptor */ /* t at Device Capability Bitmap */ t = g + 1 + 2 * ETH_ALEN; if (t > cend) return -1; /* invalid data */ cli->p2p_device_addr = g + 1; cli->p2p_interface_addr = g + 1 + ETH_ALEN; cli->dev_capab = t[0]; if (t + 1 + 2 + 8 + 1 > cend) return -1; /* invalid data */ cli->config_methods = WPA_GET_BE16(&t[1]); cli->pri_dev_type = &t[3]; t += 1 + 2 + 8; /* t at Number of Secondary Device Types */ cli->num_sec_dev_types = *t++; if (t + 8 * cli->num_sec_dev_types > cend) return -1; /* invalid data */ cli->sec_dev_types = t; t += 8 * cli->num_sec_dev_types; /* t at Device Name in WPS TLV format */ if (t + 2 + 2 > cend) return -1; /* invalid data */ if (WPA_GET_BE16(t) != ATTR_DEV_NAME) return -1; /* invalid Device Name TLV */ t += 2; count = WPA_GET_BE16(t); t += 2; if (count > cend - t) return -1; /* invalid Device Name TLV */ if (count >= 32) count = 32; cli->dev_name = (const char *) t; cli->dev_name_len = count; g = cend; info->num_clients++; if (info->num_clients == P2P_MAX_GROUP_ENTRIES) return -1; } return 0; } static int p2p_group_info_text(const u8 *gi, size_t gi_len, char *buf, char *end) { char *pos = buf; int ret; struct p2p_group_info info; unsigned int i; if (p2p_group_info_parse(gi, gi_len, &info) < 0) return 0; for (i = 0; i < info.num_clients; i++) { struct p2p_client_info *cli; char name[33]; char devtype[WPS_DEV_TYPE_BUFSIZE]; u8 s; int count; cli = &info.client[i]; ret = os_snprintf(pos, end - pos, "p2p_group_client: " "dev=" MACSTR " iface=" MACSTR, MAC2STR(cli->p2p_device_addr), MAC2STR(cli->p2p_interface_addr)); if (ret < 0 || ret >= end - pos) return pos - buf; pos += ret; ret = os_snprintf(pos, end - pos, " dev_capab=0x%x config_methods=0x%x " "dev_type=%s", cli->dev_capab, cli->config_methods, wps_dev_type_bin2str(cli->pri_dev_type, devtype, sizeof(devtype))); if (ret < 0 || ret >= end - pos) return pos - buf; pos += ret; for (s = 0; s < cli->num_sec_dev_types; s++) { ret = os_snprintf(pos, end - pos, " dev_type=%s", wps_dev_type_bin2str( &cli->sec_dev_types[s * 8], devtype, sizeof(devtype))); if (ret < 0 || ret >= end - pos) return pos - buf; pos += ret; } os_memcpy(name, cli->dev_name, cli->dev_name_len); name[cli->dev_name_len] = '\0'; count = (int) cli->dev_name_len - 1; while (count >= 0) { if (name[count] > 0 && name[count] < 32) name[count] = '_'; count--; } ret = os_snprintf(pos, end - pos, " dev_name='%s'\n", name); if (ret < 0 || ret >= end - pos) return pos - buf; pos += ret; } return pos - buf; } /** * p2p_attr_text - Build text format description of P2P IE attributes * @data: P2P IE contents * @buf: Buffer for returning text * @end: Pointer to the end of the buf area * Returns: Number of octets written to the buffer or -1 on faikure * * This function can be used to parse P2P IE contents into text format * field=value lines. */ int p2p_attr_text(struct wpabuf *data, char *buf, char *end) { struct p2p_message msg; char *pos = buf; int ret; os_memset(&msg, 0, sizeof(msg)); if (p2p_parse_p2p_ie(data, &msg)) return -1; if (msg.capability) { ret = os_snprintf(pos, end - pos, "p2p_dev_capab=0x%x\n" "p2p_group_capab=0x%x\n", msg.capability[0], msg.capability[1]); if (ret < 0 || ret >= end - pos) return pos - buf; pos += ret; } if (msg.pri_dev_type) { char devtype[WPS_DEV_TYPE_BUFSIZE]; ret = os_snprintf(pos, end - pos, "p2p_primary_device_type=%s\n", wps_dev_type_bin2str(msg.pri_dev_type, devtype, sizeof(devtype))); if (ret < 0 || ret >= end - pos) return pos - buf; pos += ret; } ret = os_snprintf(pos, end - pos, "p2p_device_name=%s\n", msg.device_name); if (ret < 0 || ret >= end - pos) return pos - buf; pos += ret; if (msg.p2p_device_addr) { ret = os_snprintf(pos, end - pos, "p2p_device_addr=" MACSTR "\n", MAC2STR(msg.p2p_device_addr)); if (ret < 0 || ret >= end - pos) return pos - buf; pos += ret; } ret = os_snprintf(pos, end - pos, "p2p_config_methods=0x%x\n", msg.config_methods); if (ret < 0 || ret >= end - pos) return pos - buf; pos += ret; ret = p2p_group_info_text(msg.group_info, msg.group_info_len, pos, end); if (ret < 0) return pos - buf; pos += ret; return pos - buf; } int p2p_get_cross_connect_disallowed(const struct wpabuf *p2p_ie) { struct p2p_message msg; os_memset(&msg, 0, sizeof(msg)); if (p2p_parse_p2p_ie(p2p_ie, &msg)) return 0; if (!msg.manageability) return 0; return !(msg.manageability[0] & P2P_MAN_CROSS_CONNECTION_PERMITTED); } u8 p2p_get_group_capab(const struct wpabuf *p2p_ie) { struct p2p_message msg; os_memset(&msg, 0, sizeof(msg)); if (p2p_parse_p2p_ie(p2p_ie, &msg)) return 0; if (!msg.capability) return 0; return msg.capability[1]; } const u8 * p2p_get_go_dev_addr(const struct wpabuf *p2p_ie) { struct p2p_message msg; os_memset(&msg, 0, sizeof(msg)); if (p2p_parse_p2p_ie(p2p_ie, &msg)) return NULL; if (msg.p2p_device_addr) return msg.p2p_device_addr; if (msg.device_id) return msg.device_id; return NULL; } wpa-2.1/src/p2p/p2p_pd.c000066400000000000000000000305211227414666700150030ustar00rootroot00000000000000/* * Wi-Fi Direct - P2P provision discovery * Copyright (c) 2009-2010, Atheros Communications * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "includes.h" #include "common.h" #include "common/ieee802_11_defs.h" #include "wps/wps_defs.h" #include "p2p_i.h" #include "p2p.h" /* * Number of retries to attempt for provision discovery requests * in case the peer is not listening. */ #define MAX_PROV_DISC_REQ_RETRIES 120 static void p2p_build_wps_ie_config_methods(struct wpabuf *buf, u16 config_methods) { u8 *len; wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC); len = wpabuf_put(buf, 1); wpabuf_put_be32(buf, WPS_DEV_OUI_WFA); /* Config Methods */ wpabuf_put_be16(buf, ATTR_CONFIG_METHODS); wpabuf_put_be16(buf, 2); wpabuf_put_be16(buf, config_methods); p2p_buf_update_ie_hdr(buf, len); } static struct wpabuf * p2p_build_prov_disc_req(struct p2p_data *p2p, u8 dialog_token, u16 config_methods, struct p2p_device *go) { struct wpabuf *buf; u8 *len; size_t extra = 0; #ifdef CONFIG_WIFI_DISPLAY if (p2p->wfd_ie_prov_disc_req) extra = wpabuf_len(p2p->wfd_ie_prov_disc_req); #endif /* CONFIG_WIFI_DISPLAY */ buf = wpabuf_alloc(1000 + extra); if (buf == NULL) return NULL; p2p_buf_add_public_action_hdr(buf, P2P_PROV_DISC_REQ, dialog_token); len = p2p_buf_add_ie_hdr(buf); p2p_buf_add_capability(buf, p2p->dev_capab & ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, 0); p2p_buf_add_device_info(buf, p2p, NULL); if (go) { p2p_buf_add_group_id(buf, go->info.p2p_device_addr, go->oper_ssid, go->oper_ssid_len); } p2p_buf_update_ie_hdr(buf, len); /* WPS IE with Config Methods attribute */ p2p_build_wps_ie_config_methods(buf, config_methods); #ifdef CONFIG_WIFI_DISPLAY if (p2p->wfd_ie_prov_disc_req) wpabuf_put_buf(buf, p2p->wfd_ie_prov_disc_req); #endif /* CONFIG_WIFI_DISPLAY */ return buf; } static struct wpabuf * p2p_build_prov_disc_resp(struct p2p_data *p2p, u8 dialog_token, u16 config_methods, const u8 *group_id, size_t group_id_len) { struct wpabuf *buf; size_t extra = 0; #ifdef CONFIG_WIFI_DISPLAY struct wpabuf *wfd_ie = p2p->wfd_ie_prov_disc_resp; if (wfd_ie && group_id) { size_t i; for (i = 0; i < p2p->num_groups; i++) { struct p2p_group *g = p2p->groups[i]; struct wpabuf *ie; if (!p2p_group_is_group_id_match(g, group_id, group_id_len)) continue; ie = p2p_group_get_wfd_ie(g); if (ie) { wfd_ie = ie; break; } } } if (wfd_ie) extra = wpabuf_len(wfd_ie); #endif /* CONFIG_WIFI_DISPLAY */ buf = wpabuf_alloc(100 + extra); if (buf == NULL) return NULL; p2p_buf_add_public_action_hdr(buf, P2P_PROV_DISC_RESP, dialog_token); /* WPS IE with Config Methods attribute */ p2p_build_wps_ie_config_methods(buf, config_methods); #ifdef CONFIG_WIFI_DISPLAY if (wfd_ie) wpabuf_put_buf(buf, wfd_ie); #endif /* CONFIG_WIFI_DISPLAY */ return buf; } void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, const u8 *data, size_t len, int rx_freq) { struct p2p_message msg; struct p2p_device *dev; int freq; int reject = 1; struct wpabuf *resp; if (p2p_parse(data, len, &msg)) return; p2p_dbg(p2p, "Received Provision Discovery Request from " MACSTR " with config methods 0x%x (freq=%d)", MAC2STR(sa), msg.wps_config_methods, rx_freq); dev = p2p_get_device(p2p, sa); if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) { p2p_dbg(p2p, "Provision Discovery Request from unknown peer " MACSTR, MAC2STR(sa)); if (p2p_add_device(p2p, sa, rx_freq, NULL, 0, data + 1, len - 1, 0)) { p2p_dbg(p2p, "Provision Discovery Request add device failed " MACSTR, MAC2STR(sa)); } } else if (msg.wfd_subelems) { wpabuf_free(dev->info.wfd_subelems); dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems); } if (!(msg.wps_config_methods & (WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD | WPS_CONFIG_PUSHBUTTON))) { p2p_dbg(p2p, "Unsupported Config Methods in Provision Discovery Request"); goto out; } if (msg.group_id) { size_t i; for (i = 0; i < p2p->num_groups; i++) { if (p2p_group_is_group_id_match(p2p->groups[i], msg.group_id, msg.group_id_len)) break; } if (i == p2p->num_groups) { p2p_dbg(p2p, "PD request for unknown P2P Group ID - reject"); goto out; } } if (dev) dev->flags &= ~(P2P_DEV_PD_PEER_DISPLAY | P2P_DEV_PD_PEER_KEYPAD); if (msg.wps_config_methods & WPS_CONFIG_DISPLAY) { p2p_dbg(p2p, "Peer " MACSTR " requested us to show a PIN on display", MAC2STR(sa)); if (dev) dev->flags |= P2P_DEV_PD_PEER_KEYPAD; } else if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) { p2p_dbg(p2p, "Peer " MACSTR " requested us to write its PIN using keypad", MAC2STR(sa)); if (dev) dev->flags |= P2P_DEV_PD_PEER_DISPLAY; } reject = 0; out: resp = p2p_build_prov_disc_resp(p2p, msg.dialog_token, reject ? 0 : msg.wps_config_methods, msg.group_id, msg.group_id_len); if (resp == NULL) { p2p_parse_free(&msg); return; } p2p_dbg(p2p, "Sending Provision Discovery Response"); if (rx_freq > 0) freq = rx_freq; else freq = p2p_channel_to_freq(p2p->cfg->reg_class, p2p->cfg->channel); if (freq < 0) { p2p_dbg(p2p, "Unknown regulatory class/channel"); wpabuf_free(resp); p2p_parse_free(&msg); return; } p2p->pending_action_state = P2P_NO_PENDING_ACTION; if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr, p2p->cfg->dev_addr, wpabuf_head(resp), wpabuf_len(resp), 200) < 0) { p2p_dbg(p2p, "Failed to send Action frame"); } wpabuf_free(resp); if (!reject && p2p->cfg->prov_disc_req) { const u8 *dev_addr = sa; if (msg.p2p_device_addr) dev_addr = msg.p2p_device_addr; p2p->cfg->prov_disc_req(p2p->cfg->cb_ctx, sa, msg.wps_config_methods, dev_addr, msg.pri_dev_type, msg.device_name, msg.config_methods, msg.capability ? msg.capability[0] : 0, msg.capability ? msg.capability[1] : 0, msg.group_id, msg.group_id_len); } p2p_parse_free(&msg); } void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, const u8 *data, size_t len) { struct p2p_message msg; struct p2p_device *dev; u16 report_config_methods = 0, req_config_methods; int success = 0; if (p2p_parse(data, len, &msg)) return; p2p_dbg(p2p, "Received Provision Discovery Response from " MACSTR " with config methods 0x%x", MAC2STR(sa), msg.wps_config_methods); dev = p2p_get_device(p2p, sa); if (dev == NULL || !dev->req_config_methods) { p2p_dbg(p2p, "Ignore Provision Discovery Response from " MACSTR " with no pending request", MAC2STR(sa)); p2p_parse_free(&msg); return; } if (dev->dialog_token != msg.dialog_token) { p2p_dbg(p2p, "Ignore Provision Discovery Response with unexpected Dialog Token %u (expected %u)", msg.dialog_token, dev->dialog_token); p2p_parse_free(&msg); return; } if (p2p->pending_action_state == P2P_PENDING_PD) { os_memset(p2p->pending_pd_devaddr, 0, ETH_ALEN); p2p->pending_action_state = P2P_NO_PENDING_ACTION; } /* * Use a local copy of the requested config methods since * p2p_reset_pending_pd() can clear this in the peer entry. */ req_config_methods = dev->req_config_methods; /* * If the response is from the peer to whom a user initiated request * was sent earlier, we reset that state info here. */ if (p2p->user_initiated_pd && os_memcmp(p2p->pending_pd_devaddr, sa, ETH_ALEN) == 0) p2p_reset_pending_pd(p2p); if (msg.wps_config_methods != req_config_methods) { p2p_dbg(p2p, "Peer rejected our Provision Discovery Request (received config_methods 0x%x expected 0x%x", msg.wps_config_methods, req_config_methods); if (p2p->cfg->prov_disc_fail) p2p->cfg->prov_disc_fail(p2p->cfg->cb_ctx, sa, P2P_PROV_DISC_REJECTED); p2p_parse_free(&msg); goto out; } report_config_methods = req_config_methods; dev->flags &= ~(P2P_DEV_PD_PEER_DISPLAY | P2P_DEV_PD_PEER_KEYPAD); if (req_config_methods & WPS_CONFIG_DISPLAY) { p2p_dbg(p2p, "Peer " MACSTR " accepted to show a PIN on display", MAC2STR(sa)); dev->flags |= P2P_DEV_PD_PEER_DISPLAY; } else if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) { p2p_dbg(p2p, "Peer " MACSTR " accepted to write our PIN using keypad", MAC2STR(sa)); dev->flags |= P2P_DEV_PD_PEER_KEYPAD; } /* Store the provisioning info */ dev->wps_prov_info = msg.wps_config_methods; p2p_parse_free(&msg); success = 1; out: dev->req_config_methods = 0; p2p->cfg->send_action_done(p2p->cfg->cb_ctx); if (dev->flags & P2P_DEV_PD_BEFORE_GO_NEG) { p2p_dbg(p2p, "Start GO Neg after the PD-before-GO-Neg workaround with " MACSTR, MAC2STR(dev->info.p2p_device_addr)); dev->flags &= ~P2P_DEV_PD_BEFORE_GO_NEG; p2p_connect_send(p2p, dev); return; } if (success && p2p->cfg->prov_disc_resp) p2p->cfg->prov_disc_resp(p2p->cfg->cb_ctx, sa, report_config_methods); if (p2p->state == P2P_PD_DURING_FIND) { p2p_clear_timeout(p2p); p2p_continue_find(p2p); } } int p2p_send_prov_disc_req(struct p2p_data *p2p, struct p2p_device *dev, int join, int force_freq) { struct wpabuf *req; int freq; if (force_freq > 0) freq = force_freq; else freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq; if (freq <= 0) { p2p_dbg(p2p, "No Listen/Operating frequency known for the peer " MACSTR " to send Provision Discovery Request", MAC2STR(dev->info.p2p_device_addr)); return -1; } if (dev->flags & P2P_DEV_GROUP_CLIENT_ONLY) { if (!(dev->info.dev_capab & P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY)) { p2p_dbg(p2p, "Cannot use PD with P2P Device " MACSTR " that is in a group and is not discoverable", MAC2STR(dev->info.p2p_device_addr)); return -1; } /* TODO: use device discoverability request through GO */ } req = p2p_build_prov_disc_req(p2p, dev->dialog_token, dev->req_config_methods, join ? dev : NULL); if (req == NULL) return -1; if (p2p->state != P2P_IDLE) p2p_stop_listen_for_freq(p2p, freq); p2p->pending_action_state = P2P_PENDING_PD; if (p2p_send_action(p2p, freq, dev->info.p2p_device_addr, p2p->cfg->dev_addr, dev->info.p2p_device_addr, wpabuf_head(req), wpabuf_len(req), 200) < 0) { p2p_dbg(p2p, "Failed to send Action frame"); wpabuf_free(req); return -1; } os_memcpy(p2p->pending_pd_devaddr, dev->info.p2p_device_addr, ETH_ALEN); wpabuf_free(req); return 0; } int p2p_prov_disc_req(struct p2p_data *p2p, const u8 *peer_addr, u16 config_methods, int join, int force_freq, int user_initiated_pd) { struct p2p_device *dev; dev = p2p_get_device(p2p, peer_addr); if (dev == NULL) dev = p2p_get_device_interface(p2p, peer_addr); if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) { p2p_dbg(p2p, "Provision Discovery Request destination " MACSTR " not yet known", MAC2STR(peer_addr)); return -1; } p2p_dbg(p2p, "Provision Discovery Request with " MACSTR " (config methods 0x%x)", MAC2STR(peer_addr), config_methods); if (config_methods == 0) return -1; /* Reset provisioning info */ dev->wps_prov_info = 0; dev->req_config_methods = config_methods; if (join) dev->flags |= P2P_DEV_PD_FOR_JOIN; else dev->flags &= ~P2P_DEV_PD_FOR_JOIN; if (p2p->state != P2P_IDLE && p2p->state != P2P_SEARCH && p2p->state != P2P_LISTEN_ONLY) { p2p_dbg(p2p, "Busy with other operations; postpone Provision Discovery Request with " MACSTR " (config methods 0x%x)", MAC2STR(peer_addr), config_methods); return 0; } p2p->user_initiated_pd = user_initiated_pd; p2p->pd_force_freq = force_freq; if (p2p->user_initiated_pd) p2p->pd_retries = MAX_PROV_DISC_REQ_RETRIES; /* * Assign dialog token here to use the same value in each retry within * the same PD exchange. */ dev->dialog_token++; if (dev->dialog_token == 0) dev->dialog_token = 1; return p2p_send_prov_disc_req(p2p, dev, join, force_freq); } void p2p_reset_pending_pd(struct p2p_data *p2p) { struct p2p_device *dev; dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) { if (os_memcmp(p2p->pending_pd_devaddr, dev->info.p2p_device_addr, ETH_ALEN)) continue; if (!dev->req_config_methods) continue; if (dev->flags & P2P_DEV_PD_FOR_JOIN) continue; /* Reset the config methods of the device */ dev->req_config_methods = 0; } p2p->user_initiated_pd = 0; os_memset(p2p->pending_pd_devaddr, 0, ETH_ALEN); p2p->pd_retries = 0; p2p->pd_force_freq = 0; } wpa-2.1/src/p2p/p2p_sd.c000066400000000000000000000476161227414666700150230ustar00rootroot00000000000000/* * Wi-Fi Direct - P2P service discovery * Copyright (c) 2009, Atheros Communications * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "includes.h" #include "common.h" #include "common/ieee802_11_defs.h" #include "common/gas.h" #include "p2p_i.h" #include "p2p.h" #ifdef CONFIG_WIFI_DISPLAY static int wfd_wsd_supported(struct wpabuf *wfd) { const u8 *pos, *end; u8 subelem; u16 len; if (wfd == NULL) return 0; pos = wpabuf_head(wfd); end = pos + wpabuf_len(wfd); while (pos + 3 <= end) { subelem = *pos++; len = WPA_GET_BE16(pos); pos += 2; if (pos + len > end) break; if (subelem == WFD_SUBELEM_DEVICE_INFO && len >= 6) { u16 info = WPA_GET_BE16(pos); return !!(info & 0x0040); } pos += len; } return 0; } #endif /* CONFIG_WIFI_DISPLAY */ struct p2p_sd_query * p2p_pending_sd_req(struct p2p_data *p2p, struct p2p_device *dev) { struct p2p_sd_query *q; int wsd = 0; if (!(dev->info.dev_capab & P2P_DEV_CAPAB_SERVICE_DISCOVERY)) return NULL; /* peer does not support SD */ #ifdef CONFIG_WIFI_DISPLAY if (wfd_wsd_supported(dev->info.wfd_subelems)) wsd = 1; #endif /* CONFIG_WIFI_DISPLAY */ for (q = p2p->sd_queries; q; q = q->next) { /* Use WSD only if the peer indicates support or it */ if (q->wsd && !wsd) continue; if (q->for_all_peers && !(dev->flags & P2P_DEV_SD_INFO)) return q; if (!q->for_all_peers && os_memcmp(q->peer, dev->info.p2p_device_addr, ETH_ALEN) == 0) return q; } return NULL; } static int p2p_unlink_sd_query(struct p2p_data *p2p, struct p2p_sd_query *query) { struct p2p_sd_query *q, *prev; q = p2p->sd_queries; prev = NULL; while (q) { if (q == query) { if (prev) prev->next = q->next; else p2p->sd_queries = q->next; if (p2p->sd_query == query) p2p->sd_query = NULL; return 1; } prev = q; q = q->next; } return 0; } static void p2p_free_sd_query(struct p2p_sd_query *q) { if (q == NULL) return; wpabuf_free(q->tlvs); os_free(q); } void p2p_free_sd_queries(struct p2p_data *p2p) { struct p2p_sd_query *q, *prev; q = p2p->sd_queries; p2p->sd_queries = NULL; while (q) { prev = q; q = q->next; p2p_free_sd_query(prev); } } static struct wpabuf * p2p_build_sd_query(u16 update_indic, struct wpabuf *tlvs) { struct wpabuf *buf; u8 *len_pos; buf = gas_anqp_build_initial_req(0, 100 + wpabuf_len(tlvs)); if (buf == NULL) return NULL; /* ANQP Query Request Frame */ len_pos = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); wpabuf_put_be24(buf, OUI_WFA); wpabuf_put_u8(buf, P2P_OUI_TYPE); wpabuf_put_le16(buf, update_indic); /* Service Update Indicator */ wpabuf_put_buf(buf, tlvs); gas_anqp_set_element_len(buf, len_pos); gas_anqp_set_len(buf); return buf; } static void p2p_send_gas_comeback_req(struct p2p_data *p2p, const u8 *dst, u8 dialog_token, int freq) { struct wpabuf *req; req = gas_build_comeback_req(dialog_token); if (req == NULL) return; p2p->pending_action_state = P2P_NO_PENDING_ACTION; if (p2p_send_action(p2p, freq, dst, p2p->cfg->dev_addr, dst, wpabuf_head(req), wpabuf_len(req), 200) < 0) p2p_dbg(p2p, "Failed to send Action frame"); wpabuf_free(req); } static struct wpabuf * p2p_build_sd_response(u8 dialog_token, u16 status_code, u16 comeback_delay, u16 update_indic, const struct wpabuf *tlvs) { struct wpabuf *buf; u8 *len_pos; buf = gas_anqp_build_initial_resp(dialog_token, status_code, comeback_delay, 100 + (tlvs ? wpabuf_len(tlvs) : 0)); if (buf == NULL) return NULL; if (tlvs) { /* ANQP Query Response Frame */ len_pos = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); wpabuf_put_be24(buf, OUI_WFA); wpabuf_put_u8(buf, P2P_OUI_TYPE); /* Service Update Indicator */ wpabuf_put_le16(buf, update_indic); wpabuf_put_buf(buf, tlvs); gas_anqp_set_element_len(buf, len_pos); } gas_anqp_set_len(buf); return buf; } static struct wpabuf * p2p_build_gas_comeback_resp(u8 dialog_token, u16 status_code, u16 update_indic, const u8 *data, size_t len, u8 frag_id, u8 more, u16 total_len) { struct wpabuf *buf; buf = gas_anqp_build_comeback_resp(dialog_token, status_code, frag_id, more, 0, 100 + len); if (buf == NULL) return NULL; if (frag_id == 0) { /* ANQP Query Response Frame */ wpabuf_put_le16(buf, ANQP_VENDOR_SPECIFIC); /* Info ID */ wpabuf_put_le16(buf, 3 + 1 + 2 + total_len); wpabuf_put_be24(buf, OUI_WFA); wpabuf_put_u8(buf, P2P_OUI_TYPE); /* Service Update Indicator */ wpabuf_put_le16(buf, update_indic); } wpabuf_put_data(buf, data, len); gas_anqp_set_len(buf); return buf; } int p2p_start_sd(struct p2p_data *p2p, struct p2p_device *dev) { struct wpabuf *req; int ret = 0; struct p2p_sd_query *query; int freq; freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq; if (freq <= 0) { p2p_dbg(p2p, "No Listen/Operating frequency known for the peer " MACSTR " to send SD Request", MAC2STR(dev->info.p2p_device_addr)); return -1; } query = p2p_pending_sd_req(p2p, dev); if (query == NULL) return -1; p2p_dbg(p2p, "Start Service Discovery with " MACSTR, MAC2STR(dev->info.p2p_device_addr)); req = p2p_build_sd_query(p2p->srv_update_indic, query->tlvs); if (req == NULL) return -1; p2p->sd_peer = dev; p2p->sd_query = query; p2p->pending_action_state = P2P_PENDING_SD; if (p2p_send_action(p2p, freq, dev->info.p2p_device_addr, p2p->cfg->dev_addr, dev->info.p2p_device_addr, wpabuf_head(req), wpabuf_len(req), 5000) < 0) { p2p_dbg(p2p, "Failed to send Action frame"); ret = -1; } wpabuf_free(req); return ret; } void p2p_rx_gas_initial_req(struct p2p_data *p2p, const u8 *sa, const u8 *data, size_t len, int rx_freq) { const u8 *pos = data; const u8 *end = data + len; const u8 *next; u8 dialog_token; u16 slen; int freq; u16 update_indic; if (p2p->cfg->sd_request == NULL) return; if (rx_freq > 0) freq = rx_freq; else freq = p2p_channel_to_freq(p2p->cfg->reg_class, p2p->cfg->channel); if (freq < 0) return; if (len < 1 + 2) return; dialog_token = *pos++; p2p_dbg(p2p, "GAS Initial Request from " MACSTR " (dialog token %u, freq %d)", MAC2STR(sa), dialog_token, rx_freq); if (*pos != WLAN_EID_ADV_PROTO) { p2p_dbg(p2p, "Unexpected IE in GAS Initial Request: %u", *pos); return; } pos++; slen = *pos++; next = pos + slen; if (next > end || slen < 2) { p2p_dbg(p2p, "Invalid IE in GAS Initial Request"); return; } pos++; /* skip QueryRespLenLimit and PAME-BI */ if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) { p2p_dbg(p2p, "Unsupported GAS advertisement protocol id %u", *pos); return; } pos = next; /* Query Request */ if (pos + 2 > end) return; slen = WPA_GET_LE16(pos); pos += 2; if (pos + slen > end) return; end = pos + slen; /* ANQP Query Request */ if (pos + 4 > end) return; if (WPA_GET_LE16(pos) != ANQP_VENDOR_SPECIFIC) { p2p_dbg(p2p, "Unsupported ANQP Info ID %u", WPA_GET_LE16(pos)); return; } pos += 2; slen = WPA_GET_LE16(pos); pos += 2; if (pos + slen > end || slen < 3 + 1) { p2p_dbg(p2p, "Invalid ANQP Query Request length"); return; } if (WPA_GET_BE24(pos) != OUI_WFA) { p2p_dbg(p2p, "Unsupported ANQP OUI %06x", WPA_GET_BE24(pos)); return; } pos += 3; if (*pos != P2P_OUI_TYPE) { p2p_dbg(p2p, "Unsupported ANQP vendor type %u", *pos); return; } pos++; if (pos + 2 > end) return; update_indic = WPA_GET_LE16(pos); p2p_dbg(p2p, "Service Update Indicator: %u", update_indic); pos += 2; p2p->cfg->sd_request(p2p->cfg->cb_ctx, freq, sa, dialog_token, update_indic, pos, end - pos); /* the response will be indicated with a call to p2p_sd_response() */ } void p2p_sd_response(struct p2p_data *p2p, int freq, const u8 *dst, u8 dialog_token, const struct wpabuf *resp_tlvs) { struct wpabuf *resp; /* TODO: fix the length limit to match with the maximum frame length */ if (wpabuf_len(resp_tlvs) > 1400) { p2p_dbg(p2p, "SD response long enough to require fragmentation"); if (p2p->sd_resp) { /* * TODO: Could consider storing the fragmented response * separately for each peer to avoid having to drop old * one if there is more than one pending SD query. * Though, that would eat more memory, so there are * also benefits to just using a single buffer. */ p2p_dbg(p2p, "Drop previous SD response"); wpabuf_free(p2p->sd_resp); } p2p->sd_resp = wpabuf_dup(resp_tlvs); if (p2p->sd_resp == NULL) { p2p_err(p2p, "Failed to allocate SD response fragmentation area"); return; } os_memcpy(p2p->sd_resp_addr, dst, ETH_ALEN); p2p->sd_resp_dialog_token = dialog_token; p2p->sd_resp_pos = 0; p2p->sd_frag_id = 0; resp = p2p_build_sd_response(dialog_token, WLAN_STATUS_SUCCESS, 1, p2p->srv_update_indic, NULL); } else { p2p_dbg(p2p, "SD response fits in initial response"); resp = p2p_build_sd_response(dialog_token, WLAN_STATUS_SUCCESS, 0, p2p->srv_update_indic, resp_tlvs); } if (resp == NULL) return; p2p->pending_action_state = P2P_NO_PENDING_ACTION; if (p2p_send_action(p2p, freq, dst, p2p->cfg->dev_addr, p2p->cfg->dev_addr, wpabuf_head(resp), wpabuf_len(resp), 200) < 0) p2p_dbg(p2p, "Failed to send Action frame"); wpabuf_free(resp); } void p2p_rx_gas_initial_resp(struct p2p_data *p2p, const u8 *sa, const u8 *data, size_t len, int rx_freq) { const u8 *pos = data; const u8 *end = data + len; const u8 *next; u8 dialog_token; u16 status_code; u16 comeback_delay; u16 slen; u16 update_indic; if (p2p->state != P2P_SD_DURING_FIND || p2p->sd_peer == NULL || os_memcmp(sa, p2p->sd_peer->info.p2p_device_addr, ETH_ALEN) != 0) { p2p_dbg(p2p, "Ignore unexpected GAS Initial Response from " MACSTR, MAC2STR(sa)); return; } p2p->cfg->send_action_done(p2p->cfg->cb_ctx); p2p_clear_timeout(p2p); p2p_dbg(p2p, "Received GAS Initial Response from " MACSTR " (len=%d)", MAC2STR(sa), (int) len); if (len < 5 + 2) { p2p_dbg(p2p, "Too short GAS Initial Response frame"); return; } dialog_token = *pos++; /* TODO: check dialog_token match */ status_code = WPA_GET_LE16(pos); pos += 2; comeback_delay = WPA_GET_LE16(pos); pos += 2; p2p_dbg(p2p, "dialog_token=%u status_code=%u comeback_delay=%u", dialog_token, status_code, comeback_delay); if (status_code) { p2p_dbg(p2p, "Service Discovery failed: status code %u", status_code); return; } if (*pos != WLAN_EID_ADV_PROTO) { p2p_dbg(p2p, "Unexpected IE in GAS Initial Response: %u", *pos); return; } pos++; slen = *pos++; next = pos + slen; if (next > end || slen < 2) { p2p_dbg(p2p, "Invalid IE in GAS Initial Response"); return; } pos++; /* skip QueryRespLenLimit and PAME-BI */ if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) { p2p_dbg(p2p, "Unsupported GAS advertisement protocol id %u", *pos); return; } pos = next; /* Query Response */ if (pos + 2 > end) { p2p_dbg(p2p, "Too short Query Response"); return; } slen = WPA_GET_LE16(pos); pos += 2; p2p_dbg(p2p, "Query Response Length: %d", slen); if (pos + slen > end) { p2p_dbg(p2p, "Not enough Query Response data"); return; } end = pos + slen; if (comeback_delay) { p2p_dbg(p2p, "Fragmented response - request fragments"); if (p2p->sd_rx_resp) { p2p_dbg(p2p, "Drop old SD reassembly buffer"); wpabuf_free(p2p->sd_rx_resp); p2p->sd_rx_resp = NULL; } p2p_send_gas_comeback_req(p2p, sa, dialog_token, rx_freq); return; } /* ANQP Query Response */ if (pos + 4 > end) return; if (WPA_GET_LE16(pos) != ANQP_VENDOR_SPECIFIC) { p2p_dbg(p2p, "Unsupported ANQP Info ID %u", WPA_GET_LE16(pos)); return; } pos += 2; slen = WPA_GET_LE16(pos); pos += 2; if (pos + slen > end || slen < 3 + 1) { p2p_dbg(p2p, "Invalid ANQP Query Response length"); return; } if (WPA_GET_BE24(pos) != OUI_WFA) { p2p_dbg(p2p, "Unsupported ANQP OUI %06x", WPA_GET_BE24(pos)); return; } pos += 3; if (*pos != P2P_OUI_TYPE) { p2p_dbg(p2p, "Unsupported ANQP vendor type %u", *pos); return; } pos++; if (pos + 2 > end) return; update_indic = WPA_GET_LE16(pos); p2p_dbg(p2p, "Service Update Indicator: %u", update_indic); pos += 2; p2p->sd_peer->flags |= P2P_DEV_SD_INFO; p2p->sd_peer->flags &= ~P2P_DEV_SD_SCHEDULE; p2p->sd_peer = NULL; if (p2p->sd_query) { if (!p2p->sd_query->for_all_peers) { struct p2p_sd_query *q; p2p_dbg(p2p, "Remove completed SD query %p", p2p->sd_query); q = p2p->sd_query; p2p_unlink_sd_query(p2p, p2p->sd_query); p2p_free_sd_query(q); } p2p->sd_query = NULL; } if (p2p->cfg->sd_response) p2p->cfg->sd_response(p2p->cfg->cb_ctx, sa, update_indic, pos, end - pos); p2p_continue_find(p2p); } void p2p_rx_gas_comeback_req(struct p2p_data *p2p, const u8 *sa, const u8 *data, size_t len, int rx_freq) { struct wpabuf *resp; u8 dialog_token; size_t frag_len; int more = 0; wpa_hexdump(MSG_DEBUG, "P2P: RX GAS Comeback Request", data, len); if (len < 1) return; dialog_token = *data; p2p_dbg(p2p, "Dialog Token: %u", dialog_token); if (dialog_token != p2p->sd_resp_dialog_token) { p2p_dbg(p2p, "No pending SD response fragment for dialog token %u", dialog_token); return; } if (p2p->sd_resp == NULL) { p2p_dbg(p2p, "No pending SD response fragment available"); return; } if (os_memcmp(sa, p2p->sd_resp_addr, ETH_ALEN) != 0) { p2p_dbg(p2p, "No pending SD response fragment for " MACSTR, MAC2STR(sa)); return; } frag_len = wpabuf_len(p2p->sd_resp) - p2p->sd_resp_pos; if (frag_len > 1400) { frag_len = 1400; more = 1; } resp = p2p_build_gas_comeback_resp(dialog_token, WLAN_STATUS_SUCCESS, p2p->srv_update_indic, wpabuf_head_u8(p2p->sd_resp) + p2p->sd_resp_pos, frag_len, p2p->sd_frag_id, more, wpabuf_len(p2p->sd_resp)); if (resp == NULL) return; p2p_dbg(p2p, "Send GAS Comeback Response (frag_id %d more=%d frag_len=%d)", p2p->sd_frag_id, more, (int) frag_len); p2p->sd_frag_id++; p2p->sd_resp_pos += frag_len; if (more) { p2p_dbg(p2p, "%d more bytes remain to be sent", (int) (wpabuf_len(p2p->sd_resp) - p2p->sd_resp_pos)); } else { p2p_dbg(p2p, "All fragments of SD response sent"); wpabuf_free(p2p->sd_resp); p2p->sd_resp = NULL; } p2p->pending_action_state = P2P_NO_PENDING_ACTION; if (p2p_send_action(p2p, rx_freq, sa, p2p->cfg->dev_addr, p2p->cfg->dev_addr, wpabuf_head(resp), wpabuf_len(resp), 200) < 0) p2p_dbg(p2p, "Failed to send Action frame"); wpabuf_free(resp); } void p2p_rx_gas_comeback_resp(struct p2p_data *p2p, const u8 *sa, const u8 *data, size_t len, int rx_freq) { const u8 *pos = data; const u8 *end = data + len; const u8 *next; u8 dialog_token; u16 status_code; u8 frag_id; u8 more_frags; u16 comeback_delay; u16 slen; wpa_hexdump(MSG_DEBUG, "P2P: RX GAS Comeback Response", data, len); if (p2p->state != P2P_SD_DURING_FIND || p2p->sd_peer == NULL || os_memcmp(sa, p2p->sd_peer->info.p2p_device_addr, ETH_ALEN) != 0) { p2p_dbg(p2p, "Ignore unexpected GAS Comeback Response from " MACSTR, MAC2STR(sa)); return; } p2p->cfg->send_action_done(p2p->cfg->cb_ctx); p2p_clear_timeout(p2p); p2p_dbg(p2p, "Received GAS Comeback Response from " MACSTR " (len=%d)", MAC2STR(sa), (int) len); if (len < 6 + 2) { p2p_dbg(p2p, "Too short GAS Comeback Response frame"); return; } dialog_token = *pos++; /* TODO: check dialog_token match */ status_code = WPA_GET_LE16(pos); pos += 2; frag_id = *pos & 0x7f; more_frags = (*pos & 0x80) >> 7; pos++; comeback_delay = WPA_GET_LE16(pos); pos += 2; p2p_dbg(p2p, "dialog_token=%u status_code=%u frag_id=%d more_frags=%d " "comeback_delay=%u", dialog_token, status_code, frag_id, more_frags, comeback_delay); /* TODO: check frag_id match */ if (status_code) { p2p_dbg(p2p, "Service Discovery failed: status code %u", status_code); return; } if (*pos != WLAN_EID_ADV_PROTO) { p2p_dbg(p2p, "Unexpected IE in GAS Comeback Response: %u", *pos); return; } pos++; slen = *pos++; next = pos + slen; if (next > end || slen < 2) { p2p_dbg(p2p, "Invalid IE in GAS Comeback Response"); return; } pos++; /* skip QueryRespLenLimit and PAME-BI */ if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) { p2p_dbg(p2p, "Unsupported GAS advertisement protocol id %u", *pos); return; } pos = next; /* Query Response */ if (pos + 2 > end) { p2p_dbg(p2p, "Too short Query Response"); return; } slen = WPA_GET_LE16(pos); pos += 2; p2p_dbg(p2p, "Query Response Length: %d", slen); if (pos + slen > end) { p2p_dbg(p2p, "Not enough Query Response data"); return; } if (slen == 0) { p2p_dbg(p2p, "No Query Response data"); return; } end = pos + slen; if (p2p->sd_rx_resp) { /* * ANQP header is only included in the first fragment; rest of * the fragments start with continue TLVs. */ goto skip_nqp_header; } /* ANQP Query Response */ if (pos + 4 > end) return; if (WPA_GET_LE16(pos) != ANQP_VENDOR_SPECIFIC) { p2p_dbg(p2p, "Unsupported ANQP Info ID %u", WPA_GET_LE16(pos)); return; } pos += 2; slen = WPA_GET_LE16(pos); pos += 2; p2p_dbg(p2p, "ANQP Query Response length: %u", slen); if (slen < 3 + 1) { p2p_dbg(p2p, "Invalid ANQP Query Response length"); return; } if (pos + 4 > end) return; if (WPA_GET_BE24(pos) != OUI_WFA) { p2p_dbg(p2p, "Unsupported ANQP OUI %06x", WPA_GET_BE24(pos)); return; } pos += 3; if (*pos != P2P_OUI_TYPE) { p2p_dbg(p2p, "Unsupported ANQP vendor type %u", *pos); return; } pos++; if (pos + 2 > end) return; p2p->sd_rx_update_indic = WPA_GET_LE16(pos); p2p_dbg(p2p, "Service Update Indicator: %u", p2p->sd_rx_update_indic); pos += 2; skip_nqp_header: if (wpabuf_resize(&p2p->sd_rx_resp, end - pos) < 0) return; wpabuf_put_data(p2p->sd_rx_resp, pos, end - pos); p2p_dbg(p2p, "Current SD reassembly buffer length: %u", (unsigned int) wpabuf_len(p2p->sd_rx_resp)); if (more_frags) { p2p_dbg(p2p, "More fragments remains"); /* TODO: what would be a good size limit? */ if (wpabuf_len(p2p->sd_rx_resp) > 64000) { wpabuf_free(p2p->sd_rx_resp); p2p->sd_rx_resp = NULL; p2p_dbg(p2p, "Too long SD response - drop it"); return; } p2p_send_gas_comeback_req(p2p, sa, dialog_token, rx_freq); return; } p2p->sd_peer->flags |= P2P_DEV_SD_INFO; p2p->sd_peer->flags &= ~P2P_DEV_SD_SCHEDULE; p2p->sd_peer = NULL; if (p2p->sd_query) { if (!p2p->sd_query->for_all_peers) { struct p2p_sd_query *q; p2p_dbg(p2p, "Remove completed SD query %p", p2p->sd_query); q = p2p->sd_query; p2p_unlink_sd_query(p2p, p2p->sd_query); p2p_free_sd_query(q); } p2p->sd_query = NULL; } if (p2p->cfg->sd_response) p2p->cfg->sd_response(p2p->cfg->cb_ctx, sa, p2p->sd_rx_update_indic, wpabuf_head(p2p->sd_rx_resp), wpabuf_len(p2p->sd_rx_resp)); wpabuf_free(p2p->sd_rx_resp); p2p->sd_rx_resp = NULL; p2p_continue_find(p2p); } void * p2p_sd_request(struct p2p_data *p2p, const u8 *dst, const struct wpabuf *tlvs) { struct p2p_sd_query *q; q = os_zalloc(sizeof(*q)); if (q == NULL) return NULL; if (dst) os_memcpy(q->peer, dst, ETH_ALEN); else q->for_all_peers = 1; q->tlvs = wpabuf_dup(tlvs); if (q->tlvs == NULL) { p2p_free_sd_query(q); return NULL; } q->next = p2p->sd_queries; p2p->sd_queries = q; p2p_dbg(p2p, "Added SD Query %p", q); if (dst == NULL) { struct p2p_device *dev; dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) dev->flags &= ~P2P_DEV_SD_INFO; } return q; } #ifdef CONFIG_WIFI_DISPLAY void * p2p_sd_request_wfd(struct p2p_data *p2p, const u8 *dst, const struct wpabuf *tlvs) { struct p2p_sd_query *q; q = p2p_sd_request(p2p, dst, tlvs); if (q) q->wsd = 1; return q; } #endif /* CONFIG_WIFI_DISPLAY */ void p2p_sd_service_update(struct p2p_data *p2p) { p2p->srv_update_indic++; } int p2p_sd_cancel_request(struct p2p_data *p2p, void *req) { if (p2p_unlink_sd_query(p2p, req)) { p2p_dbg(p2p, "Cancel pending SD query %p", req); p2p_free_sd_query(req); return 0; } return -1; } wpa-2.1/src/p2p/p2p_utils.c000066400000000000000000000262721227414666700155500ustar00rootroot00000000000000/* * P2P - generic helper functions * Copyright (c) 2009, Atheros Communications * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "includes.h" #include "common.h" #include "p2p_i.h" /** * p2p_random - Generate random string for SSID and passphrase * @buf: Buffer for returning the result * @len: Number of octets to write to the buffer * Returns: 0 on success, -1 on failure * * This function generates a random string using the following character set: * 'A'-'Z', 'a'-'z', '0'-'9'. */ int p2p_random(char *buf, size_t len) { u8 val; size_t i; u8 letters = 'Z' - 'A' + 1; u8 numbers = 10; if (os_get_random((unsigned char *) buf, len)) return -1; /* Character set: 'A'-'Z', 'a'-'z', '0'-'9' */ for (i = 0; i < len; i++) { val = buf[i]; val %= 2 * letters + numbers; if (val < letters) buf[i] = 'A' + val; else if (val < 2 * letters) buf[i] = 'a' + (val - letters); else buf[i] = '0' + (val - 2 * letters); } return 0; } /** * p2p_channel_to_freq - Convert channel info to frequency * @op_class: Operating class * @channel: Channel number * Returns: Frequency in MHz or -1 if the specified channel is unknown */ int p2p_channel_to_freq(int op_class, int channel) { /* Table E-4 in IEEE Std 802.11-2012 - Global operating classes */ /* TODO: more operating classes */ switch (op_class) { case 81: /* channels 1..13 */ if (channel < 1 || channel > 13) return -1; return 2407 + 5 * channel; case 82: /* channel 14 */ if (channel != 14) return -1; return 2414 + 5 * channel; case 83: /* channels 1..9; 40 MHz */ case 84: /* channels 5..13; 40 MHz */ if (channel < 1 || channel > 13) return -1; return 2407 + 5 * channel; case 115: /* channels 36,40,44,48; indoor only */ case 118: /* channels 52,56,60,64; dfs */ if (channel < 36 || channel > 64) return -1; return 5000 + 5 * channel; case 124: /* channels 149,153,157,161 */ case 125: /* channels 149,153,157,161,165,169 */ if (channel < 149 || channel > 161) return -1; return 5000 + 5 * channel; case 116: /* channels 36,44; 40 MHz; indoor only */ case 117: /* channels 40,48; 40 MHz; indoor only */ case 119: /* channels 52,60; 40 MHz; dfs */ case 120: /* channels 56,64; 40 MHz; dfs */ if (channel < 36 || channel > 64) return -1; return 5000 + 5 * channel; case 126: /* channels 149,157; 40 MHz */ case 127: /* channels 153,161; 40 MHz */ if (channel < 149 || channel > 161) return -1; return 5000 + 5 * channel; case 128: /* center freqs 42, 58, 106, 122, 138, 155; 80 MHz */ if (channel < 36 || channel > 161) return -1; return 5000 + 5 * channel; } return -1; } /** * p2p_freq_to_channel - Convert frequency into channel info * @op_class: Buffer for returning operating class * @channel: Buffer for returning channel number * Returns: 0 on success, -1 if the specified frequency is unknown */ int p2p_freq_to_channel(unsigned int freq, u8 *op_class, u8 *channel) { /* TODO: more operating classes */ if (freq >= 2412 && freq <= 2472) { if ((freq - 2407) % 5) return -1; *op_class = 81; /* 2.407 GHz, channels 1..13 */ *channel = (freq - 2407) / 5; return 0; } if (freq == 2484) { *op_class = 82; /* channel 14 */ *channel = 14; return 0; } if (freq >= 5180 && freq <= 5240) { if ((freq - 5000) % 5) return -1; *op_class = 115; /* 5 GHz, channels 36..48 */ *channel = (freq - 5000) / 5; return 0; } if (freq >= 5745 && freq <= 5805) { if ((freq - 5000) % 5) return -1; *op_class = 124; /* 5 GHz, channels 149..161 */ *channel = (freq - 5000) / 5; return 0; } return -1; } static void p2p_reg_class_intersect(const struct p2p_reg_class *a, const struct p2p_reg_class *b, struct p2p_reg_class *res) { size_t i, j; res->reg_class = a->reg_class; for (i = 0; i < a->channels; i++) { for (j = 0; j < b->channels; j++) { if (a->channel[i] != b->channel[j]) continue; res->channel[res->channels] = a->channel[i]; res->channels++; if (res->channels == P2P_MAX_REG_CLASS_CHANNELS) return; } } } /** * p2p_channels_intersect - Intersection of supported channel lists * @a: First set of supported channels * @b: Second set of supported channels * @res: Data structure for returning the intersection of support channels * * This function can be used to find a common set of supported channels. Both * input channels sets are assumed to use the same country code. If different * country codes are used, the regulatory class numbers may not be matched * correctly and results are undefined. */ void p2p_channels_intersect(const struct p2p_channels *a, const struct p2p_channels *b, struct p2p_channels *res) { size_t i, j; os_memset(res, 0, sizeof(*res)); for (i = 0; i < a->reg_classes; i++) { const struct p2p_reg_class *a_reg = &a->reg_class[i]; for (j = 0; j < b->reg_classes; j++) { const struct p2p_reg_class *b_reg = &b->reg_class[j]; if (a_reg->reg_class != b_reg->reg_class) continue; p2p_reg_class_intersect( a_reg, b_reg, &res->reg_class[res->reg_classes]); if (res->reg_class[res->reg_classes].channels) { res->reg_classes++; if (res->reg_classes == P2P_MAX_REG_CLASSES) return; } } } } static void p2p_op_class_union(struct p2p_reg_class *cl, const struct p2p_reg_class *b_cl) { size_t i, j; for (i = 0; i < b_cl->channels; i++) { for (j = 0; j < cl->channels; j++) { if (b_cl->channel[i] == cl->channel[j]) break; } if (j == cl->channels) { if (cl->channels == P2P_MAX_REG_CLASS_CHANNELS) return; cl->channel[cl->channels++] = b_cl->channel[i]; } } } /** * p2p_channels_union - Union of channel lists * @a: First set of channels * @b: Second set of channels * @res: Data structure for returning the union of channels */ void p2p_channels_union(const struct p2p_channels *a, const struct p2p_channels *b, struct p2p_channels *res) { size_t i, j; if (a != res) os_memcpy(res, a, sizeof(*res)); for (i = 0; i < res->reg_classes; i++) { struct p2p_reg_class *cl = &res->reg_class[i]; for (j = 0; j < b->reg_classes; j++) { const struct p2p_reg_class *b_cl = &b->reg_class[j]; if (cl->reg_class != b_cl->reg_class) continue; p2p_op_class_union(cl, b_cl); } } for (j = 0; j < b->reg_classes; j++) { const struct p2p_reg_class *b_cl = &b->reg_class[j]; for (i = 0; i < res->reg_classes; i++) { struct p2p_reg_class *cl = &res->reg_class[i]; if (cl->reg_class == b_cl->reg_class) break; } if (i == res->reg_classes) { if (res->reg_classes == P2P_MAX_REG_CLASSES) return; os_memcpy(&res->reg_class[res->reg_classes++], b_cl, sizeof(struct p2p_reg_class)); } } } void p2p_channels_remove_freqs(struct p2p_channels *chan, const struct wpa_freq_range_list *list) { size_t o, c; if (list == NULL) return; o = 0; while (o < chan->reg_classes) { struct p2p_reg_class *op = &chan->reg_class[o]; c = 0; while (c < op->channels) { int freq = p2p_channel_to_freq(op->reg_class, op->channel[c]); if (freq > 0 && freq_range_list_includes(list, freq)) { op->channels--; os_memmove(&op->channel[c], &op->channel[c + 1], op->channels - c); } else c++; } if (op->channels == 0) { chan->reg_classes--; os_memmove(&chan->reg_class[o], &chan->reg_class[o + 1], (chan->reg_classes - o) * sizeof(struct p2p_reg_class)); } else o++; } } /** * p2p_channels_includes - Check whether a channel is included in the list * @channels: List of supported channels * @reg_class: Regulatory class of the channel to search * @channel: Channel number of the channel to search * Returns: 1 if channel was found or 0 if not */ int p2p_channels_includes(const struct p2p_channels *channels, u8 reg_class, u8 channel) { size_t i, j; for (i = 0; i < channels->reg_classes; i++) { const struct p2p_reg_class *reg = &channels->reg_class[i]; if (reg->reg_class != reg_class) continue; for (j = 0; j < reg->channels; j++) { if (reg->channel[j] == channel) return 1; } } return 0; } int p2p_channels_includes_freq(const struct p2p_channels *channels, unsigned int freq) { size_t i, j; for (i = 0; i < channels->reg_classes; i++) { const struct p2p_reg_class *reg = &channels->reg_class[i]; for (j = 0; j < reg->channels; j++) { if (p2p_channel_to_freq(reg->reg_class, reg->channel[j]) == (int) freq) return 1; } } return 0; } int p2p_supported_freq(struct p2p_data *p2p, unsigned int freq) { u8 op_reg_class, op_channel; if (p2p_freq_to_channel(freq, &op_reg_class, &op_channel) < 0) return 0; return p2p_channels_includes(&p2p->cfg->channels, op_reg_class, op_channel); } int p2p_supported_freq_go(struct p2p_data *p2p, unsigned int freq) { u8 op_reg_class, op_channel; if (p2p_freq_to_channel(freq, &op_reg_class, &op_channel) < 0) return 0; return p2p_channels_includes(&p2p->cfg->channels, op_reg_class, op_channel) && !freq_range_list_includes(&p2p->no_go_freq, freq); } int p2p_supported_freq_cli(struct p2p_data *p2p, unsigned int freq) { u8 op_reg_class, op_channel; if (p2p_freq_to_channel(freq, &op_reg_class, &op_channel) < 0) return 0; return p2p_channels_includes(&p2p->cfg->channels, op_reg_class, op_channel) || p2p_channels_includes(&p2p->cfg->cli_channels, op_reg_class, op_channel); } unsigned int p2p_get_pref_freq(struct p2p_data *p2p, const struct p2p_channels *channels) { unsigned int i; int freq = 0; if (channels == NULL) { if (p2p->cfg->num_pref_chan) { freq = p2p_channel_to_freq( p2p->cfg->pref_chan[0].op_class, p2p->cfg->pref_chan[0].chan); if (freq < 0) freq = 0; } return freq; } for (i = 0; p2p->cfg->pref_chan && i < p2p->cfg->num_pref_chan; i++) { freq = p2p_channel_to_freq(p2p->cfg->pref_chan[i].op_class, p2p->cfg->pref_chan[i].chan); if (p2p_channels_includes_freq(channels, freq)) return freq; } return 0; } void p2p_channels_dump(struct p2p_data *p2p, const char *title, const struct p2p_channels *chan) { char buf[500], *pos, *end; size_t i, j; int ret; pos = buf; end = pos + sizeof(buf); for (i = 0; i < chan->reg_classes; i++) { const struct p2p_reg_class *c; c = &chan->reg_class[i]; ret = os_snprintf(pos, end - pos, " %u:", c->reg_class); if (ret < 0 || ret >= end - pos) break; pos += ret; for (j = 0; j < c->channels; j++) { ret = os_snprintf(pos, end - pos, "%s%u", j == 0 ? "" : ",", c->channel[j]); if (ret < 0 || ret >= end - pos) break; pos += ret; } } *pos = '\0'; p2p_dbg(p2p, "%s:%s", title, buf); } int p2p_channel_select(struct p2p_channels *chans, const int *classes, u8 *op_class, u8 *op_channel) { unsigned int i, j, r; for (j = 0; classes[j]; j++) { for (i = 0; i < chans->reg_classes; i++) { struct p2p_reg_class *c = &chans->reg_class[i]; if (c->channels == 0) continue; if (c->reg_class == classes[j]) { /* * Pick one of the available channels in the * operating class at random. */ os_get_random((u8 *) &r, sizeof(r)); r %= c->channels; *op_class = c->reg_class; *op_channel = c->channel[r]; return 0; } } } return -1; } wpa-2.1/src/radius/000077500000000000000000000000001227414666700142405ustar00rootroot00000000000000wpa-2.1/src/radius/.gitignore000066400000000000000000000000141227414666700162230ustar00rootroot00000000000000libradius.a wpa-2.1/src/radius/Makefile000066400000000000000000000004431227414666700157010ustar00rootroot00000000000000all: libradius.a clean: rm -f *~ *.o *.d *.gcno *.gcda *.gcov libradius.a install: @echo Nothing to be made. include ../lib.rules CFLAGS += -DCONFIG_IPV6 LIB_OBJS= \ radius.o \ radius_client.o \ radius_server.o libradius.a: $(LIB_OBJS) $(AR) crT $@ $? -include $(OBJS:%.o=%.d) wpa-2.1/src/radius/radius.c000066400000000000000000001123301227414666700156730ustar00rootroot00000000000000/* * RADIUS message processing * Copyright (c) 2002-2009, 2011-2012, 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/wpabuf.h" #include "crypto/md5.h" #include "crypto/crypto.h" #include "radius.h" /** * struct radius_msg - RADIUS message structure for new and parsed messages */ struct radius_msg { /** * buf - Allocated buffer for RADIUS message */ struct wpabuf *buf; /** * hdr - Pointer to the RADIUS header in buf */ struct radius_hdr *hdr; /** * attr_pos - Array of indexes to attributes * * The values are number of bytes from buf to the beginning of * struct radius_attr_hdr. */ size_t *attr_pos; /** * attr_size - Total size of the attribute pointer array */ size_t attr_size; /** * attr_used - Total number of attributes in the array */ size_t attr_used; }; struct radius_hdr * radius_msg_get_hdr(struct radius_msg *msg) { return msg->hdr; } struct wpabuf * radius_msg_get_buf(struct radius_msg *msg) { return msg->buf; } static struct radius_attr_hdr * radius_get_attr_hdr(struct radius_msg *msg, int idx) { return (struct radius_attr_hdr *) (wpabuf_mhead_u8(msg->buf) + msg->attr_pos[idx]); } static void radius_msg_set_hdr(struct radius_msg *msg, u8 code, u8 identifier) { msg->hdr->code = code; msg->hdr->identifier = identifier; } static int radius_msg_initialize(struct radius_msg *msg) { msg->attr_pos = os_calloc(RADIUS_DEFAULT_ATTR_COUNT, sizeof(*msg->attr_pos)); if (msg->attr_pos == NULL) return -1; msg->attr_size = RADIUS_DEFAULT_ATTR_COUNT; msg->attr_used = 0; return 0; } /** * radius_msg_new - Create a new RADIUS message * @code: Code for RADIUS header * @identifier: Identifier for RADIUS header * Returns: Context for RADIUS message or %NULL on failure * * The caller is responsible for freeing the returned data with * radius_msg_free(). */ struct radius_msg * radius_msg_new(u8 code, u8 identifier) { struct radius_msg *msg; msg = os_zalloc(sizeof(*msg)); if (msg == NULL) return NULL; msg->buf = wpabuf_alloc(RADIUS_DEFAULT_MSG_SIZE); if (msg->buf == NULL || radius_msg_initialize(msg)) { radius_msg_free(msg); return NULL; } msg->hdr = wpabuf_put(msg->buf, sizeof(struct radius_hdr)); radius_msg_set_hdr(msg, code, identifier); return msg; } /** * radius_msg_free - Free a RADIUS message * @msg: RADIUS message from radius_msg_new() or radius_msg_parse() */ void radius_msg_free(struct radius_msg *msg) { if (msg == NULL) return; wpabuf_free(msg->buf); os_free(msg->attr_pos); os_free(msg); } static const char *radius_code_string(u8 code) { switch (code) { case RADIUS_CODE_ACCESS_REQUEST: return "Access-Request"; case RADIUS_CODE_ACCESS_ACCEPT: return "Access-Accept"; case RADIUS_CODE_ACCESS_REJECT: return "Access-Reject"; case RADIUS_CODE_ACCOUNTING_REQUEST: return "Accounting-Request"; case RADIUS_CODE_ACCOUNTING_RESPONSE: return "Accounting-Response"; case RADIUS_CODE_ACCESS_CHALLENGE: return "Access-Challenge"; case RADIUS_CODE_STATUS_SERVER: return "Status-Server"; case RADIUS_CODE_STATUS_CLIENT: return "Status-Client"; case RADIUS_CODE_RESERVED: return "Reserved"; case RADIUS_CODE_DISCONNECT_REQUEST: return "Disconnect-Request"; case RADIUS_CODE_DISCONNECT_ACK: return "Disconnect-ACK"; case RADIUS_CODE_DISCONNECT_NAK: return "Disconnect-NAK"; case RADIUS_CODE_COA_REQUEST: return "CoA-Request"; case RADIUS_CODE_COA_ACK: return "CoA-ACK"; case RADIUS_CODE_COA_NAK: return "CoA-NAK"; default: return "?Unknown?"; } } struct radius_attr_type { u8 type; char *name; enum { RADIUS_ATTR_UNDIST, RADIUS_ATTR_TEXT, RADIUS_ATTR_IP, RADIUS_ATTR_HEXDUMP, RADIUS_ATTR_INT32, RADIUS_ATTR_IPV6 } data_type; }; static struct radius_attr_type radius_attrs[] = { { RADIUS_ATTR_USER_NAME, "User-Name", RADIUS_ATTR_TEXT }, { RADIUS_ATTR_USER_PASSWORD, "User-Password", RADIUS_ATTR_UNDIST }, { RADIUS_ATTR_NAS_IP_ADDRESS, "NAS-IP-Address", RADIUS_ATTR_IP }, { RADIUS_ATTR_NAS_PORT, "NAS-Port", RADIUS_ATTR_INT32 }, { RADIUS_ATTR_FRAMED_MTU, "Framed-MTU", RADIUS_ATTR_INT32 }, { RADIUS_ATTR_REPLY_MESSAGE, "Reply-Message", RADIUS_ATTR_TEXT }, { RADIUS_ATTR_STATE, "State", RADIUS_ATTR_UNDIST }, { RADIUS_ATTR_CLASS, "Class", RADIUS_ATTR_UNDIST }, { RADIUS_ATTR_VENDOR_SPECIFIC, "Vendor-Specific", RADIUS_ATTR_UNDIST }, { RADIUS_ATTR_SESSION_TIMEOUT, "Session-Timeout", RADIUS_ATTR_INT32 }, { RADIUS_ATTR_IDLE_TIMEOUT, "Idle-Timeout", RADIUS_ATTR_INT32 }, { RADIUS_ATTR_TERMINATION_ACTION, "Termination-Action", RADIUS_ATTR_INT32 }, { RADIUS_ATTR_CALLED_STATION_ID, "Called-Station-Id", RADIUS_ATTR_TEXT }, { RADIUS_ATTR_CALLING_STATION_ID, "Calling-Station-Id", RADIUS_ATTR_TEXT }, { RADIUS_ATTR_NAS_IDENTIFIER, "NAS-Identifier", RADIUS_ATTR_TEXT }, { RADIUS_ATTR_PROXY_STATE, "Proxy-State", RADIUS_ATTR_UNDIST }, { RADIUS_ATTR_ACCT_STATUS_TYPE, "Acct-Status-Type", RADIUS_ATTR_INT32 }, { RADIUS_ATTR_ACCT_DELAY_TIME, "Acct-Delay-Time", RADIUS_ATTR_INT32 }, { RADIUS_ATTR_ACCT_INPUT_OCTETS, "Acct-Input-Octets", RADIUS_ATTR_INT32 }, { RADIUS_ATTR_ACCT_OUTPUT_OCTETS, "Acct-Output-Octets", RADIUS_ATTR_INT32 }, { RADIUS_ATTR_ACCT_SESSION_ID, "Acct-Session-Id", RADIUS_ATTR_TEXT }, { RADIUS_ATTR_ACCT_AUTHENTIC, "Acct-Authentic", RADIUS_ATTR_INT32 }, { RADIUS_ATTR_ACCT_SESSION_TIME, "Acct-Session-Time", RADIUS_ATTR_INT32 }, { RADIUS_ATTR_ACCT_INPUT_PACKETS, "Acct-Input-Packets", RADIUS_ATTR_INT32 }, { RADIUS_ATTR_ACCT_OUTPUT_PACKETS, "Acct-Output-Packets", RADIUS_ATTR_INT32 }, { RADIUS_ATTR_ACCT_TERMINATE_CAUSE, "Acct-Terminate-Cause", RADIUS_ATTR_INT32 }, { RADIUS_ATTR_ACCT_MULTI_SESSION_ID, "Acct-Multi-Session-Id", RADIUS_ATTR_TEXT }, { RADIUS_ATTR_ACCT_LINK_COUNT, "Acct-Link-Count", RADIUS_ATTR_INT32 }, { RADIUS_ATTR_ACCT_INPUT_GIGAWORDS, "Acct-Input-Gigawords", RADIUS_ATTR_INT32 }, { RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS, "Acct-Output-Gigawords", RADIUS_ATTR_INT32 }, { RADIUS_ATTR_EVENT_TIMESTAMP, "Event-Timestamp", RADIUS_ATTR_INT32 }, { RADIUS_ATTR_NAS_PORT_TYPE, "NAS-Port-Type", RADIUS_ATTR_INT32 }, { RADIUS_ATTR_TUNNEL_TYPE, "Tunnel-Type", RADIUS_ATTR_HEXDUMP }, { RADIUS_ATTR_TUNNEL_MEDIUM_TYPE, "Tunnel-Medium-Type", RADIUS_ATTR_HEXDUMP }, { RADIUS_ATTR_TUNNEL_PASSWORD, "Tunnel-Password", RADIUS_ATTR_UNDIST }, { RADIUS_ATTR_CONNECT_INFO, "Connect-Info", RADIUS_ATTR_TEXT }, { RADIUS_ATTR_EAP_MESSAGE, "EAP-Message", RADIUS_ATTR_UNDIST }, { RADIUS_ATTR_MESSAGE_AUTHENTICATOR, "Message-Authenticator", RADIUS_ATTR_UNDIST }, { RADIUS_ATTR_TUNNEL_PRIVATE_GROUP_ID, "Tunnel-Private-Group-Id", RADIUS_ATTR_HEXDUMP }, { RADIUS_ATTR_ACCT_INTERIM_INTERVAL, "Acct-Interim-Interval", RADIUS_ATTR_INT32 }, { RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, "Chargeable-User-Identity", RADIUS_ATTR_TEXT }, { RADIUS_ATTR_NAS_IPV6_ADDRESS, "NAS-IPv6-Address", RADIUS_ATTR_IPV6 }, { RADIUS_ATTR_ERROR_CAUSE, "Error-Cause", RADIUS_ATTR_INT32 } }; #define RADIUS_ATTRS ARRAY_SIZE(radius_attrs) static struct radius_attr_type *radius_get_attr_type(u8 type) { size_t i; for (i = 0; i < RADIUS_ATTRS; i++) { if (type == radius_attrs[i].type) return &radius_attrs[i]; } return NULL; } static void print_char(char c) { if (c >= 32 && c < 127) printf("%c", c); else printf("<%02x>", c); } static void radius_msg_dump_attr(struct radius_attr_hdr *hdr) { struct radius_attr_type *attr; int i, len; unsigned char *pos; attr = radius_get_attr_type(hdr->type); printf(" Attribute %d (%s) length=%d\n", hdr->type, attr ? attr->name : "?Unknown?", hdr->length); if (attr == NULL || hdr->length < sizeof(struct radius_attr_hdr)) return; len = hdr->length - sizeof(struct radius_attr_hdr); pos = (unsigned char *) (hdr + 1); switch (attr->data_type) { case RADIUS_ATTR_TEXT: printf(" Value: '"); for (i = 0; i < len; i++) print_char(pos[i]); printf("'\n"); break; case RADIUS_ATTR_IP: if (len == 4) { struct in_addr addr; os_memcpy(&addr, pos, 4); printf(" Value: %s\n", inet_ntoa(addr)); } else printf(" Invalid IP address length %d\n", len); break; #ifdef CONFIG_IPV6 case RADIUS_ATTR_IPV6: if (len == 16) { char buf[128]; const char *atxt; struct in6_addr *addr = (struct in6_addr *) pos; atxt = inet_ntop(AF_INET6, addr, buf, sizeof(buf)); printf(" Value: %s\n", atxt ? atxt : "?"); } else printf(" Invalid IPv6 address length %d\n", len); break; #endif /* CONFIG_IPV6 */ case RADIUS_ATTR_HEXDUMP: case RADIUS_ATTR_UNDIST: printf(" Value:"); for (i = 0; i < len; i++) printf(" %02x", pos[i]); printf("\n"); break; case RADIUS_ATTR_INT32: if (len == 4) printf(" Value: %u\n", WPA_GET_BE32(pos)); else printf(" Invalid INT32 length %d\n", len); break; default: break; } } void radius_msg_dump(struct radius_msg *msg) { size_t i; printf("RADIUS message: code=%d (%s) identifier=%d length=%d\n", msg->hdr->code, radius_code_string(msg->hdr->code), msg->hdr->identifier, be_to_host16(msg->hdr->length)); for (i = 0; i < msg->attr_used; i++) { struct radius_attr_hdr *attr = radius_get_attr_hdr(msg, i); radius_msg_dump_attr(attr); } } int radius_msg_finish(struct radius_msg *msg, const u8 *secret, size_t secret_len) { if (secret) { u8 auth[MD5_MAC_LEN]; struct radius_attr_hdr *attr; os_memset(auth, 0, MD5_MAC_LEN); attr = radius_msg_add_attr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR, auth, MD5_MAC_LEN); if (attr == NULL) { wpa_printf(MSG_WARNING, "RADIUS: Could not add " "Message-Authenticator"); return -1; } msg->hdr->length = host_to_be16(wpabuf_len(msg->buf)); hmac_md5(secret, secret_len, wpabuf_head(msg->buf), wpabuf_len(msg->buf), (u8 *) (attr + 1)); } else msg->hdr->length = host_to_be16(wpabuf_len(msg->buf)); if (wpabuf_len(msg->buf) > 0xffff) { wpa_printf(MSG_WARNING, "RADIUS: Too long message (%lu)", (unsigned long) wpabuf_len(msg->buf)); return -1; } return 0; } int radius_msg_finish_srv(struct radius_msg *msg, const u8 *secret, size_t secret_len, const u8 *req_authenticator) { u8 auth[MD5_MAC_LEN]; struct radius_attr_hdr *attr; const u8 *addr[4]; size_t len[4]; os_memset(auth, 0, MD5_MAC_LEN); attr = radius_msg_add_attr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR, auth, MD5_MAC_LEN); if (attr == NULL) { printf("WARNING: Could not add Message-Authenticator\n"); return -1; } msg->hdr->length = host_to_be16(wpabuf_len(msg->buf)); os_memcpy(msg->hdr->authenticator, req_authenticator, sizeof(msg->hdr->authenticator)); hmac_md5(secret, secret_len, wpabuf_head(msg->buf), wpabuf_len(msg->buf), (u8 *) (attr + 1)); /* ResponseAuth = MD5(Code+ID+Length+RequestAuth+Attributes+Secret) */ addr[0] = (u8 *) msg->hdr; len[0] = 1 + 1 + 2; addr[1] = req_authenticator; len[1] = MD5_MAC_LEN; addr[2] = wpabuf_head_u8(msg->buf) + sizeof(struct radius_hdr); len[2] = wpabuf_len(msg->buf) - sizeof(struct radius_hdr); addr[3] = secret; len[3] = secret_len; md5_vector(4, addr, len, msg->hdr->authenticator); if (wpabuf_len(msg->buf) > 0xffff) { wpa_printf(MSG_WARNING, "RADIUS: Too long message (%lu)", (unsigned long) wpabuf_len(msg->buf)); return -1; } return 0; } int radius_msg_finish_das_resp(struct radius_msg *msg, const u8 *secret, size_t secret_len, const struct radius_hdr *req_hdr) { const u8 *addr[2]; size_t len[2]; u8 auth[MD5_MAC_LEN]; struct radius_attr_hdr *attr; os_memset(auth, 0, MD5_MAC_LEN); attr = radius_msg_add_attr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR, auth, MD5_MAC_LEN); if (attr == NULL) { wpa_printf(MSG_WARNING, "Could not add Message-Authenticator"); return -1; } msg->hdr->length = host_to_be16(wpabuf_len(msg->buf)); os_memcpy(msg->hdr->authenticator, req_hdr->authenticator, 16); hmac_md5(secret, secret_len, wpabuf_head(msg->buf), wpabuf_len(msg->buf), (u8 *) (attr + 1)); /* ResponseAuth = MD5(Code+ID+Length+RequestAuth+Attributes+Secret) */ addr[0] = wpabuf_head_u8(msg->buf); len[0] = wpabuf_len(msg->buf); addr[1] = secret; len[1] = secret_len; if (md5_vector(2, addr, len, msg->hdr->authenticator) < 0) return -1; if (wpabuf_len(msg->buf) > 0xffff) { wpa_printf(MSG_WARNING, "RADIUS: Too long message (%lu)", (unsigned long) wpabuf_len(msg->buf)); return -1; } return 0; } void radius_msg_finish_acct(struct radius_msg *msg, const u8 *secret, size_t secret_len) { const u8 *addr[2]; size_t len[2]; msg->hdr->length = host_to_be16(wpabuf_len(msg->buf)); os_memset(msg->hdr->authenticator, 0, MD5_MAC_LEN); addr[0] = wpabuf_head(msg->buf); len[0] = wpabuf_len(msg->buf); addr[1] = secret; len[1] = secret_len; md5_vector(2, addr, len, msg->hdr->authenticator); if (wpabuf_len(msg->buf) > 0xffff) { wpa_printf(MSG_WARNING, "RADIUS: Too long messages (%lu)", (unsigned long) wpabuf_len(msg->buf)); } } int radius_msg_verify_acct_req(struct radius_msg *msg, const u8 *secret, size_t secret_len) { const u8 *addr[4]; size_t len[4]; u8 zero[MD5_MAC_LEN]; u8 hash[MD5_MAC_LEN]; os_memset(zero, 0, sizeof(zero)); addr[0] = (u8 *) msg->hdr; len[0] = sizeof(struct radius_hdr) - MD5_MAC_LEN; addr[1] = zero; len[1] = MD5_MAC_LEN; addr[2] = (u8 *) (msg->hdr + 1); len[2] = wpabuf_len(msg->buf) - sizeof(struct radius_hdr); addr[3] = secret; len[3] = secret_len; md5_vector(4, addr, len, hash); return os_memcmp(msg->hdr->authenticator, hash, MD5_MAC_LEN) != 0; } int radius_msg_verify_das_req(struct radius_msg *msg, const u8 *secret, size_t secret_len) { const u8 *addr[4]; size_t len[4]; u8 zero[MD5_MAC_LEN]; u8 hash[MD5_MAC_LEN]; u8 auth[MD5_MAC_LEN], orig[MD5_MAC_LEN]; u8 orig_authenticator[16]; struct radius_attr_hdr *attr = NULL, *tmp; size_t i; os_memset(zero, 0, sizeof(zero)); addr[0] = (u8 *) msg->hdr; len[0] = sizeof(struct radius_hdr) - MD5_MAC_LEN; addr[1] = zero; len[1] = MD5_MAC_LEN; addr[2] = (u8 *) (msg->hdr + 1); len[2] = wpabuf_len(msg->buf) - sizeof(struct radius_hdr); addr[3] = secret; len[3] = secret_len; md5_vector(4, addr, len, hash); if (os_memcmp(msg->hdr->authenticator, hash, MD5_MAC_LEN) != 0) return 1; for (i = 0; i < msg->attr_used; i++) { tmp = radius_get_attr_hdr(msg, i); if (tmp->type == RADIUS_ATTR_MESSAGE_AUTHENTICATOR) { if (attr != NULL) { wpa_printf(MSG_WARNING, "Multiple " "Message-Authenticator attributes " "in RADIUS message"); return 1; } attr = tmp; } } if (attr == NULL) { /* Message-Authenticator is MAY; not required */ return 0; } os_memcpy(orig, attr + 1, MD5_MAC_LEN); os_memset(attr + 1, 0, MD5_MAC_LEN); os_memcpy(orig_authenticator, msg->hdr->authenticator, sizeof(orig_authenticator)); os_memset(msg->hdr->authenticator, 0, sizeof(msg->hdr->authenticator)); hmac_md5(secret, secret_len, wpabuf_head(msg->buf), wpabuf_len(msg->buf), auth); os_memcpy(attr + 1, orig, MD5_MAC_LEN); os_memcpy(msg->hdr->authenticator, orig_authenticator, sizeof(orig_authenticator)); return os_memcmp(orig, auth, MD5_MAC_LEN) != 0; } static int radius_msg_add_attr_to_array(struct radius_msg *msg, struct radius_attr_hdr *attr) { if (msg->attr_used >= msg->attr_size) { size_t *nattr_pos; int nlen = msg->attr_size * 2; nattr_pos = os_realloc_array(msg->attr_pos, nlen, sizeof(*msg->attr_pos)); if (nattr_pos == NULL) return -1; msg->attr_pos = nattr_pos; msg->attr_size = nlen; } msg->attr_pos[msg->attr_used++] = (unsigned char *) attr - wpabuf_head_u8(msg->buf); return 0; } struct radius_attr_hdr *radius_msg_add_attr(struct radius_msg *msg, u8 type, const u8 *data, size_t data_len) { size_t buf_needed; struct radius_attr_hdr *attr; if (data_len > RADIUS_MAX_ATTR_LEN) { printf("radius_msg_add_attr: too long attribute (%lu bytes)\n", (unsigned long) data_len); return NULL; } buf_needed = sizeof(*attr) + data_len; if (wpabuf_tailroom(msg->buf) < buf_needed) { /* allocate more space for message buffer */ if (wpabuf_resize(&msg->buf, buf_needed) < 0) return NULL; msg->hdr = wpabuf_mhead(msg->buf); } attr = wpabuf_put(msg->buf, sizeof(struct radius_attr_hdr)); attr->type = type; attr->length = sizeof(*attr) + data_len; wpabuf_put_data(msg->buf, data, data_len); if (radius_msg_add_attr_to_array(msg, attr)) return NULL; return attr; } /** * radius_msg_parse - Parse a RADIUS message * @data: RADIUS message to be parsed * @len: Length of data buffer in octets * Returns: Parsed RADIUS message or %NULL on failure * * This parses a RADIUS message and makes a copy of its data. The caller is * responsible for freeing the returned data with radius_msg_free(). */ struct radius_msg * radius_msg_parse(const u8 *data, size_t len) { struct radius_msg *msg; struct radius_hdr *hdr; struct radius_attr_hdr *attr; size_t msg_len; unsigned char *pos, *end; if (data == NULL || len < sizeof(*hdr)) return NULL; hdr = (struct radius_hdr *) data; msg_len = be_to_host16(hdr->length); if (msg_len < sizeof(*hdr) || msg_len > len) { wpa_printf(MSG_INFO, "RADIUS: Invalid message length"); return NULL; } if (msg_len < len) { wpa_printf(MSG_DEBUG, "RADIUS: Ignored %lu extra bytes after " "RADIUS message", (unsigned long) len - msg_len); } msg = os_zalloc(sizeof(*msg)); if (msg == NULL) return NULL; msg->buf = wpabuf_alloc_copy(data, msg_len); if (msg->buf == NULL || radius_msg_initialize(msg)) { radius_msg_free(msg); return NULL; } msg->hdr = wpabuf_mhead(msg->buf); /* parse attributes */ pos = wpabuf_mhead_u8(msg->buf) + sizeof(struct radius_hdr); end = wpabuf_mhead_u8(msg->buf) + wpabuf_len(msg->buf); while (pos < end) { if ((size_t) (end - pos) < sizeof(*attr)) goto fail; attr = (struct radius_attr_hdr *) pos; if (pos + attr->length > end || attr->length < sizeof(*attr)) goto fail; /* TODO: check that attr->length is suitable for attr->type */ if (radius_msg_add_attr_to_array(msg, attr)) goto fail; pos += attr->length; } return msg; fail: radius_msg_free(msg); return NULL; } int radius_msg_add_eap(struct radius_msg *msg, const u8 *data, size_t data_len) { const u8 *pos = data; size_t left = data_len; while (left > 0) { int len; if (left > RADIUS_MAX_ATTR_LEN) len = RADIUS_MAX_ATTR_LEN; else len = left; if (!radius_msg_add_attr(msg, RADIUS_ATTR_EAP_MESSAGE, pos, len)) return 0; pos += len; left -= len; } return 1; } struct wpabuf * radius_msg_get_eap(struct radius_msg *msg) { struct wpabuf *eap; size_t len, i; struct radius_attr_hdr *attr; if (msg == NULL) return NULL; len = 0; for (i = 0; i < msg->attr_used; i++) { attr = radius_get_attr_hdr(msg, i); if (attr->type == RADIUS_ATTR_EAP_MESSAGE && attr->length > sizeof(struct radius_attr_hdr)) len += attr->length - sizeof(struct radius_attr_hdr); } if (len == 0) return NULL; eap = wpabuf_alloc(len); if (eap == NULL) return NULL; for (i = 0; i < msg->attr_used; i++) { attr = radius_get_attr_hdr(msg, i); if (attr->type == RADIUS_ATTR_EAP_MESSAGE && attr->length > sizeof(struct radius_attr_hdr)) { int flen = attr->length - sizeof(*attr); wpabuf_put_data(eap, attr + 1, flen); } } return eap; } int radius_msg_verify_msg_auth(struct radius_msg *msg, const u8 *secret, size_t secret_len, const u8 *req_auth) { u8 auth[MD5_MAC_LEN], orig[MD5_MAC_LEN]; u8 orig_authenticator[16]; struct radius_attr_hdr *attr = NULL, *tmp; size_t i; for (i = 0; i < msg->attr_used; i++) { tmp = radius_get_attr_hdr(msg, i); if (tmp->type == RADIUS_ATTR_MESSAGE_AUTHENTICATOR) { if (attr != NULL) { printf("Multiple Message-Authenticator " "attributes in RADIUS message\n"); return 1; } attr = tmp; } } if (attr == NULL) { printf("No Message-Authenticator attribute found\n"); return 1; } os_memcpy(orig, attr + 1, MD5_MAC_LEN); os_memset(attr + 1, 0, MD5_MAC_LEN); if (req_auth) { os_memcpy(orig_authenticator, msg->hdr->authenticator, sizeof(orig_authenticator)); os_memcpy(msg->hdr->authenticator, req_auth, sizeof(msg->hdr->authenticator)); } hmac_md5(secret, secret_len, wpabuf_head(msg->buf), wpabuf_len(msg->buf), auth); os_memcpy(attr + 1, orig, MD5_MAC_LEN); if (req_auth) { os_memcpy(msg->hdr->authenticator, orig_authenticator, sizeof(orig_authenticator)); } if (os_memcmp(orig, auth, MD5_MAC_LEN) != 0) { printf("Invalid Message-Authenticator!\n"); return 1; } return 0; } int radius_msg_verify(struct radius_msg *msg, const u8 *secret, size_t secret_len, struct radius_msg *sent_msg, int auth) { const u8 *addr[4]; size_t len[4]; u8 hash[MD5_MAC_LEN]; if (sent_msg == NULL) { printf("No matching Access-Request message found\n"); return 1; } if (auth && radius_msg_verify_msg_auth(msg, secret, secret_len, sent_msg->hdr->authenticator)) { return 1; } /* ResponseAuth = MD5(Code+ID+Length+RequestAuth+Attributes+Secret) */ addr[0] = (u8 *) msg->hdr; len[0] = 1 + 1 + 2; addr[1] = sent_msg->hdr->authenticator; len[1] = MD5_MAC_LEN; addr[2] = wpabuf_head_u8(msg->buf) + sizeof(struct radius_hdr); len[2] = wpabuf_len(msg->buf) - sizeof(struct radius_hdr); addr[3] = secret; len[3] = secret_len; md5_vector(4, addr, len, hash); if (os_memcmp(hash, msg->hdr->authenticator, MD5_MAC_LEN) != 0) { printf("Response Authenticator invalid!\n"); return 1; } return 0; } int radius_msg_copy_attr(struct radius_msg *dst, struct radius_msg *src, u8 type) { struct radius_attr_hdr *attr; size_t i; int count = 0; for (i = 0; i < src->attr_used; i++) { attr = radius_get_attr_hdr(src, i); if (attr->type == type && attr->length >= sizeof(*attr)) { if (!radius_msg_add_attr(dst, type, (u8 *) (attr + 1), attr->length - sizeof(*attr))) return -1; count++; } } return count; } /* Create Request Authenticator. The value should be unique over the lifetime * of the shared secret between authenticator and authentication server. * Use one-way MD5 hash calculated from current timestamp and some data given * by the caller. */ void radius_msg_make_authenticator(struct radius_msg *msg, const u8 *data, size_t len) { struct os_time tv; long int l; const u8 *addr[3]; size_t elen[3]; os_get_time(&tv); l = os_random(); addr[0] = (u8 *) &tv; elen[0] = sizeof(tv); addr[1] = data; elen[1] = len; addr[2] = (u8 *) &l; elen[2] = sizeof(l); md5_vector(3, addr, elen, msg->hdr->authenticator); } /* Get Vendor-specific RADIUS Attribute from a parsed RADIUS message. * Returns the Attribute payload and sets alen to indicate the length of the * payload if a vendor attribute with subtype is found, otherwise returns NULL. * The returned payload is allocated with os_malloc() and caller must free it * by calling os_free(). */ static u8 *radius_msg_get_vendor_attr(struct radius_msg *msg, u32 vendor, u8 subtype, size_t *alen) { u8 *data, *pos; size_t i, len; if (msg == NULL) return NULL; for (i = 0; i < msg->attr_used; i++) { struct radius_attr_hdr *attr = radius_get_attr_hdr(msg, i); size_t left; u32 vendor_id; struct radius_attr_vendor *vhdr; if (attr->type != RADIUS_ATTR_VENDOR_SPECIFIC || attr->length < sizeof(*attr)) continue; left = attr->length - sizeof(*attr); if (left < 4) continue; pos = (u8 *) (attr + 1); os_memcpy(&vendor_id, pos, 4); pos += 4; left -= 4; if (ntohl(vendor_id) != vendor) continue; while (left >= sizeof(*vhdr)) { vhdr = (struct radius_attr_vendor *) pos; if (vhdr->vendor_length > left || vhdr->vendor_length < sizeof(*vhdr)) { left = 0; break; } if (vhdr->vendor_type != subtype) { pos += vhdr->vendor_length; left -= vhdr->vendor_length; continue; } len = vhdr->vendor_length - sizeof(*vhdr); data = os_malloc(len); if (data == NULL) return NULL; os_memcpy(data, pos + sizeof(*vhdr), len); if (alen) *alen = len; return data; } } return NULL; } static u8 * decrypt_ms_key(const u8 *key, size_t len, const u8 *req_authenticator, const u8 *secret, size_t secret_len, size_t *reslen) { u8 *plain, *ppos, *res; const u8 *pos; size_t left, plen; u8 hash[MD5_MAC_LEN]; int i, first = 1; const u8 *addr[3]; size_t elen[3]; /* key: 16-bit salt followed by encrypted key info */ if (len < 2 + 16) return NULL; pos = key + 2; left = len - 2; if (left % 16) { printf("Invalid ms key len %lu\n", (unsigned long) left); return NULL; } plen = left; ppos = plain = os_malloc(plen); if (plain == NULL) return NULL; plain[0] = 0; while (left > 0) { /* b(1) = MD5(Secret + Request-Authenticator + Salt) * b(i) = MD5(Secret + c(i - 1)) for i > 1 */ addr[0] = secret; elen[0] = secret_len; if (first) { addr[1] = req_authenticator; elen[1] = MD5_MAC_LEN; addr[2] = key; elen[2] = 2; /* Salt */ } else { addr[1] = pos - MD5_MAC_LEN; elen[1] = MD5_MAC_LEN; } md5_vector(first ? 3 : 2, addr, elen, hash); first = 0; for (i = 0; i < MD5_MAC_LEN; i++) *ppos++ = *pos++ ^ hash[i]; left -= MD5_MAC_LEN; } if (plain[0] == 0 || plain[0] > plen - 1) { printf("Failed to decrypt MPPE key\n"); os_free(plain); return NULL; } res = os_malloc(plain[0]); if (res == NULL) { os_free(plain); return NULL; } os_memcpy(res, plain + 1, plain[0]); if (reslen) *reslen = plain[0]; os_free(plain); return res; } static void encrypt_ms_key(const u8 *key, size_t key_len, u16 salt, const u8 *req_authenticator, const u8 *secret, size_t secret_len, u8 *ebuf, size_t *elen) { int i, len, first = 1; u8 hash[MD5_MAC_LEN], saltbuf[2], *pos; const u8 *addr[3]; size_t _len[3]; WPA_PUT_BE16(saltbuf, salt); len = 1 + key_len; if (len & 0x0f) { len = (len & 0xf0) + 16; } os_memset(ebuf, 0, len); ebuf[0] = key_len; os_memcpy(ebuf + 1, key, key_len); *elen = len; pos = ebuf; while (len > 0) { /* b(1) = MD5(Secret + Request-Authenticator + Salt) * b(i) = MD5(Secret + c(i - 1)) for i > 1 */ addr[0] = secret; _len[0] = secret_len; if (first) { addr[1] = req_authenticator; _len[1] = MD5_MAC_LEN; addr[2] = saltbuf; _len[2] = sizeof(saltbuf); } else { addr[1] = pos - MD5_MAC_LEN; _len[1] = MD5_MAC_LEN; } md5_vector(first ? 3 : 2, addr, _len, hash); first = 0; for (i = 0; i < MD5_MAC_LEN; i++) *pos++ ^= hash[i]; len -= MD5_MAC_LEN; } } struct radius_ms_mppe_keys * radius_msg_get_ms_keys(struct radius_msg *msg, struct radius_msg *sent_msg, const u8 *secret, size_t secret_len) { u8 *key; size_t keylen; struct radius_ms_mppe_keys *keys; if (msg == NULL || sent_msg == NULL) return NULL; keys = os_zalloc(sizeof(*keys)); if (keys == NULL) return NULL; key = radius_msg_get_vendor_attr(msg, RADIUS_VENDOR_ID_MICROSOFT, RADIUS_VENDOR_ATTR_MS_MPPE_SEND_KEY, &keylen); if (key) { keys->send = decrypt_ms_key(key, keylen, sent_msg->hdr->authenticator, secret, secret_len, &keys->send_len); os_free(key); } key = radius_msg_get_vendor_attr(msg, RADIUS_VENDOR_ID_MICROSOFT, RADIUS_VENDOR_ATTR_MS_MPPE_RECV_KEY, &keylen); if (key) { keys->recv = decrypt_ms_key(key, keylen, sent_msg->hdr->authenticator, secret, secret_len, &keys->recv_len); os_free(key); } return keys; } struct radius_ms_mppe_keys * radius_msg_get_cisco_keys(struct radius_msg *msg, struct radius_msg *sent_msg, const u8 *secret, size_t secret_len) { u8 *key; size_t keylen; struct radius_ms_mppe_keys *keys; if (msg == NULL || sent_msg == NULL) return NULL; keys = os_zalloc(sizeof(*keys)); if (keys == NULL) return NULL; key = radius_msg_get_vendor_attr(msg, RADIUS_VENDOR_ID_CISCO, RADIUS_CISCO_AV_PAIR, &keylen); if (key && keylen == 51 && os_memcmp(key, "leap:session-key=", 17) == 0) { keys->recv = decrypt_ms_key(key + 17, keylen - 17, sent_msg->hdr->authenticator, secret, secret_len, &keys->recv_len); } os_free(key); return keys; } int radius_msg_add_mppe_keys(struct radius_msg *msg, const u8 *req_authenticator, const u8 *secret, size_t secret_len, const u8 *send_key, size_t send_key_len, const u8 *recv_key, size_t recv_key_len) { struct radius_attr_hdr *attr; u32 vendor_id = htonl(RADIUS_VENDOR_ID_MICROSOFT); u8 *buf; struct radius_attr_vendor *vhdr; u8 *pos; size_t elen; int hlen; u16 salt; hlen = sizeof(vendor_id) + sizeof(*vhdr) + 2; /* MS-MPPE-Send-Key */ buf = os_malloc(hlen + send_key_len + 16); if (buf == NULL) { return 0; } pos = buf; os_memcpy(pos, &vendor_id, sizeof(vendor_id)); pos += sizeof(vendor_id); vhdr = (struct radius_attr_vendor *) pos; vhdr->vendor_type = RADIUS_VENDOR_ATTR_MS_MPPE_SEND_KEY; pos = (u8 *) (vhdr + 1); salt = os_random() | 0x8000; WPA_PUT_BE16(pos, salt); pos += 2; encrypt_ms_key(send_key, send_key_len, salt, req_authenticator, secret, secret_len, pos, &elen); vhdr->vendor_length = hlen + elen - sizeof(vendor_id); attr = radius_msg_add_attr(msg, RADIUS_ATTR_VENDOR_SPECIFIC, buf, hlen + elen); os_free(buf); if (attr == NULL) { return 0; } /* MS-MPPE-Recv-Key */ buf = os_malloc(hlen + send_key_len + 16); if (buf == NULL) { return 0; } pos = buf; os_memcpy(pos, &vendor_id, sizeof(vendor_id)); pos += sizeof(vendor_id); vhdr = (struct radius_attr_vendor *) pos; vhdr->vendor_type = RADIUS_VENDOR_ATTR_MS_MPPE_RECV_KEY; pos = (u8 *) (vhdr + 1); salt ^= 1; WPA_PUT_BE16(pos, salt); pos += 2; encrypt_ms_key(recv_key, recv_key_len, salt, req_authenticator, secret, secret_len, pos, &elen); vhdr->vendor_length = hlen + elen - sizeof(vendor_id); attr = radius_msg_add_attr(msg, RADIUS_ATTR_VENDOR_SPECIFIC, buf, hlen + elen); os_free(buf); if (attr == NULL) { return 0; } return 1; } /* Add User-Password attribute to a RADIUS message and encrypt it as specified * in RFC 2865, Chap. 5.2 */ struct radius_attr_hdr * radius_msg_add_attr_user_password(struct radius_msg *msg, const u8 *data, size_t data_len, const u8 *secret, size_t secret_len) { u8 buf[128]; size_t padlen, i, buf_len, pos; const u8 *addr[2]; size_t len[2]; u8 hash[16]; if (data_len > 128) return NULL; os_memcpy(buf, data, data_len); buf_len = data_len; padlen = data_len % 16; if (padlen && data_len < sizeof(buf)) { padlen = 16 - padlen; os_memset(buf + data_len, 0, padlen); buf_len += padlen; } addr[0] = secret; len[0] = secret_len; addr[1] = msg->hdr->authenticator; len[1] = 16; md5_vector(2, addr, len, hash); for (i = 0; i < 16; i++) buf[i] ^= hash[i]; pos = 16; while (pos < buf_len) { addr[0] = secret; len[0] = secret_len; addr[1] = &buf[pos - 16]; len[1] = 16; md5_vector(2, addr, len, hash); for (i = 0; i < 16; i++) buf[pos + i] ^= hash[i]; pos += 16; } return radius_msg_add_attr(msg, RADIUS_ATTR_USER_PASSWORD, buf, buf_len); } int radius_msg_get_attr(struct radius_msg *msg, u8 type, u8 *buf, size_t len) { struct radius_attr_hdr *attr = NULL, *tmp; size_t i, dlen; for (i = 0; i < msg->attr_used; i++) { tmp = radius_get_attr_hdr(msg, i); if (tmp->type == type) { attr = tmp; break; } } if (!attr || attr->length < sizeof(*attr)) return -1; dlen = attr->length - sizeof(*attr); if (buf) os_memcpy(buf, (attr + 1), dlen > len ? len : dlen); return dlen; } int radius_msg_get_attr_ptr(struct radius_msg *msg, u8 type, u8 **buf, size_t *len, const u8 *start) { size_t i; struct radius_attr_hdr *attr = NULL, *tmp; for (i = 0; i < msg->attr_used; i++) { tmp = radius_get_attr_hdr(msg, i); if (tmp->type == type && (start == NULL || (u8 *) tmp > start)) { attr = tmp; break; } } if (!attr || attr->length < sizeof(*attr)) return -1; *buf = (u8 *) (attr + 1); *len = attr->length - sizeof(*attr); return 0; } int radius_msg_count_attr(struct radius_msg *msg, u8 type, int min_len) { size_t i; int count; for (count = 0, i = 0; i < msg->attr_used; i++) { struct radius_attr_hdr *attr = radius_get_attr_hdr(msg, i); if (attr->type == type && attr->length >= sizeof(struct radius_attr_hdr) + min_len) count++; } return count; } struct radius_tunnel_attrs { int tag_used; int type; /* Tunnel-Type */ int medium_type; /* Tunnel-Medium-Type */ int vlanid; }; /** * radius_msg_get_vlanid - Parse RADIUS attributes for VLAN tunnel information * @msg: RADIUS message * Returns: VLAN ID for the first tunnel configuration of -1 if none is found */ int radius_msg_get_vlanid(struct radius_msg *msg) { struct radius_tunnel_attrs tunnel[RADIUS_TUNNEL_TAGS], *tun; size_t i; struct radius_attr_hdr *attr = NULL; const u8 *data; char buf[10]; size_t dlen; os_memset(&tunnel, 0, sizeof(tunnel)); for (i = 0; i < msg->attr_used; i++) { attr = radius_get_attr_hdr(msg, i); if (attr->length < sizeof(*attr)) return -1; data = (const u8 *) (attr + 1); dlen = attr->length - sizeof(*attr); if (attr->length < 3) continue; if (data[0] >= RADIUS_TUNNEL_TAGS) tun = &tunnel[0]; else tun = &tunnel[data[0]]; switch (attr->type) { case RADIUS_ATTR_TUNNEL_TYPE: if (attr->length != 6) break; tun->tag_used++; tun->type = WPA_GET_BE24(data + 1); break; case RADIUS_ATTR_TUNNEL_MEDIUM_TYPE: if (attr->length != 6) break; tun->tag_used++; tun->medium_type = WPA_GET_BE24(data + 1); break; case RADIUS_ATTR_TUNNEL_PRIVATE_GROUP_ID: if (data[0] < RADIUS_TUNNEL_TAGS) { data++; dlen--; } if (dlen >= sizeof(buf)) break; os_memcpy(buf, data, dlen); buf[dlen] = '\0'; tun->tag_used++; tun->vlanid = atoi(buf); break; } } for (i = 0; i < RADIUS_TUNNEL_TAGS; i++) { tun = &tunnel[i]; if (tun->tag_used && tun->type == RADIUS_TUNNEL_TYPE_VLAN && tun->medium_type == RADIUS_TUNNEL_MEDIUM_TYPE_802 && tun->vlanid > 0) return tun->vlanid; } return -1; } /** * radius_msg_get_tunnel_password - Parse RADIUS attribute Tunnel-Password * @msg: Received RADIUS message * @keylen: Length of returned password * @secret: RADIUS shared secret * @secret_len: Length of secret * @sent_msg: Sent RADIUS message * @n: Number of password attribute to return (starting with 0) * Returns: Pointer to n-th password (free with os_free) or %NULL */ char * radius_msg_get_tunnel_password(struct radius_msg *msg, int *keylen, const u8 *secret, size_t secret_len, struct radius_msg *sent_msg, size_t n) { u8 *buf = NULL; size_t buflen; const u8 *salt; u8 *str; const u8 *addr[3]; size_t len[3]; u8 hash[16]; u8 *pos; size_t i, j = 0; struct radius_attr_hdr *attr; const u8 *data; size_t dlen; const u8 *fdata = NULL; /* points to found item */ size_t fdlen = -1; char *ret = NULL; /* find n-th valid Tunnel-Password attribute */ for (i = 0; i < msg->attr_used; i++) { attr = radius_get_attr_hdr(msg, i); if (attr == NULL || attr->type != RADIUS_ATTR_TUNNEL_PASSWORD) { continue; } if (attr->length <= 5) continue; data = (const u8 *) (attr + 1); dlen = attr->length - sizeof(*attr); if (dlen <= 3 || dlen % 16 != 3) continue; j++; if (j <= n) continue; fdata = data; fdlen = dlen; break; } if (fdata == NULL) goto out; /* alloc writable memory for decryption */ buf = os_malloc(fdlen); if (buf == NULL) goto out; os_memcpy(buf, fdata, fdlen); buflen = fdlen; /* init pointers */ salt = buf + 1; str = buf + 3; /* decrypt blocks */ pos = buf + buflen - 16; /* last block */ while (pos >= str + 16) { /* all but the first block */ addr[0] = secret; len[0] = secret_len; addr[1] = pos - 16; len[1] = 16; md5_vector(2, addr, len, hash); for (i = 0; i < 16; i++) pos[i] ^= hash[i]; pos -= 16; } /* decrypt first block */ if (str != pos) goto out; addr[0] = secret; len[0] = secret_len; addr[1] = sent_msg->hdr->authenticator; len[1] = 16; addr[2] = salt; len[2] = 2; md5_vector(3, addr, len, hash); for (i = 0; i < 16; i++) pos[i] ^= hash[i]; /* derive plaintext length from first subfield */ *keylen = (unsigned char) str[0]; if ((u8 *) (str + *keylen) >= (u8 *) (buf + buflen)) { /* decryption error - invalid key length */ goto out; } if (*keylen == 0) { /* empty password */ goto out; } /* copy passphrase into new buffer */ ret = os_malloc(*keylen); if (ret) os_memcpy(ret, str + 1, *keylen); out: /* return new buffer */ os_free(buf); return ret; } void radius_free_class(struct radius_class_data *c) { size_t i; if (c == NULL) return; for (i = 0; i < c->count; i++) os_free(c->attr[i].data); os_free(c->attr); c->attr = NULL; c->count = 0; } int radius_copy_class(struct radius_class_data *dst, const struct radius_class_data *src) { size_t i; if (src->attr == NULL) return 0; dst->attr = os_calloc(src->count, sizeof(struct radius_attr_data)); if (dst->attr == NULL) return -1; dst->count = 0; for (i = 0; i < src->count; i++) { dst->attr[i].data = os_malloc(src->attr[i].len); if (dst->attr[i].data == NULL) break; dst->count++; os_memcpy(dst->attr[i].data, src->attr[i].data, src->attr[i].len); dst->attr[i].len = src->attr[i].len; } return 0; } u8 radius_msg_find_unlisted_attr(struct radius_msg *msg, u8 *attrs) { size_t i, j; struct radius_attr_hdr *attr; for (i = 0; i < msg->attr_used; i++) { attr = radius_get_attr_hdr(msg, i); for (j = 0; attrs[j]; j++) { if (attr->type == attrs[j]) break; } if (attrs[j] == 0) return attr->type; /* unlisted attr */ } return 0; } wpa-2.1/src/radius/radius.h000066400000000000000000000226771227414666700157160ustar00rootroot00000000000000/* * RADIUS message processing * Copyright (c) 2002-2009, 2012, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef RADIUS_H #define RADIUS_H /* RFC 2865 - RADIUS */ #ifdef _MSC_VER #pragma pack(push, 1) #endif /* _MSC_VER */ struct radius_hdr { u8 code; u8 identifier; be16 length; /* including this header */ u8 authenticator[16]; /* followed by length-20 octets of attributes */ } STRUCT_PACKED; enum { RADIUS_CODE_ACCESS_REQUEST = 1, RADIUS_CODE_ACCESS_ACCEPT = 2, RADIUS_CODE_ACCESS_REJECT = 3, RADIUS_CODE_ACCOUNTING_REQUEST = 4, RADIUS_CODE_ACCOUNTING_RESPONSE = 5, RADIUS_CODE_ACCESS_CHALLENGE = 11, RADIUS_CODE_STATUS_SERVER = 12, RADIUS_CODE_STATUS_CLIENT = 13, RADIUS_CODE_DISCONNECT_REQUEST = 40, RADIUS_CODE_DISCONNECT_ACK = 41, RADIUS_CODE_DISCONNECT_NAK = 42, RADIUS_CODE_COA_REQUEST = 43, RADIUS_CODE_COA_ACK = 44, RADIUS_CODE_COA_NAK = 45, RADIUS_CODE_RESERVED = 255 }; struct radius_attr_hdr { u8 type; u8 length; /* including this header */ /* followed by length-2 octets of attribute value */ } STRUCT_PACKED; #define RADIUS_MAX_ATTR_LEN (255 - sizeof(struct radius_attr_hdr)) enum { RADIUS_ATTR_USER_NAME = 1, RADIUS_ATTR_USER_PASSWORD = 2, RADIUS_ATTR_NAS_IP_ADDRESS = 4, RADIUS_ATTR_NAS_PORT = 5, RADIUS_ATTR_FRAMED_MTU = 12, RADIUS_ATTR_REPLY_MESSAGE = 18, RADIUS_ATTR_STATE = 24, RADIUS_ATTR_CLASS = 25, RADIUS_ATTR_VENDOR_SPECIFIC = 26, RADIUS_ATTR_SESSION_TIMEOUT = 27, RADIUS_ATTR_IDLE_TIMEOUT = 28, RADIUS_ATTR_TERMINATION_ACTION = 29, RADIUS_ATTR_CALLED_STATION_ID = 30, RADIUS_ATTR_CALLING_STATION_ID = 31, RADIUS_ATTR_NAS_IDENTIFIER = 32, RADIUS_ATTR_PROXY_STATE = 33, RADIUS_ATTR_ACCT_STATUS_TYPE = 40, RADIUS_ATTR_ACCT_DELAY_TIME = 41, RADIUS_ATTR_ACCT_INPUT_OCTETS = 42, RADIUS_ATTR_ACCT_OUTPUT_OCTETS = 43, RADIUS_ATTR_ACCT_SESSION_ID = 44, RADIUS_ATTR_ACCT_AUTHENTIC = 45, RADIUS_ATTR_ACCT_SESSION_TIME = 46, RADIUS_ATTR_ACCT_INPUT_PACKETS = 47, RADIUS_ATTR_ACCT_OUTPUT_PACKETS = 48, RADIUS_ATTR_ACCT_TERMINATE_CAUSE = 49, RADIUS_ATTR_ACCT_MULTI_SESSION_ID = 50, RADIUS_ATTR_ACCT_LINK_COUNT = 51, RADIUS_ATTR_ACCT_INPUT_GIGAWORDS = 52, RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS = 53, RADIUS_ATTR_EVENT_TIMESTAMP = 55, RADIUS_ATTR_NAS_PORT_TYPE = 61, RADIUS_ATTR_TUNNEL_TYPE = 64, RADIUS_ATTR_TUNNEL_MEDIUM_TYPE = 65, RADIUS_ATTR_TUNNEL_PASSWORD = 69, RADIUS_ATTR_CONNECT_INFO = 77, RADIUS_ATTR_EAP_MESSAGE = 79, RADIUS_ATTR_MESSAGE_AUTHENTICATOR = 80, RADIUS_ATTR_TUNNEL_PRIVATE_GROUP_ID = 81, RADIUS_ATTR_ACCT_INTERIM_INTERVAL = 85, RADIUS_ATTR_CHARGEABLE_USER_IDENTITY = 89, RADIUS_ATTR_NAS_IPV6_ADDRESS = 95, RADIUS_ATTR_ERROR_CAUSE = 101 }; /* Termination-Action */ #define RADIUS_TERMINATION_ACTION_DEFAULT 0 #define RADIUS_TERMINATION_ACTION_RADIUS_REQUEST 1 /* NAS-Port-Type */ #define RADIUS_NAS_PORT_TYPE_IEEE_802_11 19 /* Acct-Status-Type */ #define RADIUS_ACCT_STATUS_TYPE_START 1 #define RADIUS_ACCT_STATUS_TYPE_STOP 2 #define RADIUS_ACCT_STATUS_TYPE_INTERIM_UPDATE 3 #define RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_ON 7 #define RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_OFF 8 /* Acct-Authentic */ #define RADIUS_ACCT_AUTHENTIC_RADIUS 1 #define RADIUS_ACCT_AUTHENTIC_LOCAL 2 #define RADIUS_ACCT_AUTHENTIC_REMOTE 3 /* Acct-Terminate-Cause */ #define RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST 1 #define RADIUS_ACCT_TERMINATE_CAUSE_LOST_CARRIER 2 #define RADIUS_ACCT_TERMINATE_CAUSE_LOST_SERVICE 3 #define RADIUS_ACCT_TERMINATE_CAUSE_IDLE_TIMEOUT 4 #define RADIUS_ACCT_TERMINATE_CAUSE_SESSION_TIMEOUT 5 #define RADIUS_ACCT_TERMINATE_CAUSE_ADMIN_RESET 6 #define RADIUS_ACCT_TERMINATE_CAUSE_ADMIN_REBOOT 7 #define RADIUS_ACCT_TERMINATE_CAUSE_PORT_ERROR 8 #define RADIUS_ACCT_TERMINATE_CAUSE_NAS_ERROR 9 #define RADIUS_ACCT_TERMINATE_CAUSE_NAS_REQUEST 10 #define RADIUS_ACCT_TERMINATE_CAUSE_NAS_REBOOT 11 #define RADIUS_ACCT_TERMINATE_CAUSE_PORT_UNNEEDED 12 #define RADIUS_ACCT_TERMINATE_CAUSE_PORT_PREEMPTED 13 #define RADIUS_ACCT_TERMINATE_CAUSE_PORT_SUSPENDED 14 #define RADIUS_ACCT_TERMINATE_CAUSE_SERVICE_UNAVAILABLE 15 #define RADIUS_ACCT_TERMINATE_CAUSE_CALLBACK 16 #define RADIUS_ACCT_TERMINATE_CAUSE_USER_ERROR 17 #define RADIUS_ACCT_TERMINATE_CAUSE_HOST_REQUEST 18 #define RADIUS_TUNNEL_TAGS 32 /* Tunnel-Type */ #define RADIUS_TUNNEL_TYPE_PPTP 1 #define RADIUS_TUNNEL_TYPE_L2TP 3 #define RADIUS_TUNNEL_TYPE_IPIP 7 #define RADIUS_TUNNEL_TYPE_GRE 10 #define RADIUS_TUNNEL_TYPE_VLAN 13 /* Tunnel-Medium-Type */ #define RADIUS_TUNNEL_MEDIUM_TYPE_IPV4 1 #define RADIUS_TUNNEL_MEDIUM_TYPE_IPV6 2 #define RADIUS_TUNNEL_MEDIUM_TYPE_802 6 struct radius_attr_vendor { u8 vendor_type; u8 vendor_length; } STRUCT_PACKED; #define RADIUS_VENDOR_ID_CISCO 9 #define RADIUS_CISCO_AV_PAIR 1 /* RFC 2548 - Microsoft Vendor-specific RADIUS Attributes */ #define RADIUS_VENDOR_ID_MICROSOFT 311 enum { RADIUS_VENDOR_ATTR_MS_MPPE_SEND_KEY = 16, RADIUS_VENDOR_ATTR_MS_MPPE_RECV_KEY = 17 }; #ifdef _MSC_VER #pragma pack(pop) #endif /* _MSC_VER */ struct radius_ms_mppe_keys { u8 *send; size_t send_len; u8 *recv; size_t recv_len; }; struct radius_msg; /* Default size to be allocated for new RADIUS messages */ #define RADIUS_DEFAULT_MSG_SIZE 1024 /* Default size to be allocated for attribute array */ #define RADIUS_DEFAULT_ATTR_COUNT 16 /* MAC address ASCII format for IEEE 802.1X use * (draft-congdon-radius-8021x-20.txt) */ #define RADIUS_802_1X_ADDR_FORMAT "%02X-%02X-%02X-%02X-%02X-%02X" /* MAC address ASCII format for non-802.1X use */ #define RADIUS_ADDR_FORMAT "%02x%02x%02x%02x%02x%02x" struct radius_hdr * radius_msg_get_hdr(struct radius_msg *msg); struct wpabuf * radius_msg_get_buf(struct radius_msg *msg); struct radius_msg * radius_msg_new(u8 code, u8 identifier); void radius_msg_free(struct radius_msg *msg); void radius_msg_dump(struct radius_msg *msg); int radius_msg_finish(struct radius_msg *msg, const u8 *secret, size_t secret_len); int radius_msg_finish_srv(struct radius_msg *msg, const u8 *secret, size_t secret_len, const u8 *req_authenticator); int radius_msg_finish_das_resp(struct radius_msg *msg, const u8 *secret, size_t secret_len, const struct radius_hdr *req_hdr); void radius_msg_finish_acct(struct radius_msg *msg, const u8 *secret, size_t secret_len); int radius_msg_verify_acct_req(struct radius_msg *msg, const u8 *secret, size_t secret_len); int radius_msg_verify_das_req(struct radius_msg *msg, const u8 *secret, size_t secret_len); struct radius_attr_hdr * radius_msg_add_attr(struct radius_msg *msg, u8 type, const u8 *data, size_t data_len); struct radius_msg * radius_msg_parse(const u8 *data, size_t len); int radius_msg_add_eap(struct radius_msg *msg, const u8 *data, size_t data_len); struct wpabuf * radius_msg_get_eap(struct radius_msg *msg); int radius_msg_verify(struct radius_msg *msg, const u8 *secret, size_t secret_len, struct radius_msg *sent_msg, int auth); int radius_msg_verify_msg_auth(struct radius_msg *msg, const u8 *secret, size_t secret_len, const u8 *req_auth); int radius_msg_copy_attr(struct radius_msg *dst, struct radius_msg *src, u8 type); void radius_msg_make_authenticator(struct radius_msg *msg, const u8 *data, size_t len); struct radius_ms_mppe_keys * radius_msg_get_ms_keys(struct radius_msg *msg, struct radius_msg *sent_msg, const u8 *secret, size_t secret_len); struct radius_ms_mppe_keys * radius_msg_get_cisco_keys(struct radius_msg *msg, struct radius_msg *sent_msg, const u8 *secret, size_t secret_len); int radius_msg_add_mppe_keys(struct radius_msg *msg, const u8 *req_authenticator, const u8 *secret, size_t secret_len, const u8 *send_key, size_t send_key_len, const u8 *recv_key, size_t recv_key_len); struct radius_attr_hdr * radius_msg_add_attr_user_password(struct radius_msg *msg, const u8 *data, size_t data_len, const u8 *secret, size_t secret_len); int radius_msg_get_attr(struct radius_msg *msg, u8 type, u8 *buf, size_t len); int radius_msg_get_vlanid(struct radius_msg *msg); char * radius_msg_get_tunnel_password(struct radius_msg *msg, int *keylen, const u8 *secret, size_t secret_len, struct radius_msg *sent_msg, size_t n); static inline int radius_msg_add_attr_int32(struct radius_msg *msg, u8 type, u32 value) { u32 val = htonl(value); return radius_msg_add_attr(msg, type, (u8 *) &val, 4) != NULL; } static inline int radius_msg_get_attr_int32(struct radius_msg *msg, u8 type, u32 *value) { u32 val; int res; res = radius_msg_get_attr(msg, type, (u8 *) &val, 4); if (res != 4) return -1; *value = ntohl(val); return 0; } int radius_msg_get_attr_ptr(struct radius_msg *msg, u8 type, u8 **buf, size_t *len, const u8 *start); int radius_msg_count_attr(struct radius_msg *msg, u8 type, int min_len); struct radius_attr_data { u8 *data; size_t len; }; struct radius_class_data { struct radius_attr_data *attr; size_t count; }; void radius_free_class(struct radius_class_data *c); int radius_copy_class(struct radius_class_data *dst, const struct radius_class_data *src); u8 radius_msg_find_unlisted_attr(struct radius_msg *msg, u8 *attrs); #endif /* RADIUS_H */ wpa-2.1/src/radius/radius_client.c000066400000000000000000001167371227414666700172500ustar00rootroot00000000000000/* * RADIUS client * Copyright (c) 2002-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 "radius.h" #include "radius_client.h" #include "eloop.h" /* Defaults for RADIUS retransmit values (exponential backoff) */ /** * RADIUS_CLIENT_FIRST_WAIT - RADIUS client timeout for first retry in seconds */ #define RADIUS_CLIENT_FIRST_WAIT 3 /** * RADIUS_CLIENT_MAX_WAIT - RADIUS client maximum retry timeout in seconds */ #define RADIUS_CLIENT_MAX_WAIT 120 /** * RADIUS_CLIENT_MAX_RETRIES - RADIUS client maximum retries * * Maximum number of retransmit attempts before the entry is removed from * retransmit list. */ #define RADIUS_CLIENT_MAX_RETRIES 10 /** * RADIUS_CLIENT_MAX_ENTRIES - RADIUS client maximum pending messages * * Maximum number of entries in retransmit list (oldest entries will be * removed, if this limit is exceeded). */ #define RADIUS_CLIENT_MAX_ENTRIES 30 /** * RADIUS_CLIENT_NUM_FAILOVER - RADIUS client failover point * * The number of failed retry attempts after which the RADIUS server will be * changed (if one of more backup servers are configured). */ #define RADIUS_CLIENT_NUM_FAILOVER 4 /** * struct radius_rx_handler - RADIUS client RX handler * * This data structure is used internally inside the RADIUS client module to * store registered RX handlers. These handlers are registered by calls to * radius_client_register() and unregistered when the RADIUS client is * deinitialized with a call to radius_client_deinit(). */ struct radius_rx_handler { /** * handler - Received RADIUS message handler */ RadiusRxResult (*handler)(struct radius_msg *msg, struct radius_msg *req, const u8 *shared_secret, size_t shared_secret_len, void *data); /** * data - Context data for the handler */ void *data; }; /** * struct radius_msg_list - RADIUS client message retransmit list * * This data structure is used internally inside the RADIUS client module to * store pending RADIUS requests that may still need to be retransmitted. */ struct radius_msg_list { /** * addr - STA/client address * * This is used to find RADIUS messages for the same STA. */ u8 addr[ETH_ALEN]; /** * msg - RADIUS message */ struct radius_msg *msg; /** * msg_type - Message type */ RadiusType msg_type; /** * first_try - Time of the first transmission attempt */ os_time_t first_try; /** * next_try - Time for the next transmission attempt */ os_time_t next_try; /** * attempts - Number of transmission attempts */ int attempts; /** * next_wait - Next retransmission wait time in seconds */ int next_wait; /** * last_attempt - Time of the last transmission attempt */ struct os_reltime last_attempt; /** * shared_secret - Shared secret with the target RADIUS server */ const u8 *shared_secret; /** * shared_secret_len - shared_secret length in octets */ size_t shared_secret_len; /* TODO: server config with failover to backup server(s) */ /** * next - Next message in the list */ struct radius_msg_list *next; }; /** * struct radius_client_data - Internal RADIUS client data * * This data structure is used internally inside the RADIUS client module. * External users allocate this by calling radius_client_init() and free it by * calling radius_client_deinit(). The pointer to this opaque data is used in * calls to other functions as an identifier for the RADIUS client instance. */ struct radius_client_data { /** * ctx - Context pointer for hostapd_logger() callbacks */ void *ctx; /** * conf - RADIUS client configuration (list of RADIUS servers to use) */ struct hostapd_radius_servers *conf; /** * auth_serv_sock - IPv4 socket for RADIUS authentication messages */ int auth_serv_sock; /** * acct_serv_sock - IPv4 socket for RADIUS accounting messages */ int acct_serv_sock; /** * auth_serv_sock6 - IPv6 socket for RADIUS authentication messages */ int auth_serv_sock6; /** * acct_serv_sock6 - IPv6 socket for RADIUS accounting messages */ int acct_serv_sock6; /** * auth_sock - Currently used socket for RADIUS authentication server */ int auth_sock; /** * acct_sock - Currently used socket for RADIUS accounting server */ int acct_sock; /** * auth_handlers - Authentication message handlers */ struct radius_rx_handler *auth_handlers; /** * num_auth_handlers - Number of handlers in auth_handlers */ size_t num_auth_handlers; /** * acct_handlers - Accounting message handlers */ struct radius_rx_handler *acct_handlers; /** * num_acct_handlers - Number of handlers in acct_handlers */ size_t num_acct_handlers; /** * msgs - Pending outgoing RADIUS messages */ struct radius_msg_list *msgs; /** * num_msgs - Number of pending messages in the msgs list */ size_t num_msgs; /** * next_radius_identifier - Next RADIUS message identifier to use */ u8 next_radius_identifier; }; static int radius_change_server(struct radius_client_data *radius, struct hostapd_radius_server *nserv, struct hostapd_radius_server *oserv, int sock, int sock6, int auth); static int radius_client_init_acct(struct radius_client_data *radius); static int radius_client_init_auth(struct radius_client_data *radius); static void radius_client_msg_free(struct radius_msg_list *req) { radius_msg_free(req->msg); os_free(req); } /** * radius_client_register - Register a RADIUS client RX handler * @radius: RADIUS client context from radius_client_init() * @msg_type: RADIUS client type (RADIUS_AUTH or RADIUS_ACCT) * @handler: Handler for received RADIUS messages * @data: Context pointer for handler callbacks * Returns: 0 on success, -1 on failure * * This function is used to register a handler for processing received RADIUS * authentication and accounting messages. The handler() callback function will * be called whenever a RADIUS message is received from the active server. * * There can be multiple registered RADIUS message handlers. The handlers will * be called in order until one of them indicates that it has processed or * queued the message. */ int radius_client_register(struct radius_client_data *radius, RadiusType msg_type, RadiusRxResult (*handler)(struct radius_msg *msg, struct radius_msg *req, const u8 *shared_secret, size_t shared_secret_len, void *data), void *data) { struct radius_rx_handler **handlers, *newh; size_t *num; if (msg_type == RADIUS_ACCT) { handlers = &radius->acct_handlers; num = &radius->num_acct_handlers; } else { handlers = &radius->auth_handlers; num = &radius->num_auth_handlers; } newh = os_realloc_array(*handlers, *num + 1, sizeof(struct radius_rx_handler)); if (newh == NULL) return -1; newh[*num].handler = handler; newh[*num].data = data; (*num)++; *handlers = newh; return 0; } static void radius_client_handle_send_error(struct radius_client_data *radius, int s, RadiusType msg_type) { #ifndef CONFIG_NATIVE_WINDOWS int _errno = errno; wpa_printf(MSG_INFO, "send[RADIUS]: %s", strerror(errno)); if (_errno == ENOTCONN || _errno == EDESTADDRREQ || _errno == EINVAL || _errno == EBADF) { hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_INFO, "Send failed - maybe interface status changed -" " try to connect again"); eloop_unregister_read_sock(s); close(s); if (msg_type == RADIUS_ACCT || msg_type == RADIUS_ACCT_INTERIM) radius_client_init_acct(radius); else radius_client_init_auth(radius); } #endif /* CONFIG_NATIVE_WINDOWS */ } static int radius_client_retransmit(struct radius_client_data *radius, struct radius_msg_list *entry, os_time_t now) { struct hostapd_radius_servers *conf = radius->conf; int s; struct wpabuf *buf; if (entry->msg_type == RADIUS_ACCT || entry->msg_type == RADIUS_ACCT_INTERIM) { s = radius->acct_sock; if (entry->attempts == 0) conf->acct_server->requests++; else { conf->acct_server->timeouts++; conf->acct_server->retransmissions++; } } else { s = radius->auth_sock; if (entry->attempts == 0) conf->auth_server->requests++; else { conf->auth_server->timeouts++; conf->auth_server->retransmissions++; } } /* retransmit; remove entry if too many attempts */ entry->attempts++; hostapd_logger(radius->ctx, entry->addr, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_DEBUG, "Resending RADIUS message (id=%d)", radius_msg_get_hdr(entry->msg)->identifier); os_get_reltime(&entry->last_attempt); buf = radius_msg_get_buf(entry->msg); if (send(s, wpabuf_head(buf), wpabuf_len(buf), 0) < 0) radius_client_handle_send_error(radius, s, entry->msg_type); entry->next_try = now + entry->next_wait; entry->next_wait *= 2; if (entry->next_wait > RADIUS_CLIENT_MAX_WAIT) entry->next_wait = RADIUS_CLIENT_MAX_WAIT; if (entry->attempts >= RADIUS_CLIENT_MAX_RETRIES) { wpa_printf(MSG_INFO, "RADIUS: Removing un-ACKed message due to too many failed retransmit attempts"); return 1; } return 0; } static void radius_client_timer(void *eloop_ctx, void *timeout_ctx) { struct radius_client_data *radius = eloop_ctx; struct hostapd_radius_servers *conf = radius->conf; struct os_reltime now; os_time_t first; struct radius_msg_list *entry, *prev, *tmp; int auth_failover = 0, acct_failover = 0; char abuf[50]; entry = radius->msgs; if (!entry) return; os_get_reltime(&now); first = 0; prev = NULL; while (entry) { if (now.sec >= entry->next_try && radius_client_retransmit(radius, entry, now.sec)) { if (prev) prev->next = entry->next; else radius->msgs = entry->next; tmp = entry; entry = entry->next; radius_client_msg_free(tmp); radius->num_msgs--; continue; } if (entry->attempts > RADIUS_CLIENT_NUM_FAILOVER) { if (entry->msg_type == RADIUS_ACCT || entry->msg_type == RADIUS_ACCT_INTERIM) acct_failover++; else auth_failover++; } if (first == 0 || entry->next_try < first) first = entry->next_try; prev = entry; entry = entry->next; } if (radius->msgs) { if (first < now.sec) first = now.sec; eloop_register_timeout(first - now.sec, 0, radius_client_timer, radius, NULL); hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_DEBUG, "Next RADIUS client " "retransmit in %ld seconds", (long int) (first - now.sec)); } if (auth_failover && conf->num_auth_servers > 1) { struct hostapd_radius_server *next, *old; old = conf->auth_server; hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_NOTICE, "No response from Authentication server " "%s:%d - failover", hostapd_ip_txt(&old->addr, abuf, sizeof(abuf)), old->port); for (entry = radius->msgs; entry; entry = entry->next) { if (entry->msg_type == RADIUS_AUTH) old->timeouts++; } next = old + 1; if (next > &(conf->auth_servers[conf->num_auth_servers - 1])) next = conf->auth_servers; conf->auth_server = next; radius_change_server(radius, next, old, radius->auth_serv_sock, radius->auth_serv_sock6, 1); } if (acct_failover && conf->num_acct_servers > 1) { struct hostapd_radius_server *next, *old; old = conf->acct_server; hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_NOTICE, "No response from Accounting server " "%s:%d - failover", hostapd_ip_txt(&old->addr, abuf, sizeof(abuf)), old->port); for (entry = radius->msgs; entry; entry = entry->next) { if (entry->msg_type == RADIUS_ACCT || entry->msg_type == RADIUS_ACCT_INTERIM) old->timeouts++; } next = old + 1; if (next > &conf->acct_servers[conf->num_acct_servers - 1]) next = conf->acct_servers; conf->acct_server = next; radius_change_server(radius, next, old, radius->acct_serv_sock, radius->acct_serv_sock6, 0); } } static void radius_client_update_timeout(struct radius_client_data *radius) { struct os_reltime now; os_time_t first; struct radius_msg_list *entry; eloop_cancel_timeout(radius_client_timer, radius, NULL); if (radius->msgs == NULL) { return; } first = 0; for (entry = radius->msgs; entry; entry = entry->next) { if (first == 0 || entry->next_try < first) first = entry->next_try; } os_get_reltime(&now); if (first < now.sec) first = now.sec; eloop_register_timeout(first - now.sec, 0, radius_client_timer, radius, NULL); hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_DEBUG, "Next RADIUS client retransmit in" " %ld seconds", (long int) (first - now.sec)); } static void radius_client_list_add(struct radius_client_data *radius, struct radius_msg *msg, RadiusType msg_type, const u8 *shared_secret, size_t shared_secret_len, const u8 *addr) { struct radius_msg_list *entry, *prev; if (eloop_terminated()) { /* No point in adding entries to retransmit queue since event * loop has already been terminated. */ radius_msg_free(msg); return; } entry = os_zalloc(sizeof(*entry)); if (entry == NULL) { wpa_printf(MSG_INFO, "RADIUS: Failed to add packet into retransmit list"); radius_msg_free(msg); return; } if (addr) os_memcpy(entry->addr, addr, ETH_ALEN); entry->msg = msg; entry->msg_type = msg_type; entry->shared_secret = shared_secret; entry->shared_secret_len = shared_secret_len; os_get_reltime(&entry->last_attempt); entry->first_try = entry->last_attempt.sec; entry->next_try = entry->first_try + RADIUS_CLIENT_FIRST_WAIT; entry->attempts = 1; entry->next_wait = RADIUS_CLIENT_FIRST_WAIT * 2; entry->next = radius->msgs; radius->msgs = entry; radius_client_update_timeout(radius); if (radius->num_msgs >= RADIUS_CLIENT_MAX_ENTRIES) { wpa_printf(MSG_INFO, "RADIUS: Removing the oldest un-ACKed packet due to retransmit list limits"); prev = NULL; while (entry->next) { prev = entry; entry = entry->next; } if (prev) { prev->next = NULL; radius_client_msg_free(entry); } } else radius->num_msgs++; } static void radius_client_list_del(struct radius_client_data *radius, RadiusType msg_type, const u8 *addr) { struct radius_msg_list *entry, *prev, *tmp; if (addr == NULL) return; entry = radius->msgs; prev = NULL; while (entry) { if (entry->msg_type == msg_type && os_memcmp(entry->addr, addr, ETH_ALEN) == 0) { if (prev) prev->next = entry->next; else radius->msgs = entry->next; tmp = entry; entry = entry->next; hostapd_logger(radius->ctx, addr, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_DEBUG, "Removing matching RADIUS message"); radius_client_msg_free(tmp); radius->num_msgs--; continue; } prev = entry; entry = entry->next; } } /** * radius_client_send - Send a RADIUS request * @radius: RADIUS client context from radius_client_init() * @msg: RADIUS message to be sent * @msg_type: Message type (RADIUS_AUTH, RADIUS_ACCT, RADIUS_ACCT_INTERIM) * @addr: MAC address of the device related to this message or %NULL * Returns: 0 on success, -1 on failure * * This function is used to transmit a RADIUS authentication (RADIUS_AUTH) or * accounting request (RADIUS_ACCT or RADIUS_ACCT_INTERIM). The only difference * between accounting and interim accounting messages is that the interim * message will override any pending interim accounting updates while a new * accounting message does not remove any pending messages. * * The message is added on the retransmission queue and will be retransmitted * automatically until a response is received or maximum number of retries * (RADIUS_CLIENT_MAX_RETRIES) is reached. * * The related device MAC address can be used to identify pending messages that * can be removed with radius_client_flush_auth() or with interim accounting * updates. */ int radius_client_send(struct radius_client_data *radius, struct radius_msg *msg, RadiusType msg_type, const u8 *addr) { struct hostapd_radius_servers *conf = radius->conf; const u8 *shared_secret; size_t shared_secret_len; char *name; int s, res; struct wpabuf *buf; if (msg_type == RADIUS_ACCT_INTERIM) { /* Remove any pending interim acct update for the same STA. */ radius_client_list_del(radius, msg_type, addr); } if (msg_type == RADIUS_ACCT || msg_type == RADIUS_ACCT_INTERIM) { if (conf->acct_server == NULL) { hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_INFO, "No accounting server configured"); return -1; } shared_secret = conf->acct_server->shared_secret; shared_secret_len = conf->acct_server->shared_secret_len; radius_msg_finish_acct(msg, shared_secret, shared_secret_len); name = "accounting"; s = radius->acct_sock; conf->acct_server->requests++; } else { if (conf->auth_server == NULL) { hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_INFO, "No authentication server configured"); return -1; } shared_secret = conf->auth_server->shared_secret; shared_secret_len = conf->auth_server->shared_secret_len; radius_msg_finish(msg, shared_secret, shared_secret_len); name = "authentication"; s = radius->auth_sock; conf->auth_server->requests++; } hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_DEBUG, "Sending RADIUS message to %s " "server", name); if (conf->msg_dumps) radius_msg_dump(msg); buf = radius_msg_get_buf(msg); res = send(s, wpabuf_head(buf), wpabuf_len(buf), 0); if (res < 0) radius_client_handle_send_error(radius, s, msg_type); radius_client_list_add(radius, msg, msg_type, shared_secret, shared_secret_len, addr); return 0; } static void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx) { struct radius_client_data *radius = eloop_ctx; struct hostapd_radius_servers *conf = radius->conf; RadiusType msg_type = (RadiusType) sock_ctx; int len, roundtrip; unsigned char buf[3000]; struct radius_msg *msg; struct radius_hdr *hdr; struct radius_rx_handler *handlers; size_t num_handlers, i; struct radius_msg_list *req, *prev_req; struct os_reltime now; struct hostapd_radius_server *rconf; int invalid_authenticator = 0; if (msg_type == RADIUS_ACCT) { handlers = radius->acct_handlers; num_handlers = radius->num_acct_handlers; rconf = conf->acct_server; } else { handlers = radius->auth_handlers; num_handlers = radius->num_auth_handlers; rconf = conf->auth_server; } len = recv(sock, buf, sizeof(buf), MSG_DONTWAIT); if (len < 0) { wpa_printf(MSG_INFO, "recv[RADIUS]: %s", strerror(errno)); return; } hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_DEBUG, "Received %d bytes from RADIUS " "server", len); if (len == sizeof(buf)) { wpa_printf(MSG_INFO, "RADIUS: Possibly too long UDP frame for our buffer - dropping it"); return; } msg = radius_msg_parse(buf, len); if (msg == NULL) { wpa_printf(MSG_INFO, "RADIUS: Parsing incoming frame failed"); rconf->malformed_responses++; return; } hdr = radius_msg_get_hdr(msg); hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_DEBUG, "Received RADIUS message"); if (conf->msg_dumps) radius_msg_dump(msg); switch (hdr->code) { case RADIUS_CODE_ACCESS_ACCEPT: rconf->access_accepts++; break; case RADIUS_CODE_ACCESS_REJECT: rconf->access_rejects++; break; case RADIUS_CODE_ACCESS_CHALLENGE: rconf->access_challenges++; break; case RADIUS_CODE_ACCOUNTING_RESPONSE: rconf->responses++; break; } prev_req = NULL; req = radius->msgs; while (req) { /* TODO: also match by src addr:port of the packet when using * alternative RADIUS servers (?) */ if ((req->msg_type == msg_type || (req->msg_type == RADIUS_ACCT_INTERIM && msg_type == RADIUS_ACCT)) && radius_msg_get_hdr(req->msg)->identifier == hdr->identifier) break; prev_req = req; req = req->next; } if (req == NULL) { hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_DEBUG, "No matching RADIUS request found (type=%d " "id=%d) - dropping packet", msg_type, hdr->identifier); goto fail; } os_get_reltime(&now); roundtrip = (now.sec - req->last_attempt.sec) * 100 + (now.usec - req->last_attempt.usec) / 10000; hostapd_logger(radius->ctx, req->addr, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_DEBUG, "Received RADIUS packet matched with a pending " "request, round trip time %d.%02d sec", roundtrip / 100, roundtrip % 100); rconf->round_trip_time = roundtrip; /* Remove ACKed RADIUS packet from retransmit list */ if (prev_req) prev_req->next = req->next; else radius->msgs = req->next; radius->num_msgs--; for (i = 0; i < num_handlers; i++) { RadiusRxResult res; res = handlers[i].handler(msg, req->msg, req->shared_secret, req->shared_secret_len, handlers[i].data); switch (res) { case RADIUS_RX_PROCESSED: radius_msg_free(msg); /* continue */ case RADIUS_RX_QUEUED: radius_client_msg_free(req); return; case RADIUS_RX_INVALID_AUTHENTICATOR: invalid_authenticator++; /* continue */ case RADIUS_RX_UNKNOWN: /* continue with next handler */ break; } } if (invalid_authenticator) rconf->bad_authenticators++; else rconf->unknown_types++; hostapd_logger(radius->ctx, req->addr, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_DEBUG, "No RADIUS RX handler found " "(type=%d code=%d id=%d)%s - dropping packet", msg_type, hdr->code, hdr->identifier, invalid_authenticator ? " [INVALID AUTHENTICATOR]" : ""); radius_client_msg_free(req); fail: radius_msg_free(msg); } /** * radius_client_get_id - Get an identifier for a new RADIUS message * @radius: RADIUS client context from radius_client_init() * Returns: Allocated identifier * * This function is used to fetch a unique (among pending requests) identifier * for a new RADIUS message. */ u8 radius_client_get_id(struct radius_client_data *radius) { struct radius_msg_list *entry, *prev, *_remove; u8 id = radius->next_radius_identifier++; /* remove entries with matching id from retransmit list to avoid * using new reply from the RADIUS server with an old request */ entry = radius->msgs; prev = NULL; while (entry) { if (radius_msg_get_hdr(entry->msg)->identifier == id) { hostapd_logger(radius->ctx, entry->addr, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_DEBUG, "Removing pending RADIUS message, " "since its id (%d) is reused", id); if (prev) prev->next = entry->next; else radius->msgs = entry->next; _remove = entry; } else { _remove = NULL; prev = entry; } entry = entry->next; if (_remove) radius_client_msg_free(_remove); } return id; } /** * radius_client_flush - Flush all pending RADIUS client messages * @radius: RADIUS client context from radius_client_init() * @only_auth: Whether only authentication messages are removed */ void radius_client_flush(struct radius_client_data *radius, int only_auth) { struct radius_msg_list *entry, *prev, *tmp; if (!radius) return; prev = NULL; entry = radius->msgs; while (entry) { if (!only_auth || entry->msg_type == RADIUS_AUTH) { if (prev) prev->next = entry->next; else radius->msgs = entry->next; tmp = entry; entry = entry->next; radius_client_msg_free(tmp); radius->num_msgs--; } else { prev = entry; entry = entry->next; } } if (radius->msgs == NULL) eloop_cancel_timeout(radius_client_timer, radius, NULL); } static void radius_client_update_acct_msgs(struct radius_client_data *radius, const u8 *shared_secret, size_t shared_secret_len) { struct radius_msg_list *entry; if (!radius) return; for (entry = radius->msgs; entry; entry = entry->next) { if (entry->msg_type == RADIUS_ACCT) { entry->shared_secret = shared_secret; entry->shared_secret_len = shared_secret_len; radius_msg_finish_acct(entry->msg, shared_secret, shared_secret_len); } } } static int radius_change_server(struct radius_client_data *radius, struct hostapd_radius_server *nserv, struct hostapd_radius_server *oserv, int sock, int sock6, int auth) { struct sockaddr_in serv, claddr; #ifdef CONFIG_IPV6 struct sockaddr_in6 serv6, claddr6; #endif /* CONFIG_IPV6 */ struct sockaddr *addr, *cl_addr; socklen_t addrlen, claddrlen; char abuf[50]; int sel_sock; struct radius_msg_list *entry; struct hostapd_radius_servers *conf = radius->conf; hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_INFO, "%s server %s:%d", auth ? "Authentication" : "Accounting", hostapd_ip_txt(&nserv->addr, abuf, sizeof(abuf)), nserv->port); if (!oserv || nserv->shared_secret_len != oserv->shared_secret_len || os_memcmp(nserv->shared_secret, oserv->shared_secret, nserv->shared_secret_len) != 0) { /* Pending RADIUS packets used different shared secret, so * they need to be modified. Update accounting message * authenticators here. Authentication messages are removed * since they would require more changes and the new RADIUS * server may not be prepared to receive them anyway due to * missing state information. Client will likely retry * authentication, so this should not be an issue. */ if (auth) radius_client_flush(radius, 1); else { radius_client_update_acct_msgs( radius, nserv->shared_secret, nserv->shared_secret_len); } } /* Reset retry counters for the new server */ for (entry = radius->msgs; entry; entry = entry->next) { if ((auth && entry->msg_type != RADIUS_AUTH) || (!auth && entry->msg_type != RADIUS_ACCT)) continue; entry->next_try = entry->first_try + RADIUS_CLIENT_FIRST_WAIT; entry->attempts = 0; entry->next_wait = RADIUS_CLIENT_FIRST_WAIT * 2; } if (radius->msgs) { eloop_cancel_timeout(radius_client_timer, radius, NULL); eloop_register_timeout(RADIUS_CLIENT_FIRST_WAIT, 0, radius_client_timer, radius, NULL); } switch (nserv->addr.af) { case AF_INET: os_memset(&serv, 0, sizeof(serv)); serv.sin_family = AF_INET; serv.sin_addr.s_addr = nserv->addr.u.v4.s_addr; serv.sin_port = htons(nserv->port); addr = (struct sockaddr *) &serv; addrlen = sizeof(serv); sel_sock = sock; break; #ifdef CONFIG_IPV6 case AF_INET6: os_memset(&serv6, 0, sizeof(serv6)); serv6.sin6_family = AF_INET6; os_memcpy(&serv6.sin6_addr, &nserv->addr.u.v6, sizeof(struct in6_addr)); serv6.sin6_port = htons(nserv->port); addr = (struct sockaddr *) &serv6; addrlen = sizeof(serv6); sel_sock = sock6; break; #endif /* CONFIG_IPV6 */ default: return -1; } if (conf->force_client_addr) { switch (conf->client_addr.af) { case AF_INET: os_memset(&claddr, 0, sizeof(claddr)); claddr.sin_family = AF_INET; claddr.sin_addr.s_addr = conf->client_addr.u.v4.s_addr; claddr.sin_port = htons(0); cl_addr = (struct sockaddr *) &claddr; claddrlen = sizeof(claddr); break; #ifdef CONFIG_IPV6 case AF_INET6: os_memset(&claddr6, 0, sizeof(claddr6)); claddr6.sin6_family = AF_INET6; os_memcpy(&claddr6.sin6_addr, &conf->client_addr.u.v6, sizeof(struct in6_addr)); claddr6.sin6_port = htons(0); cl_addr = (struct sockaddr *) &claddr6; claddrlen = sizeof(claddr6); break; #endif /* CONFIG_IPV6 */ default: return -1; } if (bind(sel_sock, cl_addr, claddrlen) < 0) { wpa_printf(MSG_INFO, "bind[radius]: %s", strerror(errno)); return -1; } } if (connect(sel_sock, addr, addrlen) < 0) { wpa_printf(MSG_INFO, "connect[radius]: %s", strerror(errno)); return -1; } #ifndef CONFIG_NATIVE_WINDOWS switch (nserv->addr.af) { case AF_INET: claddrlen = sizeof(claddr); getsockname(sel_sock, (struct sockaddr *) &claddr, &claddrlen); wpa_printf(MSG_DEBUG, "RADIUS local address: %s:%u", inet_ntoa(claddr.sin_addr), ntohs(claddr.sin_port)); break; #ifdef CONFIG_IPV6 case AF_INET6: { claddrlen = sizeof(claddr6); getsockname(sel_sock, (struct sockaddr *) &claddr6, &claddrlen); wpa_printf(MSG_DEBUG, "RADIUS local address: %s:%u", inet_ntop(AF_INET6, &claddr6.sin6_addr, abuf, sizeof(abuf)), ntohs(claddr6.sin6_port)); break; } #endif /* CONFIG_IPV6 */ } #endif /* CONFIG_NATIVE_WINDOWS */ if (auth) radius->auth_sock = sel_sock; else radius->acct_sock = sel_sock; return 0; } static void radius_retry_primary_timer(void *eloop_ctx, void *timeout_ctx) { struct radius_client_data *radius = eloop_ctx; struct hostapd_radius_servers *conf = radius->conf; struct hostapd_radius_server *oserv; if (radius->auth_sock >= 0 && conf->auth_servers && conf->auth_server != conf->auth_servers) { oserv = conf->auth_server; conf->auth_server = conf->auth_servers; radius_change_server(radius, conf->auth_server, oserv, radius->auth_serv_sock, radius->auth_serv_sock6, 1); } if (radius->acct_sock >= 0 && conf->acct_servers && conf->acct_server != conf->acct_servers) { oserv = conf->acct_server; conf->acct_server = conf->acct_servers; radius_change_server(radius, conf->acct_server, oserv, radius->acct_serv_sock, radius->acct_serv_sock6, 0); } if (conf->retry_primary_interval) eloop_register_timeout(conf->retry_primary_interval, 0, radius_retry_primary_timer, radius, NULL); } static int radius_client_disable_pmtu_discovery(int s) { int r = -1; #if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT) /* Turn off Path MTU discovery on IPv4/UDP sockets. */ int action = IP_PMTUDISC_DONT; r = setsockopt(s, IPPROTO_IP, IP_MTU_DISCOVER, &action, sizeof(action)); if (r == -1) wpa_printf(MSG_ERROR, "RADIUS: Failed to set IP_MTU_DISCOVER: %s", strerror(errno)); #endif return r; } static int radius_client_init_auth(struct radius_client_data *radius) { struct hostapd_radius_servers *conf = radius->conf; int ok = 0; radius->auth_serv_sock = socket(PF_INET, SOCK_DGRAM, 0); if (radius->auth_serv_sock < 0) wpa_printf(MSG_INFO, "RADIUS: socket[PF_INET,SOCK_DGRAM]: %s", strerror(errno)); else { radius_client_disable_pmtu_discovery(radius->auth_serv_sock); ok++; } #ifdef CONFIG_IPV6 radius->auth_serv_sock6 = socket(PF_INET6, SOCK_DGRAM, 0); if (radius->auth_serv_sock6 < 0) wpa_printf(MSG_INFO, "RADIUS: socket[PF_INET6,SOCK_DGRAM]: %s", strerror(errno)); else ok++; #endif /* CONFIG_IPV6 */ if (ok == 0) return -1; radius_change_server(radius, conf->auth_server, NULL, radius->auth_serv_sock, radius->auth_serv_sock6, 1); if (radius->auth_serv_sock >= 0 && eloop_register_read_sock(radius->auth_serv_sock, radius_client_receive, radius, (void *) RADIUS_AUTH)) { wpa_printf(MSG_INFO, "RADIUS: Could not register read socket for authentication server"); return -1; } #ifdef CONFIG_IPV6 if (radius->auth_serv_sock6 >= 0 && eloop_register_read_sock(radius->auth_serv_sock6, radius_client_receive, radius, (void *) RADIUS_AUTH)) { wpa_printf(MSG_INFO, "RADIUS: Could not register read socket for authentication server"); return -1; } #endif /* CONFIG_IPV6 */ return 0; } static int radius_client_init_acct(struct radius_client_data *radius) { struct hostapd_radius_servers *conf = radius->conf; int ok = 0; radius->acct_serv_sock = socket(PF_INET, SOCK_DGRAM, 0); if (radius->acct_serv_sock < 0) wpa_printf(MSG_INFO, "RADIUS: socket[PF_INET,SOCK_DGRAM]: %s", strerror(errno)); else { radius_client_disable_pmtu_discovery(radius->acct_serv_sock); ok++; } #ifdef CONFIG_IPV6 radius->acct_serv_sock6 = socket(PF_INET6, SOCK_DGRAM, 0); if (radius->acct_serv_sock6 < 0) wpa_printf(MSG_INFO, "RADIUS: socket[PF_INET6,SOCK_DGRAM]: %s", strerror(errno)); else ok++; #endif /* CONFIG_IPV6 */ if (ok == 0) return -1; radius_change_server(radius, conf->acct_server, NULL, radius->acct_serv_sock, radius->acct_serv_sock6, 0); if (radius->acct_serv_sock >= 0 && eloop_register_read_sock(radius->acct_serv_sock, radius_client_receive, radius, (void *) RADIUS_ACCT)) { wpa_printf(MSG_INFO, "RADIUS: Could not register read socket for accounting server"); return -1; } #ifdef CONFIG_IPV6 if (radius->acct_serv_sock6 >= 0 && eloop_register_read_sock(radius->acct_serv_sock6, radius_client_receive, radius, (void *) RADIUS_ACCT)) { wpa_printf(MSG_INFO, "RADIUS: Could not register read socket for accounting server"); return -1; } #endif /* CONFIG_IPV6 */ return 0; } /** * radius_client_init - Initialize RADIUS client * @ctx: Callback context to be used in hostapd_logger() calls * @conf: RADIUS client configuration (RADIUS servers) * Returns: Pointer to private RADIUS client context or %NULL on failure * * The caller is responsible for keeping the configuration data available for * the lifetime of the RADIUS client, i.e., until radius_client_deinit() is * called for the returned context pointer. */ struct radius_client_data * radius_client_init(void *ctx, struct hostapd_radius_servers *conf) { struct radius_client_data *radius; radius = os_zalloc(sizeof(struct radius_client_data)); if (radius == NULL) return NULL; radius->ctx = ctx; radius->conf = conf; radius->auth_serv_sock = radius->acct_serv_sock = radius->auth_serv_sock6 = radius->acct_serv_sock6 = radius->auth_sock = radius->acct_sock = -1; if (conf->auth_server && radius_client_init_auth(radius)) { radius_client_deinit(radius); return NULL; } if (conf->acct_server && radius_client_init_acct(radius)) { radius_client_deinit(radius); return NULL; } if (conf->retry_primary_interval) eloop_register_timeout(conf->retry_primary_interval, 0, radius_retry_primary_timer, radius, NULL); return radius; } /** * radius_client_deinit - Deinitialize RADIUS client * @radius: RADIUS client context from radius_client_init() */ void radius_client_deinit(struct radius_client_data *radius) { if (!radius) return; if (radius->auth_serv_sock >= 0) eloop_unregister_read_sock(radius->auth_serv_sock); if (radius->acct_serv_sock >= 0) eloop_unregister_read_sock(radius->acct_serv_sock); #ifdef CONFIG_IPV6 if (radius->auth_serv_sock6 >= 0) eloop_unregister_read_sock(radius->auth_serv_sock6); if (radius->acct_serv_sock6 >= 0) eloop_unregister_read_sock(radius->acct_serv_sock6); #endif /* CONFIG_IPV6 */ eloop_cancel_timeout(radius_retry_primary_timer, radius, NULL); radius_client_flush(radius, 0); os_free(radius->auth_handlers); os_free(radius->acct_handlers); os_free(radius); } /** * radius_client_flush_auth - Flush pending RADIUS messages for an address * @radius: RADIUS client context from radius_client_init() * @addr: MAC address of the related device * * This function can be used to remove pending RADIUS authentication messages * that are related to a specific device. The addr parameter is matched with * the one used in radius_client_send() call that was used to transmit the * authentication request. */ void radius_client_flush_auth(struct radius_client_data *radius, const u8 *addr) { struct radius_msg_list *entry, *prev, *tmp; prev = NULL; entry = radius->msgs; while (entry) { if (entry->msg_type == RADIUS_AUTH && os_memcmp(entry->addr, addr, ETH_ALEN) == 0) { hostapd_logger(radius->ctx, addr, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_DEBUG, "Removing pending RADIUS authentication" " message for removed client"); if (prev) prev->next = entry->next; else radius->msgs = entry->next; tmp = entry; entry = entry->next; radius_client_msg_free(tmp); radius->num_msgs--; continue; } prev = entry; entry = entry->next; } } static int radius_client_dump_auth_server(char *buf, size_t buflen, struct hostapd_radius_server *serv, struct radius_client_data *cli) { int pending = 0; struct radius_msg_list *msg; char abuf[50]; if (cli) { for (msg = cli->msgs; msg; msg = msg->next) { if (msg->msg_type == RADIUS_AUTH) pending++; } } return os_snprintf(buf, buflen, "radiusAuthServerIndex=%d\n" "radiusAuthServerAddress=%s\n" "radiusAuthClientServerPortNumber=%d\n" "radiusAuthClientRoundTripTime=%d\n" "radiusAuthClientAccessRequests=%u\n" "radiusAuthClientAccessRetransmissions=%u\n" "radiusAuthClientAccessAccepts=%u\n" "radiusAuthClientAccessRejects=%u\n" "radiusAuthClientAccessChallenges=%u\n" "radiusAuthClientMalformedAccessResponses=%u\n" "radiusAuthClientBadAuthenticators=%u\n" "radiusAuthClientPendingRequests=%u\n" "radiusAuthClientTimeouts=%u\n" "radiusAuthClientUnknownTypes=%u\n" "radiusAuthClientPacketsDropped=%u\n", serv->index, hostapd_ip_txt(&serv->addr, abuf, sizeof(abuf)), serv->port, serv->round_trip_time, serv->requests, serv->retransmissions, serv->access_accepts, serv->access_rejects, serv->access_challenges, serv->malformed_responses, serv->bad_authenticators, pending, serv->timeouts, serv->unknown_types, serv->packets_dropped); } static int radius_client_dump_acct_server(char *buf, size_t buflen, struct hostapd_radius_server *serv, struct radius_client_data *cli) { int pending = 0; struct radius_msg_list *msg; char abuf[50]; if (cli) { for (msg = cli->msgs; msg; msg = msg->next) { if (msg->msg_type == RADIUS_ACCT || msg->msg_type == RADIUS_ACCT_INTERIM) pending++; } } return os_snprintf(buf, buflen, "radiusAccServerIndex=%d\n" "radiusAccServerAddress=%s\n" "radiusAccClientServerPortNumber=%d\n" "radiusAccClientRoundTripTime=%d\n" "radiusAccClientRequests=%u\n" "radiusAccClientRetransmissions=%u\n" "radiusAccClientResponses=%u\n" "radiusAccClientMalformedResponses=%u\n" "radiusAccClientBadAuthenticators=%u\n" "radiusAccClientPendingRequests=%u\n" "radiusAccClientTimeouts=%u\n" "radiusAccClientUnknownTypes=%u\n" "radiusAccClientPacketsDropped=%u\n", serv->index, hostapd_ip_txt(&serv->addr, abuf, sizeof(abuf)), serv->port, serv->round_trip_time, serv->requests, serv->retransmissions, serv->responses, serv->malformed_responses, serv->bad_authenticators, pending, serv->timeouts, serv->unknown_types, serv->packets_dropped); } /** * radius_client_get_mib - Get RADIUS client MIB information * @radius: RADIUS client context from radius_client_init() * @buf: Buffer for returning MIB data in text format * @buflen: Maximum buf length in octets * Returns: Number of octets written into the buffer */ int radius_client_get_mib(struct radius_client_data *radius, char *buf, size_t buflen) { struct hostapd_radius_servers *conf = radius->conf; int i; struct hostapd_radius_server *serv; int count = 0; if (conf->auth_servers) { for (i = 0; i < conf->num_auth_servers; i++) { serv = &conf->auth_servers[i]; count += radius_client_dump_auth_server( buf + count, buflen - count, serv, serv == conf->auth_server ? radius : NULL); } } if (conf->acct_servers) { for (i = 0; i < conf->num_acct_servers; i++) { serv = &conf->acct_servers[i]; count += radius_client_dump_acct_server( buf + count, buflen - count, serv, serv == conf->acct_server ? radius : NULL); } } return count; } void radius_client_reconfig(struct radius_client_data *radius, struct hostapd_radius_servers *conf) { if (radius) radius->conf = conf; } wpa-2.1/src/radius/radius_client.h000066400000000000000000000144201227414666700172370ustar00rootroot00000000000000/* * RADIUS client * Copyright (c) 2002-2009, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef RADIUS_CLIENT_H #define RADIUS_CLIENT_H #include "ip_addr.h" struct radius_msg; /** * struct hostapd_radius_server - RADIUS server information for RADIUS client * * This structure contains information about a RADIUS server. The values are * mainly for MIB information. The MIB variable prefix (radiusAuth or * radiusAcc) depends on whether this is an authentication or accounting * server. * * radiusAuthClientPendingRequests (or radiusAccClientPendingRequests) is the * number struct radius_client_data::msgs for matching msg_type. */ struct hostapd_radius_server { /** * addr - radiusAuthServerAddress or radiusAccServerAddress */ struct hostapd_ip_addr addr; /** * port - radiusAuthClientServerPortNumber or radiusAccClientServerPortNumber */ int port; /** * shared_secret - Shared secret for authenticating RADIUS messages */ u8 *shared_secret; /** * shared_secret_len - Length of shared_secret in octets */ size_t shared_secret_len; /* Dynamic (not from configuration file) MIB data */ /** * index - radiusAuthServerIndex or radiusAccServerIndex */ int index; /** * round_trip_time - radiusAuthClientRoundTripTime or radiusAccClientRoundTripTime * Round-trip time in hundredths of a second. */ int round_trip_time; /** * requests - radiusAuthClientAccessRequests or radiusAccClientRequests */ u32 requests; /** * retransmissions - radiusAuthClientAccessRetransmissions or radiusAccClientRetransmissions */ u32 retransmissions; /** * access_accepts - radiusAuthClientAccessAccepts */ u32 access_accepts; /** * access_rejects - radiusAuthClientAccessRejects */ u32 access_rejects; /** * access_challenges - radiusAuthClientAccessChallenges */ u32 access_challenges; /** * responses - radiusAccClientResponses */ u32 responses; /** * malformed_responses - radiusAuthClientMalformedAccessResponses or radiusAccClientMalformedResponses */ u32 malformed_responses; /** * bad_authenticators - radiusAuthClientBadAuthenticators or radiusAccClientBadAuthenticators */ u32 bad_authenticators; /** * timeouts - radiusAuthClientTimeouts or radiusAccClientTimeouts */ u32 timeouts; /** * unknown_types - radiusAuthClientUnknownTypes or radiusAccClientUnknownTypes */ u32 unknown_types; /** * packets_dropped - radiusAuthClientPacketsDropped or radiusAccClientPacketsDropped */ u32 packets_dropped; }; /** * struct hostapd_radius_servers - RADIUS servers for RADIUS client */ struct hostapd_radius_servers { /** * auth_servers - RADIUS Authentication servers in priority order */ struct hostapd_radius_server *auth_servers; /** * num_auth_servers - Number of auth_servers entries */ int num_auth_servers; /** * auth_server - The current Authentication server */ struct hostapd_radius_server *auth_server; /** * acct_servers - RADIUS Accounting servers in priority order */ struct hostapd_radius_server *acct_servers; /** * num_acct_servers - Number of acct_servers entries */ int num_acct_servers; /** * acct_server - The current Accounting server */ struct hostapd_radius_server *acct_server; /** * retry_primary_interval - Retry interval for trying primary server * * This specifies a retry interval in sexconds for trying to return to * the primary RADIUS server. 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 (non-zero), the primary server * will be retried after the specified number of seconds has passed * even if the current used secondary server is still working. */ int retry_primary_interval; /** * msg_dumps - Whether RADIUS message details are shown in stdout */ int msg_dumps; /** * client_addr - Client (local) address to use if force_client_addr */ struct hostapd_ip_addr client_addr; /** * force_client_addr - Whether to force client (local) address */ int force_client_addr; }; /** * RadiusType - RADIUS server type for RADIUS client */ typedef enum { /** * RADIUS authentication */ RADIUS_AUTH, /** * RADIUS_ACCT - RADIUS accounting */ RADIUS_ACCT, /** * RADIUS_ACCT_INTERIM - RADIUS interim accounting message * * Used only with radius_client_send(). This behaves just like * RADIUS_ACCT, but removes any pending interim RADIUS Accounting * messages for the same STA before sending the new interim update. */ RADIUS_ACCT_INTERIM } RadiusType; /** * RadiusRxResult - RADIUS client RX handler result */ typedef enum { /** * RADIUS_RX_PROCESSED - Message processed * * This stops handler calls and frees the message. */ RADIUS_RX_PROCESSED, /** * RADIUS_RX_QUEUED - Message has been queued * * This stops handler calls, but does not free the message; the handler * that returned this is responsible for eventually freeing the * message. */ RADIUS_RX_QUEUED, /** * RADIUS_RX_UNKNOWN - Message is not for this handler */ RADIUS_RX_UNKNOWN, /** * RADIUS_RX_INVALID_AUTHENTICATOR - Message has invalid Authenticator */ RADIUS_RX_INVALID_AUTHENTICATOR } RadiusRxResult; struct radius_client_data; int radius_client_register(struct radius_client_data *radius, RadiusType msg_type, RadiusRxResult (*handler) (struct radius_msg *msg, struct radius_msg *req, const u8 *shared_secret, size_t shared_secret_len, void *data), void *data); int radius_client_send(struct radius_client_data *radius, struct radius_msg *msg, RadiusType msg_type, const u8 *addr); u8 radius_client_get_id(struct radius_client_data *radius); void radius_client_flush(struct radius_client_data *radius, int only_auth); struct radius_client_data * radius_client_init(void *ctx, struct hostapd_radius_servers *conf); void radius_client_deinit(struct radius_client_data *radius); void radius_client_flush_auth(struct radius_client_data *radius, const u8 *addr); int radius_client_get_mib(struct radius_client_data *radius, char *buf, size_t buflen); void radius_client_reconfig(struct radius_client_data *radius, struct hostapd_radius_servers *conf); #endif /* RADIUS_CLIENT_H */ wpa-2.1/src/radius/radius_das.c000066400000000000000000000206211227414666700165230ustar00rootroot00000000000000/* * RADIUS Dynamic Authorization Server (DAS) (RFC 5176) * Copyright (c) 2012-2013, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "includes.h" #include #include "utils/common.h" #include "utils/eloop.h" #include "utils/ip_addr.h" #include "radius.h" #include "radius_das.h" struct radius_das_data { int sock; u8 *shared_secret; size_t shared_secret_len; struct hostapd_ip_addr client_addr; unsigned int time_window; int require_event_timestamp; void *ctx; enum radius_das_res (*disconnect)(void *ctx, struct radius_das_attrs *attr); }; static struct radius_msg * radius_das_disconnect(struct radius_das_data *das, struct radius_msg *msg, const char *abuf, int from_port) { struct radius_hdr *hdr; struct radius_msg *reply; u8 allowed[] = { RADIUS_ATTR_USER_NAME, RADIUS_ATTR_CALLING_STATION_ID, RADIUS_ATTR_ACCT_SESSION_ID, RADIUS_ATTR_EVENT_TIMESTAMP, RADIUS_ATTR_MESSAGE_AUTHENTICATOR, RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, 0 }; int error = 405; u8 attr; enum radius_das_res res; struct radius_das_attrs attrs; u8 *buf; size_t len; char tmp[100]; u8 sta_addr[ETH_ALEN]; hdr = radius_msg_get_hdr(msg); attr = radius_msg_find_unlisted_attr(msg, allowed); if (attr) { wpa_printf(MSG_INFO, "DAS: Unsupported attribute %u in " "Disconnect-Request from %s:%d", attr, abuf, from_port); error = 401; goto fail; } os_memset(&attrs, 0, sizeof(attrs)); if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CALLING_STATION_ID, &buf, &len, NULL) == 0) { if (len >= sizeof(tmp)) len = sizeof(tmp) - 1; os_memcpy(tmp, buf, len); tmp[len] = '\0'; if (hwaddr_aton2(tmp, sta_addr) < 0) { wpa_printf(MSG_INFO, "DAS: Invalid Calling-Station-Id " "'%s' from %s:%d", tmp, abuf, from_port); error = 407; goto fail; } attrs.sta_addr = sta_addr; } if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_USER_NAME, &buf, &len, NULL) == 0) { attrs.user_name = buf; attrs.user_name_len = len; } if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_ACCT_SESSION_ID, &buf, &len, NULL) == 0) { attrs.acct_session_id = buf; attrs.acct_session_id_len = len; } if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, &buf, &len, NULL) == 0) { attrs.cui = buf; attrs.cui_len = len; } res = das->disconnect(das->ctx, &attrs); switch (res) { case RADIUS_DAS_NAS_MISMATCH: wpa_printf(MSG_INFO, "DAS: NAS mismatch from %s:%d", abuf, from_port); error = 403; break; case RADIUS_DAS_SESSION_NOT_FOUND: wpa_printf(MSG_INFO, "DAS: Session not found for request from " "%s:%d", abuf, from_port); error = 503; break; case RADIUS_DAS_SUCCESS: error = 0; break; } fail: reply = radius_msg_new(error ? RADIUS_CODE_DISCONNECT_NAK : RADIUS_CODE_DISCONNECT_ACK, hdr->identifier); if (reply == NULL) return NULL; if (error) { if (!radius_msg_add_attr_int32(reply, RADIUS_ATTR_ERROR_CAUSE, error)) { radius_msg_free(reply); return NULL; } } return reply; } static void radius_das_receive(int sock, void *eloop_ctx, void *sock_ctx) { struct radius_das_data *das = eloop_ctx; u8 buf[1500]; union { struct sockaddr_storage ss; struct sockaddr_in sin; #ifdef CONFIG_IPV6 struct sockaddr_in6 sin6; #endif /* CONFIG_IPV6 */ } from; char abuf[50]; int from_port = 0; socklen_t fromlen; int len; struct radius_msg *msg, *reply = NULL; struct radius_hdr *hdr; struct wpabuf *rbuf; u32 val; int res; struct os_time now; fromlen = sizeof(from); len = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *) &from.ss, &fromlen); if (len < 0) { wpa_printf(MSG_ERROR, "DAS: recvfrom: %s", strerror(errno)); return; } os_strlcpy(abuf, inet_ntoa(from.sin.sin_addr), sizeof(abuf)); from_port = ntohs(from.sin.sin_port); wpa_printf(MSG_DEBUG, "DAS: Received %d bytes from %s:%d", len, abuf, from_port); if (das->client_addr.u.v4.s_addr != from.sin.sin_addr.s_addr) { wpa_printf(MSG_DEBUG, "DAS: Drop message from unknown client"); return; } msg = radius_msg_parse(buf, len); if (msg == NULL) { wpa_printf(MSG_DEBUG, "DAS: Parsing incoming RADIUS packet " "from %s:%d failed", abuf, from_port); return; } if (wpa_debug_level <= MSG_MSGDUMP) radius_msg_dump(msg); if (radius_msg_verify_das_req(msg, das->shared_secret, das->shared_secret_len)) { wpa_printf(MSG_DEBUG, "DAS: Invalid authenticator in packet " "from %s:%d - drop", abuf, from_port); goto fail; } os_get_time(&now); res = radius_msg_get_attr(msg, RADIUS_ATTR_EVENT_TIMESTAMP, (u8 *) &val, 4); if (res == 4) { u32 timestamp = ntohl(val); if ((unsigned int) abs(now.sec - timestamp) > das->time_window) { wpa_printf(MSG_DEBUG, "DAS: Unacceptable " "Event-Timestamp (%u; local time %u) in " "packet from %s:%d - drop", timestamp, (unsigned int) now.sec, abuf, from_port); goto fail; } } else if (das->require_event_timestamp) { wpa_printf(MSG_DEBUG, "DAS: Missing Event-Timestamp in packet " "from %s:%d - drop", abuf, from_port); goto fail; } hdr = radius_msg_get_hdr(msg); switch (hdr->code) { case RADIUS_CODE_DISCONNECT_REQUEST: reply = radius_das_disconnect(das, msg, abuf, from_port); break; case RADIUS_CODE_COA_REQUEST: /* TODO */ reply = radius_msg_new(RADIUS_CODE_COA_NAK, hdr->identifier); if (reply == NULL) break; /* Unsupported Service */ if (!radius_msg_add_attr_int32(reply, RADIUS_ATTR_ERROR_CAUSE, 405)) { radius_msg_free(reply); reply = NULL; break; } break; default: wpa_printf(MSG_DEBUG, "DAS: Unexpected RADIUS code %u in " "packet from %s:%d", hdr->code, abuf, from_port); } if (reply) { wpa_printf(MSG_DEBUG, "DAS: Reply to %s:%d", abuf, from_port); if (!radius_msg_add_attr_int32(reply, RADIUS_ATTR_EVENT_TIMESTAMP, now.sec)) { wpa_printf(MSG_DEBUG, "DAS: Failed to add " "Event-Timestamp attribute"); } if (radius_msg_finish_das_resp(reply, das->shared_secret, das->shared_secret_len, hdr) < 0) { wpa_printf(MSG_DEBUG, "DAS: Failed to add " "Message-Authenticator attribute"); } if (wpa_debug_level <= MSG_MSGDUMP) radius_msg_dump(reply); rbuf = radius_msg_get_buf(reply); res = sendto(das->sock, wpabuf_head(rbuf), wpabuf_len(rbuf), 0, (struct sockaddr *) &from.ss, fromlen); if (res < 0) { wpa_printf(MSG_ERROR, "DAS: sendto(to %s:%d): %s", abuf, from_port, strerror(errno)); } } fail: radius_msg_free(msg); radius_msg_free(reply); } static int radius_das_open_socket(int port) { int s; struct sockaddr_in addr; s = socket(PF_INET, SOCK_DGRAM, 0); if (s < 0) { wpa_printf(MSG_INFO, "RADIUS DAS: socket: %s", strerror(errno)); return -1; } os_memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(port); if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { wpa_printf(MSG_INFO, "RADIUS DAS: bind: %s", strerror(errno)); close(s); return -1; } return s; } struct radius_das_data * radius_das_init(struct radius_das_conf *conf) { struct radius_das_data *das; if (conf->port == 0 || conf->shared_secret == NULL || conf->client_addr == NULL) return NULL; das = os_zalloc(sizeof(*das)); if (das == NULL) return NULL; das->time_window = conf->time_window; das->require_event_timestamp = conf->require_event_timestamp; das->ctx = conf->ctx; das->disconnect = conf->disconnect; os_memcpy(&das->client_addr, conf->client_addr, sizeof(das->client_addr)); das->shared_secret = os_malloc(conf->shared_secret_len); if (das->shared_secret == NULL) { radius_das_deinit(das); return NULL; } os_memcpy(das->shared_secret, conf->shared_secret, conf->shared_secret_len); das->shared_secret_len = conf->shared_secret_len; das->sock = radius_das_open_socket(conf->port); if (das->sock < 0) { wpa_printf(MSG_ERROR, "Failed to open UDP socket for RADIUS " "DAS"); radius_das_deinit(das); return NULL; } if (eloop_register_read_sock(das->sock, radius_das_receive, das, NULL)) { radius_das_deinit(das); return NULL; } return das; } void radius_das_deinit(struct radius_das_data *das) { if (das == NULL) return; if (das->sock >= 0) { eloop_unregister_read_sock(das->sock); close(das->sock); } os_free(das->shared_secret); os_free(das); } wpa-2.1/src/radius/radius_das.h000066400000000000000000000017611227414666700165340ustar00rootroot00000000000000/* * RADIUS Dynamic Authorization Server (DAS) * Copyright (c) 2012, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef RADIUS_DAS_H #define RADIUS_DAS_H struct radius_das_data; enum radius_das_res { RADIUS_DAS_SUCCESS, RADIUS_DAS_NAS_MISMATCH, RADIUS_DAS_SESSION_NOT_FOUND }; struct radius_das_attrs { const u8 *sta_addr; const u8 *user_name; size_t user_name_len; const u8 *acct_session_id; size_t acct_session_id_len; const u8 *cui; size_t cui_len; }; struct radius_das_conf { int port; const u8 *shared_secret; size_t shared_secret_len; const struct hostapd_ip_addr *client_addr; unsigned int time_window; int require_event_timestamp; void *ctx; enum radius_das_res (*disconnect)(void *ctx, struct radius_das_attrs *attr); }; struct radius_das_data * radius_das_init(struct radius_das_conf *conf); void radius_das_deinit(struct radius_das_data *data); #endif /* RADIUS_DAS_H */ wpa-2.1/src/radius/radius_server.c000066400000000000000000001162561227414666700172740ustar00rootroot00000000000000/* * RADIUS authentication server * Copyright (c) 2005-2009, 2011, 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.h" #include "radius.h" #include "eloop.h" #include "eap_server/eap.h" #include "radius_server.h" /** * RADIUS_SESSION_TIMEOUT - Session timeout in seconds */ #define RADIUS_SESSION_TIMEOUT 60 /** * RADIUS_MAX_SESSION - Maximum number of active sessions */ #define RADIUS_MAX_SESSION 100 /** * RADIUS_MAX_MSG_LEN - Maximum message length for incoming RADIUS messages */ #define RADIUS_MAX_MSG_LEN 3000 static struct eapol_callbacks radius_server_eapol_cb; struct radius_client; struct radius_server_data; /** * struct radius_server_counters - RADIUS server statistics counters */ struct radius_server_counters { u32 access_requests; u32 invalid_requests; u32 dup_access_requests; u32 access_accepts; u32 access_rejects; u32 access_challenges; u32 malformed_access_requests; u32 bad_authenticators; u32 packets_dropped; u32 unknown_types; }; /** * struct radius_session - Internal RADIUS server data for a session */ struct radius_session { struct radius_session *next; struct radius_client *client; struct radius_server_data *server; unsigned int sess_id; struct eap_sm *eap; struct eap_eapol_interface *eap_if; struct radius_msg *last_msg; char *last_from_addr; int last_from_port; struct sockaddr_storage last_from; socklen_t last_fromlen; u8 last_identifier; struct radius_msg *last_reply; u8 last_authenticator[16]; }; /** * struct radius_client - Internal RADIUS server data for a client */ struct radius_client { struct radius_client *next; struct in_addr addr; struct in_addr mask; #ifdef CONFIG_IPV6 struct in6_addr addr6; struct in6_addr mask6; #endif /* CONFIG_IPV6 */ char *shared_secret; int shared_secret_len; struct radius_session *sessions; struct radius_server_counters counters; }; /** * struct radius_server_data - Internal RADIUS server data */ struct radius_server_data { /** * auth_sock - Socket for RADIUS authentication messages */ int auth_sock; /** * clients - List of authorized RADIUS clients */ struct radius_client *clients; /** * next_sess_id - Next session identifier */ unsigned int next_sess_id; /** * conf_ctx - Context pointer for callbacks * * This is used as the ctx argument in get_eap_user() calls. */ void *conf_ctx; /** * num_sess - Number of active sessions */ int num_sess; /** * eap_sim_db_priv - EAP-SIM/AKA database context * * This is passed to the EAP-SIM/AKA server implementation as a * callback context. */ void *eap_sim_db_priv; /** * ssl_ctx - TLS context * * This is passed to the EAP server implementation as a callback * context for TLS operations. */ void *ssl_ctx; /** * pac_opaque_encr_key - PAC-Opaque encryption key for EAP-FAST * * This parameter is used to set a key for EAP-FAST to encrypt the * PAC-Opaque data. It can be set to %NULL if EAP-FAST is not used. If * set, must point to a 16-octet key. */ u8 *pac_opaque_encr_key; /** * eap_fast_a_id - EAP-FAST authority identity (A-ID) * * If EAP-FAST is not used, this can be set to %NULL. 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 recommended to use * that length for the field to provide interoperability with deployed * peer implementations. */ u8 *eap_fast_a_id; /** * eap_fast_a_id_len - Length of eap_fast_a_id buffer in octets */ size_t eap_fast_a_id_len; /** * eap_fast_a_id_info - EAP-FAST authority identifier information * * This A-ID-Info contains a user-friendly name for the A-ID. For * example, this could be the enterprise and server names in * human-readable format. This field is encoded as UTF-8. If EAP-FAST * is not used, this can be set to %NULL. */ char *eap_fast_a_id_info; /** * eap_fast_prov - EAP-FAST provisioning modes * * 0 = provisioning disabled, 1 = only anonymous provisioning allowed, * 2 = only authenticated provisioning allowed, 3 = both provisioning * modes allowed. */ int eap_fast_prov; /** * pac_key_lifetime - EAP-FAST PAC-Key lifetime in seconds * * This is the hard limit on how long a provisioned PAC-Key can be * used. */ int pac_key_lifetime; /** * pac_key_refresh_time - EAP-FAST PAC-Key refresh time in seconds * * This is a soft limit on the PAC-Key. The server will automatically * generate a new PAC-Key when this number of seconds (or fewer) of the * lifetime remains. */ int pac_key_refresh_time; /** * eap_sim_aka_result_ind - EAP-SIM/AKA protected success indication * * This controls whether the protected success/failure indication * (AT_RESULT_IND) is used with EAP-SIM and EAP-AKA. */ int eap_sim_aka_result_ind; /** * tnc - Trusted Network Connect (TNC) * * This controls whether TNC is enabled and 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. */ int tnc; /** * pwd_group - The D-H group assigned for EAP-pwd * * If EAP-pwd is not used it can be set to zero. */ u16 pwd_group; /** * server_id - Server identity */ const char *server_id; /** * wps - Wi-Fi Protected Setup context * * If WPS is used with an external RADIUS server (which is quite * unlikely configuration), this is used to provide a pointer to WPS * context data. Normally, this can be set to %NULL. */ struct wps_context *wps; /** * ipv6 - Whether to enable IPv6 support in the RADIUS server */ int ipv6; /** * start_time - Timestamp of server start */ struct os_reltime start_time; /** * counters - Statistics counters for server operations * * These counters are the sum over all clients. */ struct radius_server_counters counters; /** * get_eap_user - Callback for fetching EAP user information * @ctx: Context data from conf_ctx * @identity: User identity * @identity_len: identity buffer length in octets * @phase2: Whether this is for Phase 2 identity * @user: Data structure for filling in the user information * Returns: 0 on success, -1 on failure * * This is used to fetch information from user database. The callback * will fill in information about allowed EAP methods and the user * password. The password field will be an allocated copy of the * password data and RADIUS server will free it after use. */ int (*get_eap_user)(void *ctx, const u8 *identity, size_t identity_len, int phase2, struct eap_user *user); /** * eap_req_id_text - Optional data for EAP-Request/Identity * * This can be used to configure an optional, displayable message that * will be sent in EAP-Request/Identity. This string can contain an * ASCII-0 character (nul) to separate network infromation per RFC * 4284. The actual string length is explicit provided in * eap_req_id_text_len since nul character will not be used as a string * terminator. */ char *eap_req_id_text; /** * eap_req_id_text_len - Length of eap_req_id_text buffer in octets */ size_t eap_req_id_text_len; /* * msg_ctx - Context data for wpa_msg() calls */ void *msg_ctx; #ifdef CONFIG_RADIUS_TEST char *dump_msk_file; #endif /* CONFIG_RADIUS_TEST */ }; #define RADIUS_DEBUG(args...) \ wpa_printf(MSG_DEBUG, "RADIUS SRV: " args) #define RADIUS_ERROR(args...) \ wpa_printf(MSG_ERROR, "RADIUS SRV: " args) #define RADIUS_DUMP(args...) \ wpa_hexdump(MSG_MSGDUMP, "RADIUS SRV: " args) #define RADIUS_DUMP_ASCII(args...) \ wpa_hexdump_ascii(MSG_MSGDUMP, "RADIUS SRV: " args) static void radius_server_session_timeout(void *eloop_ctx, void *timeout_ctx); static void radius_server_session_remove_timeout(void *eloop_ctx, void *timeout_ctx); static struct radius_client * radius_server_get_client(struct radius_server_data *data, struct in_addr *addr, int ipv6) { struct radius_client *client = data->clients; while (client) { #ifdef CONFIG_IPV6 if (ipv6) { struct in6_addr *addr6; int i; addr6 = (struct in6_addr *) addr; for (i = 0; i < 16; i++) { if ((addr6->s6_addr[i] & client->mask6.s6_addr[i]) != (client->addr6.s6_addr[i] & client->mask6.s6_addr[i])) { i = 17; break; } } if (i == 16) { break; } } #endif /* CONFIG_IPV6 */ if (!ipv6 && (client->addr.s_addr & client->mask.s_addr) == (addr->s_addr & client->mask.s_addr)) { break; } client = client->next; } return client; } static struct radius_session * radius_server_get_session(struct radius_client *client, unsigned int sess_id) { struct radius_session *sess = client->sessions; while (sess) { if (sess->sess_id == sess_id) { break; } sess = sess->next; } return sess; } static void radius_server_session_free(struct radius_server_data *data, struct radius_session *sess) { eloop_cancel_timeout(radius_server_session_timeout, data, sess); eloop_cancel_timeout(radius_server_session_remove_timeout, data, sess); eap_server_sm_deinit(sess->eap); radius_msg_free(sess->last_msg); os_free(sess->last_from_addr); radius_msg_free(sess->last_reply); os_free(sess); data->num_sess--; } static void radius_server_session_remove(struct radius_server_data *data, struct radius_session *sess) { struct radius_client *client = sess->client; struct radius_session *session, *prev; eloop_cancel_timeout(radius_server_session_remove_timeout, data, sess); prev = NULL; session = client->sessions; while (session) { if (session == sess) { if (prev == NULL) { client->sessions = sess->next; } else { prev->next = sess->next; } radius_server_session_free(data, sess); break; } prev = session; session = session->next; } } static void radius_server_session_remove_timeout(void *eloop_ctx, void *timeout_ctx) { struct radius_server_data *data = eloop_ctx; struct radius_session *sess = timeout_ctx; RADIUS_DEBUG("Removing completed session 0x%x", sess->sess_id); radius_server_session_remove(data, sess); } static void radius_server_session_timeout(void *eloop_ctx, void *timeout_ctx) { struct radius_server_data *data = eloop_ctx; struct radius_session *sess = timeout_ctx; RADIUS_DEBUG("Timing out authentication session 0x%x", sess->sess_id); radius_server_session_remove(data, sess); } static struct radius_session * radius_server_new_session(struct radius_server_data *data, struct radius_client *client) { struct radius_session *sess; if (data->num_sess >= RADIUS_MAX_SESSION) { RADIUS_DEBUG("Maximum number of existing session - no room " "for a new session"); return NULL; } sess = os_zalloc(sizeof(*sess)); if (sess == NULL) return NULL; sess->server = data; sess->client = client; sess->sess_id = data->next_sess_id++; sess->next = client->sessions; client->sessions = sess; eloop_register_timeout(RADIUS_SESSION_TIMEOUT, 0, radius_server_session_timeout, data, sess); data->num_sess++; return sess; } static struct radius_session * radius_server_get_new_session(struct radius_server_data *data, struct radius_client *client, struct radius_msg *msg) { u8 *user; size_t user_len; int res; struct radius_session *sess; struct eap_config eap_conf; RADIUS_DEBUG("Creating a new session"); user = os_malloc(256); if (user == NULL) { return NULL; } res = radius_msg_get_attr(msg, RADIUS_ATTR_USER_NAME, user, 256); if (res < 0 || res > 256) { RADIUS_DEBUG("Could not get User-Name"); os_free(user); return NULL; } user_len = res; RADIUS_DUMP_ASCII("User-Name", user, user_len); res = data->get_eap_user(data->conf_ctx, user, user_len, 0, NULL); os_free(user); if (res == 0) { RADIUS_DEBUG("Matching user entry found"); sess = radius_server_new_session(data, client); if (sess == NULL) { RADIUS_DEBUG("Failed to create a new session"); return NULL; } } else { RADIUS_DEBUG("User-Name not found from user database"); return NULL; } os_memset(&eap_conf, 0, sizeof(eap_conf)); eap_conf.ssl_ctx = data->ssl_ctx; eap_conf.msg_ctx = data->msg_ctx; eap_conf.eap_sim_db_priv = data->eap_sim_db_priv; eap_conf.backend_auth = TRUE; eap_conf.eap_server = 1; eap_conf.pac_opaque_encr_key = data->pac_opaque_encr_key; eap_conf.eap_fast_a_id = data->eap_fast_a_id; eap_conf.eap_fast_a_id_len = data->eap_fast_a_id_len; eap_conf.eap_fast_a_id_info = data->eap_fast_a_id_info; eap_conf.eap_fast_prov = data->eap_fast_prov; eap_conf.pac_key_lifetime = data->pac_key_lifetime; eap_conf.pac_key_refresh_time = data->pac_key_refresh_time; eap_conf.eap_sim_aka_result_ind = data->eap_sim_aka_result_ind; eap_conf.tnc = data->tnc; eap_conf.wps = data->wps; eap_conf.pwd_group = data->pwd_group; eap_conf.server_id = (const u8 *) data->server_id; eap_conf.server_id_len = os_strlen(data->server_id); sess->eap = eap_server_sm_init(sess, &radius_server_eapol_cb, &eap_conf); if (sess->eap == NULL) { RADIUS_DEBUG("Failed to initialize EAP state machine for the " "new session"); radius_server_session_free(data, sess); return NULL; } sess->eap_if = eap_get_interface(sess->eap); sess->eap_if->eapRestart = TRUE; sess->eap_if->portEnabled = TRUE; RADIUS_DEBUG("New session 0x%x initialized", sess->sess_id); return sess; } static struct radius_msg * radius_server_encapsulate_eap(struct radius_server_data *data, struct radius_client *client, struct radius_session *sess, struct radius_msg *request) { struct radius_msg *msg; int code; unsigned int sess_id; struct radius_hdr *hdr = radius_msg_get_hdr(request); if (sess->eap_if->eapFail) { sess->eap_if->eapFail = FALSE; code = RADIUS_CODE_ACCESS_REJECT; } else if (sess->eap_if->eapSuccess) { sess->eap_if->eapSuccess = FALSE; code = RADIUS_CODE_ACCESS_ACCEPT; } else { sess->eap_if->eapReq = FALSE; code = RADIUS_CODE_ACCESS_CHALLENGE; } msg = radius_msg_new(code, hdr->identifier); if (msg == NULL) { RADIUS_DEBUG("Failed to allocate reply message"); return NULL; } sess_id = htonl(sess->sess_id); if (code == RADIUS_CODE_ACCESS_CHALLENGE && !radius_msg_add_attr(msg, RADIUS_ATTR_STATE, (u8 *) &sess_id, sizeof(sess_id))) { RADIUS_DEBUG("Failed to add State attribute"); } if (sess->eap_if->eapReqData && !radius_msg_add_eap(msg, wpabuf_head(sess->eap_if->eapReqData), wpabuf_len(sess->eap_if->eapReqData))) { RADIUS_DEBUG("Failed to add EAP-Message attribute"); } if (code == RADIUS_CODE_ACCESS_ACCEPT && sess->eap_if->eapKeyData) { int len; #ifdef CONFIG_RADIUS_TEST if (data->dump_msk_file) { FILE *f; char buf[2 * 64 + 1]; f = fopen(data->dump_msk_file, "a"); if (f) { len = sess->eap_if->eapKeyDataLen; if (len > 64) len = 64; len = wpa_snprintf_hex( buf, sizeof(buf), sess->eap_if->eapKeyData, len); buf[len] = '\0'; fprintf(f, "%s\n", buf); fclose(f); } } #endif /* CONFIG_RADIUS_TEST */ if (sess->eap_if->eapKeyDataLen > 64) { len = 32; } else { len = sess->eap_if->eapKeyDataLen / 2; } if (!radius_msg_add_mppe_keys(msg, hdr->authenticator, (u8 *) client->shared_secret, client->shared_secret_len, sess->eap_if->eapKeyData + len, len, sess->eap_if->eapKeyData, len)) { RADIUS_DEBUG("Failed to add MPPE key attributes"); } } if (radius_msg_copy_attr(msg, request, RADIUS_ATTR_PROXY_STATE) < 0) { RADIUS_DEBUG("Failed to copy Proxy-State attribute(s)"); radius_msg_free(msg); return NULL; } if (radius_msg_finish_srv(msg, (u8 *) client->shared_secret, client->shared_secret_len, hdr->authenticator) < 0) { RADIUS_DEBUG("Failed to add Message-Authenticator attribute"); } return msg; } static int radius_server_reject(struct radius_server_data *data, struct radius_client *client, struct radius_msg *request, struct sockaddr *from, socklen_t fromlen, const char *from_addr, int from_port) { struct radius_msg *msg; int ret = 0; struct eap_hdr eapfail; struct wpabuf *buf; struct radius_hdr *hdr = radius_msg_get_hdr(request); RADIUS_DEBUG("Reject invalid request from %s:%d", from_addr, from_port); msg = radius_msg_new(RADIUS_CODE_ACCESS_REJECT, hdr->identifier); if (msg == NULL) { return -1; } os_memset(&eapfail, 0, sizeof(eapfail)); eapfail.code = EAP_CODE_FAILURE; eapfail.identifier = 0; eapfail.length = host_to_be16(sizeof(eapfail)); if (!radius_msg_add_eap(msg, (u8 *) &eapfail, sizeof(eapfail))) { RADIUS_DEBUG("Failed to add EAP-Message attribute"); } if (radius_msg_copy_attr(msg, request, RADIUS_ATTR_PROXY_STATE) < 0) { RADIUS_DEBUG("Failed to copy Proxy-State attribute(s)"); radius_msg_free(msg); return -1; } if (radius_msg_finish_srv(msg, (u8 *) client->shared_secret, client->shared_secret_len, hdr->authenticator) < 0) { RADIUS_DEBUG("Failed to add Message-Authenticator attribute"); } if (wpa_debug_level <= MSG_MSGDUMP) { radius_msg_dump(msg); } data->counters.access_rejects++; client->counters.access_rejects++; buf = radius_msg_get_buf(msg); if (sendto(data->auth_sock, wpabuf_head(buf), wpabuf_len(buf), 0, (struct sockaddr *) from, sizeof(*from)) < 0) { wpa_printf(MSG_INFO, "sendto[RADIUS SRV]: %s", strerror(errno)); ret = -1; } radius_msg_free(msg); return ret; } static int radius_server_request(struct radius_server_data *data, struct radius_msg *msg, struct sockaddr *from, socklen_t fromlen, struct radius_client *client, const char *from_addr, int from_port, struct radius_session *force_sess) { struct wpabuf *eap = NULL; int res, state_included = 0; u8 statebuf[4]; unsigned int state; struct radius_session *sess; struct radius_msg *reply; int is_complete = 0; if (force_sess) sess = force_sess; else { res = radius_msg_get_attr(msg, RADIUS_ATTR_STATE, statebuf, sizeof(statebuf)); state_included = res >= 0; if (res == sizeof(statebuf)) { state = WPA_GET_BE32(statebuf); sess = radius_server_get_session(client, state); } else { sess = NULL; } } if (sess) { RADIUS_DEBUG("Request for session 0x%x", sess->sess_id); } else if (state_included) { RADIUS_DEBUG("State attribute included but no session found"); radius_server_reject(data, client, msg, from, fromlen, from_addr, from_port); return -1; } else { sess = radius_server_get_new_session(data, client, msg); if (sess == NULL) { RADIUS_DEBUG("Could not create a new session"); radius_server_reject(data, client, msg, from, fromlen, from_addr, from_port); return -1; } } if (sess->last_from_port == from_port && sess->last_identifier == radius_msg_get_hdr(msg)->identifier && os_memcmp(sess->last_authenticator, radius_msg_get_hdr(msg)->authenticator, 16) == 0) { RADIUS_DEBUG("Duplicate message from %s", from_addr); data->counters.dup_access_requests++; client->counters.dup_access_requests++; if (sess->last_reply) { struct wpabuf *buf; buf = radius_msg_get_buf(sess->last_reply); res = sendto(data->auth_sock, wpabuf_head(buf), wpabuf_len(buf), 0, (struct sockaddr *) from, fromlen); if (res < 0) { wpa_printf(MSG_INFO, "sendto[RADIUS SRV]: %s", strerror(errno)); } return 0; } RADIUS_DEBUG("No previous reply available for duplicate " "message"); return -1; } eap = radius_msg_get_eap(msg); if (eap == NULL) { RADIUS_DEBUG("No EAP-Message in RADIUS packet from %s", from_addr); data->counters.packets_dropped++; client->counters.packets_dropped++; return -1; } RADIUS_DUMP("Received EAP data", wpabuf_head(eap), wpabuf_len(eap)); /* FIX: if Code is Request, Success, or Failure, send Access-Reject; * RFC3579 Sect. 2.6.2. * Include EAP-Response/Nak with no preferred method if * code == request. * If code is not 1-4, discard the packet silently. * Or is this already done by the EAP state machine? */ wpabuf_free(sess->eap_if->eapRespData); sess->eap_if->eapRespData = eap; sess->eap_if->eapResp = TRUE; eap_server_sm_step(sess->eap); if ((sess->eap_if->eapReq || sess->eap_if->eapSuccess || sess->eap_if->eapFail) && sess->eap_if->eapReqData) { RADIUS_DUMP("EAP data from the state machine", wpabuf_head(sess->eap_if->eapReqData), wpabuf_len(sess->eap_if->eapReqData)); } else if (sess->eap_if->eapFail) { RADIUS_DEBUG("No EAP data from the state machine, but eapFail " "set"); } else if (eap_sm_method_pending(sess->eap)) { radius_msg_free(sess->last_msg); sess->last_msg = msg; sess->last_from_port = from_port; os_free(sess->last_from_addr); sess->last_from_addr = os_strdup(from_addr); sess->last_fromlen = fromlen; os_memcpy(&sess->last_from, from, fromlen); return -2; } else { RADIUS_DEBUG("No EAP data from the state machine - ignore this" " Access-Request silently (assuming it was a " "duplicate)"); data->counters.packets_dropped++; client->counters.packets_dropped++; return -1; } if (sess->eap_if->eapSuccess || sess->eap_if->eapFail) is_complete = 1; reply = radius_server_encapsulate_eap(data, client, sess, msg); if (reply) { struct wpabuf *buf; struct radius_hdr *hdr; RADIUS_DEBUG("Reply to %s:%d", from_addr, from_port); if (wpa_debug_level <= MSG_MSGDUMP) { radius_msg_dump(reply); } switch (radius_msg_get_hdr(reply)->code) { case RADIUS_CODE_ACCESS_ACCEPT: data->counters.access_accepts++; client->counters.access_accepts++; break; case RADIUS_CODE_ACCESS_REJECT: data->counters.access_rejects++; client->counters.access_rejects++; break; case RADIUS_CODE_ACCESS_CHALLENGE: data->counters.access_challenges++; client->counters.access_challenges++; break; } buf = radius_msg_get_buf(reply); res = sendto(data->auth_sock, wpabuf_head(buf), wpabuf_len(buf), 0, (struct sockaddr *) from, fromlen); if (res < 0) { wpa_printf(MSG_INFO, "sendto[RADIUS SRV]: %s", strerror(errno)); } radius_msg_free(sess->last_reply); sess->last_reply = reply; sess->last_from_port = from_port; hdr = radius_msg_get_hdr(msg); sess->last_identifier = hdr->identifier; os_memcpy(sess->last_authenticator, hdr->authenticator, 16); } else { data->counters.packets_dropped++; client->counters.packets_dropped++; } if (is_complete) { RADIUS_DEBUG("Removing completed session 0x%x after timeout", sess->sess_id); eloop_cancel_timeout(radius_server_session_remove_timeout, data, sess); eloop_register_timeout(10, 0, radius_server_session_remove_timeout, data, sess); } return 0; } static void radius_server_receive_auth(int sock, void *eloop_ctx, void *sock_ctx) { struct radius_server_data *data = eloop_ctx; u8 *buf = NULL; union { struct sockaddr_storage ss; struct sockaddr_in sin; #ifdef CONFIG_IPV6 struct sockaddr_in6 sin6; #endif /* CONFIG_IPV6 */ } from; socklen_t fromlen; int len; struct radius_client *client = NULL; struct radius_msg *msg = NULL; char abuf[50]; int from_port = 0; buf = os_malloc(RADIUS_MAX_MSG_LEN); if (buf == NULL) { goto fail; } fromlen = sizeof(from); len = recvfrom(sock, buf, RADIUS_MAX_MSG_LEN, 0, (struct sockaddr *) &from.ss, &fromlen); if (len < 0) { wpa_printf(MSG_INFO, "recvfrom[radius_server]: %s", strerror(errno)); goto fail; } #ifdef CONFIG_IPV6 if (data->ipv6) { if (inet_ntop(AF_INET6, &from.sin6.sin6_addr, abuf, sizeof(abuf)) == NULL) abuf[0] = '\0'; from_port = ntohs(from.sin6.sin6_port); RADIUS_DEBUG("Received %d bytes from %s:%d", len, abuf, from_port); client = radius_server_get_client(data, (struct in_addr *) &from.sin6.sin6_addr, 1); } #endif /* CONFIG_IPV6 */ if (!data->ipv6) { os_strlcpy(abuf, inet_ntoa(from.sin.sin_addr), sizeof(abuf)); from_port = ntohs(from.sin.sin_port); RADIUS_DEBUG("Received %d bytes from %s:%d", len, abuf, from_port); client = radius_server_get_client(data, &from.sin.sin_addr, 0); } RADIUS_DUMP("Received data", buf, len); if (client == NULL) { RADIUS_DEBUG("Unknown client %s - packet ignored", abuf); data->counters.invalid_requests++; goto fail; } msg = radius_msg_parse(buf, len); if (msg == NULL) { RADIUS_DEBUG("Parsing incoming RADIUS frame failed"); data->counters.malformed_access_requests++; client->counters.malformed_access_requests++; goto fail; } os_free(buf); buf = NULL; if (wpa_debug_level <= MSG_MSGDUMP) { radius_msg_dump(msg); } if (radius_msg_get_hdr(msg)->code != RADIUS_CODE_ACCESS_REQUEST) { RADIUS_DEBUG("Unexpected RADIUS code %d", radius_msg_get_hdr(msg)->code); data->counters.unknown_types++; client->counters.unknown_types++; goto fail; } data->counters.access_requests++; client->counters.access_requests++; if (radius_msg_verify_msg_auth(msg, (u8 *) client->shared_secret, client->shared_secret_len, NULL)) { RADIUS_DEBUG("Invalid Message-Authenticator from %s", abuf); data->counters.bad_authenticators++; client->counters.bad_authenticators++; goto fail; } if (radius_server_request(data, msg, (struct sockaddr *) &from, fromlen, client, abuf, from_port, NULL) == -2) return; /* msg was stored with the session */ fail: radius_msg_free(msg); os_free(buf); } static int radius_server_disable_pmtu_discovery(int s) { int r = -1; #if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT) /* Turn off Path MTU discovery on IPv4/UDP sockets. */ int action = IP_PMTUDISC_DONT; r = setsockopt(s, IPPROTO_IP, IP_MTU_DISCOVER, &action, sizeof(action)); if (r == -1) wpa_printf(MSG_ERROR, "Failed to set IP_MTU_DISCOVER: " "%s", strerror(errno)); #endif return r; } static int radius_server_open_socket(int port) { int s; struct sockaddr_in addr; s = socket(PF_INET, SOCK_DGRAM, 0); if (s < 0) { wpa_printf(MSG_INFO, "RADIUS: socket: %s", strerror(errno)); return -1; } radius_server_disable_pmtu_discovery(s); os_memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(port); if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { wpa_printf(MSG_INFO, "RADIUS: bind: %s", strerror(errno)); close(s); return -1; } return s; } #ifdef CONFIG_IPV6 static int radius_server_open_socket6(int port) { int s; struct sockaddr_in6 addr; s = socket(PF_INET6, SOCK_DGRAM, 0); if (s < 0) { wpa_printf(MSG_INFO, "RADIUS: socket[IPv6]: %s", strerror(errno)); return -1; } os_memset(&addr, 0, sizeof(addr)); addr.sin6_family = AF_INET6; os_memcpy(&addr.sin6_addr, &in6addr_any, sizeof(in6addr_any)); addr.sin6_port = htons(port); if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { wpa_printf(MSG_INFO, "RADIUS: bind: %s", strerror(errno)); close(s); return -1; } return s; } #endif /* CONFIG_IPV6 */ static void radius_server_free_sessions(struct radius_server_data *data, struct radius_session *sessions) { struct radius_session *session, *prev; session = sessions; while (session) { prev = session; session = session->next; radius_server_session_free(data, prev); } } static void radius_server_free_clients(struct radius_server_data *data, struct radius_client *clients) { struct radius_client *client, *prev; client = clients; while (client) { prev = client; client = client->next; radius_server_free_sessions(data, prev->sessions); os_free(prev->shared_secret); os_free(prev); } } static struct radius_client * radius_server_read_clients(const char *client_file, int ipv6) { FILE *f; const int buf_size = 1024; char *buf, *pos; struct radius_client *clients, *tail, *entry; int line = 0, mask, failed = 0, i; struct in_addr addr; #ifdef CONFIG_IPV6 struct in6_addr addr6; #endif /* CONFIG_IPV6 */ unsigned int val; f = fopen(client_file, "r"); if (f == NULL) { RADIUS_ERROR("Could not open client file '%s'", client_file); return NULL; } buf = os_malloc(buf_size); if (buf == NULL) { fclose(f); return NULL; } clients = tail = NULL; while (fgets(buf, buf_size, f)) { /* Configuration file format: * 192.168.1.0/24 secret * 192.168.1.2 secret * fe80::211:22ff:fe33:4455/64 secretipv6 */ line++; buf[buf_size - 1] = '\0'; pos = buf; while (*pos != '\0' && *pos != '\n') pos++; if (*pos == '\n') *pos = '\0'; if (*buf == '\0' || *buf == '#') continue; pos = buf; while ((*pos >= '0' && *pos <= '9') || *pos == '.' || (*pos >= 'a' && *pos <= 'f') || *pos == ':' || (*pos >= 'A' && *pos <= 'F')) { pos++; } if (*pos == '\0') { failed = 1; break; } if (*pos == '/') { char *end; *pos++ = '\0'; mask = strtol(pos, &end, 10); if ((pos == end) || (mask < 0 || mask > (ipv6 ? 128 : 32))) { failed = 1; break; } pos = end; } else { mask = ipv6 ? 128 : 32; *pos++ = '\0'; } if (!ipv6 && inet_aton(buf, &addr) == 0) { failed = 1; break; } #ifdef CONFIG_IPV6 if (ipv6 && inet_pton(AF_INET6, buf, &addr6) <= 0) { if (inet_pton(AF_INET, buf, &addr) <= 0) { failed = 1; break; } /* Convert IPv4 address to IPv6 */ if (mask <= 32) mask += (128 - 32); os_memset(addr6.s6_addr, 0, 10); addr6.s6_addr[10] = 0xff; addr6.s6_addr[11] = 0xff; os_memcpy(addr6.s6_addr + 12, (char *) &addr.s_addr, 4); } #endif /* CONFIG_IPV6 */ while (*pos == ' ' || *pos == '\t') { pos++; } if (*pos == '\0') { failed = 1; break; } entry = os_zalloc(sizeof(*entry)); if (entry == NULL) { failed = 1; break; } entry->shared_secret = os_strdup(pos); if (entry->shared_secret == NULL) { failed = 1; os_free(entry); break; } entry->shared_secret_len = os_strlen(entry->shared_secret); entry->addr.s_addr = addr.s_addr; if (!ipv6) { val = 0; for (i = 0; i < mask; i++) val |= 1 << (31 - i); entry->mask.s_addr = htonl(val); } #ifdef CONFIG_IPV6 if (ipv6) { int offset = mask / 8; os_memcpy(entry->addr6.s6_addr, addr6.s6_addr, 16); os_memset(entry->mask6.s6_addr, 0xff, offset); val = 0; for (i = 0; i < (mask % 8); i++) val |= 1 << (7 - i); if (offset < 16) entry->mask6.s6_addr[offset] = val; } #endif /* CONFIG_IPV6 */ if (tail == NULL) { clients = tail = entry; } else { tail->next = entry; tail = entry; } } if (failed) { RADIUS_ERROR("Invalid line %d in '%s'", line, client_file); radius_server_free_clients(NULL, clients); clients = NULL; } os_free(buf); fclose(f); return clients; } /** * radius_server_init - Initialize RADIUS server * @conf: Configuration for the RADIUS server * Returns: Pointer to private RADIUS server context or %NULL on failure * * This initializes a RADIUS server instance and returns a context pointer that * will be used in other calls to the RADIUS server module. The server can be * deinitialize by calling radius_server_deinit(). */ struct radius_server_data * radius_server_init(struct radius_server_conf *conf) { struct radius_server_data *data; #ifndef CONFIG_IPV6 if (conf->ipv6) { wpa_printf(MSG_ERROR, "RADIUS server compiled without IPv6 support"); return NULL; } #endif /* CONFIG_IPV6 */ data = os_zalloc(sizeof(*data)); if (data == NULL) return NULL; os_get_reltime(&data->start_time); data->conf_ctx = conf->conf_ctx; data->eap_sim_db_priv = conf->eap_sim_db_priv; data->ssl_ctx = conf->ssl_ctx; data->msg_ctx = conf->msg_ctx; data->ipv6 = conf->ipv6; if (conf->pac_opaque_encr_key) { data->pac_opaque_encr_key = os_malloc(16); os_memcpy(data->pac_opaque_encr_key, conf->pac_opaque_encr_key, 16); } if (conf->eap_fast_a_id) { data->eap_fast_a_id = os_malloc(conf->eap_fast_a_id_len); if (data->eap_fast_a_id) { os_memcpy(data->eap_fast_a_id, conf->eap_fast_a_id, conf->eap_fast_a_id_len); data->eap_fast_a_id_len = conf->eap_fast_a_id_len; } } if (conf->eap_fast_a_id_info) data->eap_fast_a_id_info = os_strdup(conf->eap_fast_a_id_info); data->eap_fast_prov = conf->eap_fast_prov; data->pac_key_lifetime = conf->pac_key_lifetime; data->pac_key_refresh_time = conf->pac_key_refresh_time; data->get_eap_user = conf->get_eap_user; data->eap_sim_aka_result_ind = conf->eap_sim_aka_result_ind; data->tnc = conf->tnc; data->wps = conf->wps; data->pwd_group = conf->pwd_group; data->server_id = conf->server_id; if (conf->eap_req_id_text) { data->eap_req_id_text = os_malloc(conf->eap_req_id_text_len); if (data->eap_req_id_text) { os_memcpy(data->eap_req_id_text, conf->eap_req_id_text, conf->eap_req_id_text_len); data->eap_req_id_text_len = conf->eap_req_id_text_len; } } #ifdef CONFIG_RADIUS_TEST if (conf->dump_msk_file) data->dump_msk_file = os_strdup(conf->dump_msk_file); #endif /* CONFIG_RADIUS_TEST */ data->clients = radius_server_read_clients(conf->client_file, conf->ipv6); if (data->clients == NULL) { wpa_printf(MSG_ERROR, "No RADIUS clients configured"); radius_server_deinit(data); return NULL; } #ifdef CONFIG_IPV6 if (conf->ipv6) data->auth_sock = radius_server_open_socket6(conf->auth_port); else #endif /* CONFIG_IPV6 */ data->auth_sock = radius_server_open_socket(conf->auth_port); if (data->auth_sock < 0) { wpa_printf(MSG_ERROR, "Failed to open UDP socket for RADIUS authentication server"); radius_server_deinit(data); return NULL; } if (eloop_register_read_sock(data->auth_sock, radius_server_receive_auth, data, NULL)) { radius_server_deinit(data); return NULL; } return data; } /** * radius_server_deinit - Deinitialize RADIUS server * @data: RADIUS server context from radius_server_init() */ void radius_server_deinit(struct radius_server_data *data) { if (data == NULL) return; if (data->auth_sock >= 0) { eloop_unregister_read_sock(data->auth_sock); close(data->auth_sock); } radius_server_free_clients(data, data->clients); os_free(data->pac_opaque_encr_key); os_free(data->eap_fast_a_id); os_free(data->eap_fast_a_id_info); os_free(data->eap_req_id_text); #ifdef CONFIG_RADIUS_TEST os_free(data->dump_msk_file); #endif /* CONFIG_RADIUS_TEST */ os_free(data); } /** * radius_server_get_mib - Get RADIUS server MIB information * @data: RADIUS server context from radius_server_init() * @buf: Buffer for returning the MIB data in text format * @buflen: buf length in octets * Returns: Number of octets written into buf */ int radius_server_get_mib(struct radius_server_data *data, char *buf, size_t buflen) { int ret, uptime; unsigned int idx; char *end, *pos; struct os_reltime now; struct radius_client *cli; /* RFC 2619 - RADIUS Authentication Server MIB */ if (data == NULL || buflen == 0) return 0; pos = buf; end = buf + buflen; os_get_reltime(&now); uptime = (now.sec - data->start_time.sec) * 100 + ((now.usec - data->start_time.usec) / 10000) % 100; ret = os_snprintf(pos, end - pos, "RADIUS-AUTH-SERVER-MIB\n" "radiusAuthServIdent=hostapd\n" "radiusAuthServUpTime=%d\n" "radiusAuthServResetTime=0\n" "radiusAuthServConfigReset=4\n", uptime); if (ret < 0 || ret >= end - pos) { *pos = '\0'; return pos - buf; } pos += ret; ret = os_snprintf(pos, end - pos, "radiusAuthServTotalAccessRequests=%u\n" "radiusAuthServTotalInvalidRequests=%u\n" "radiusAuthServTotalDupAccessRequests=%u\n" "radiusAuthServTotalAccessAccepts=%u\n" "radiusAuthServTotalAccessRejects=%u\n" "radiusAuthServTotalAccessChallenges=%u\n" "radiusAuthServTotalMalformedAccessRequests=%u\n" "radiusAuthServTotalBadAuthenticators=%u\n" "radiusAuthServTotalPacketsDropped=%u\n" "radiusAuthServTotalUnknownTypes=%u\n", data->counters.access_requests, data->counters.invalid_requests, data->counters.dup_access_requests, data->counters.access_accepts, data->counters.access_rejects, data->counters.access_challenges, data->counters.malformed_access_requests, data->counters.bad_authenticators, data->counters.packets_dropped, data->counters.unknown_types); if (ret < 0 || ret >= end - pos) { *pos = '\0'; return pos - buf; } pos += ret; for (cli = data->clients, idx = 0; cli; cli = cli->next, idx++) { char abuf[50], mbuf[50]; #ifdef CONFIG_IPV6 if (data->ipv6) { if (inet_ntop(AF_INET6, &cli->addr6, abuf, sizeof(abuf)) == NULL) abuf[0] = '\0'; if (inet_ntop(AF_INET6, &cli->mask6, abuf, sizeof(mbuf)) == NULL) mbuf[0] = '\0'; } #endif /* CONFIG_IPV6 */ if (!data->ipv6) { os_strlcpy(abuf, inet_ntoa(cli->addr), sizeof(abuf)); os_strlcpy(mbuf, inet_ntoa(cli->mask), sizeof(mbuf)); } ret = os_snprintf(pos, end - pos, "radiusAuthClientIndex=%u\n" "radiusAuthClientAddress=%s/%s\n" "radiusAuthServAccessRequests=%u\n" "radiusAuthServDupAccessRequests=%u\n" "radiusAuthServAccessAccepts=%u\n" "radiusAuthServAccessRejects=%u\n" "radiusAuthServAccessChallenges=%u\n" "radiusAuthServMalformedAccessRequests=%u\n" "radiusAuthServBadAuthenticators=%u\n" "radiusAuthServPacketsDropped=%u\n" "radiusAuthServUnknownTypes=%u\n", idx, abuf, mbuf, cli->counters.access_requests, cli->counters.dup_access_requests, cli->counters.access_accepts, cli->counters.access_rejects, cli->counters.access_challenges, cli->counters.malformed_access_requests, cli->counters.bad_authenticators, cli->counters.packets_dropped, cli->counters.unknown_types); if (ret < 0 || ret >= end - pos) { *pos = '\0'; return pos - buf; } pos += ret; } return pos - buf; } static int radius_server_get_eap_user(void *ctx, const u8 *identity, size_t identity_len, int phase2, struct eap_user *user) { struct radius_session *sess = ctx; struct radius_server_data *data = sess->server; return data->get_eap_user(data->conf_ctx, identity, identity_len, phase2, user); } static const char * radius_server_get_eap_req_id_text(void *ctx, size_t *len) { struct radius_session *sess = ctx; struct radius_server_data *data = sess->server; *len = data->eap_req_id_text_len; return data->eap_req_id_text; } static struct eapol_callbacks radius_server_eapol_cb = { .get_eap_user = radius_server_get_eap_user, .get_eap_req_id_text = radius_server_get_eap_req_id_text, }; /** * radius_server_eap_pending_cb - Pending EAP data notification * @data: RADIUS server context from radius_server_init() * @ctx: Pending EAP context pointer * * This function is used to notify EAP server module that a pending operation * has been completed and processing of the EAP session can proceed. */ void radius_server_eap_pending_cb(struct radius_server_data *data, void *ctx) { struct radius_client *cli; struct radius_session *s, *sess = NULL; struct radius_msg *msg; if (data == NULL) return; for (cli = data->clients; cli; cli = cli->next) { for (s = cli->sessions; s; s = s->next) { if (s->eap == ctx && s->last_msg) { sess = s; break; } if (sess) break; } if (sess) break; } if (sess == NULL) { RADIUS_DEBUG("No session matched callback ctx"); return; } msg = sess->last_msg; sess->last_msg = NULL; eap_sm_pending_cb(sess->eap); if (radius_server_request(data, msg, (struct sockaddr *) &sess->last_from, sess->last_fromlen, cli, sess->last_from_addr, sess->last_from_port, sess) == -2) return; /* msg was stored with the session */ radius_msg_free(msg); } wpa-2.1/src/radius/radius_server.h000066400000000000000000000141771227414666700173000ustar00rootroot00000000000000/* * RADIUS authentication server * Copyright (c) 2005-2009, 2011, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef RADIUS_SERVER_H #define RADIUS_SERVER_H struct radius_server_data; struct eap_user; /** * struct radius_server_conf - RADIUS server configuration */ struct radius_server_conf { /** * auth_port - UDP port to listen to as an authentication server */ int auth_port; /** * client_file - RADIUS client configuration file * * This file contains the RADIUS clients and the shared secret to be * used with them in a format where each client is on its own line. The * first item on the line is the IPv4 or IPv6 address of the client * with an optional address mask to allow full network to be specified * (e.g., 192.168.1.2 or 192.168.1.0/24). This is followed by white * space (space or tabulator) and the shared secret. Lines starting * with '#' are skipped and can be used as comments. */ char *client_file; /** * conf_ctx - Context pointer for callbacks * * This is used as the ctx argument in get_eap_user() calls. */ void *conf_ctx; /** * eap_sim_db_priv - EAP-SIM/AKA database context * * This is passed to the EAP-SIM/AKA server implementation as a * callback context. */ void *eap_sim_db_priv; /** * ssl_ctx - TLS context * * This is passed to the EAP server implementation as a callback * context for TLS operations. */ void *ssl_ctx; /** * pac_opaque_encr_key - PAC-Opaque encryption key for EAP-FAST * * This parameter is used to set a key for EAP-FAST to encrypt the * PAC-Opaque data. It can be set to %NULL if EAP-FAST is not used. If * set, must point to a 16-octet key. */ u8 *pac_opaque_encr_key; /** * eap_fast_a_id - EAP-FAST authority identity (A-ID) * * If EAP-FAST is not used, this can be set to %NULL. 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 recommended to use * that length for the field to provide interoperability with deployed * peer implementations. */ u8 *eap_fast_a_id; /** * eap_fast_a_id_len - Length of eap_fast_a_id buffer in octets */ size_t eap_fast_a_id_len; /** * eap_fast_a_id_info - EAP-FAST authority identifier information * * This A-ID-Info contains a user-friendly name for the A-ID. For * example, this could be the enterprise and server names in * human-readable format. This field is encoded as UTF-8. If EAP-FAST * is not used, this can be set to %NULL. */ char *eap_fast_a_id_info; /** * eap_fast_prov - EAP-FAST provisioning modes * * 0 = provisioning disabled, 1 = only anonymous provisioning allowed, * 2 = only authenticated provisioning allowed, 3 = both provisioning * modes allowed. */ int eap_fast_prov; /** * pac_key_lifetime - EAP-FAST PAC-Key lifetime in seconds * * This is the hard limit on how long a provisioned PAC-Key can be * used. */ int pac_key_lifetime; /** * pac_key_refresh_time - EAP-FAST PAC-Key refresh time in seconds * * This is a soft limit on the PAC-Key. The server will automatically * generate a new PAC-Key when this number of seconds (or fewer) of the * lifetime remains. */ int pac_key_refresh_time; /** * eap_sim_aka_result_ind - EAP-SIM/AKA protected success indication * * This controls whether the protected success/failure indication * (AT_RESULT_IND) is used with EAP-SIM and EAP-AKA. */ int eap_sim_aka_result_ind; /** * tnc - Trusted Network Connect (TNC) * * This controls whether TNC is enabled and 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. */ int tnc; /** * pwd_group - EAP-pwd D-H group * * This is used to select which D-H group to use with EAP-pwd. */ u16 pwd_group; /** * server_id - Server identity */ const char *server_id; /** * wps - Wi-Fi Protected Setup context * * If WPS is used with an external RADIUS server (which is quite * unlikely configuration), this is used to provide a pointer to WPS * context data. Normally, this can be set to %NULL. */ struct wps_context *wps; /** * ipv6 - Whether to enable IPv6 support in the RADIUS server */ int ipv6; /** * get_eap_user - Callback for fetching EAP user information * @ctx: Context data from conf_ctx * @identity: User identity * @identity_len: identity buffer length in octets * @phase2: Whether this is for Phase 2 identity * @user: Data structure for filling in the user information * Returns: 0 on success, -1 on failure * * This is used to fetch information from user database. The callback * will fill in information about allowed EAP methods and the user * password. The password field will be an allocated copy of the * password data and RADIUS server will free it after use. */ int (*get_eap_user)(void *ctx, const u8 *identity, size_t identity_len, int phase2, struct eap_user *user); /** * eap_req_id_text - Optional data for EAP-Request/Identity * * This can be used to configure an optional, displayable message that * will be sent in EAP-Request/Identity. This string can contain an * ASCII-0 character (nul) to separate network infromation per RFC * 4284. The actual string length is explicit provided in * eap_req_id_text_len since nul character will not be used as a string * terminator. */ const char *eap_req_id_text; /** * eap_req_id_text_len - Length of eap_req_id_text buffer in octets */ size_t eap_req_id_text_len; /* * msg_ctx - Context data for wpa_msg() calls */ void *msg_ctx; #ifdef CONFIG_RADIUS_TEST const char *dump_msk_file; #endif /* CONFIG_RADIUS_TEST */ }; struct radius_server_data * radius_server_init(struct radius_server_conf *conf); void radius_server_deinit(struct radius_server_data *data); int radius_server_get_mib(struct radius_server_data *data, char *buf, size_t buflen); void radius_server_eap_pending_cb(struct radius_server_data *data, void *ctx); #endif /* RADIUS_SERVER_H */ wpa-2.1/src/rsn_supp/000077500000000000000000000000001227414666700146225ustar00rootroot00000000000000wpa-2.1/src/rsn_supp/Makefile000066400000000000000000000001641227414666700162630ustar00rootroot00000000000000all: @echo Nothing to be made. clean: rm -f *~ *.o *.d *.gcno *.gcda *.gcov install: @echo Nothing to be made. wpa-2.1/src/rsn_supp/peerkey.c000066400000000000000000001016401227414666700164340ustar00rootroot00000000000000/* * WPA Supplicant - PeerKey for Direct Link Setup (DLS) * Copyright (c) 2006-2008, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "includes.h" #ifdef CONFIG_PEERKEY #include "common.h" #include "eloop.h" #include "crypto/sha1.h" #include "crypto/sha256.h" #include "crypto/random.h" #include "common/ieee802_11_defs.h" #include "wpa.h" #include "wpa_i.h" #include "wpa_ie.h" #include "peerkey.h" static u8 * wpa_add_ie(u8 *pos, const u8 *ie, size_t ie_len) { os_memcpy(pos, ie, ie_len); return pos + ie_len; } static u8 * wpa_add_kde(u8 *pos, u32 kde, const u8 *data, size_t data_len) { *pos++ = WLAN_EID_VENDOR_SPECIFIC; *pos++ = RSN_SELECTOR_LEN + data_len; RSN_SELECTOR_PUT(pos, kde); pos += RSN_SELECTOR_LEN; os_memcpy(pos, data, data_len); pos += data_len; return pos; } static void wpa_supplicant_smk_timeout(void *eloop_ctx, void *timeout_ctx) { #if 0 struct wpa_sm *sm = eloop_ctx; struct wpa_peerkey *peerkey = timeout_ctx; #endif /* TODO: time out SMK and any STK that was generated using this SMK */ } static void wpa_supplicant_peerkey_free(struct wpa_sm *sm, struct wpa_peerkey *peerkey) { eloop_cancel_timeout(wpa_supplicant_smk_timeout, sm, peerkey); os_free(peerkey); } static int wpa_supplicant_send_smk_error(struct wpa_sm *sm, const u8 *dst, const u8 *peer, u16 mui, u16 error_type, int ver) { size_t rlen; struct wpa_eapol_key *err; struct rsn_error_kde error; u8 *rbuf, *pos; size_t kde_len; u16 key_info; kde_len = 2 + RSN_SELECTOR_LEN + sizeof(error); if (peer) kde_len += 2 + RSN_SELECTOR_LEN + ETH_ALEN; rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL, sizeof(*err) + kde_len, &rlen, (void *) &err); if (rbuf == NULL) return -1; err->type = EAPOL_KEY_TYPE_RSN; key_info = ver | WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_MIC | WPA_KEY_INFO_SECURE | WPA_KEY_INFO_ERROR | WPA_KEY_INFO_REQUEST; WPA_PUT_BE16(err->key_info, key_info); WPA_PUT_BE16(err->key_length, 0); os_memcpy(err->replay_counter, sm->request_counter, WPA_REPLAY_COUNTER_LEN); inc_byte_array(sm->request_counter, WPA_REPLAY_COUNTER_LEN); WPA_PUT_BE16(err->key_data_length, (u16) kde_len); pos = (u8 *) (err + 1); if (peer) { /* Peer MAC Address KDE */ pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peer, ETH_ALEN); } /* Error KDE */ error.mui = host_to_be16(mui); error.error_type = host_to_be16(error_type); wpa_add_kde(pos, RSN_KEY_DATA_ERROR, (u8 *) &error, sizeof(error)); if (peer) { wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key SMK Error (peer " MACSTR " mui %d error_type %d)", MAC2STR(peer), mui, error_type); } else { wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key SMK Error " "(mui %d error_type %d)", mui, error_type); } wpa_eapol_key_send(sm, sm->ptk.kck, ver, dst, ETH_P_EAPOL, rbuf, rlen, err->key_mic); return 0; } static int wpa_supplicant_send_smk_m3(struct wpa_sm *sm, const unsigned char *src_addr, const struct wpa_eapol_key *key, int ver, struct wpa_peerkey *peerkey) { size_t rlen; struct wpa_eapol_key *reply; u8 *rbuf, *pos; size_t kde_len; u16 key_info; /* KDEs: Peer RSN IE, Initiator MAC Address, Initiator Nonce */ kde_len = peerkey->rsnie_p_len + 2 + RSN_SELECTOR_LEN + ETH_ALEN + 2 + RSN_SELECTOR_LEN + WPA_NONCE_LEN; rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL, sizeof(*reply) + kde_len, &rlen, (void *) &reply); if (rbuf == NULL) return -1; reply->type = EAPOL_KEY_TYPE_RSN; key_info = ver | WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_MIC | WPA_KEY_INFO_SECURE; WPA_PUT_BE16(reply->key_info, key_info); WPA_PUT_BE16(reply->key_length, 0); os_memcpy(reply->replay_counter, key->replay_counter, WPA_REPLAY_COUNTER_LEN); os_memcpy(reply->key_nonce, peerkey->pnonce, WPA_NONCE_LEN); WPA_PUT_BE16(reply->key_data_length, (u16) kde_len); pos = (u8 *) (reply + 1); /* Peer RSN IE */ pos = wpa_add_ie(pos, peerkey->rsnie_p, peerkey->rsnie_p_len); /* Initiator MAC Address KDE */ pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peerkey->addr, ETH_ALEN); /* Initiator Nonce */ wpa_add_kde(pos, RSN_KEY_DATA_NONCE, peerkey->inonce, WPA_NONCE_LEN); wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key SMK M3"); wpa_eapol_key_send(sm, sm->ptk.kck, ver, src_addr, ETH_P_EAPOL, rbuf, rlen, reply->key_mic); return 0; } static int wpa_supplicant_process_smk_m2( struct wpa_sm *sm, const unsigned char *src_addr, const struct wpa_eapol_key *key, size_t extra_len, int ver) { struct wpa_peerkey *peerkey; struct wpa_eapol_ie_parse kde; struct wpa_ie_data ie; int cipher; struct rsn_ie_hdr *hdr; u8 *pos; wpa_printf(MSG_DEBUG, "RSN: Received SMK M2"); if (!sm->peerkey_enabled || sm->proto != WPA_PROTO_RSN) { wpa_printf(MSG_INFO, "RSN: SMK handshake not allowed for " "the current network"); return -1; } if (wpa_supplicant_parse_ies((const u8 *) (key + 1), extra_len, &kde) < 0) { wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M2"); return -1; } if (kde.rsn_ie == NULL || kde.mac_addr == NULL || kde.mac_addr_len < ETH_ALEN) { wpa_printf(MSG_INFO, "RSN: No RSN IE or MAC address KDE in " "SMK M2"); return -1; } wpa_printf(MSG_DEBUG, "RSN: SMK M2 - SMK initiator " MACSTR, MAC2STR(kde.mac_addr)); if (kde.rsn_ie_len > PEERKEY_MAX_IE_LEN) { wpa_printf(MSG_INFO, "RSN: Too long Initiator RSN IE in SMK " "M2"); return -1; } if (wpa_parse_wpa_ie_rsn(kde.rsn_ie, kde.rsn_ie_len, &ie) < 0) { wpa_printf(MSG_INFO, "RSN: Failed to parse RSN IE in SMK M2"); return -1; } cipher = wpa_pick_pairwise_cipher(ie.pairwise_cipher & sm->allowed_pairwise_cipher, 0); if (cipher < 0) { wpa_printf(MSG_INFO, "RSN: No acceptable cipher in SMK M2"); wpa_supplicant_send_smk_error(sm, src_addr, kde.mac_addr, STK_MUI_SMK, STK_ERR_CPHR_NS, ver); return -1; } wpa_printf(MSG_DEBUG, "RSN: Using %s for PeerKey", wpa_cipher_txt(cipher)); /* TODO: find existing entry and if found, use that instead of adding * a new one; how to handle the case where both ends initiate at the * same time? */ peerkey = os_zalloc(sizeof(*peerkey)); if (peerkey == NULL) return -1; os_memcpy(peerkey->addr, kde.mac_addr, ETH_ALEN); os_memcpy(peerkey->inonce, key->key_nonce, WPA_NONCE_LEN); os_memcpy(peerkey->rsnie_i, kde.rsn_ie, kde.rsn_ie_len); peerkey->rsnie_i_len = kde.rsn_ie_len; peerkey->cipher = cipher; #ifdef CONFIG_IEEE80211W if (ie.key_mgmt & (WPA_KEY_MGMT_IEEE8021X_SHA256 | WPA_KEY_MGMT_PSK_SHA256)) peerkey->use_sha256 = 1; #endif /* CONFIG_IEEE80211W */ if (random_get_bytes(peerkey->pnonce, WPA_NONCE_LEN)) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Failed to get random data for PNonce"); wpa_supplicant_peerkey_free(sm, peerkey); return -1; } hdr = (struct rsn_ie_hdr *) peerkey->rsnie_p; hdr->elem_id = WLAN_EID_RSN; WPA_PUT_LE16(hdr->version, RSN_VERSION); pos = (u8 *) (hdr + 1); /* Group Suite can be anything for SMK RSN IE; receiver will just * ignore it. */ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP); pos += RSN_SELECTOR_LEN; /* Include only the selected cipher in pairwise cipher suite */ WPA_PUT_LE16(pos, 1); pos += 2; RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN, cipher)); pos += RSN_SELECTOR_LEN; hdr->len = (pos - peerkey->rsnie_p) - 2; peerkey->rsnie_p_len = pos - peerkey->rsnie_p; wpa_hexdump(MSG_DEBUG, "WPA: RSN IE for SMK handshake", peerkey->rsnie_p, peerkey->rsnie_p_len); wpa_supplicant_send_smk_m3(sm, src_addr, key, ver, peerkey); peerkey->next = sm->peerkey; sm->peerkey = peerkey; return 0; } /** * rsn_smkid - Derive SMK identifier * @smk: Station master key (32 bytes) * @pnonce: Peer Nonce * @mac_p: Peer MAC address * @inonce: Initiator Nonce * @mac_i: Initiator MAC address * @use_sha256: Whether to use SHA256-based KDF * * 8.5.1.4 Station to station (STK) key hierarchy * SMKID = HMAC-SHA1-128(SMK, "SMK Name" || PNonce || MAC_P || INonce || MAC_I) */ static void rsn_smkid(const u8 *smk, const u8 *pnonce, const u8 *mac_p, const u8 *inonce, const u8 *mac_i, u8 *smkid, int use_sha256) { char *title = "SMK Name"; const u8 *addr[5]; const size_t len[5] = { 8, WPA_NONCE_LEN, ETH_ALEN, WPA_NONCE_LEN, ETH_ALEN }; unsigned char hash[SHA256_MAC_LEN]; addr[0] = (u8 *) title; addr[1] = pnonce; addr[2] = mac_p; addr[3] = inonce; addr[4] = mac_i; #ifdef CONFIG_IEEE80211W if (use_sha256) hmac_sha256_vector(smk, PMK_LEN, 5, addr, len, hash); else #endif /* CONFIG_IEEE80211W */ hmac_sha1_vector(smk, PMK_LEN, 5, addr, len, hash); os_memcpy(smkid, hash, PMKID_LEN); } static void wpa_supplicant_send_stk_1_of_4(struct wpa_sm *sm, struct wpa_peerkey *peerkey) { size_t mlen; struct wpa_eapol_key *msg; u8 *mbuf; size_t kde_len; u16 key_info, ver; kde_len = 2 + RSN_SELECTOR_LEN + PMKID_LEN; mbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL, sizeof(*msg) + kde_len, &mlen, (void *) &msg); if (mbuf == NULL) return; msg->type = EAPOL_KEY_TYPE_RSN; if (peerkey->cipher != WPA_CIPHER_TKIP) ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES; else ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4; key_info = ver | WPA_KEY_INFO_KEY_TYPE | WPA_KEY_INFO_ACK; WPA_PUT_BE16(msg->key_info, key_info); if (peerkey->cipher != WPA_CIPHER_TKIP) WPA_PUT_BE16(msg->key_length, 16); else WPA_PUT_BE16(msg->key_length, 32); os_memcpy(msg->replay_counter, peerkey->replay_counter, WPA_REPLAY_COUNTER_LEN); inc_byte_array(peerkey->replay_counter, WPA_REPLAY_COUNTER_LEN); WPA_PUT_BE16(msg->key_data_length, kde_len); wpa_add_kde((u8 *) (msg + 1), RSN_KEY_DATA_PMKID, peerkey->smkid, PMKID_LEN); if (random_get_bytes(peerkey->inonce, WPA_NONCE_LEN)) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "RSN: Failed to get random data for INonce (STK)"); os_free(mbuf); return; } wpa_hexdump(MSG_DEBUG, "RSN: INonce for STK 4-Way Handshake", peerkey->inonce, WPA_NONCE_LEN); os_memcpy(msg->key_nonce, peerkey->inonce, WPA_NONCE_LEN); wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key STK 1/4 to " MACSTR, MAC2STR(peerkey->addr)); wpa_eapol_key_send(sm, NULL, ver, peerkey->addr, ETH_P_EAPOL, mbuf, mlen, NULL); } static void wpa_supplicant_send_stk_3_of_4(struct wpa_sm *sm, struct wpa_peerkey *peerkey) { size_t mlen; struct wpa_eapol_key *msg; u8 *mbuf, *pos; size_t kde_len; u16 key_info, ver; be32 lifetime; kde_len = peerkey->rsnie_i_len + 2 + RSN_SELECTOR_LEN + sizeof(lifetime); mbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL, sizeof(*msg) + kde_len, &mlen, (void *) &msg); if (mbuf == NULL) return; msg->type = EAPOL_KEY_TYPE_RSN; if (peerkey->cipher != WPA_CIPHER_TKIP) ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES; else ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4; key_info = ver | WPA_KEY_INFO_KEY_TYPE | WPA_KEY_INFO_ACK | WPA_KEY_INFO_MIC | WPA_KEY_INFO_SECURE; WPA_PUT_BE16(msg->key_info, key_info); if (peerkey->cipher != WPA_CIPHER_TKIP) WPA_PUT_BE16(msg->key_length, 16); else WPA_PUT_BE16(msg->key_length, 32); os_memcpy(msg->replay_counter, peerkey->replay_counter, WPA_REPLAY_COUNTER_LEN); inc_byte_array(peerkey->replay_counter, WPA_REPLAY_COUNTER_LEN); WPA_PUT_BE16(msg->key_data_length, kde_len); pos = (u8 *) (msg + 1); pos = wpa_add_ie(pos, peerkey->rsnie_i, peerkey->rsnie_i_len); lifetime = host_to_be32(peerkey->lifetime); wpa_add_kde(pos, RSN_KEY_DATA_LIFETIME, (u8 *) &lifetime, sizeof(lifetime)); os_memcpy(msg->key_nonce, peerkey->inonce, WPA_NONCE_LEN); wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key STK 3/4 to " MACSTR, MAC2STR(peerkey->addr)); wpa_eapol_key_send(sm, peerkey->stk.kck, ver, peerkey->addr, ETH_P_EAPOL, mbuf, mlen, msg->key_mic); } static int wpa_supplicant_process_smk_m4(struct wpa_peerkey *peerkey, struct wpa_eapol_ie_parse *kde) { wpa_printf(MSG_DEBUG, "RSN: Received SMK M4 (Initiator " MACSTR ")", MAC2STR(kde->mac_addr)); if (os_memcmp(kde->smk + PMK_LEN, peerkey->pnonce, WPA_NONCE_LEN) != 0) { wpa_printf(MSG_INFO, "RSN: PNonce in SMK KDE does not " "match with the one used in SMK M3"); return -1; } if (os_memcmp(kde->nonce, peerkey->inonce, WPA_NONCE_LEN) != 0) { wpa_printf(MSG_INFO, "RSN: INonce in SMK M4 did not " "match with the one received in SMK M2"); return -1; } return 0; } static int wpa_supplicant_process_smk_m5(struct wpa_sm *sm, const unsigned char *src_addr, const struct wpa_eapol_key *key, int ver, struct wpa_peerkey *peerkey, struct wpa_eapol_ie_parse *kde) { int cipher; struct wpa_ie_data ie; wpa_printf(MSG_DEBUG, "RSN: Received SMK M5 (Peer " MACSTR ")", MAC2STR(kde->mac_addr)); if (kde->rsn_ie == NULL || kde->rsn_ie_len > PEERKEY_MAX_IE_LEN || wpa_parse_wpa_ie_rsn(kde->rsn_ie, kde->rsn_ie_len, &ie) < 0) { wpa_printf(MSG_INFO, "RSN: No RSN IE in SMK M5"); /* TODO: abort negotiation */ return -1; } if (os_memcmp(key->key_nonce, peerkey->inonce, WPA_NONCE_LEN) != 0) { wpa_printf(MSG_INFO, "RSN: Key Nonce in SMK M5 does " "not match with INonce used in SMK M1"); return -1; } if (os_memcmp(kde->smk + PMK_LEN, peerkey->inonce, WPA_NONCE_LEN) != 0) { wpa_printf(MSG_INFO, "RSN: INonce in SMK KDE does not " "match with the one used in SMK M1"); return -1; } os_memcpy(peerkey->rsnie_p, kde->rsn_ie, kde->rsn_ie_len); peerkey->rsnie_p_len = kde->rsn_ie_len; os_memcpy(peerkey->pnonce, kde->nonce, WPA_NONCE_LEN); cipher = wpa_pick_pairwise_cipher(ie.pairwise_cipher & sm->allowed_pairwise_cipher, 0); if (cipher < 0) { wpa_printf(MSG_INFO, "RSN: SMK Peer STA " MACSTR " selected " "unacceptable cipher", MAC2STR(kde->mac_addr)); wpa_supplicant_send_smk_error(sm, src_addr, kde->mac_addr, STK_MUI_SMK, STK_ERR_CPHR_NS, ver); /* TODO: abort negotiation */ return -1; } wpa_printf(MSG_DEBUG, "RSN: Using %s for PeerKey", wpa_cipher_txt(cipher)); peerkey->cipher = cipher; return 0; } static int wpa_supplicant_process_smk_m45( struct wpa_sm *sm, const unsigned char *src_addr, const struct wpa_eapol_key *key, size_t extra_len, int ver) { struct wpa_peerkey *peerkey; struct wpa_eapol_ie_parse kde; u32 lifetime; if (!sm->peerkey_enabled || sm->proto != WPA_PROTO_RSN) { wpa_printf(MSG_DEBUG, "RSN: SMK handshake not allowed for " "the current network"); return -1; } if (wpa_supplicant_parse_ies((const u8 *) (key + 1), extra_len, &kde) < 0) { wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M4/M5"); return -1; } if (kde.mac_addr == NULL || kde.mac_addr_len < ETH_ALEN || kde.nonce == NULL || kde.nonce_len < WPA_NONCE_LEN || kde.smk == NULL || kde.smk_len < PMK_LEN + WPA_NONCE_LEN || kde.lifetime == NULL || kde.lifetime_len < 4) { wpa_printf(MSG_INFO, "RSN: No MAC Address, Nonce, SMK, or " "Lifetime KDE in SMK M4/M5"); return -1; } for (peerkey = sm->peerkey; peerkey; peerkey = peerkey->next) { if (os_memcmp(peerkey->addr, kde.mac_addr, ETH_ALEN) == 0 && os_memcmp(peerkey->initiator ? peerkey->inonce : peerkey->pnonce, key->key_nonce, WPA_NONCE_LEN) == 0) break; } if (peerkey == NULL) { wpa_printf(MSG_INFO, "RSN: No matching SMK handshake found " "for SMK M4/M5: peer " MACSTR, MAC2STR(kde.mac_addr)); return -1; } if (peerkey->initiator) { if (wpa_supplicant_process_smk_m5(sm, src_addr, key, ver, peerkey, &kde) < 0) return -1; } else { if (wpa_supplicant_process_smk_m4(peerkey, &kde) < 0) return -1; } os_memcpy(peerkey->smk, kde.smk, PMK_LEN); peerkey->smk_complete = 1; wpa_hexdump_key(MSG_DEBUG, "RSN: SMK", peerkey->smk, PMK_LEN); lifetime = WPA_GET_BE32(kde.lifetime); wpa_printf(MSG_DEBUG, "RSN: SMK lifetime %u seconds", lifetime); if (lifetime > 1000000000) lifetime = 1000000000; /* avoid overflowing eloop time */ peerkey->lifetime = lifetime; eloop_register_timeout(lifetime, 0, wpa_supplicant_smk_timeout, sm, peerkey); if (peerkey->initiator) { rsn_smkid(peerkey->smk, peerkey->pnonce, peerkey->addr, peerkey->inonce, sm->own_addr, peerkey->smkid, peerkey->use_sha256); wpa_supplicant_send_stk_1_of_4(sm, peerkey); } else { rsn_smkid(peerkey->smk, peerkey->pnonce, sm->own_addr, peerkey->inonce, peerkey->addr, peerkey->smkid, peerkey->use_sha256); } wpa_hexdump(MSG_DEBUG, "RSN: SMKID", peerkey->smkid, PMKID_LEN); return 0; } static int wpa_supplicant_process_smk_error( struct wpa_sm *sm, const unsigned char *src_addr, const struct wpa_eapol_key *key, size_t extra_len) { struct wpa_eapol_ie_parse kde; struct rsn_error_kde error; u8 peer[ETH_ALEN]; u16 error_type; wpa_printf(MSG_DEBUG, "RSN: Received SMK Error"); if (!sm->peerkey_enabled || sm->proto != WPA_PROTO_RSN) { wpa_printf(MSG_DEBUG, "RSN: SMK handshake not allowed for " "the current network"); return -1; } if (wpa_supplicant_parse_ies((const u8 *) (key + 1), extra_len, &kde) < 0) { wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK Error"); return -1; } if (kde.error == NULL || kde.error_len < sizeof(error)) { wpa_printf(MSG_INFO, "RSN: No Error KDE in SMK Error"); return -1; } if (kde.mac_addr && kde.mac_addr_len >= ETH_ALEN) os_memcpy(peer, kde.mac_addr, ETH_ALEN); else os_memset(peer, 0, ETH_ALEN); os_memcpy(&error, kde.error, sizeof(error)); error_type = be_to_host16(error.error_type); wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "RSN: SMK Error KDE received: MUI %d error_type %d peer " MACSTR, be_to_host16(error.mui), error_type, MAC2STR(peer)); if (kde.mac_addr && (error_type == STK_ERR_STA_NR || error_type == STK_ERR_STA_NRSN || error_type == STK_ERR_CPHR_NS)) { struct wpa_peerkey *peerkey; for (peerkey = sm->peerkey; peerkey; peerkey = peerkey->next) { if (os_memcmp(peerkey->addr, kde.mac_addr, ETH_ALEN) == 0) break; } if (peerkey == NULL) { wpa_printf(MSG_DEBUG, "RSN: No matching SMK handshake " "found for SMK Error"); return -1; } /* TODO: abort SMK/STK handshake and remove all related keys */ } return 0; } static void wpa_supplicant_process_stk_1_of_4(struct wpa_sm *sm, struct wpa_peerkey *peerkey, const struct wpa_eapol_key *key, u16 ver) { struct wpa_eapol_ie_parse ie; const u8 *kde; size_t len, kde_buf_len; struct wpa_ptk *stk; u8 buf[8], *kde_buf, *pos; be32 lifetime; wpa_printf(MSG_DEBUG, "RSN: RX message 1 of STK 4-Way Handshake from " MACSTR " (ver=%d)", MAC2STR(peerkey->addr), ver); os_memset(&ie, 0, sizeof(ie)); /* RSN: msg 1/4 should contain SMKID for the selected SMK */ kde = (const u8 *) (key + 1); len = WPA_GET_BE16(key->key_data_length); wpa_hexdump(MSG_DEBUG, "RSN: msg 1/4 key data", kde, len); if (wpa_supplicant_parse_ies(kde, len, &ie) < 0 || ie.pmkid == NULL) { wpa_printf(MSG_DEBUG, "RSN: No SMKID in STK 1/4"); return; } if (os_memcmp(ie.pmkid, peerkey->smkid, PMKID_LEN) != 0) { wpa_hexdump(MSG_DEBUG, "RSN: Unknown SMKID in STK 1/4", ie.pmkid, PMKID_LEN); return; } if (random_get_bytes(peerkey->pnonce, WPA_NONCE_LEN)) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "RSN: Failed to get random data for PNonce"); return; } wpa_hexdump(MSG_DEBUG, "WPA: Renewed PNonce", peerkey->pnonce, WPA_NONCE_LEN); /* Calculate STK which will be stored as a temporary STK until it has * been verified when processing message 3/4. */ stk = &peerkey->tstk; wpa_pmk_to_ptk(peerkey->smk, PMK_LEN, "Peer key expansion", sm->own_addr, peerkey->addr, peerkey->pnonce, key->key_nonce, (u8 *) stk, sizeof(*stk), peerkey->use_sha256); /* Supplicant: swap tx/rx Mic keys */ os_memcpy(buf, stk->u.auth.tx_mic_key, 8); os_memcpy(stk->u.auth.tx_mic_key, stk->u.auth.rx_mic_key, 8); os_memcpy(stk->u.auth.rx_mic_key, buf, 8); peerkey->tstk_set = 1; kde_buf_len = peerkey->rsnie_p_len + 2 + RSN_SELECTOR_LEN + sizeof(lifetime) + 2 + RSN_SELECTOR_LEN + PMKID_LEN; kde_buf = os_malloc(kde_buf_len); if (kde_buf == NULL) return; pos = kde_buf; pos = wpa_add_ie(pos, peerkey->rsnie_p, peerkey->rsnie_p_len); lifetime = host_to_be32(peerkey->lifetime); pos = wpa_add_kde(pos, RSN_KEY_DATA_LIFETIME, (u8 *) &lifetime, sizeof(lifetime)); wpa_add_kde(pos, RSN_KEY_DATA_PMKID, peerkey->smkid, PMKID_LEN); if (wpa_supplicant_send_2_of_4(sm, peerkey->addr, key, ver, peerkey->pnonce, kde_buf, kde_buf_len, stk)) { os_free(kde_buf); return; } os_free(kde_buf); os_memcpy(peerkey->inonce, key->key_nonce, WPA_NONCE_LEN); } static void wpa_supplicant_update_smk_lifetime(struct wpa_sm *sm, struct wpa_peerkey *peerkey, struct wpa_eapol_ie_parse *kde) { u32 lifetime; if (kde->lifetime == NULL || kde->lifetime_len < sizeof(lifetime)) return; lifetime = WPA_GET_BE32(kde->lifetime); if (lifetime >= peerkey->lifetime) { wpa_printf(MSG_DEBUG, "RSN: Peer used SMK lifetime %u seconds " "which is larger than or equal to own value %u " "seconds - ignored", lifetime, peerkey->lifetime); return; } wpa_printf(MSG_DEBUG, "RSN: Peer used shorter SMK lifetime %u seconds " "(own was %u seconds) - updated", lifetime, peerkey->lifetime); peerkey->lifetime = lifetime; eloop_cancel_timeout(wpa_supplicant_smk_timeout, sm, peerkey); eloop_register_timeout(lifetime, 0, wpa_supplicant_smk_timeout, sm, peerkey); } static void wpa_supplicant_process_stk_2_of_4(struct wpa_sm *sm, struct wpa_peerkey *peerkey, const struct wpa_eapol_key *key, u16 ver) { struct wpa_eapol_ie_parse kde; const u8 *keydata; size_t len; wpa_printf(MSG_DEBUG, "RSN: RX message 2 of STK 4-Way Handshake from " MACSTR " (ver=%d)", MAC2STR(peerkey->addr), ver); os_memset(&kde, 0, sizeof(kde)); /* RSN: msg 2/4 should contain SMKID for the selected SMK and RSN IE * from the peer. It may also include Lifetime KDE. */ keydata = (const u8 *) (key + 1); len = WPA_GET_BE16(key->key_data_length); wpa_hexdump(MSG_DEBUG, "RSN: msg 2/4 key data", keydata, len); if (wpa_supplicant_parse_ies(keydata, len, &kde) < 0 || kde.pmkid == NULL || kde.rsn_ie == NULL) { wpa_printf(MSG_DEBUG, "RSN: No SMKID or RSN IE in STK 2/4"); return; } if (os_memcmp(kde.pmkid, peerkey->smkid, PMKID_LEN) != 0) { wpa_hexdump(MSG_DEBUG, "RSN: Unknown SMKID in STK 2/4", kde.pmkid, PMKID_LEN); return; } if (kde.rsn_ie_len != peerkey->rsnie_p_len || os_memcmp(kde.rsn_ie, peerkey->rsnie_p, kde.rsn_ie_len) != 0) { wpa_printf(MSG_INFO, "RSN: Peer RSN IE in SMK and STK " "handshakes did not match"); wpa_hexdump(MSG_DEBUG, "RSN: Peer RSN IE in SMK handshake", peerkey->rsnie_p, peerkey->rsnie_p_len); wpa_hexdump(MSG_DEBUG, "RSN: Peer RSN IE in STK handshake", kde.rsn_ie, kde.rsn_ie_len); return; } wpa_supplicant_update_smk_lifetime(sm, peerkey, &kde); wpa_supplicant_send_stk_3_of_4(sm, peerkey); os_memcpy(peerkey->pnonce, key->key_nonce, WPA_NONCE_LEN); } static void wpa_supplicant_process_stk_3_of_4(struct wpa_sm *sm, struct wpa_peerkey *peerkey, const struct wpa_eapol_key *key, u16 ver) { struct wpa_eapol_ie_parse kde; const u8 *keydata; size_t len, key_len; const u8 *_key; u8 key_buf[32], rsc[6]; wpa_printf(MSG_DEBUG, "RSN: RX message 3 of STK 4-Way Handshake from " MACSTR " (ver=%d)", MAC2STR(peerkey->addr), ver); os_memset(&kde, 0, sizeof(kde)); /* RSN: msg 3/4 should contain Initiator RSN IE. It may also include * Lifetime KDE. */ keydata = (const u8 *) (key + 1); len = WPA_GET_BE16(key->key_data_length); wpa_hexdump(MSG_DEBUG, "RSN: msg 3/4 key data", keydata, len); if (wpa_supplicant_parse_ies(keydata, len, &kde) < 0) { wpa_printf(MSG_DEBUG, "RSN: Failed to parse key data in " "STK 3/4"); return; } if (kde.rsn_ie_len != peerkey->rsnie_i_len || os_memcmp(kde.rsn_ie, peerkey->rsnie_i, kde.rsn_ie_len) != 0) { wpa_printf(MSG_INFO, "RSN: Initiator RSN IE in SMK and STK " "handshakes did not match"); wpa_hexdump(MSG_DEBUG, "RSN: Initiator RSN IE in SMK " "handshake", peerkey->rsnie_i, peerkey->rsnie_i_len); wpa_hexdump(MSG_DEBUG, "RSN: Initiator RSN IE in STK " "handshake", kde.rsn_ie, kde.rsn_ie_len); return; } if (os_memcmp(peerkey->inonce, key->key_nonce, WPA_NONCE_LEN) != 0) { wpa_printf(MSG_WARNING, "RSN: INonce from message 1 of STK " "4-Way Handshake differs from 3 of STK 4-Way " "Handshake - drop packet (src=" MACSTR ")", MAC2STR(peerkey->addr)); return; } wpa_supplicant_update_smk_lifetime(sm, peerkey, &kde); if (wpa_supplicant_send_4_of_4(sm, peerkey->addr, key, ver, WPA_GET_BE16(key->key_info), NULL, 0, &peerkey->stk)) return; _key = (u8 *) peerkey->stk.tk1; if (peerkey->cipher == WPA_CIPHER_TKIP) { /* Swap Tx/Rx keys for Michael MIC */ os_memcpy(key_buf, _key, 16); os_memcpy(key_buf + 16, peerkey->stk.u.auth.rx_mic_key, 8); os_memcpy(key_buf + 24, peerkey->stk.u.auth.tx_mic_key, 8); _key = key_buf; key_len = 32; } else key_len = 16; os_memset(rsc, 0, 6); if (wpa_sm_set_key(sm, peerkey->cipher, peerkey->addr, 0, 1, rsc, sizeof(rsc), _key, key_len) < 0) { wpa_printf(MSG_WARNING, "RSN: Failed to set STK to the " "driver."); return; } } static void wpa_supplicant_process_stk_4_of_4(struct wpa_sm *sm, struct wpa_peerkey *peerkey, const struct wpa_eapol_key *key, u16 ver) { u8 rsc[6]; wpa_printf(MSG_DEBUG, "RSN: RX message 4 of STK 4-Way Handshake from " MACSTR " (ver=%d)", MAC2STR(peerkey->addr), ver); os_memset(rsc, 0, 6); if (wpa_sm_set_key(sm, peerkey->cipher, peerkey->addr, 0, 1, rsc, sizeof(rsc), (u8 *) peerkey->stk.tk1, peerkey->cipher == WPA_CIPHER_TKIP ? 32 : 16) < 0) { wpa_printf(MSG_WARNING, "RSN: Failed to set STK to the " "driver."); return; } } /** * peerkey_verify_eapol_key_mic - Verify PeerKey MIC * @sm: Pointer to WPA state machine data from wpa_sm_init() * @peerkey: Pointer to the PeerKey data for the peer * @key: Pointer to the EAPOL-Key frame header * @ver: Version bits from EAPOL-Key Key Info * @buf: Pointer to the beginning of EAPOL-Key frame * @len: Length of the EAPOL-Key frame * Returns: 0 on success, -1 on failure */ int peerkey_verify_eapol_key_mic(struct wpa_sm *sm, struct wpa_peerkey *peerkey, struct wpa_eapol_key *key, u16 ver, const u8 *buf, size_t len) { u8 mic[16]; int ok = 0; if (peerkey->initiator && !peerkey->stk_set) { wpa_pmk_to_ptk(peerkey->smk, PMK_LEN, "Peer key expansion", sm->own_addr, peerkey->addr, peerkey->inonce, key->key_nonce, (u8 *) &peerkey->stk, sizeof(peerkey->stk), peerkey->use_sha256); peerkey->stk_set = 1; } os_memcpy(mic, key->key_mic, 16); if (peerkey->tstk_set) { os_memset(key->key_mic, 0, 16); wpa_eapol_key_mic(peerkey->tstk.kck, ver, buf, len, key->key_mic); if (os_memcmp(mic, key->key_mic, 16) != 0) { wpa_printf(MSG_WARNING, "RSN: Invalid EAPOL-Key MIC " "when using TSTK - ignoring TSTK"); } else { ok = 1; peerkey->tstk_set = 0; peerkey->stk_set = 1; os_memcpy(&peerkey->stk, &peerkey->tstk, sizeof(peerkey->stk)); } } if (!ok && peerkey->stk_set) { os_memset(key->key_mic, 0, 16); wpa_eapol_key_mic(peerkey->stk.kck, ver, buf, len, key->key_mic); if (os_memcmp(mic, key->key_mic, 16) != 0) { wpa_printf(MSG_WARNING, "RSN: Invalid EAPOL-Key MIC " "- dropping packet"); return -1; } ok = 1; } if (!ok) { wpa_printf(MSG_WARNING, "RSN: Could not verify EAPOL-Key MIC " "- dropping packet"); return -1; } os_memcpy(peerkey->replay_counter, key->replay_counter, WPA_REPLAY_COUNTER_LEN); peerkey->replay_counter_set = 1; return 0; } /** * wpa_sm_stkstart - Send EAPOL-Key Request for STK handshake (STK M1) * @sm: Pointer to WPA state machine data from wpa_sm_init() * @peer: MAC address of the peer STA * Returns: 0 on success, or -1 on failure * * Send an EAPOL-Key Request to the current authenticator to start STK * handshake with the peer. */ int wpa_sm_stkstart(struct wpa_sm *sm, const u8 *peer) { size_t rlen, kde_len; struct wpa_eapol_key *req; int key_info, ver; u8 bssid[ETH_ALEN], *rbuf, *pos, *count_pos; u16 count; struct rsn_ie_hdr *hdr; struct wpa_peerkey *peerkey; struct wpa_ie_data ie; if (sm->proto != WPA_PROTO_RSN || !sm->ptk_set || !sm->peerkey_enabled) return -1; if (sm->ap_rsn_ie && wpa_parse_wpa_ie_rsn(sm->ap_rsn_ie, sm->ap_rsn_ie_len, &ie) == 0 && !(ie.capabilities & WPA_CAPABILITY_PEERKEY_ENABLED)) { wpa_printf(MSG_DEBUG, "RSN: Current AP does not support STK"); return -1; } if (sm->pairwise_cipher != WPA_CIPHER_TKIP) ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES; else ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4; if (wpa_sm_get_bssid(sm, bssid) < 0) { wpa_printf(MSG_WARNING, "Failed to read BSSID for EAPOL-Key " "SMK M1"); return -1; } /* TODO: find existing entry and if found, use that instead of adding * a new one */ peerkey = os_zalloc(sizeof(*peerkey)); if (peerkey == NULL) return -1; peerkey->initiator = 1; os_memcpy(peerkey->addr, peer, ETH_ALEN); #ifdef CONFIG_IEEE80211W if (wpa_key_mgmt_sha256(sm->key_mgmt)) peerkey->use_sha256 = 1; #endif /* CONFIG_IEEE80211W */ /* SMK M1: * EAPOL-Key(S=1, M=1, A=0, I=0, K=0, SM=1, KeyRSC=0, Nonce=INonce, * MIC=MIC, DataKDs=(RSNIE_I, MAC_P KDE)) */ hdr = (struct rsn_ie_hdr *) peerkey->rsnie_i; hdr->elem_id = WLAN_EID_RSN; WPA_PUT_LE16(hdr->version, RSN_VERSION); pos = (u8 *) (hdr + 1); /* Group Suite can be anything for SMK RSN IE; receiver will just * ignore it. */ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP); pos += RSN_SELECTOR_LEN; count_pos = pos; pos += 2; count = rsn_cipher_put_suites(pos, sm->allowed_pairwise_cipher); pos += count * RSN_SELECTOR_LEN; WPA_PUT_LE16(count_pos, count); hdr->len = (pos - peerkey->rsnie_i) - 2; peerkey->rsnie_i_len = pos - peerkey->rsnie_i; wpa_hexdump(MSG_DEBUG, "WPA: RSN IE for SMK handshake", peerkey->rsnie_i, peerkey->rsnie_i_len); kde_len = peerkey->rsnie_i_len + 2 + RSN_SELECTOR_LEN + ETH_ALEN; rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL, sizeof(*req) + kde_len, &rlen, (void *) &req); if (rbuf == NULL) { wpa_supplicant_peerkey_free(sm, peerkey); return -1; } req->type = EAPOL_KEY_TYPE_RSN; key_info = WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_MIC | WPA_KEY_INFO_SECURE | WPA_KEY_INFO_REQUEST | ver; WPA_PUT_BE16(req->key_info, key_info); WPA_PUT_BE16(req->key_length, 0); os_memcpy(req->replay_counter, sm->request_counter, WPA_REPLAY_COUNTER_LEN); inc_byte_array(sm->request_counter, WPA_REPLAY_COUNTER_LEN); if (random_get_bytes(peerkey->inonce, WPA_NONCE_LEN)) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Failed to get random data for INonce"); os_free(rbuf); wpa_supplicant_peerkey_free(sm, peerkey); return -1; } os_memcpy(req->key_nonce, peerkey->inonce, WPA_NONCE_LEN); wpa_hexdump(MSG_DEBUG, "WPA: INonce for SMK handshake", req->key_nonce, WPA_NONCE_LEN); WPA_PUT_BE16(req->key_data_length, (u16) kde_len); pos = (u8 *) (req + 1); /* Initiator RSN IE */ pos = wpa_add_ie(pos, peerkey->rsnie_i, peerkey->rsnie_i_len); /* Peer MAC address KDE */ wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peer, ETH_ALEN); wpa_printf(MSG_INFO, "RSN: Sending EAPOL-Key SMK M1 Request (peer " MACSTR ")", MAC2STR(peer)); wpa_eapol_key_send(sm, sm->ptk.kck, ver, bssid, ETH_P_EAPOL, rbuf, rlen, req->key_mic); peerkey->next = sm->peerkey; sm->peerkey = peerkey; return 0; } /** * peerkey_deinit - Free PeerKey values * @sm: Pointer to WPA state machine data from wpa_sm_init() */ void peerkey_deinit(struct wpa_sm *sm) { struct wpa_peerkey *prev, *peerkey = sm->peerkey; while (peerkey) { prev = peerkey; peerkey = peerkey->next; wpa_supplicant_peerkey_free(sm, prev); } sm->peerkey = NULL; } void peerkey_rx_eapol_4way(struct wpa_sm *sm, struct wpa_peerkey *peerkey, struct wpa_eapol_key *key, u16 key_info, u16 ver) { if ((key_info & (WPA_KEY_INFO_MIC | WPA_KEY_INFO_ACK)) == (WPA_KEY_INFO_MIC | WPA_KEY_INFO_ACK)) { /* 3/4 STK 4-Way Handshake */ wpa_supplicant_process_stk_3_of_4(sm, peerkey, key, ver); } else if (key_info & WPA_KEY_INFO_ACK) { /* 1/4 STK 4-Way Handshake */ wpa_supplicant_process_stk_1_of_4(sm, peerkey, key, ver); } else if (key_info & WPA_KEY_INFO_SECURE) { /* 4/4 STK 4-Way Handshake */ wpa_supplicant_process_stk_4_of_4(sm, peerkey, key, ver); } else { /* 2/4 STK 4-Way Handshake */ wpa_supplicant_process_stk_2_of_4(sm, peerkey, key, ver); } } void peerkey_rx_eapol_smk(struct wpa_sm *sm, const u8 *src_addr, struct wpa_eapol_key *key, size_t extra_len, u16 key_info, u16 ver) { if (key_info & WPA_KEY_INFO_ERROR) { /* SMK Error */ wpa_supplicant_process_smk_error(sm, src_addr, key, extra_len); } else if (key_info & WPA_KEY_INFO_ACK) { /* SMK M2 */ wpa_supplicant_process_smk_m2(sm, src_addr, key, extra_len, ver); } else { /* SMK M4 or M5 */ wpa_supplicant_process_smk_m45(sm, src_addr, key, extra_len, ver); } } #endif /* CONFIG_PEERKEY */ wpa-2.1/src/rsn_supp/peerkey.h000066400000000000000000000041751227414666700164460ustar00rootroot00000000000000/* * WPA Supplicant - PeerKey for Direct Link Setup (DLS) * Copyright (c) 2006-2008, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef PEERKEY_H #define PEERKEY_H #define PEERKEY_MAX_IE_LEN 80 struct wpa_peerkey { struct wpa_peerkey *next; int initiator; /* whether this end was initator for SMK handshake */ u8 addr[ETH_ALEN]; /* other end MAC address */ u8 inonce[WPA_NONCE_LEN]; /* Initiator Nonce */ u8 pnonce[WPA_NONCE_LEN]; /* Peer Nonce */ u8 rsnie_i[PEERKEY_MAX_IE_LEN]; /* Initiator RSN IE */ size_t rsnie_i_len; u8 rsnie_p[PEERKEY_MAX_IE_LEN]; /* Peer RSN IE */ size_t rsnie_p_len; u8 smk[PMK_LEN]; int smk_complete; u8 smkid[PMKID_LEN]; u32 lifetime; int cipher; /* Selected cipher (WPA_CIPHER_*) */ u8 replay_counter[WPA_REPLAY_COUNTER_LEN]; int replay_counter_set; int use_sha256; /* whether AKMP indicate SHA256-based derivations */ struct wpa_ptk stk, tstk; int stk_set, tstk_set; }; #ifdef CONFIG_PEERKEY int peerkey_verify_eapol_key_mic(struct wpa_sm *sm, struct wpa_peerkey *peerkey, struct wpa_eapol_key *key, u16 ver, const u8 *buf, size_t len); void peerkey_rx_eapol_4way(struct wpa_sm *sm, struct wpa_peerkey *peerkey, struct wpa_eapol_key *key, u16 key_info, u16 ver); void peerkey_rx_eapol_smk(struct wpa_sm *sm, const u8 *src_addr, struct wpa_eapol_key *key, size_t extra_len, u16 key_info, u16 ver); void peerkey_deinit(struct wpa_sm *sm); #else /* CONFIG_PEERKEY */ static inline int peerkey_verify_eapol_key_mic(struct wpa_sm *sm, struct wpa_peerkey *peerkey, struct wpa_eapol_key *key, u16 ver, const u8 *buf, size_t len) { return -1; } static inline void peerkey_rx_eapol_4way(struct wpa_sm *sm, struct wpa_peerkey *peerkey, struct wpa_eapol_key *key, u16 key_info, u16 ver) { } static inline void peerkey_rx_eapol_smk(struct wpa_sm *sm, const u8 *src_addr, struct wpa_eapol_key *key, size_t extra_len, u16 key_info, u16 ver) { } static inline void peerkey_deinit(struct wpa_sm *sm) { } #endif /* CONFIG_PEERKEY */ #endif /* PEERKEY_H */ wpa-2.1/src/rsn_supp/pmksa_cache.c000066400000000000000000000352561227414666700172370ustar00rootroot00000000000000/* * WPA Supplicant - RSN PMKSA cache * Copyright (c) 2004-2009, 2011-2012, 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 "eloop.h" #include "eapol_supp/eapol_supp_sm.h" #include "wpa.h" #include "wpa_i.h" #include "pmksa_cache.h" #ifdef IEEE8021X_EAPOL static const int pmksa_cache_max_entries = 32; struct rsn_pmksa_cache { struct rsn_pmksa_cache_entry *pmksa; /* PMKSA cache */ int pmksa_count; /* number of entries in PMKSA cache */ struct wpa_sm *sm; /* TODO: get rid of this reference(?) */ void (*free_cb)(struct rsn_pmksa_cache_entry *entry, void *ctx, enum pmksa_free_reason reason); void *ctx; }; static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa); static void _pmksa_cache_free_entry(struct rsn_pmksa_cache_entry *entry) { os_free(entry); } static void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa, struct rsn_pmksa_cache_entry *entry, enum pmksa_free_reason reason) { wpa_sm_remove_pmkid(pmksa->sm, entry->aa, entry->pmkid); pmksa->pmksa_count--; pmksa->free_cb(entry, pmksa->ctx, reason); _pmksa_cache_free_entry(entry); } static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx) { struct rsn_pmksa_cache *pmksa = eloop_ctx; struct os_reltime now; os_get_reltime(&now); while (pmksa->pmksa && pmksa->pmksa->expiration <= now.sec) { struct rsn_pmksa_cache_entry *entry = pmksa->pmksa; pmksa->pmksa = entry->next; wpa_printf(MSG_DEBUG, "RSN: expired PMKSA cache entry for " MACSTR, MAC2STR(entry->aa)); pmksa_cache_free_entry(pmksa, entry, PMKSA_EXPIRE); } pmksa_cache_set_expiration(pmksa); } static void pmksa_cache_reauth(void *eloop_ctx, void *timeout_ctx) { struct rsn_pmksa_cache *pmksa = eloop_ctx; pmksa->sm->cur_pmksa = NULL; eapol_sm_request_reauth(pmksa->sm->eapol); } static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa) { int sec; struct rsn_pmksa_cache_entry *entry; struct os_reltime now; eloop_cancel_timeout(pmksa_cache_expire, pmksa, NULL); eloop_cancel_timeout(pmksa_cache_reauth, pmksa, NULL); if (pmksa->pmksa == NULL) return; os_get_reltime(&now); sec = pmksa->pmksa->expiration - now.sec; if (sec < 0) sec = 0; eloop_register_timeout(sec + 1, 0, pmksa_cache_expire, pmksa, NULL); entry = pmksa->sm->cur_pmksa ? pmksa->sm->cur_pmksa : pmksa_cache_get(pmksa, pmksa->sm->bssid, NULL, NULL); if (entry) { sec = pmksa->pmksa->reauth_time - now.sec; if (sec < 0) sec = 0; eloop_register_timeout(sec, 0, pmksa_cache_reauth, pmksa, NULL); } } /** * pmksa_cache_add - Add a PMKSA cache entry * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() * @pmk: The new pairwise master key * @pmk_len: PMK length in bytes, usually PMK_LEN (32) * @aa: Authenticator address * @spa: Supplicant address * @network_ctx: Network configuration context for this PMK * @akmp: WPA_KEY_MGMT_* used in key derivation * Returns: Pointer to the added PMKSA cache entry or %NULL on error * * This function create a PMKSA entry for a new PMK and adds it to the PMKSA * cache. If an old entry is already in the cache for the same Authenticator, * this entry will be replaced with the new entry. PMKID will be calculated * based on the PMK and the driver interface is notified of the new PMKID. */ struct rsn_pmksa_cache_entry * pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, const u8 *aa, const u8 *spa, void *network_ctx, int akmp) { struct rsn_pmksa_cache_entry *entry, *pos, *prev; struct os_reltime now; if (pmk_len > PMK_LEN) return NULL; entry = os_zalloc(sizeof(*entry)); if (entry == NULL) return NULL; os_memcpy(entry->pmk, pmk, pmk_len); entry->pmk_len = pmk_len; rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid, wpa_key_mgmt_sha256(akmp)); os_get_reltime(&now); entry->expiration = now.sec + pmksa->sm->dot11RSNAConfigPMKLifetime; entry->reauth_time = now.sec + pmksa->sm->dot11RSNAConfigPMKLifetime * pmksa->sm->dot11RSNAConfigPMKReauthThreshold / 100; entry->akmp = akmp; os_memcpy(entry->aa, aa, ETH_ALEN); entry->network_ctx = network_ctx; /* Replace an old entry for the same Authenticator (if found) with the * new entry */ pos = pmksa->pmksa; prev = NULL; while (pos) { if (os_memcmp(aa, pos->aa, ETH_ALEN) == 0) { if (pos->pmk_len == pmk_len && os_memcmp(pos->pmk, pmk, pmk_len) == 0 && os_memcmp(pos->pmkid, entry->pmkid, PMKID_LEN) == 0) { wpa_printf(MSG_DEBUG, "WPA: reusing previous " "PMKSA entry"); os_free(entry); return pos; } if (prev == NULL) pmksa->pmksa = pos->next; else prev->next = pos->next; /* * If OKC is used, there may be other PMKSA cache * entries based on the same PMK. These needs to be * flushed so that a new entry can be created based on * the new PMK. Only clear other entries if they have a * matching PMK and this PMK has been used successfully * with the current AP, i.e., if opportunistic flag has * been cleared in wpa_supplicant_key_neg_complete(). */ wpa_printf(MSG_DEBUG, "RSN: Replace PMKSA entry for " "the current AP and any PMKSA cache entry " "that was based on the old PMK"); if (!pos->opportunistic) pmksa_cache_flush(pmksa, network_ctx, pos->pmk, pos->pmk_len); pmksa_cache_free_entry(pmksa, pos, PMKSA_REPLACE); break; } prev = pos; pos = pos->next; } if (pmksa->pmksa_count >= pmksa_cache_max_entries && pmksa->pmksa) { /* Remove the oldest entry to make room for the new entry */ pos = pmksa->pmksa; if (pos == pmksa->sm->cur_pmksa) { /* * Never remove the current PMKSA cache entry, since * it's in use, and removing it triggers a needless * deauthentication. */ pos = pos->next; pmksa->pmksa->next = pos ? pos->next : NULL; } else pmksa->pmksa = pos->next; if (pos) { wpa_printf(MSG_DEBUG, "RSN: removed the oldest idle " "PMKSA cache entry (for " MACSTR ") to " "make room for new one", MAC2STR(pos->aa)); pmksa_cache_free_entry(pmksa, pos, PMKSA_FREE); } } /* Add the new entry; order by expiration time */ pos = pmksa->pmksa; prev = NULL; while (pos) { if (pos->expiration > entry->expiration) break; prev = pos; pos = pos->next; } if (prev == NULL) { entry->next = pmksa->pmksa; pmksa->pmksa = entry; pmksa_cache_set_expiration(pmksa); } else { entry->next = prev->next; prev->next = entry; } pmksa->pmksa_count++; wpa_printf(MSG_DEBUG, "RSN: Added PMKSA cache entry for " MACSTR " network_ctx=%p", MAC2STR(entry->aa), network_ctx); wpa_sm_add_pmkid(pmksa->sm, entry->aa, entry->pmkid); return entry; } /** * pmksa_cache_flush - Flush PMKSA cache entries for a specific network * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() * @network_ctx: Network configuration context or %NULL to flush all entries * @pmk: PMK to match for or %NYLL to match all PMKs * @pmk_len: PMK length */ void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, void *network_ctx, const u8 *pmk, size_t pmk_len) { struct rsn_pmksa_cache_entry *entry, *prev = NULL, *tmp; int removed = 0; entry = pmksa->pmksa; while (entry) { if ((entry->network_ctx == network_ctx || network_ctx == NULL) && (pmk == NULL || (pmk_len == entry->pmk_len && os_memcmp(pmk, entry->pmk, pmk_len) == 0))) { wpa_printf(MSG_DEBUG, "RSN: Flush PMKSA cache entry " "for " MACSTR, MAC2STR(entry->aa)); if (prev) prev->next = entry->next; else pmksa->pmksa = entry->next; tmp = entry; entry = entry->next; pmksa_cache_free_entry(pmksa, tmp, PMKSA_FREE); removed++; } else { prev = entry; entry = entry->next; } } if (removed) pmksa_cache_set_expiration(pmksa); } /** * pmksa_cache_deinit - Free all entries in PMKSA cache * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() */ void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa) { struct rsn_pmksa_cache_entry *entry, *prev; if (pmksa == NULL) return; entry = pmksa->pmksa; pmksa->pmksa = NULL; while (entry) { prev = entry; entry = entry->next; os_free(prev); } pmksa_cache_set_expiration(pmksa); os_free(pmksa); } /** * pmksa_cache_get - Fetch a PMKSA cache entry * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() * @aa: Authenticator address or %NULL to match any * @pmkid: PMKID or %NULL to match any * @network_ctx: Network context or %NULL to match any * Returns: Pointer to PMKSA cache entry or %NULL if no match was found */ struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa, const u8 *aa, const u8 *pmkid, const void *network_ctx) { struct rsn_pmksa_cache_entry *entry = pmksa->pmksa; while (entry) { if ((aa == NULL || os_memcmp(entry->aa, aa, ETH_ALEN) == 0) && (pmkid == NULL || os_memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0) && (network_ctx == NULL || network_ctx == entry->network_ctx)) return entry; entry = entry->next; } return NULL; } static struct rsn_pmksa_cache_entry * pmksa_cache_clone_entry(struct rsn_pmksa_cache *pmksa, const struct rsn_pmksa_cache_entry *old_entry, const u8 *aa) { struct rsn_pmksa_cache_entry *new_entry; new_entry = pmksa_cache_add(pmksa, old_entry->pmk, old_entry->pmk_len, aa, pmksa->sm->own_addr, old_entry->network_ctx, old_entry->akmp); if (new_entry == NULL) return NULL; /* TODO: reorder entries based on expiration time? */ new_entry->expiration = old_entry->expiration; new_entry->opportunistic = 1; return new_entry; } /** * pmksa_cache_get_opportunistic - Try to get an opportunistic PMKSA entry * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() * @network_ctx: Network configuration context * @aa: Authenticator address for the new AP * Returns: Pointer to a new PMKSA cache entry or %NULL if not available * * Try to create a new PMKSA cache entry opportunistically by guessing that the * new AP is sharing the same PMK as another AP that has the same SSID and has * already an entry in PMKSA cache. */ struct rsn_pmksa_cache_entry * pmksa_cache_get_opportunistic(struct rsn_pmksa_cache *pmksa, void *network_ctx, const u8 *aa) { struct rsn_pmksa_cache_entry *entry = pmksa->pmksa; wpa_printf(MSG_DEBUG, "RSN: Consider " MACSTR " for OKC", MAC2STR(aa)); if (network_ctx == NULL) return NULL; while (entry) { if (entry->network_ctx == network_ctx) { entry = pmksa_cache_clone_entry(pmksa, entry, aa); if (entry) { wpa_printf(MSG_DEBUG, "RSN: added " "opportunistic PMKSA cache entry " "for " MACSTR, MAC2STR(aa)); } return entry; } entry = entry->next; } return NULL; } /** * pmksa_cache_get_current - Get the current used PMKSA entry * @sm: Pointer to WPA state machine data from wpa_sm_init() * Returns: Pointer to the current PMKSA cache entry or %NULL if not available */ struct rsn_pmksa_cache_entry * pmksa_cache_get_current(struct wpa_sm *sm) { if (sm == NULL) return NULL; return sm->cur_pmksa; } /** * pmksa_cache_clear_current - Clear the current PMKSA entry selection * @sm: Pointer to WPA state machine data from wpa_sm_init() */ void pmksa_cache_clear_current(struct wpa_sm *sm) { if (sm == NULL) return; sm->cur_pmksa = NULL; } /** * pmksa_cache_set_current - Set the current PMKSA entry selection * @sm: Pointer to WPA state machine data from wpa_sm_init() * @pmkid: PMKID for selecting PMKSA or %NULL if not used * @bssid: BSSID for PMKSA or %NULL if not used * @network_ctx: Network configuration context * @try_opportunistic: Whether to allow opportunistic PMKSA caching * Returns: 0 if PMKSA was found or -1 if no matching entry was found */ int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid, const u8 *bssid, void *network_ctx, int try_opportunistic) { struct rsn_pmksa_cache *pmksa = sm->pmksa; wpa_printf(MSG_DEBUG, "RSN: PMKSA cache search - network_ctx=%p " "try_opportunistic=%d", network_ctx, try_opportunistic); if (pmkid) wpa_hexdump(MSG_DEBUG, "RSN: Search for PMKID", pmkid, PMKID_LEN); if (bssid) wpa_printf(MSG_DEBUG, "RSN: Search for BSSID " MACSTR, MAC2STR(bssid)); sm->cur_pmksa = NULL; if (pmkid) sm->cur_pmksa = pmksa_cache_get(pmksa, NULL, pmkid, network_ctx); if (sm->cur_pmksa == NULL && bssid) sm->cur_pmksa = pmksa_cache_get(pmksa, bssid, NULL, network_ctx); if (sm->cur_pmksa == NULL && try_opportunistic && bssid) sm->cur_pmksa = pmksa_cache_get_opportunistic(pmksa, network_ctx, bssid); if (sm->cur_pmksa) { wpa_hexdump(MSG_DEBUG, "RSN: PMKSA cache entry found - PMKID", sm->cur_pmksa->pmkid, PMKID_LEN); return 0; } wpa_printf(MSG_DEBUG, "RSN: No PMKSA cache entry found"); return -1; } /** * pmksa_cache_list - Dump text list of entries in PMKSA cache * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() * @buf: Buffer for the list * @len: Length of the buffer * Returns: number of bytes written to buffer * * This function is used to generate a text format representation of the * current PMKSA cache contents for the ctrl_iface PMKSA command. */ int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len) { int i, ret; char *pos = buf; struct rsn_pmksa_cache_entry *entry; struct os_reltime now; os_get_reltime(&now); ret = os_snprintf(pos, buf + len - pos, "Index / AA / PMKID / expiration (in seconds) / " "opportunistic\n"); if (ret < 0 || ret >= buf + len - pos) return pos - buf; pos += ret; i = 0; entry = pmksa->pmksa; while (entry) { i++; ret = os_snprintf(pos, buf + len - pos, "%d " MACSTR " ", i, MAC2STR(entry->aa)); if (ret < 0 || ret >= buf + len - pos) return pos - buf; pos += ret; pos += wpa_snprintf_hex(pos, buf + len - pos, entry->pmkid, PMKID_LEN); ret = os_snprintf(pos, buf + len - pos, " %d %d\n", (int) (entry->expiration - now.sec), entry->opportunistic); if (ret < 0 || ret >= buf + len - pos) return pos - buf; pos += ret; entry = entry->next; } return pos - buf; } /** * pmksa_cache_init - Initialize PMKSA cache * @free_cb: Callback function to be called when a PMKSA cache entry is freed * @ctx: Context pointer for free_cb function * @sm: Pointer to WPA state machine data from wpa_sm_init() * Returns: Pointer to PMKSA cache data or %NULL on failure */ struct rsn_pmksa_cache * pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry, void *ctx, enum pmksa_free_reason reason), void *ctx, struct wpa_sm *sm) { struct rsn_pmksa_cache *pmksa; pmksa = os_zalloc(sizeof(*pmksa)); if (pmksa) { pmksa->free_cb = free_cb; pmksa->ctx = ctx; pmksa->sm = sm; } return pmksa; } #endif /* IEEE8021X_EAPOL */ wpa-2.1/src/rsn_supp/pmksa_cache.h000066400000000000000000000070061227414666700172340ustar00rootroot00000000000000/* * wpa_supplicant - WPA2/RSN PMKSA cache functions * Copyright (c) 2003-2009, 2011-2012, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef PMKSA_CACHE_H #define PMKSA_CACHE_H /** * struct rsn_pmksa_cache_entry - PMKSA cache entry */ struct rsn_pmksa_cache_entry { struct rsn_pmksa_cache_entry *next; u8 pmkid[PMKID_LEN]; u8 pmk[PMK_LEN]; size_t pmk_len; os_time_t expiration; int akmp; /* WPA_KEY_MGMT_* */ u8 aa[ETH_ALEN]; os_time_t reauth_time; /** * network_ctx - Network configuration context * * This field is only used to match PMKSA cache entries to a specific * network configuration (e.g., a specific SSID and security policy). * This can be a pointer to the configuration entry, but PMKSA caching * code does not dereference the value and this could be any kind of * identifier. */ void *network_ctx; int opportunistic; }; struct rsn_pmksa_cache; enum pmksa_free_reason { PMKSA_FREE, PMKSA_REPLACE, PMKSA_EXPIRE, }; #ifdef IEEE8021X_EAPOL struct rsn_pmksa_cache * pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry, void *ctx, enum pmksa_free_reason reason), void *ctx, struct wpa_sm *sm); void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa); struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa, const u8 *aa, const u8 *pmkid, const void *network_ctx); int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len); struct rsn_pmksa_cache_entry * pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, const u8 *aa, const u8 *spa, void *network_ctx, int akmp); struct rsn_pmksa_cache_entry * pmksa_cache_get_current(struct wpa_sm *sm); void pmksa_cache_clear_current(struct wpa_sm *sm); int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid, const u8 *bssid, void *network_ctx, int try_opportunistic); struct rsn_pmksa_cache_entry * pmksa_cache_get_opportunistic(struct rsn_pmksa_cache *pmksa, void *network_ctx, const u8 *aa); void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, void *network_ctx, const u8 *pmk, size_t pmk_len); #else /* IEEE8021X_EAPOL */ static inline struct rsn_pmksa_cache * pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry, void *ctx, enum pmksa_free_reason reason), void *ctx, struct wpa_sm *sm) { return (void *) -1; } static inline void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa) { } static inline struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa, const u8 *aa, const u8 *pmkid, const void *network_ctx) { return NULL; } static inline struct rsn_pmksa_cache_entry * pmksa_cache_get_current(struct wpa_sm *sm) { return NULL; } static inline int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len) { return -1; } static inline struct rsn_pmksa_cache_entry * pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, const u8 *aa, const u8 *spa, void *network_ctx, int akmp) { return NULL; } static inline void pmksa_cache_clear_current(struct wpa_sm *sm) { } static inline int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid, const u8 *bssid, void *network_ctx, int try_opportunistic) { return -1; } static inline void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, void *network_ctx, const u8 *pmk, size_t pmk_len) { } #endif /* IEEE8021X_EAPOL */ #endif /* PMKSA_CACHE_H */ wpa-2.1/src/rsn_supp/preauth.c000066400000000000000000000345401227414666700164440ustar00rootroot00000000000000/* * RSN pre-authentication (supplicant) * Copyright (c) 2003-2012, 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 "wpa.h" #include "eloop.h" #include "l2_packet/l2_packet.h" #include "eapol_supp/eapol_supp_sm.h" #include "preauth.h" #include "pmksa_cache.h" #include "wpa_i.h" #ifdef IEEE8021X_EAPOL #define PMKID_CANDIDATE_PRIO_SCAN 1000 struct rsn_pmksa_candidate { struct dl_list list; u8 bssid[ETH_ALEN]; int priority; }; /** * pmksa_candidate_free - Free all entries in PMKSA candidate list * @sm: Pointer to WPA state machine data from wpa_sm_init() */ void pmksa_candidate_free(struct wpa_sm *sm) { struct rsn_pmksa_candidate *entry, *n; if (sm == NULL) return; dl_list_for_each_safe(entry, n, &sm->pmksa_candidates, struct rsn_pmksa_candidate, list) { dl_list_del(&entry->list); os_free(entry); } } static void rsn_preauth_receive(void *ctx, const u8 *src_addr, const u8 *buf, size_t len) { struct wpa_sm *sm = ctx; wpa_printf(MSG_DEBUG, "RX pre-auth from " MACSTR, MAC2STR(src_addr)); wpa_hexdump(MSG_MSGDUMP, "RX pre-auth", buf, len); if (sm->preauth_eapol == NULL || is_zero_ether_addr(sm->preauth_bssid) || os_memcmp(sm->preauth_bssid, src_addr, ETH_ALEN) != 0) { wpa_printf(MSG_WARNING, "RSN pre-auth frame received from " "unexpected source " MACSTR " - dropped", MAC2STR(src_addr)); return; } eapol_sm_rx_eapol(sm->preauth_eapol, src_addr, buf, len); } static void rsn_preauth_eapol_cb(struct eapol_sm *eapol, enum eapol_supp_result result, void *ctx) { struct wpa_sm *sm = ctx; u8 pmk[PMK_LEN]; if (result == EAPOL_SUPP_RESULT_SUCCESS) { int res, pmk_len; pmk_len = PMK_LEN; res = eapol_sm_get_key(eapol, pmk, PMK_LEN); if (res) { /* * EAP-LEAP is an exception from other EAP methods: it * uses only 16-byte PMK. */ res = eapol_sm_get_key(eapol, pmk, 16); pmk_len = 16; } if (res == 0) { wpa_hexdump_key(MSG_DEBUG, "RSN: PMK from pre-auth", pmk, pmk_len); sm->pmk_len = pmk_len; pmksa_cache_add(sm->pmksa, pmk, pmk_len, sm->preauth_bssid, sm->own_addr, sm->network_ctx, WPA_KEY_MGMT_IEEE8021X); } else { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "RSN: failed to get master session key from " "pre-auth EAPOL state machines"); result = EAPOL_SUPP_RESULT_FAILURE; } } wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "RSN: pre-authentication with " MACSTR " %s", MAC2STR(sm->preauth_bssid), result == EAPOL_SUPP_RESULT_SUCCESS ? "completed successfully" : "failed"); rsn_preauth_deinit(sm); rsn_preauth_candidate_process(sm); } static void rsn_preauth_timeout(void *eloop_ctx, void *timeout_ctx) { struct wpa_sm *sm = eloop_ctx; wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "RSN: pre-authentication with " MACSTR " timed out", MAC2STR(sm->preauth_bssid)); rsn_preauth_deinit(sm); rsn_preauth_candidate_process(sm); } static int rsn_preauth_eapol_send(void *ctx, int type, const u8 *buf, size_t len) { struct wpa_sm *sm = ctx; u8 *msg; size_t msglen; int res; /* TODO: could add l2_packet_sendmsg that allows fragments to avoid * extra copy here */ if (sm->l2_preauth == NULL) return -1; msg = wpa_sm_alloc_eapol(sm, type, buf, len, &msglen, NULL); if (msg == NULL) return -1; wpa_hexdump(MSG_MSGDUMP, "TX EAPOL (preauth)", msg, msglen); res = l2_packet_send(sm->l2_preauth, sm->preauth_bssid, ETH_P_RSN_PREAUTH, msg, msglen); os_free(msg); return res; } /** * rsn_preauth_init - Start new RSN pre-authentication * @sm: Pointer to WPA state machine data from wpa_sm_init() * @dst: Authenticator address (BSSID) with which to preauthenticate * @eap_conf: Current EAP configuration * Returns: 0 on success, -1 on another pre-authentication is in progress, * -2 on layer 2 packet initialization failure, -3 on EAPOL state machine * initialization failure, -4 on memory allocation failure * * This function request an RSN pre-authentication with a given destination * address. This is usually called for PMKSA candidates found from scan results * or from driver reports. In addition, ctrl_iface PREAUTH command can trigger * pre-authentication. */ int rsn_preauth_init(struct wpa_sm *sm, const u8 *dst, struct eap_peer_config *eap_conf) { struct eapol_config eapol_conf; struct eapol_ctx *ctx; if (sm->preauth_eapol) return -1; wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: starting pre-authentication with " MACSTR, MAC2STR(dst)); sm->l2_preauth = l2_packet_init(sm->ifname, sm->own_addr, ETH_P_RSN_PREAUTH, rsn_preauth_receive, sm, 0); if (sm->l2_preauth == NULL) { wpa_printf(MSG_WARNING, "RSN: Failed to initialize L2 packet " "processing for pre-authentication"); return -2; } if (sm->bridge_ifname) { sm->l2_preauth_br = l2_packet_init(sm->bridge_ifname, sm->own_addr, ETH_P_RSN_PREAUTH, rsn_preauth_receive, sm, 0); if (sm->l2_preauth_br == NULL) { wpa_printf(MSG_WARNING, "RSN: Failed to initialize L2 " "packet processing (bridge) for " "pre-authentication"); return -2; } } ctx = os_zalloc(sizeof(*ctx)); if (ctx == NULL) { wpa_printf(MSG_WARNING, "Failed to allocate EAPOL context."); return -4; } ctx->ctx = sm->ctx->ctx; ctx->msg_ctx = sm->ctx->ctx; ctx->preauth = 1; ctx->cb = rsn_preauth_eapol_cb; ctx->cb_ctx = sm; ctx->scard_ctx = sm->scard_ctx; ctx->eapol_send = rsn_preauth_eapol_send; ctx->eapol_send_ctx = sm; ctx->set_config_blob = sm->ctx->set_config_blob; ctx->get_config_blob = sm->ctx->get_config_blob; sm->preauth_eapol = eapol_sm_init(ctx); if (sm->preauth_eapol == NULL) { os_free(ctx); wpa_printf(MSG_WARNING, "RSN: Failed to initialize EAPOL " "state machines for pre-authentication"); return -3; } os_memset(&eapol_conf, 0, sizeof(eapol_conf)); eapol_conf.accept_802_1x_keys = 0; eapol_conf.required_keys = 0; eapol_conf.fast_reauth = sm->fast_reauth; eapol_conf.workaround = sm->eap_workaround; eapol_sm_notify_config(sm->preauth_eapol, eap_conf, &eapol_conf); /* * Use a shorter startPeriod with preauthentication since the first * preauth EAPOL-Start frame may end up being dropped due to race * condition in the AP between the data receive and key configuration * after the 4-Way Handshake. */ eapol_sm_configure(sm->preauth_eapol, -1, -1, 5, 6); os_memcpy(sm->preauth_bssid, dst, ETH_ALEN); eapol_sm_notify_portValid(sm->preauth_eapol, TRUE); /* 802.1X::portControl = Auto */ eapol_sm_notify_portEnabled(sm->preauth_eapol, TRUE); eloop_register_timeout(sm->dot11RSNAConfigSATimeout, 0, rsn_preauth_timeout, sm, NULL); return 0; } /** * rsn_preauth_deinit - Abort RSN pre-authentication * @sm: Pointer to WPA state machine data from wpa_sm_init() * * This function aborts the current RSN pre-authentication (if one is started) * and frees resources allocated for it. */ void rsn_preauth_deinit(struct wpa_sm *sm) { if (sm == NULL || !sm->preauth_eapol) return; eloop_cancel_timeout(rsn_preauth_timeout, sm, NULL); eapol_sm_deinit(sm->preauth_eapol); sm->preauth_eapol = NULL; os_memset(sm->preauth_bssid, 0, ETH_ALEN); l2_packet_deinit(sm->l2_preauth); sm->l2_preauth = NULL; if (sm->l2_preauth_br) { l2_packet_deinit(sm->l2_preauth_br); sm->l2_preauth_br = NULL; } } /** * rsn_preauth_candidate_process - Process PMKSA candidates * @sm: Pointer to WPA state machine data from wpa_sm_init() * * Go through the PMKSA candidates and start pre-authentication if a candidate * without an existing PMKSA cache entry is found. Processed candidates will be * removed from the list. */ void rsn_preauth_candidate_process(struct wpa_sm *sm) { struct rsn_pmksa_candidate *candidate, *n; if (dl_list_empty(&sm->pmksa_candidates)) return; /* TODO: drop priority for old candidate entries */ wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: processing PMKSA candidate " "list"); if (sm->preauth_eapol || sm->proto != WPA_PROTO_RSN || wpa_sm_get_state(sm) != WPA_COMPLETED || (sm->key_mgmt != WPA_KEY_MGMT_IEEE8021X && sm->key_mgmt != WPA_KEY_MGMT_IEEE8021X_SHA256)) { wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: not in suitable " "state for new pre-authentication"); return; /* invalid state for new pre-auth */ } dl_list_for_each_safe(candidate, n, &sm->pmksa_candidates, struct rsn_pmksa_candidate, list) { struct rsn_pmksa_cache_entry *p = NULL; p = pmksa_cache_get(sm->pmksa, candidate->bssid, NULL, NULL); if (os_memcmp(sm->bssid, candidate->bssid, ETH_ALEN) != 0 && (p == NULL || p->opportunistic)) { wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: PMKSA " "candidate " MACSTR " selected for pre-authentication", MAC2STR(candidate->bssid)); dl_list_del(&candidate->list); rsn_preauth_init(sm, candidate->bssid, sm->eap_conf_ctx); os_free(candidate); return; } wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: PMKSA candidate " MACSTR " does not need pre-authentication anymore", MAC2STR(candidate->bssid)); /* Some drivers (e.g., NDIS) expect to get notified about the * PMKIDs again, so report the existing data now. */ if (p) { wpa_sm_add_pmkid(sm, candidate->bssid, p->pmkid); } dl_list_del(&candidate->list); os_free(candidate); } wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: no more pending PMKSA " "candidates"); } /** * pmksa_candidate_add - Add a new PMKSA candidate * @sm: Pointer to WPA state machine data from wpa_sm_init() * @bssid: BSSID (authenticator address) of the candidate * @prio: Priority (the smaller number, the higher priority) * @preauth: Whether the candidate AP advertises support for pre-authentication * * This function is used to add PMKSA candidates for RSN pre-authentication. It * is called from scan result processing and from driver events for PMKSA * candidates, i.e., EVENT_PMKID_CANDIDATE events to wpa_supplicant_event(). */ void pmksa_candidate_add(struct wpa_sm *sm, const u8 *bssid, int prio, int preauth) { struct rsn_pmksa_candidate *cand, *pos; if (sm->network_ctx && sm->proactive_key_caching) pmksa_cache_get_opportunistic(sm->pmksa, sm->network_ctx, bssid); if (!preauth) { wpa_printf(MSG_DEBUG, "RSN: Ignored PMKID candidate without " "preauth flag"); return; } /* If BSSID already on candidate list, update the priority of the old * entry. Do not override priority based on normal scan results. */ cand = NULL; dl_list_for_each(pos, &sm->pmksa_candidates, struct rsn_pmksa_candidate, list) { if (os_memcmp(pos->bssid, bssid, ETH_ALEN) == 0) { cand = pos; break; } } if (cand) { dl_list_del(&cand->list); if (prio < PMKID_CANDIDATE_PRIO_SCAN) cand->priority = prio; } else { cand = os_zalloc(sizeof(*cand)); if (cand == NULL) return; os_memcpy(cand->bssid, bssid, ETH_ALEN); cand->priority = prio; } /* Add candidate to the list; order by increasing priority value. i.e., * highest priority (smallest value) first. */ dl_list_for_each(pos, &sm->pmksa_candidates, struct rsn_pmksa_candidate, list) { if (cand->priority <= pos->priority) { dl_list_add(pos->list.prev, &cand->list); cand = NULL; break; } } if (cand) dl_list_add_tail(&sm->pmksa_candidates, &cand->list); wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: added PMKSA cache " "candidate " MACSTR " prio %d", MAC2STR(bssid), prio); rsn_preauth_candidate_process(sm); } /* TODO: schedule periodic scans if current AP supports preauth */ /** * rsn_preauth_scan_results - Start processing scan results for canditates * @sm: Pointer to WPA state machine data from wpa_sm_init() * Returns: 0 if ready to process results or -1 to skip processing * * This functions is used to notify RSN code about start of new scan results * processing. The actual scan results will be provided by calling * rsn_preauth_scan_result() for each BSS if this function returned 0. */ int rsn_preauth_scan_results(struct wpa_sm *sm) { if (sm->ssid_len == 0) return -1; /* * TODO: is it ok to free all candidates? What about the entries * received from EVENT_PMKID_CANDIDATE? */ pmksa_candidate_free(sm); return 0; } /** * rsn_preauth_scan_result - Processing scan result for PMKSA canditates * @sm: Pointer to WPA state machine data from wpa_sm_init() * * Add all suitable APs (Authenticators) from scan results into PMKSA * candidate list. */ void rsn_preauth_scan_result(struct wpa_sm *sm, const u8 *bssid, const u8 *ssid, const u8 *rsn) { struct wpa_ie_data ie; struct rsn_pmksa_cache_entry *pmksa; if (ssid[1] != sm->ssid_len || os_memcmp(ssid + 2, sm->ssid, sm->ssid_len) != 0) return; /* Not for the current SSID */ if (os_memcmp(bssid, sm->bssid, ETH_ALEN) == 0) return; /* Ignore current AP */ if (wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ie)) return; pmksa = pmksa_cache_get(sm->pmksa, bssid, NULL, NULL); if (pmksa && (!pmksa->opportunistic || !(ie.capabilities & WPA_CAPABILITY_PREAUTH))) return; /* Give less priority to candidates found from normal scan results. */ pmksa_candidate_add(sm, bssid, PMKID_CANDIDATE_PRIO_SCAN, ie.capabilities & WPA_CAPABILITY_PREAUTH); } #ifdef CONFIG_CTRL_IFACE /** * rsn_preauth_get_status - Get pre-authentication status * @sm: Pointer to WPA state machine data from wpa_sm_init() * @buf: Buffer for status information * @buflen: Maximum buffer length * @verbose: Whether to include verbose status information * Returns: Number of bytes written to buf. * * Query WPA2 pre-authentication for status information. This function fills in * a text area with current status information. If the buffer (buf) is not * large enough, status information will be truncated to fit the buffer. */ int rsn_preauth_get_status(struct wpa_sm *sm, char *buf, size_t buflen, int verbose) { char *pos = buf, *end = buf + buflen; int res, ret; if (sm->preauth_eapol) { ret = os_snprintf(pos, end - pos, "Pre-authentication " "EAPOL state machines:\n"); if (ret < 0 || ret >= end - pos) return pos - buf; pos += ret; res = eapol_sm_get_status(sm->preauth_eapol, pos, end - pos, verbose); if (res >= 0) pos += res; } return pos - buf; } #endif /* CONFIG_CTRL_IFACE */ /** * rsn_preauth_in_progress - Verify whether pre-authentication is in progress * @sm: Pointer to WPA state machine data from wpa_sm_init() */ int rsn_preauth_in_progress(struct wpa_sm *sm) { return sm->preauth_eapol != NULL; } #endif /* IEEE8021X_EAPOL */ wpa-2.1/src/rsn_supp/preauth.h000066400000000000000000000035171227414666700164510ustar00rootroot00000000000000/* * wpa_supplicant - WPA2/RSN pre-authentication functions * Copyright (c) 2003-2009, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef PREAUTH_H #define PREAUTH_H struct wpa_scan_results; #ifdef IEEE8021X_EAPOL void pmksa_candidate_free(struct wpa_sm *sm); int rsn_preauth_init(struct wpa_sm *sm, const u8 *dst, struct eap_peer_config *eap_conf); void rsn_preauth_deinit(struct wpa_sm *sm); int rsn_preauth_scan_results(struct wpa_sm *sm); void rsn_preauth_scan_result(struct wpa_sm *sm, const u8 *bssid, const u8 *ssid, const u8 *rsn); void pmksa_candidate_add(struct wpa_sm *sm, const u8 *bssid, int prio, int preauth); void rsn_preauth_candidate_process(struct wpa_sm *sm); int rsn_preauth_get_status(struct wpa_sm *sm, char *buf, size_t buflen, int verbose); int rsn_preauth_in_progress(struct wpa_sm *sm); #else /* IEEE8021X_EAPOL */ static inline void pmksa_candidate_free(struct wpa_sm *sm) { } static inline void rsn_preauth_candidate_process(struct wpa_sm *sm) { } static inline int rsn_preauth_init(struct wpa_sm *sm, const u8 *dst, struct eap_peer_config *eap_conf) { return -1; } static inline void rsn_preauth_deinit(struct wpa_sm *sm) { } static inline int rsn_preauth_scan_results(struct wpa_sm *sm) { return -1; } static inline void rsn_preauth_scan_result(struct wpa_sm *sm, const u8 *bssid, const u8 *ssid, const u8 *rsn) { } static inline void pmksa_candidate_add(struct wpa_sm *sm, const u8 *bssid, int prio, int preauth) { } static inline int rsn_preauth_get_status(struct wpa_sm *sm, char *buf, size_t buflen, int verbose) { return 0; } static inline int rsn_preauth_in_progress(struct wpa_sm *sm) { return 0; } #endif /* IEEE8021X_EAPOL */ #endif /* PREAUTH_H */ wpa-2.1/src/rsn_supp/tdls.c000066400000000000000000002145371227414666700157500ustar00rootroot00000000000000/* * wpa_supplicant - TDLS * Copyright (c) 2010-2011, Atheros Communications * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "utils/includes.h" #include "utils/common.h" #include "utils/eloop.h" #include "utils/os.h" #include "common/ieee802_11_defs.h" #include "crypto/sha256.h" #include "crypto/crypto.h" #include "crypto/aes_wrap.h" #include "rsn_supp/wpa.h" #include "rsn_supp/wpa_ie.h" #include "rsn_supp/wpa_i.h" #include "drivers/driver.h" #include "l2_packet/l2_packet.h" #ifdef CONFIG_TDLS_TESTING #define TDLS_TESTING_LONG_FRAME BIT(0) #define TDLS_TESTING_ALT_RSN_IE BIT(1) #define TDLS_TESTING_DIFF_BSSID BIT(2) #define TDLS_TESTING_SHORT_LIFETIME BIT(3) #define TDLS_TESTING_WRONG_LIFETIME_RESP BIT(4) #define TDLS_TESTING_WRONG_LIFETIME_CONF BIT(5) #define TDLS_TESTING_LONG_LIFETIME BIT(6) #define TDLS_TESTING_CONCURRENT_INIT BIT(7) #define TDLS_TESTING_NO_TPK_EXPIRATION BIT(8) #define TDLS_TESTING_DECLINE_RESP BIT(9) #define TDLS_TESTING_IGNORE_AP_PROHIBIT BIT(10) unsigned int tdls_testing = 0; #endif /* CONFIG_TDLS_TESTING */ #define TPK_LIFETIME 43200 /* 12 hours */ #define TPK_M1_RETRY_COUNT 3 #define TPK_M1_TIMEOUT 5000 /* in milliseconds */ #define TPK_M2_RETRY_COUNT 10 #define TPK_M2_TIMEOUT 500 /* in milliseconds */ #define TDLS_MIC_LEN 16 #define TDLS_TIMEOUT_LEN 4 struct wpa_tdls_ftie { u8 ie_type; /* FTIE */ u8 ie_len; u8 mic_ctrl[2]; u8 mic[TDLS_MIC_LEN]; u8 Anonce[WPA_NONCE_LEN]; /* Responder Nonce in TDLS */ u8 Snonce[WPA_NONCE_LEN]; /* Initiator Nonce in TDLS */ /* followed by optional elements */ } STRUCT_PACKED; struct wpa_tdls_timeoutie { u8 ie_type; /* Timeout IE */ u8 ie_len; u8 interval_type; u8 value[TDLS_TIMEOUT_LEN]; } STRUCT_PACKED; struct wpa_tdls_lnkid { u8 ie_type; /* Link Identifier IE */ u8 ie_len; u8 bssid[ETH_ALEN]; u8 init_sta[ETH_ALEN]; u8 resp_sta[ETH_ALEN]; } STRUCT_PACKED; /* TDLS frame headers as per IEEE Std 802.11z-2010 */ struct wpa_tdls_frame { u8 payloadtype; /* IEEE80211_TDLS_RFTYPE */ u8 category; /* Category */ u8 action; /* Action (enum tdls_frame_type) */ } STRUCT_PACKED; static u8 * wpa_add_tdls_timeoutie(u8 *pos, u8 *ie, size_t ie_len, u32 tsecs); static void wpa_tdls_tpk_retry_timeout(void *eloop_ctx, void *timeout_ctx); static void wpa_tdls_peer_free(struct wpa_sm *sm, struct wpa_tdls_peer *peer); static void wpa_tdls_disable_peer_link(struct wpa_sm *sm, struct wpa_tdls_peer *peer); #define TDLS_MAX_IE_LEN 80 #define IEEE80211_MAX_SUPP_RATES 32 struct wpa_tdls_peer { struct wpa_tdls_peer *next; unsigned int reconfig_key:1; int initiator; /* whether this end was initiator for TDLS setup */ u8 addr[ETH_ALEN]; /* other end MAC address */ u8 inonce[WPA_NONCE_LEN]; /* Initiator Nonce */ u8 rnonce[WPA_NONCE_LEN]; /* Responder Nonce */ u8 rsnie_i[TDLS_MAX_IE_LEN]; /* Initiator RSN IE */ size_t rsnie_i_len; u8 rsnie_p[TDLS_MAX_IE_LEN]; /* Peer RSN IE */ size_t rsnie_p_len; u32 lifetime; int cipher; /* Selected cipher (WPA_CIPHER_*) */ u8 dtoken; struct tpk { u8 kck[16]; /* TPK-KCK */ u8 tk[16]; /* TPK-TK; assuming only CCMP will be used */ } tpk; int tpk_set; int tpk_success; int tpk_in_progress; struct tpk_timer { u8 dest[ETH_ALEN]; int count; /* Retry Count */ int timer; /* Timeout in milliseconds */ u8 action_code; /* TDLS frame type */ u8 dialog_token; u16 status_code; int buf_len; /* length of TPK message for retransmission */ u8 *buf; /* buffer for TPK message */ } sm_tmr; u16 capability; u8 supp_rates[IEEE80211_MAX_SUPP_RATES]; size_t supp_rates_len; struct ieee80211_ht_capabilities *ht_capabilities; struct ieee80211_vht_capabilities *vht_capabilities; u8 qos_info; u16 aid; u8 *ext_capab; size_t ext_capab_len; u8 *supp_channels; size_t supp_channels_len; u8 *supp_oper_classes; size_t supp_oper_classes_len; }; static int wpa_tdls_get_privacy(struct wpa_sm *sm) { /* * Get info needed from supplicant to check if the current BSS supports * security. Other than OPEN mode, rest are considered secured * WEP/WPA/WPA2 hence TDLS frames are processed for TPK handshake. */ return sm->pairwise_cipher != WPA_CIPHER_NONE; } static u8 * wpa_add_ie(u8 *pos, const u8 *ie, size_t ie_len) { os_memcpy(pos, ie, ie_len); return pos + ie_len; } static int wpa_tdls_del_key(struct wpa_sm *sm, struct wpa_tdls_peer *peer) { if (wpa_sm_set_key(sm, WPA_ALG_NONE, peer->addr, 0, 0, NULL, 0, NULL, 0) < 0) { wpa_printf(MSG_WARNING, "TDLS: Failed to delete TPK-TK from " "the driver"); return -1; } return 0; } static int wpa_tdls_set_key(struct wpa_sm *sm, struct wpa_tdls_peer *peer) { u8 key_len; u8 rsc[6]; enum wpa_alg alg; os_memset(rsc, 0, 6); switch (peer->cipher) { case WPA_CIPHER_CCMP: alg = WPA_ALG_CCMP; key_len = 16; break; case WPA_CIPHER_NONE: wpa_printf(MSG_DEBUG, "TDLS: Pairwise Cipher Suite: " "NONE - do not use pairwise keys"); return -1; default: wpa_printf(MSG_WARNING, "TDLS: Unsupported pairwise cipher %d", sm->pairwise_cipher); return -1; } if (wpa_sm_set_key(sm, alg, peer->addr, -1, 1, rsc, sizeof(rsc), peer->tpk.tk, key_len) < 0) { wpa_printf(MSG_WARNING, "TDLS: Failed to set TPK to the " "driver"); return -1; } return 0; } static int wpa_tdls_send_tpk_msg(struct wpa_sm *sm, const u8 *dst, u8 action_code, u8 dialog_token, u16 status_code, const u8 *buf, size_t len) { return wpa_sm_send_tdls_mgmt(sm, dst, action_code, dialog_token, status_code, buf, len); } static int wpa_tdls_tpk_send(struct wpa_sm *sm, const u8 *dest, u8 action_code, u8 dialog_token, u16 status_code, const u8 *msg, size_t msg_len) { struct wpa_tdls_peer *peer; wpa_printf(MSG_DEBUG, "TDLS: TPK send dest=" MACSTR " action_code=%u " "dialog_token=%u status_code=%u msg_len=%u", MAC2STR(dest), action_code, dialog_token, status_code, (unsigned int) msg_len); if (wpa_tdls_send_tpk_msg(sm, dest, action_code, dialog_token, status_code, msg, msg_len)) { wpa_printf(MSG_INFO, "TDLS: Failed to send message " "(action_code=%u)", action_code); return -1; } if (action_code == WLAN_TDLS_SETUP_CONFIRM || action_code == WLAN_TDLS_TEARDOWN || action_code == WLAN_TDLS_DISCOVERY_REQUEST || action_code == WLAN_TDLS_DISCOVERY_RESPONSE) return 0; /* No retries */ for (peer = sm->tdls; peer; peer = peer->next) { if (os_memcmp(peer->addr, dest, ETH_ALEN) == 0) break; } if (peer == NULL) { wpa_printf(MSG_INFO, "TDLS: No matching entry found for " "retry " MACSTR, MAC2STR(dest)); return 0; } eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer); if (action_code == WLAN_TDLS_SETUP_RESPONSE) { peer->sm_tmr.count = TPK_M2_RETRY_COUNT; peer->sm_tmr.timer = TPK_M2_TIMEOUT; } else { peer->sm_tmr.count = TPK_M1_RETRY_COUNT; peer->sm_tmr.timer = TPK_M1_TIMEOUT; } /* Copy message to resend on timeout */ os_memcpy(peer->sm_tmr.dest, dest, ETH_ALEN); peer->sm_tmr.action_code = action_code; peer->sm_tmr.dialog_token = dialog_token; peer->sm_tmr.status_code = status_code; peer->sm_tmr.buf_len = msg_len; os_free(peer->sm_tmr.buf); peer->sm_tmr.buf = os_malloc(msg_len); if (peer->sm_tmr.buf == NULL) return -1; os_memcpy(peer->sm_tmr.buf, msg, msg_len); wpa_printf(MSG_DEBUG, "TDLS: Retry timeout registered " "(action_code=%u)", action_code); eloop_register_timeout(peer->sm_tmr.timer / 1000, (peer->sm_tmr.timer % 1000) * 1000, wpa_tdls_tpk_retry_timeout, sm, peer); return 0; } static int wpa_tdls_do_teardown(struct wpa_sm *sm, struct wpa_tdls_peer *peer, u16 reason_code) { int ret; ret = wpa_tdls_send_teardown(sm, peer->addr, reason_code); /* disable the link after teardown was sent */ wpa_tdls_disable_peer_link(sm, peer); return ret; } static void wpa_tdls_tpk_retry_timeout(void *eloop_ctx, void *timeout_ctx) { struct wpa_sm *sm = eloop_ctx; struct wpa_tdls_peer *peer = timeout_ctx; if (peer->sm_tmr.count) { peer->sm_tmr.count--; wpa_printf(MSG_INFO, "TDLS: Retrying sending of message " "(action_code=%u)", peer->sm_tmr.action_code); if (peer->sm_tmr.buf == NULL) { wpa_printf(MSG_INFO, "TDLS: No retry buffer available " "for action_code=%u", peer->sm_tmr.action_code); eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer); return; } /* resend TPK Handshake Message to Peer */ if (wpa_tdls_send_tpk_msg(sm, peer->sm_tmr.dest, peer->sm_tmr.action_code, peer->sm_tmr.dialog_token, peer->sm_tmr.status_code, peer->sm_tmr.buf, peer->sm_tmr.buf_len)) { wpa_printf(MSG_INFO, "TDLS: Failed to retry " "transmission"); } eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer); eloop_register_timeout(peer->sm_tmr.timer / 1000, (peer->sm_tmr.timer % 1000) * 1000, wpa_tdls_tpk_retry_timeout, sm, peer); } else { eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer); wpa_printf(MSG_DEBUG, "TDLS: Sending Teardown Request"); wpa_tdls_do_teardown(sm, peer, WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED); } } static void wpa_tdls_tpk_retry_timeout_cancel(struct wpa_sm *sm, struct wpa_tdls_peer *peer, u8 action_code) { if (action_code == peer->sm_tmr.action_code) { wpa_printf(MSG_DEBUG, "TDLS: Retry timeout cancelled for " "action_code=%u", action_code); /* Cancel Timeout registered */ eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer); /* free all resources meant for retry */ os_free(peer->sm_tmr.buf); peer->sm_tmr.buf = NULL; peer->sm_tmr.count = 0; peer->sm_tmr.timer = 0; peer->sm_tmr.buf_len = 0; peer->sm_tmr.action_code = 0xff; } else { wpa_printf(MSG_INFO, "TDLS: Error in cancelling retry timeout " "(Unknown action_code=%u)", action_code); } } static void wpa_tdls_generate_tpk(struct wpa_tdls_peer *peer, const u8 *own_addr, const u8 *bssid) { u8 key_input[SHA256_MAC_LEN]; const u8 *nonce[2]; size_t len[2]; u8 data[3 * ETH_ALEN]; /* IEEE Std 802.11z-2010 8.5.9.1: * TPK-Key-Input = SHA-256(min(SNonce, ANonce) || max(SNonce, ANonce)) */ len[0] = WPA_NONCE_LEN; len[1] = WPA_NONCE_LEN; if (os_memcmp(peer->inonce, peer->rnonce, WPA_NONCE_LEN) < 0) { nonce[0] = peer->inonce; nonce[1] = peer->rnonce; } else { nonce[0] = peer->rnonce; nonce[1] = peer->inonce; } wpa_hexdump(MSG_DEBUG, "TDLS: min(Nonce)", nonce[0], WPA_NONCE_LEN); wpa_hexdump(MSG_DEBUG, "TDLS: max(Nonce)", nonce[1], WPA_NONCE_LEN); sha256_vector(2, nonce, len, key_input); wpa_hexdump_key(MSG_DEBUG, "TDLS: TPK-Key-Input", key_input, SHA256_MAC_LEN); /* * TPK-Key-Data = KDF-N_KEY(TPK-Key-Input, "TDLS PMK", * min(MAC_I, MAC_R) || max(MAC_I, MAC_R) || BSSID || N_KEY) * TODO: is N_KEY really included in KDF Context and if so, in which * presentation format (little endian 16-bit?) is it used? It gets * added by the KDF anyway.. */ if (os_memcmp(own_addr, peer->addr, ETH_ALEN) < 0) { os_memcpy(data, own_addr, ETH_ALEN); os_memcpy(data + ETH_ALEN, peer->addr, ETH_ALEN); } else { os_memcpy(data, peer->addr, ETH_ALEN); os_memcpy(data + ETH_ALEN, own_addr, ETH_ALEN); } os_memcpy(data + 2 * ETH_ALEN, bssid, ETH_ALEN); wpa_hexdump(MSG_DEBUG, "TDLS: KDF Context", data, sizeof(data)); sha256_prf(key_input, SHA256_MAC_LEN, "TDLS PMK", data, sizeof(data), (u8 *) &peer->tpk, sizeof(peer->tpk)); wpa_hexdump_key(MSG_DEBUG, "TDLS: TPK-KCK", peer->tpk.kck, sizeof(peer->tpk.kck)); wpa_hexdump_key(MSG_DEBUG, "TDLS: TPK-TK", peer->tpk.tk, sizeof(peer->tpk.tk)); peer->tpk_set = 1; } /** * wpa_tdls_ftie_mic - Calculate TDLS FTIE MIC * @kck: TPK-KCK * @lnkid: Pointer to the beginning of Link Identifier IE * @rsnie: Pointer to the beginning of RSN IE used for handshake * @timeoutie: Pointer to the beginning of Timeout IE used for handshake * @ftie: Pointer to the beginning of FT IE * @mic: Pointer for writing MIC * * Calculate MIC for TDLS frame. */ static int wpa_tdls_ftie_mic(const u8 *kck, u8 trans_seq, const u8 *lnkid, const u8 *rsnie, const u8 *timeoutie, const u8 *ftie, u8 *mic) { u8 *buf, *pos; struct wpa_tdls_ftie *_ftie; const struct wpa_tdls_lnkid *_lnkid; int ret; int len = 2 * ETH_ALEN + 1 + 2 + lnkid[1] + 2 + rsnie[1] + 2 + timeoutie[1] + 2 + ftie[1]; buf = os_zalloc(len); if (!buf) { wpa_printf(MSG_WARNING, "TDLS: No memory for MIC calculation"); return -1; } pos = buf; _lnkid = (const struct wpa_tdls_lnkid *) lnkid; /* 1) TDLS initiator STA MAC address */ os_memcpy(pos, _lnkid->init_sta, ETH_ALEN); pos += ETH_ALEN; /* 2) TDLS responder STA MAC address */ os_memcpy(pos, _lnkid->resp_sta, ETH_ALEN); pos += ETH_ALEN; /* 3) Transaction Sequence number */ *pos++ = trans_seq; /* 4) Link Identifier IE */ os_memcpy(pos, lnkid, 2 + lnkid[1]); pos += 2 + lnkid[1]; /* 5) RSN IE */ os_memcpy(pos, rsnie, 2 + rsnie[1]); pos += 2 + rsnie[1]; /* 6) Timeout Interval IE */ os_memcpy(pos, timeoutie, 2 + timeoutie[1]); pos += 2 + timeoutie[1]; /* 7) FTIE, with the MIC field of the FTIE set to 0 */ os_memcpy(pos, ftie, 2 + ftie[1]); _ftie = (struct wpa_tdls_ftie *) pos; os_memset(_ftie->mic, 0, TDLS_MIC_LEN); pos += 2 + ftie[1]; wpa_hexdump(MSG_DEBUG, "TDLS: Data for FTIE MIC", buf, pos - buf); wpa_hexdump_key(MSG_DEBUG, "TDLS: KCK", kck, 16); ret = omac1_aes_128(kck, buf, pos - buf, mic); os_free(buf); wpa_hexdump(MSG_DEBUG, "TDLS: FTIE MIC", mic, 16); return ret; } /** * wpa_tdls_key_mic_teardown - Calculate TDLS FTIE MIC for Teardown frame * @kck: TPK-KCK * @trans_seq: Transaction Sequence Number (4 - Teardown) * @rcode: Reason code for Teardown * @dtoken: Dialog Token used for that particular link * @lnkid: Pointer to the beginning of Link Identifier IE * @ftie: Pointer to the beginning of FT IE * @mic: Pointer for writing MIC * * Calculate MIC for TDLS frame. */ static int wpa_tdls_key_mic_teardown(const u8 *kck, u8 trans_seq, u16 rcode, u8 dtoken, const u8 *lnkid, const u8 *ftie, u8 *mic) { u8 *buf, *pos; struct wpa_tdls_ftie *_ftie; int ret; int len; if (lnkid == NULL) return -1; len = 2 + lnkid[1] + sizeof(rcode) + sizeof(dtoken) + sizeof(trans_seq) + 2 + ftie[1]; buf = os_zalloc(len); if (!buf) { wpa_printf(MSG_WARNING, "TDLS: No memory for MIC calculation"); return -1; } pos = buf; /* 1) Link Identifier IE */ os_memcpy(pos, lnkid, 2 + lnkid[1]); pos += 2 + lnkid[1]; /* 2) Reason Code */ WPA_PUT_LE16(pos, rcode); pos += sizeof(rcode); /* 3) Dialog token */ *pos++ = dtoken; /* 4) Transaction Sequence number */ *pos++ = trans_seq; /* 7) FTIE, with the MIC field of the FTIE set to 0 */ os_memcpy(pos, ftie, 2 + ftie[1]); _ftie = (struct wpa_tdls_ftie *) pos; os_memset(_ftie->mic, 0, TDLS_MIC_LEN); pos += 2 + ftie[1]; wpa_hexdump(MSG_DEBUG, "TDLS: Data for FTIE MIC", buf, pos - buf); wpa_hexdump_key(MSG_DEBUG, "TDLS: KCK", kck, 16); ret = omac1_aes_128(kck, buf, pos - buf, mic); os_free(buf); wpa_hexdump(MSG_DEBUG, "TDLS: FTIE MIC", mic, 16); return ret; } static int wpa_supplicant_verify_tdls_mic(u8 trans_seq, struct wpa_tdls_peer *peer, const u8 *lnkid, const u8 *timeoutie, const struct wpa_tdls_ftie *ftie) { u8 mic[16]; if (peer->tpk_set) { wpa_tdls_ftie_mic(peer->tpk.kck, trans_seq, lnkid, peer->rsnie_p, timeoutie, (u8 *) ftie, mic); if (os_memcmp(mic, ftie->mic, 16) != 0) { wpa_printf(MSG_INFO, "TDLS: Invalid MIC in FTIE - " "dropping packet"); wpa_hexdump(MSG_DEBUG, "TDLS: Received MIC", ftie->mic, 16); wpa_hexdump(MSG_DEBUG, "TDLS: Calculated MIC", mic, 16); return -1; } } else { wpa_printf(MSG_WARNING, "TDLS: Could not verify TDLS MIC, " "TPK not set - dropping packet"); return -1; } return 0; } static int wpa_supplicant_verify_tdls_mic_teardown( u8 trans_seq, u16 rcode, u8 dtoken, struct wpa_tdls_peer *peer, const u8 *lnkid, const struct wpa_tdls_ftie *ftie) { u8 mic[16]; if (peer->tpk_set) { wpa_tdls_key_mic_teardown(peer->tpk.kck, trans_seq, rcode, dtoken, lnkid, (u8 *) ftie, mic); if (os_memcmp(mic, ftie->mic, 16) != 0) { wpa_printf(MSG_INFO, "TDLS: Invalid MIC in Teardown - " "dropping packet"); return -1; } } else { wpa_printf(MSG_INFO, "TDLS: Could not verify TDLS Teardown " "MIC, TPK not set - dropping packet"); return -1; } return 0; } static void wpa_tdls_tpk_timeout(void *eloop_ctx, void *timeout_ctx) { struct wpa_sm *sm = eloop_ctx; struct wpa_tdls_peer *peer = timeout_ctx; /* * On TPK lifetime expiration, we have an option of either tearing down * the direct link or trying to re-initiate it. The selection of what * to do is not strictly speaking controlled by our role in the expired * link, but for now, use that to select whether to renew or tear down * the link. */ if (peer->initiator) { wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime expired for " MACSTR " - try to renew", MAC2STR(peer->addr)); wpa_tdls_start(sm, peer->addr); } else { wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime expired for " MACSTR " - tear down", MAC2STR(peer->addr)); wpa_tdls_do_teardown(sm, peer, WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED); } } static void wpa_tdls_peer_free(struct wpa_sm *sm, struct wpa_tdls_peer *peer) { wpa_printf(MSG_DEBUG, "TDLS: Clear state for peer " MACSTR, MAC2STR(peer->addr)); eloop_cancel_timeout(wpa_tdls_tpk_timeout, sm, peer); eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer); peer->reconfig_key = 0; peer->initiator = 0; peer->tpk_in_progress = 0; os_free(peer->sm_tmr.buf); peer->sm_tmr.buf = NULL; os_free(peer->ht_capabilities); peer->ht_capabilities = NULL; os_free(peer->vht_capabilities); peer->vht_capabilities = NULL; os_free(peer->ext_capab); peer->ext_capab = NULL; os_free(peer->supp_channels); peer->supp_channels = NULL; os_free(peer->supp_oper_classes); peer->supp_oper_classes = NULL; peer->rsnie_i_len = peer->rsnie_p_len = 0; peer->cipher = 0; peer->tpk_set = peer->tpk_success = 0; os_memset(&peer->tpk, 0, sizeof(peer->tpk)); os_memset(peer->inonce, 0, WPA_NONCE_LEN); os_memset(peer->rnonce, 0, WPA_NONCE_LEN); } static void wpa_tdls_linkid(struct wpa_sm *sm, struct wpa_tdls_peer *peer, struct wpa_tdls_lnkid *lnkid) { lnkid->ie_type = WLAN_EID_LINK_ID; lnkid->ie_len = 3 * ETH_ALEN; os_memcpy(lnkid->bssid, sm->bssid, ETH_ALEN); if (peer->initiator) { os_memcpy(lnkid->init_sta, sm->own_addr, ETH_ALEN); os_memcpy(lnkid->resp_sta, peer->addr, ETH_ALEN); } else { os_memcpy(lnkid->init_sta, peer->addr, ETH_ALEN); os_memcpy(lnkid->resp_sta, sm->own_addr, ETH_ALEN); } } int wpa_tdls_send_teardown(struct wpa_sm *sm, const u8 *addr, u16 reason_code) { struct wpa_tdls_peer *peer; struct wpa_tdls_ftie *ftie; struct wpa_tdls_lnkid lnkid; u8 dialog_token; u8 *rbuf, *pos; int ielen; if (sm->tdls_disabled || !sm->tdls_supported) return -1; /* Find the node and free from the list */ for (peer = sm->tdls; peer; peer = peer->next) { if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0) break; } if (peer == NULL) { wpa_printf(MSG_INFO, "TDLS: No matching entry found for " "Teardown " MACSTR, MAC2STR(addr)); return 0; } dialog_token = peer->dtoken; wpa_printf(MSG_DEBUG, "TDLS: TDLS Teardown for " MACSTR, MAC2STR(addr)); ielen = 0; if (wpa_tdls_get_privacy(sm) && peer->tpk_set && peer->tpk_success) { /* To add FTIE for Teardown request and compute MIC */ ielen += sizeof(*ftie); #ifdef CONFIG_TDLS_TESTING if (tdls_testing & TDLS_TESTING_LONG_FRAME) ielen += 170; #endif /* CONFIG_TDLS_TESTING */ } rbuf = os_zalloc(ielen + 1); if (rbuf == NULL) return -1; pos = rbuf; if (!wpa_tdls_get_privacy(sm) || !peer->tpk_set || !peer->tpk_success) goto skip_ies; ftie = (struct wpa_tdls_ftie *) pos; ftie->ie_type = WLAN_EID_FAST_BSS_TRANSITION; /* Using the recent nonce which should be for CONFIRM frame */ os_memcpy(ftie->Anonce, peer->rnonce, WPA_NONCE_LEN); os_memcpy(ftie->Snonce, peer->inonce, WPA_NONCE_LEN); ftie->ie_len = sizeof(struct wpa_tdls_ftie) - 2; pos = (u8 *) (ftie + 1); #ifdef CONFIG_TDLS_TESTING if (tdls_testing & TDLS_TESTING_LONG_FRAME) { wpa_printf(MSG_DEBUG, "TDLS: Testing - add extra subelem to " "FTIE"); ftie->ie_len += 170; *pos++ = 255; /* FTIE subelem */ *pos++ = 168; /* FTIE subelem length */ pos += 168; } #endif /* CONFIG_TDLS_TESTING */ wpa_hexdump(MSG_DEBUG, "TDLS: FTIE for TDLS Teardown handshake", (u8 *) ftie, pos - (u8 *) ftie); /* compute MIC before sending */ wpa_tdls_linkid(sm, peer, &lnkid); wpa_tdls_key_mic_teardown(peer->tpk.kck, 4, reason_code, dialog_token, (u8 *) &lnkid, (u8 *) ftie, ftie->mic); skip_ies: /* TODO: register for a Timeout handler, if Teardown is not received at * the other end, then try again another time */ /* request driver to send Teardown using this FTIE */ wpa_tdls_tpk_send(sm, addr, WLAN_TDLS_TEARDOWN, 0, reason_code, rbuf, pos - rbuf); os_free(rbuf); /* clear the Peerkey statemachine */ wpa_tdls_peer_free(sm, peer); return 0; } int wpa_tdls_teardown_link(struct wpa_sm *sm, const u8 *addr, u16 reason_code) { struct wpa_tdls_peer *peer; if (sm->tdls_disabled || !sm->tdls_supported) return -1; for (peer = sm->tdls; peer; peer = peer->next) { if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0) break; } if (peer == NULL) { wpa_printf(MSG_DEBUG, "TDLS: Could not find peer " MACSTR " for link Teardown", MAC2STR(addr)); return -1; } if (!peer->tpk_success) { wpa_printf(MSG_DEBUG, "TDLS: Peer " MACSTR " not connected - cannot Teardown link", MAC2STR(addr)); return -1; } return wpa_tdls_do_teardown(sm, peer, reason_code); } static void wpa_tdls_disable_peer_link(struct wpa_sm *sm, struct wpa_tdls_peer *peer) { wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, peer->addr); wpa_tdls_peer_free(sm, peer); } void wpa_tdls_disable_link(struct wpa_sm *sm, const u8 *addr) { struct wpa_tdls_peer *peer; for (peer = sm->tdls; peer; peer = peer->next) { if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0) break; } if (peer) wpa_tdls_disable_peer_link(sm, peer); } const char * wpa_tdls_get_link_status(struct wpa_sm *sm, const u8 *addr) { struct wpa_tdls_peer *peer; if (sm->tdls_disabled || !sm->tdls_supported) return "disabled"; for (peer = sm->tdls; peer; peer = peer->next) { if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0) break; } if (peer == NULL) return "peer does not exist"; if (!peer->tpk_success) return "peer not connected"; return "connected"; } static int wpa_tdls_recv_teardown(struct wpa_sm *sm, const u8 *src_addr, const u8 *buf, size_t len) { struct wpa_tdls_peer *peer = NULL; struct wpa_tdls_ftie *ftie; struct wpa_tdls_lnkid *lnkid; struct wpa_eapol_ie_parse kde; u16 reason_code; const u8 *pos; int ielen; /* Find the node and free from the list */ for (peer = sm->tdls; peer; peer = peer->next) { if (os_memcmp(peer->addr, src_addr, ETH_ALEN) == 0) break; } if (peer == NULL) { wpa_printf(MSG_INFO, "TDLS: No matching entry found for " "Teardown " MACSTR, MAC2STR(src_addr)); return 0; } pos = buf; pos += 1 /* pkt_type */ + 1 /* Category */ + 1 /* Action */; reason_code = WPA_GET_LE16(pos); pos += 2; wpa_printf(MSG_DEBUG, "TDLS: TDLS Teardown Request from " MACSTR " (reason code %u)", MAC2STR(src_addr), reason_code); ielen = len - (pos - buf); /* start of IE in buf */ if (wpa_supplicant_parse_ies((const u8 *) pos, ielen, &kde) < 0) { wpa_printf(MSG_INFO, "TDLS: Failed to parse IEs in Teardown"); return -1; } if (kde.lnkid == NULL || kde.lnkid_len < 3 * ETH_ALEN) { wpa_printf(MSG_INFO, "TDLS: No Link Identifier IE in TDLS " "Teardown"); return -1; } lnkid = (struct wpa_tdls_lnkid *) kde.lnkid; if (!wpa_tdls_get_privacy(sm) || !peer->tpk_set || !peer->tpk_success) goto skip_ftie; if (kde.ftie == NULL || kde.ftie_len < sizeof(*ftie)) { wpa_printf(MSG_INFO, "TDLS: No FTIE in TDLS Teardown"); return -1; } ftie = (struct wpa_tdls_ftie *) kde.ftie; /* Process MIC check to see if TDLS Teardown is right */ if (wpa_supplicant_verify_tdls_mic_teardown(4, reason_code, peer->dtoken, peer, (u8 *) lnkid, ftie) < 0) { wpa_printf(MSG_DEBUG, "TDLS: MIC failure for TDLS " "Teardown Request from " MACSTR, MAC2STR(src_addr)); return -1; } skip_ftie: /* * Request the driver to disable the direct link and clear associated * keys. */ wpa_tdls_disable_peer_link(sm, peer); return 0; } /** * wpa_tdls_send_error - To send suitable TDLS status response with * appropriate status code mentioning reason for error/failure. * @dst - MAC addr of Peer station * @tdls_action - TDLS frame type for which error code is sent * @status - status code mentioning reason */ static int wpa_tdls_send_error(struct wpa_sm *sm, const u8 *dst, u8 tdls_action, u8 dialog_token, u16 status) { wpa_printf(MSG_DEBUG, "TDLS: Sending error to " MACSTR " (action=%u status=%u)", MAC2STR(dst), tdls_action, status); return wpa_tdls_tpk_send(sm, dst, tdls_action, dialog_token, status, NULL, 0); } static struct wpa_tdls_peer * wpa_tdls_add_peer(struct wpa_sm *sm, const u8 *addr, int *existing) { struct wpa_tdls_peer *peer; if (existing) *existing = 0; for (peer = sm->tdls; peer; peer = peer->next) { if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0) { if (existing) *existing = 1; return peer; /* re-use existing entry */ } } wpa_printf(MSG_INFO, "TDLS: Creating peer entry for " MACSTR, MAC2STR(addr)); peer = os_zalloc(sizeof(*peer)); if (peer == NULL) return NULL; os_memcpy(peer->addr, addr, ETH_ALEN); peer->next = sm->tdls; sm->tdls = peer; return peer; } static int wpa_tdls_send_tpk_m1(struct wpa_sm *sm, struct wpa_tdls_peer *peer) { size_t buf_len; struct wpa_tdls_timeoutie timeoutie; u16 rsn_capab; struct wpa_tdls_ftie *ftie; u8 *rbuf, *pos, *count_pos; u16 count; struct rsn_ie_hdr *hdr; int status; if (!wpa_tdls_get_privacy(sm)) { wpa_printf(MSG_DEBUG, "TDLS: No security used on the link"); peer->rsnie_i_len = 0; goto skip_rsnie; } /* * TPK Handshake Message 1: * FTIE: ANonce=0, SNonce=initiator nonce MIC=0, DataKDs=(RSNIE_I, * Timeout Interval IE)) */ /* Filling RSN IE */ hdr = (struct rsn_ie_hdr *) peer->rsnie_i; hdr->elem_id = WLAN_EID_RSN; WPA_PUT_LE16(hdr->version, RSN_VERSION); pos = (u8 *) (hdr + 1); RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED); pos += RSN_SELECTOR_LEN; count_pos = pos; pos += 2; count = 0; /* * AES-CCMP is the default Encryption preferred for TDLS, so * RSN IE is filled only with CCMP CIPHER * Note: TKIP is not used to encrypt TDLS link. * * Regardless of the cipher used on the AP connection, select CCMP * here. */ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP); pos += RSN_SELECTOR_LEN; count++; WPA_PUT_LE16(count_pos, count); WPA_PUT_LE16(pos, 1); pos += 2; RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_TPK_HANDSHAKE); pos += RSN_SELECTOR_LEN; rsn_capab = WPA_CAPABILITY_PEERKEY_ENABLED; rsn_capab |= RSN_NUM_REPLAY_COUNTERS_16 << 2; #ifdef CONFIG_TDLS_TESTING if (tdls_testing & TDLS_TESTING_ALT_RSN_IE) { wpa_printf(MSG_DEBUG, "TDLS: Use alternative RSN IE for " "testing"); rsn_capab = WPA_CAPABILITY_PEERKEY_ENABLED; } #endif /* CONFIG_TDLS_TESTING */ WPA_PUT_LE16(pos, rsn_capab); pos += 2; #ifdef CONFIG_TDLS_TESTING if (tdls_testing & TDLS_TESTING_ALT_RSN_IE) { /* Number of PMKIDs */ *pos++ = 0x00; *pos++ = 0x00; } #endif /* CONFIG_TDLS_TESTING */ hdr->len = (pos - peer->rsnie_i) - 2; peer->rsnie_i_len = pos - peer->rsnie_i; wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE for TPK handshake", peer->rsnie_i, peer->rsnie_i_len); skip_rsnie: buf_len = 0; if (wpa_tdls_get_privacy(sm)) buf_len += peer->rsnie_i_len + sizeof(struct wpa_tdls_ftie) + sizeof(struct wpa_tdls_timeoutie); #ifdef CONFIG_TDLS_TESTING if (wpa_tdls_get_privacy(sm) && (tdls_testing & TDLS_TESTING_LONG_FRAME)) buf_len += 170; if (tdls_testing & TDLS_TESTING_DIFF_BSSID) buf_len += sizeof(struct wpa_tdls_lnkid); #endif /* CONFIG_TDLS_TESTING */ rbuf = os_zalloc(buf_len + 1); if (rbuf == NULL) { wpa_tdls_peer_free(sm, peer); return -1; } pos = rbuf; if (!wpa_tdls_get_privacy(sm)) goto skip_ies; /* Initiator RSN IE */ pos = wpa_add_ie(pos, peer->rsnie_i, peer->rsnie_i_len); ftie = (struct wpa_tdls_ftie *) pos; ftie->ie_type = WLAN_EID_FAST_BSS_TRANSITION; ftie->ie_len = sizeof(struct wpa_tdls_ftie) - 2; if (os_get_random(peer->inonce, WPA_NONCE_LEN)) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "TDLS: Failed to get random data for initiator Nonce"); os_free(rbuf); wpa_tdls_peer_free(sm, peer); return -1; } wpa_hexdump(MSG_DEBUG, "TDLS: Initiator Nonce for TPK handshake", peer->inonce, WPA_NONCE_LEN); os_memcpy(ftie->Snonce, peer->inonce, WPA_NONCE_LEN); wpa_hexdump(MSG_DEBUG, "TDLS: FTIE for TPK Handshake M1", (u8 *) ftie, sizeof(struct wpa_tdls_ftie)); pos = (u8 *) (ftie + 1); #ifdef CONFIG_TDLS_TESTING if (tdls_testing & TDLS_TESTING_LONG_FRAME) { wpa_printf(MSG_DEBUG, "TDLS: Testing - add extra subelem to " "FTIE"); ftie->ie_len += 170; *pos++ = 255; /* FTIE subelem */ *pos++ = 168; /* FTIE subelem length */ pos += 168; } #endif /* CONFIG_TDLS_TESTING */ /* Lifetime */ peer->lifetime = TPK_LIFETIME; #ifdef CONFIG_TDLS_TESTING if (tdls_testing & TDLS_TESTING_SHORT_LIFETIME) { wpa_printf(MSG_DEBUG, "TDLS: Testing - use short TPK " "lifetime"); peer->lifetime = 301; } if (tdls_testing & TDLS_TESTING_LONG_LIFETIME) { wpa_printf(MSG_DEBUG, "TDLS: Testing - use long TPK " "lifetime"); peer->lifetime = 0xffffffff; } #endif /* CONFIG_TDLS_TESTING */ pos = wpa_add_tdls_timeoutie(pos, (u8 *) &timeoutie, sizeof(timeoutie), peer->lifetime); wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds", peer->lifetime); skip_ies: #ifdef CONFIG_TDLS_TESTING if (tdls_testing & TDLS_TESTING_DIFF_BSSID) { wpa_printf(MSG_DEBUG, "TDLS: Testing - use incorrect BSSID in " "Link Identifier"); struct wpa_tdls_lnkid *l = (struct wpa_tdls_lnkid *) pos; wpa_tdls_linkid(sm, peer, l); l->bssid[5] ^= 0x01; pos += sizeof(*l); } #endif /* CONFIG_TDLS_TESTING */ wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Setup Request / TPK " "Handshake Message 1 (peer " MACSTR ")", MAC2STR(peer->addr)); status = wpa_tdls_tpk_send(sm, peer->addr, WLAN_TDLS_SETUP_REQUEST, 1, 0, rbuf, pos - rbuf); os_free(rbuf); return status; } static int wpa_tdls_send_tpk_m2(struct wpa_sm *sm, const unsigned char *src_addr, u8 dtoken, struct wpa_tdls_lnkid *lnkid, const struct wpa_tdls_peer *peer) { u8 *rbuf, *pos; size_t buf_len; u32 lifetime; struct wpa_tdls_timeoutie timeoutie; struct wpa_tdls_ftie *ftie; int status; buf_len = 0; if (wpa_tdls_get_privacy(sm)) { /* Peer RSN IE, FTIE(Initiator Nonce, Responder Nonce), * Lifetime */ buf_len += peer->rsnie_i_len + sizeof(struct wpa_tdls_ftie) + sizeof(struct wpa_tdls_timeoutie); #ifdef CONFIG_TDLS_TESTING if (tdls_testing & TDLS_TESTING_LONG_FRAME) buf_len += 170; #endif /* CONFIG_TDLS_TESTING */ } rbuf = os_zalloc(buf_len + 1); if (rbuf == NULL) return -1; pos = rbuf; if (!wpa_tdls_get_privacy(sm)) goto skip_ies; /* Peer RSN IE */ pos = wpa_add_ie(pos, peer->rsnie_p, peer->rsnie_p_len); ftie = (struct wpa_tdls_ftie *) pos; ftie->ie_type = WLAN_EID_FAST_BSS_TRANSITION; /* TODO: ftie->mic_control to set 2-RESPONSE */ os_memcpy(ftie->Anonce, peer->rnonce, WPA_NONCE_LEN); os_memcpy(ftie->Snonce, peer->inonce, WPA_NONCE_LEN); ftie->ie_len = sizeof(struct wpa_tdls_ftie) - 2; wpa_hexdump(MSG_DEBUG, "TDLS: FTIE for TPK M2", (u8 *) ftie, sizeof(*ftie)); pos = (u8 *) (ftie + 1); #ifdef CONFIG_TDLS_TESTING if (tdls_testing & TDLS_TESTING_LONG_FRAME) { wpa_printf(MSG_DEBUG, "TDLS: Testing - add extra subelem to " "FTIE"); ftie->ie_len += 170; *pos++ = 255; /* FTIE subelem */ *pos++ = 168; /* FTIE subelem length */ pos += 168; } #endif /* CONFIG_TDLS_TESTING */ /* Lifetime */ lifetime = peer->lifetime; #ifdef CONFIG_TDLS_TESTING if (tdls_testing & TDLS_TESTING_WRONG_LIFETIME_RESP) { wpa_printf(MSG_DEBUG, "TDLS: Testing - use wrong TPK " "lifetime in response"); lifetime++; } #endif /* CONFIG_TDLS_TESTING */ pos = wpa_add_tdls_timeoutie(pos, (u8 *) &timeoutie, sizeof(timeoutie), lifetime); wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds from initiator", lifetime); /* compute MIC before sending */ wpa_tdls_ftie_mic(peer->tpk.kck, 2, (u8 *) lnkid, peer->rsnie_p, (u8 *) &timeoutie, (u8 *) ftie, ftie->mic); skip_ies: status = wpa_tdls_tpk_send(sm, src_addr, WLAN_TDLS_SETUP_RESPONSE, dtoken, 0, rbuf, pos - rbuf); os_free(rbuf); return status; } static int wpa_tdls_send_tpk_m3(struct wpa_sm *sm, const unsigned char *src_addr, u8 dtoken, struct wpa_tdls_lnkid *lnkid, const struct wpa_tdls_peer *peer) { u8 *rbuf, *pos; size_t buf_len; struct wpa_tdls_ftie *ftie; struct wpa_tdls_timeoutie timeoutie; u32 lifetime; int status; buf_len = 0; if (wpa_tdls_get_privacy(sm)) { /* Peer RSN IE, FTIE(Initiator Nonce, Responder Nonce), * Lifetime */ buf_len += peer->rsnie_i_len + sizeof(struct wpa_tdls_ftie) + sizeof(struct wpa_tdls_timeoutie); #ifdef CONFIG_TDLS_TESTING if (tdls_testing & TDLS_TESTING_LONG_FRAME) buf_len += 170; #endif /* CONFIG_TDLS_TESTING */ } rbuf = os_zalloc(buf_len + 1); if (rbuf == NULL) return -1; pos = rbuf; if (!wpa_tdls_get_privacy(sm)) goto skip_ies; /* Peer RSN IE */ pos = wpa_add_ie(pos, peer->rsnie_p, peer->rsnie_p_len); ftie = (struct wpa_tdls_ftie *) pos; ftie->ie_type = WLAN_EID_FAST_BSS_TRANSITION; /*TODO: ftie->mic_control to set 3-CONFIRM */ os_memcpy(ftie->Anonce, peer->rnonce, WPA_NONCE_LEN); os_memcpy(ftie->Snonce, peer->inonce, WPA_NONCE_LEN); ftie->ie_len = sizeof(struct wpa_tdls_ftie) - 2; pos = (u8 *) (ftie + 1); #ifdef CONFIG_TDLS_TESTING if (tdls_testing & TDLS_TESTING_LONG_FRAME) { wpa_printf(MSG_DEBUG, "TDLS: Testing - add extra subelem to " "FTIE"); ftie->ie_len += 170; *pos++ = 255; /* FTIE subelem */ *pos++ = 168; /* FTIE subelem length */ pos += 168; } #endif /* CONFIG_TDLS_TESTING */ /* Lifetime */ lifetime = peer->lifetime; #ifdef CONFIG_TDLS_TESTING if (tdls_testing & TDLS_TESTING_WRONG_LIFETIME_CONF) { wpa_printf(MSG_DEBUG, "TDLS: Testing - use wrong TPK " "lifetime in confirm"); lifetime++; } #endif /* CONFIG_TDLS_TESTING */ pos = wpa_add_tdls_timeoutie(pos, (u8 *) &timeoutie, sizeof(timeoutie), lifetime); wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds", lifetime); /* compute MIC before sending */ wpa_tdls_ftie_mic(peer->tpk.kck, 3, (u8 *) lnkid, peer->rsnie_p, (u8 *) &timeoutie, (u8 *) ftie, ftie->mic); skip_ies: status = wpa_tdls_tpk_send(sm, src_addr, WLAN_TDLS_SETUP_CONFIRM, dtoken, 0, rbuf, pos - rbuf); os_free(rbuf); return status; } static int wpa_tdls_send_discovery_response(struct wpa_sm *sm, struct wpa_tdls_peer *peer, u8 dialog_token) { wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Discovery Response " "(peer " MACSTR ")", MAC2STR(peer->addr)); return wpa_tdls_tpk_send(sm, peer->addr, WLAN_TDLS_DISCOVERY_RESPONSE, dialog_token, 0, NULL, 0); } static int wpa_tdls_process_discovery_request(struct wpa_sm *sm, const u8 *addr, const u8 *buf, size_t len) { struct wpa_eapol_ie_parse kde; const struct wpa_tdls_lnkid *lnkid; struct wpa_tdls_peer *peer; size_t min_req_len = sizeof(struct wpa_tdls_frame) + 1 /* dialog token */ + sizeof(struct wpa_tdls_lnkid); u8 dialog_token; wpa_printf(MSG_DEBUG, "TDLS: Discovery Request from " MACSTR, MAC2STR(addr)); if (len < min_req_len) { wpa_printf(MSG_DEBUG, "TDLS Discovery Request is too short: " "%d", (int) len); return -1; } dialog_token = buf[sizeof(struct wpa_tdls_frame)]; if (wpa_supplicant_parse_ies(buf + sizeof(struct wpa_tdls_frame) + 1, len - (sizeof(struct wpa_tdls_frame) + 1), &kde) < 0) return -1; if (!kde.lnkid) { wpa_printf(MSG_DEBUG, "TDLS: Link ID not found in Discovery " "Request"); return -1; } lnkid = (const struct wpa_tdls_lnkid *) kde.lnkid; if (os_memcmp(sm->bssid, lnkid->bssid, ETH_ALEN) != 0) { wpa_printf(MSG_DEBUG, "TDLS: Discovery Request from different " " BSS " MACSTR, MAC2STR(lnkid->bssid)); return -1; } peer = wpa_tdls_add_peer(sm, addr, NULL); if (peer == NULL) return -1; return wpa_tdls_send_discovery_response(sm, peer, dialog_token); } int wpa_tdls_send_discovery_request(struct wpa_sm *sm, const u8 *addr) { if (sm->tdls_disabled || !sm->tdls_supported) return -1; wpa_printf(MSG_DEBUG, "TDLS: Sending Discovery Request to peer " MACSTR, MAC2STR(addr)); return wpa_tdls_tpk_send(sm, addr, WLAN_TDLS_DISCOVERY_REQUEST, 1, 0, NULL, 0); } static int copy_supp_rates(const struct wpa_eapol_ie_parse *kde, struct wpa_tdls_peer *peer) { if (!kde->supp_rates) { wpa_printf(MSG_DEBUG, "TDLS: No supported rates received"); return -1; } peer->supp_rates_len = merge_byte_arrays( peer->supp_rates, sizeof(peer->supp_rates), kde->supp_rates + 2, kde->supp_rates_len - 2, kde->ext_supp_rates ? kde->ext_supp_rates + 2 : NULL, kde->ext_supp_rates_len - 2); return 0; } static int copy_peer_ht_capab(const struct wpa_eapol_ie_parse *kde, struct wpa_tdls_peer *peer) { if (!kde->ht_capabilities || kde->ht_capabilities_len < sizeof(struct ieee80211_ht_capabilities) ) { wpa_printf(MSG_DEBUG, "TDLS: No supported ht capabilities " "received"); return 0; } if (!peer->ht_capabilities) { peer->ht_capabilities = os_zalloc(sizeof(struct ieee80211_ht_capabilities)); if (peer->ht_capabilities == NULL) return -1; } os_memcpy(peer->ht_capabilities, kde->ht_capabilities, sizeof(struct ieee80211_ht_capabilities)); wpa_hexdump(MSG_DEBUG, "TDLS: Peer HT capabilities", (u8 *) peer->ht_capabilities, sizeof(struct ieee80211_ht_capabilities)); return 0; } static int copy_peer_vht_capab(const struct wpa_eapol_ie_parse *kde, struct wpa_tdls_peer *peer) { if (!kde->vht_capabilities || kde->vht_capabilities_len < sizeof(struct ieee80211_vht_capabilities) ) { wpa_printf(MSG_DEBUG, "TDLS: No supported vht capabilities " "received"); return 0; } if (!peer->vht_capabilities) { peer->vht_capabilities = os_zalloc(sizeof(struct ieee80211_vht_capabilities)); if (peer->vht_capabilities == NULL) return -1; } os_memcpy(peer->vht_capabilities, kde->vht_capabilities, sizeof(struct ieee80211_vht_capabilities)); wpa_hexdump(MSG_DEBUG, "TDLS: Peer VHT capabilities", (u8 *) peer->vht_capabilities, sizeof(struct ieee80211_vht_capabilities)); return 0; } static int copy_peer_ext_capab(const struct wpa_eapol_ie_parse *kde, struct wpa_tdls_peer *peer) { if (!kde->ext_capab) { wpa_printf(MSG_DEBUG, "TDLS: No extended capabilities " "received"); return 0; } if (!peer->ext_capab || peer->ext_capab_len < kde->ext_capab_len - 2) { /* Need to allocate buffer to fit the new information */ os_free(peer->ext_capab); peer->ext_capab = os_zalloc(kde->ext_capab_len - 2); if (peer->ext_capab == NULL) return -1; } peer->ext_capab_len = kde->ext_capab_len - 2; os_memcpy(peer->ext_capab, kde->ext_capab + 2, peer->ext_capab_len); return 0; } static int copy_peer_supp_channels(const struct wpa_eapol_ie_parse *kde, struct wpa_tdls_peer *peer) { if (!kde->supp_channels) { wpa_printf(MSG_DEBUG, "TDLS: No supported channels received"); return 0; } if (!peer->supp_channels || peer->supp_channels_len < kde->supp_channels_len) { os_free(peer->supp_channels); peer->supp_channels = os_zalloc(kde->supp_channels_len); if (peer->supp_channels == NULL) return -1; } peer->supp_channels_len = kde->supp_channels_len; os_memcpy(peer->supp_channels, kde->supp_channels, peer->supp_channels_len); wpa_hexdump(MSG_DEBUG, "TDLS: Peer Supported Channels", (u8 *) peer->supp_channels, peer->supp_channels_len); return 0; } static int copy_peer_supp_oper_classes(const struct wpa_eapol_ie_parse *kde, struct wpa_tdls_peer *peer) { if (!kde->supp_oper_classes) { wpa_printf(MSG_DEBUG, "TDLS: No supported operating classes received"); return 0; } if (!peer->supp_oper_classes || peer->supp_oper_classes_len < kde->supp_oper_classes_len) { os_free(peer->supp_oper_classes); peer->supp_oper_classes = os_zalloc(kde->supp_oper_classes_len); if (peer->supp_oper_classes == NULL) return -1; } peer->supp_oper_classes_len = kde->supp_oper_classes_len; os_memcpy(peer->supp_oper_classes, kde->supp_oper_classes, peer->supp_oper_classes_len); wpa_hexdump(MSG_DEBUG, "TDLS: Peer Supported Operating Classes", (u8 *) peer->supp_oper_classes, peer->supp_oper_classes_len); return 0; } static int wpa_tdls_process_tpk_m1(struct wpa_sm *sm, const u8 *src_addr, const u8 *buf, size_t len) { struct wpa_tdls_peer *peer; struct wpa_eapol_ie_parse kde; struct wpa_ie_data ie; int cipher; const u8 *cpos; struct wpa_tdls_ftie *ftie = NULL; struct wpa_tdls_timeoutie *timeoutie; struct wpa_tdls_lnkid *lnkid; u32 lifetime = 0; #if 0 struct rsn_ie_hdr *hdr; u8 *pos; u16 rsn_capab; u16 rsn_ver; #endif u8 dtoken; u16 ielen; u16 status = WLAN_STATUS_UNSPECIFIED_FAILURE; int tdls_prohibited = sm->tdls_prohibited; int existing_peer = 0; if (len < 3 + 3) return -1; cpos = buf; cpos += 1 /* pkt_type */ + 1 /* Category */ + 1 /* Action */; /* driver had already verified the frame format */ dtoken = *cpos++; /* dialog token */ wpa_printf(MSG_INFO, "TDLS: Dialog Token in TPK M1 %d", dtoken); peer = wpa_tdls_add_peer(sm, src_addr, &existing_peer); if (peer == NULL) goto error; /* If found, use existing entry instead of adding a new one; * how to handle the case where both ends initiate at the * same time? */ if (existing_peer) { if (peer->tpk_success) { wpa_printf(MSG_DEBUG, "TDLS: TDLS Setup Request while " "direct link is enabled - tear down the " "old link first"); wpa_tdls_disable_peer_link(sm, peer); } /* * An entry is already present, so check if we already sent a * TDLS Setup Request. If so, compare MAC addresses and let the * STA with the lower MAC address continue as the initiator. * The other negotiation is terminated. */ if (peer->initiator) { if (os_memcmp(sm->own_addr, src_addr, ETH_ALEN) < 0) { wpa_printf(MSG_DEBUG, "TDLS: Discard request " "from peer with higher address " MACSTR, MAC2STR(src_addr)); return -1; } else { wpa_printf(MSG_DEBUG, "TDLS: Accept request " "from peer with lower address " MACSTR " (terminate previously " "initiated negotiation", MAC2STR(src_addr)); wpa_tdls_disable_peer_link(sm, peer); } } } /* capability information */ peer->capability = WPA_GET_LE16(cpos); cpos += 2; ielen = len - (cpos - buf); /* start of IE in buf */ if (wpa_supplicant_parse_ies(cpos, ielen, &kde) < 0) { wpa_printf(MSG_INFO, "TDLS: Failed to parse IEs in TPK M1"); goto error; } if (kde.lnkid == NULL || kde.lnkid_len < 3 * ETH_ALEN) { wpa_printf(MSG_INFO, "TDLS: No valid Link Identifier IE in " "TPK M1"); goto error; } wpa_hexdump(MSG_DEBUG, "TDLS: Link ID Received from TPK M1", kde.lnkid, kde.lnkid_len); lnkid = (struct wpa_tdls_lnkid *) kde.lnkid; if (os_memcmp(sm->bssid, lnkid->bssid, ETH_ALEN) != 0) { wpa_printf(MSG_INFO, "TDLS: TPK M1 from diff BSS"); status = WLAN_STATUS_NOT_IN_SAME_BSS; goto error; } wpa_printf(MSG_DEBUG, "TDLS: TPK M1 - TPK initiator " MACSTR, MAC2STR(src_addr)); if (copy_supp_rates(&kde, peer) < 0) goto error; if (copy_peer_ht_capab(&kde, peer) < 0) goto error; if (copy_peer_vht_capab(&kde, peer) < 0) goto error; if (copy_peer_ext_capab(&kde, peer) < 0) goto error; if (copy_peer_supp_channels(&kde, peer) < 0) goto error; if (copy_peer_supp_oper_classes(&kde, peer) < 0) goto error; peer->qos_info = kde.qosinfo; peer->aid = kde.aid; #ifdef CONFIG_TDLS_TESTING if (tdls_testing & TDLS_TESTING_CONCURRENT_INIT) { peer = wpa_tdls_add_peer(sm, src_addr, NULL); if (peer == NULL) goto error; wpa_printf(MSG_DEBUG, "TDLS: Testing concurrent initiation of " "TDLS setup - send own request"); peer->initiator = 1; wpa_tdls_send_tpk_m1(sm, peer); } if ((tdls_testing & TDLS_TESTING_IGNORE_AP_PROHIBIT) && tdls_prohibited) { wpa_printf(MSG_DEBUG, "TDLS: Testing - ignore AP prohibition " "on TDLS"); tdls_prohibited = 0; } #endif /* CONFIG_TDLS_TESTING */ if (tdls_prohibited) { wpa_printf(MSG_INFO, "TDLS: TDLS prohibited in this BSS"); status = WLAN_STATUS_REQUEST_DECLINED; goto error; } if (!wpa_tdls_get_privacy(sm)) { if (kde.rsn_ie) { wpa_printf(MSG_INFO, "TDLS: RSN IE in TPK M1 while " "security is disabled"); status = WLAN_STATUS_SECURITY_DISABLED; goto error; } goto skip_rsn; } if (kde.ftie == NULL || kde.ftie_len < sizeof(*ftie) || kde.rsn_ie == NULL) { wpa_printf(MSG_INFO, "TDLS: No FTIE or RSN IE in TPK M1"); status = WLAN_STATUS_INVALID_PARAMETERS; goto error; } if (kde.rsn_ie_len > TDLS_MAX_IE_LEN) { wpa_printf(MSG_INFO, "TDLS: Too long Initiator RSN IE in " "TPK M1"); status = WLAN_STATUS_INVALID_RSNIE; goto error; } if (wpa_parse_wpa_ie_rsn(kde.rsn_ie, kde.rsn_ie_len, &ie) < 0) { wpa_printf(MSG_INFO, "TDLS: Failed to parse RSN IE in TPK M1"); status = WLAN_STATUS_INVALID_RSNIE; goto error; } cipher = ie.pairwise_cipher; if (cipher & WPA_CIPHER_CCMP) { wpa_printf(MSG_DEBUG, "TDLS: Using CCMP for direct link"); cipher = WPA_CIPHER_CCMP; } else { wpa_printf(MSG_INFO, "TDLS: No acceptable cipher in TPK M1"); status = WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID; goto error; } if ((ie.capabilities & (WPA_CAPABILITY_NO_PAIRWISE | WPA_CAPABILITY_PEERKEY_ENABLED)) != WPA_CAPABILITY_PEERKEY_ENABLED) { wpa_printf(MSG_INFO, "TDLS: Invalid RSN Capabilities in " "TPK M1"); status = WLAN_STATUS_INVALID_RSN_IE_CAPAB; goto error; } /* Lifetime */ if (kde.key_lifetime == NULL) { wpa_printf(MSG_INFO, "TDLS: No Key Lifetime IE in TPK M1"); status = WLAN_STATUS_UNACCEPTABLE_LIFETIME; goto error; } timeoutie = (struct wpa_tdls_timeoutie *) kde.key_lifetime; lifetime = WPA_GET_LE32(timeoutie->value); wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds", lifetime); if (lifetime < 300) { wpa_printf(MSG_INFO, "TDLS: Too short TPK lifetime"); status = WLAN_STATUS_UNACCEPTABLE_LIFETIME; goto error; } skip_rsn: #ifdef CONFIG_TDLS_TESTING if (tdls_testing & TDLS_TESTING_CONCURRENT_INIT) { if (os_memcmp(sm->own_addr, peer->addr, ETH_ALEN) < 0) { /* * The request frame from us is going to win, so do not * replace information based on this request frame from * the peer. */ goto skip_rsn_check; } } #endif /* CONFIG_TDLS_TESTING */ peer->initiator = 0; /* Need to check */ peer->dtoken = dtoken; if (!wpa_tdls_get_privacy(sm)) { peer->rsnie_i_len = 0; peer->rsnie_p_len = 0; peer->cipher = WPA_CIPHER_NONE; goto skip_rsn_check; } ftie = (struct wpa_tdls_ftie *) kde.ftie; os_memcpy(peer->rsnie_i, kde.rsn_ie, kde.rsn_ie_len); peer->rsnie_i_len = kde.rsn_ie_len; peer->cipher = cipher; if (os_memcmp(peer->inonce, ftie->Snonce, WPA_NONCE_LEN) != 0) { /* * There is no point in updating the RNonce for every obtained * TPK M1 frame (e.g., retransmission due to timeout) with the * same INonce (SNonce in FTIE). However, if the TPK M1 is * retransmitted with a different INonce, update the RNonce * since this is for a new TDLS session. */ wpa_printf(MSG_DEBUG, "TDLS: New TPK M1 INonce - generate new RNonce"); os_memcpy(peer->inonce, ftie->Snonce, WPA_NONCE_LEN); if (os_get_random(peer->rnonce, WPA_NONCE_LEN)) { wpa_msg(sm->ctx->ctx, MSG_WARNING, "TDLS: Failed to get random data for responder nonce"); wpa_tdls_peer_free(sm, peer); goto error; } } #if 0 /* get version info from RSNIE received from Peer */ hdr = (struct rsn_ie_hdr *) kde.rsn_ie; rsn_ver = WPA_GET_LE16(hdr->version); /* use min(peer's version, out version) */ if (rsn_ver > RSN_VERSION) rsn_ver = RSN_VERSION; hdr = (struct rsn_ie_hdr *) peer->rsnie_p; hdr->elem_id = WLAN_EID_RSN; WPA_PUT_LE16(hdr->version, rsn_ver); pos = (u8 *) (hdr + 1); RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED); pos += RSN_SELECTOR_LEN; /* Include only the selected cipher in pairwise cipher suite */ WPA_PUT_LE16(pos, 1); pos += 2; if (cipher == WPA_CIPHER_CCMP) RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP); pos += RSN_SELECTOR_LEN; WPA_PUT_LE16(pos, 1); pos += 2; RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_TPK_HANDSHAKE); pos += RSN_SELECTOR_LEN; rsn_capab = WPA_CAPABILITY_PEERKEY_ENABLED; rsn_capab |= RSN_NUM_REPLAY_COUNTERS_16 << 2; WPA_PUT_LE16(pos, rsn_capab); pos += 2; hdr->len = (pos - peer->rsnie_p) - 2; peer->rsnie_p_len = pos - peer->rsnie_p; #endif /* temp fix: validation of RSNIE later */ os_memcpy(peer->rsnie_p, peer->rsnie_i, peer->rsnie_i_len); peer->rsnie_p_len = peer->rsnie_i_len; wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE for TPK handshake", peer->rsnie_p, peer->rsnie_p_len); peer->lifetime = lifetime; wpa_tdls_generate_tpk(peer, sm->own_addr, sm->bssid); skip_rsn_check: /* add the peer to the driver as a "setup in progress" peer */ wpa_sm_tdls_peer_addset(sm, peer->addr, 1, 0, 0, NULL, 0, NULL, NULL, 0, NULL, 0, NULL, 0, NULL, 0); peer->tpk_in_progress = 1; wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Setup Response / TPK M2"); if (wpa_tdls_send_tpk_m2(sm, src_addr, dtoken, lnkid, peer) < 0) { wpa_tdls_disable_peer_link(sm, peer); goto error; } return 0; error: wpa_tdls_send_error(sm, src_addr, WLAN_TDLS_SETUP_RESPONSE, dtoken, status); return -1; } static int wpa_tdls_enable_link(struct wpa_sm *sm, struct wpa_tdls_peer *peer) { peer->tpk_success = 1; peer->tpk_in_progress = 0; eloop_cancel_timeout(wpa_tdls_tpk_timeout, sm, peer); if (wpa_tdls_get_privacy(sm)) { u32 lifetime = peer->lifetime; /* * Start the initiator process a bit earlier to avoid race * condition with the responder sending teardown request. */ if (lifetime > 3 && peer->initiator) lifetime -= 3; eloop_register_timeout(lifetime, 0, wpa_tdls_tpk_timeout, sm, peer); #ifdef CONFIG_TDLS_TESTING if (tdls_testing & TDLS_TESTING_NO_TPK_EXPIRATION) { wpa_printf(MSG_DEBUG, "TDLS: Testing - disable TPK " "expiration"); eloop_cancel_timeout(wpa_tdls_tpk_timeout, sm, peer); } #endif /* CONFIG_TDLS_TESTING */ } /* add supported rates, capabilities, and qos_info to the TDLS peer */ if (wpa_sm_tdls_peer_addset(sm, peer->addr, 0, peer->aid, peer->capability, peer->supp_rates, peer->supp_rates_len, peer->ht_capabilities, peer->vht_capabilities, peer->qos_info, peer->ext_capab, peer->ext_capab_len, peer->supp_channels, peer->supp_channels_len, peer->supp_oper_classes, peer->supp_oper_classes_len) < 0) return -1; if (peer->reconfig_key && wpa_tdls_set_key(sm, peer) < 0) { wpa_printf(MSG_INFO, "TDLS: Could not configure key to the " "driver"); return -1; } peer->reconfig_key = 0; return wpa_sm_tdls_oper(sm, TDLS_ENABLE_LINK, peer->addr); } static int wpa_tdls_process_tpk_m2(struct wpa_sm *sm, const u8 *src_addr, const u8 *buf, size_t len) { struct wpa_tdls_peer *peer; struct wpa_eapol_ie_parse kde; struct wpa_ie_data ie; int cipher; struct wpa_tdls_ftie *ftie; struct wpa_tdls_timeoutie *timeoutie; struct wpa_tdls_lnkid *lnkid; u32 lifetime; u8 dtoken; int ielen; u16 status; const u8 *pos; int ret = 0; wpa_printf(MSG_DEBUG, "TDLS: Received TDLS Setup Response / TPK M2 " "(Peer " MACSTR ")", MAC2STR(src_addr)); for (peer = sm->tdls; peer; peer = peer->next) { if (os_memcmp(peer->addr, src_addr, ETH_ALEN) == 0) break; } if (peer == NULL) { wpa_printf(MSG_INFO, "TDLS: No matching peer found for " "TPK M2: " MACSTR, MAC2STR(src_addr)); return -1; } if (!peer->initiator) { /* * This may happen if both devices try to initiate TDLS at the * same time and we accept the TPK M1 from the peer in * wpa_tdls_process_tpk_m1() and clear our previous state. */ wpa_printf(MSG_INFO, "TDLS: We were not the initiator, so " "ignore TPK M2 from " MACSTR, MAC2STR(src_addr)); return -1; } wpa_tdls_tpk_retry_timeout_cancel(sm, peer, WLAN_TDLS_SETUP_REQUEST); if (len < 3 + 2 + 1) { wpa_tdls_disable_peer_link(sm, peer); return -1; } pos = buf; pos += 1 /* pkt_type */ + 1 /* Category */ + 1 /* Action */; status = WPA_GET_LE16(pos); pos += 2 /* status code */; if (status != WLAN_STATUS_SUCCESS) { wpa_printf(MSG_INFO, "TDLS: Status code in TPK M2: %u", status); wpa_tdls_disable_peer_link(sm, peer); return -1; } status = WLAN_STATUS_UNSPECIFIED_FAILURE; /* TODO: need to verify dialog token matches here or in kernel */ dtoken = *pos++; /* dialog token */ wpa_printf(MSG_DEBUG, "TDLS: Dialog Token in TPK M2 %d", dtoken); if (len < 3 + 2 + 1 + 2) { wpa_tdls_disable_peer_link(sm, peer); return -1; } /* capability information */ peer->capability = WPA_GET_LE16(pos); pos += 2; ielen = len - (pos - buf); /* start of IE in buf */ if (wpa_supplicant_parse_ies(pos, ielen, &kde) < 0) { wpa_printf(MSG_INFO, "TDLS: Failed to parse IEs in TPK M2"); goto error; } #ifdef CONFIG_TDLS_TESTING if (tdls_testing & TDLS_TESTING_DECLINE_RESP) { wpa_printf(MSG_DEBUG, "TDLS: Testing - decline response"); status = WLAN_STATUS_REQUEST_DECLINED; goto error; } #endif /* CONFIG_TDLS_TESTING */ if (kde.lnkid == NULL || kde.lnkid_len < 3 * ETH_ALEN) { wpa_printf(MSG_INFO, "TDLS: No valid Link Identifier IE in " "TPK M2"); goto error; } wpa_hexdump(MSG_DEBUG, "TDLS: Link ID Received from TPK M2", kde.lnkid, kde.lnkid_len); lnkid = (struct wpa_tdls_lnkid *) kde.lnkid; if (os_memcmp(sm->bssid, lnkid->bssid, ETH_ALEN) != 0) { wpa_printf(MSG_INFO, "TDLS: TPK M2 from different BSS"); status = WLAN_STATUS_NOT_IN_SAME_BSS; goto error; } if (copy_supp_rates(&kde, peer) < 0) goto error; if (copy_peer_ht_capab(&kde, peer) < 0) goto error; if (copy_peer_vht_capab(&kde, peer) < 0) goto error; if (copy_peer_ext_capab(&kde, peer) < 0) goto error; if (copy_peer_supp_channels(&kde, peer) < 0) goto error; if (copy_peer_supp_oper_classes(&kde, peer) < 0) goto error; peer->qos_info = kde.qosinfo; peer->aid = kde.aid; if (!wpa_tdls_get_privacy(sm)) { peer->rsnie_p_len = 0; peer->cipher = WPA_CIPHER_NONE; goto skip_rsn; } if (kde.ftie == NULL || kde.ftie_len < sizeof(*ftie) || kde.rsn_ie == NULL) { wpa_printf(MSG_INFO, "TDLS: No FTIE or RSN IE in TPK M2"); status = WLAN_STATUS_INVALID_PARAMETERS; goto error; } wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE Received from TPK M2", kde.rsn_ie, kde.rsn_ie_len); /* * FIX: bitwise comparison of RSN IE is not the correct way of * validation this. It can be different, but certain fields must * match. Since we list only a single pairwise cipher in TPK M1, the * memcmp is likely to work in most cases, though. */ if (kde.rsn_ie_len != peer->rsnie_i_len || os_memcmp(peer->rsnie_i, kde.rsn_ie, peer->rsnie_i_len) != 0) { wpa_printf(MSG_INFO, "TDLS: RSN IE in TPK M2 does " "not match with RSN IE used in TPK M1"); wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE Sent in TPK M1", peer->rsnie_i, peer->rsnie_i_len); wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE Received from TPK M2", kde.rsn_ie, kde.rsn_ie_len); status = WLAN_STATUS_INVALID_RSNIE; goto error; } if (wpa_parse_wpa_ie_rsn(kde.rsn_ie, kde.rsn_ie_len, &ie) < 0) { wpa_printf(MSG_INFO, "TDLS: Failed to parse RSN IE in TPK M2"); status = WLAN_STATUS_INVALID_RSNIE; goto error; } cipher = ie.pairwise_cipher; if (cipher == WPA_CIPHER_CCMP) { wpa_printf(MSG_DEBUG, "TDLS: Using CCMP for direct link"); cipher = WPA_CIPHER_CCMP; } else { wpa_printf(MSG_INFO, "TDLS: No acceptable cipher in TPK M2"); status = WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID; goto error; } wpa_hexdump(MSG_DEBUG, "TDLS: FTIE Received from TPK M2", kde.ftie, sizeof(*ftie)); ftie = (struct wpa_tdls_ftie *) kde.ftie; if (!os_memcmp(peer->inonce, ftie->Snonce, WPA_NONCE_LEN) == 0) { wpa_printf(MSG_INFO, "TDLS: FTIE SNonce in TPK M2 does " "not match with FTIE SNonce used in TPK M1"); /* Silently discard the frame */ return -1; } /* Responder Nonce and RSN IE */ os_memcpy(peer->rnonce, ftie->Anonce, WPA_NONCE_LEN); os_memcpy(peer->rsnie_p, kde.rsn_ie, kde.rsn_ie_len); peer->rsnie_p_len = kde.rsn_ie_len; peer->cipher = cipher; /* Lifetime */ if (kde.key_lifetime == NULL) { wpa_printf(MSG_INFO, "TDLS: No Key Lifetime IE in TPK M2"); status = WLAN_STATUS_UNACCEPTABLE_LIFETIME; goto error; } timeoutie = (struct wpa_tdls_timeoutie *) kde.key_lifetime; lifetime = WPA_GET_LE32(timeoutie->value); wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds in TPK M2", lifetime); if (lifetime != peer->lifetime) { wpa_printf(MSG_INFO, "TDLS: Unexpected TPK lifetime %u in " "TPK M2 (expected %u)", lifetime, peer->lifetime); status = WLAN_STATUS_UNACCEPTABLE_LIFETIME; goto error; } wpa_tdls_generate_tpk(peer, sm->own_addr, sm->bssid); /* Process MIC check to see if TPK M2 is right */ if (wpa_supplicant_verify_tdls_mic(2, peer, (u8 *) lnkid, (u8 *) timeoutie, ftie) < 0) { /* Discard the frame */ wpa_tdls_del_key(sm, peer); wpa_tdls_disable_peer_link(sm, peer); return -1; } if (wpa_tdls_set_key(sm, peer) < 0) { /* * Some drivers may not be able to config the key prior to full * STA entry having been configured. */ wpa_printf(MSG_DEBUG, "TDLS: Try to configure TPK again after " "STA entry is complete"); peer->reconfig_key = 1; } skip_rsn: peer->dtoken = dtoken; wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Setup Confirm / " "TPK Handshake Message 3"); if (wpa_tdls_send_tpk_m3(sm, src_addr, dtoken, lnkid, peer) < 0) { wpa_tdls_disable_peer_link(sm, peer); return -1; } if (!peer->tpk_success) { /* * Enable Link only when tpk_success is 0, signifying that this * processing of TPK M2 frame is not because of a retransmission * during TDLS setup handshake. */ ret = wpa_tdls_enable_link(sm, peer); if (ret < 0) { wpa_printf(MSG_DEBUG, "TDLS: Could not enable link"); wpa_tdls_do_teardown( sm, peer, WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED); } } return ret; error: wpa_tdls_send_error(sm, src_addr, WLAN_TDLS_SETUP_CONFIRM, dtoken, status); wpa_tdls_disable_peer_link(sm, peer); return -1; } static int wpa_tdls_process_tpk_m3(struct wpa_sm *sm, const u8 *src_addr, const u8 *buf, size_t len) { struct wpa_tdls_peer *peer; struct wpa_eapol_ie_parse kde; struct wpa_tdls_ftie *ftie; struct wpa_tdls_timeoutie *timeoutie; struct wpa_tdls_lnkid *lnkid; int ielen; u16 status; const u8 *pos; u32 lifetime; int ret = 0; wpa_printf(MSG_DEBUG, "TDLS: Received TDLS Setup Confirm / TPK M3 " "(Peer " MACSTR ")", MAC2STR(src_addr)); for (peer = sm->tdls; peer; peer = peer->next) { if (os_memcmp(peer->addr, src_addr, ETH_ALEN) == 0) break; } if (peer == NULL) { wpa_printf(MSG_INFO, "TDLS: No matching peer found for " "TPK M3: " MACSTR, MAC2STR(src_addr)); return -1; } wpa_tdls_tpk_retry_timeout_cancel(sm, peer, WLAN_TDLS_SETUP_RESPONSE); if (len < 3 + 3) goto error; pos = buf; pos += 1 /* pkt_type */ + 1 /* Category */ + 1 /* Action */; status = WPA_GET_LE16(pos); if (status != 0) { wpa_printf(MSG_INFO, "TDLS: Status code in TPK M3: %u", status); goto error; } pos += 2 /* status code */ + 1 /* dialog token */; ielen = len - (pos - buf); /* start of IE in buf */ if (wpa_supplicant_parse_ies((const u8 *) pos, ielen, &kde) < 0) { wpa_printf(MSG_INFO, "TDLS: Failed to parse KDEs in TPK M3"); goto error; } if (kde.lnkid == NULL || kde.lnkid_len < 3 * ETH_ALEN) { wpa_printf(MSG_INFO, "TDLS: No Link Identifier IE in TPK M3"); goto error; } wpa_hexdump(MSG_DEBUG, "TDLS: Link ID Received from TPK M3", (u8 *) kde.lnkid, kde.lnkid_len); lnkid = (struct wpa_tdls_lnkid *) kde.lnkid; if (os_memcmp(sm->bssid, lnkid->bssid, ETH_ALEN) != 0) { wpa_printf(MSG_INFO, "TDLS: TPK M3 from diff BSS"); goto error; } if (!wpa_tdls_get_privacy(sm)) goto skip_rsn; if (kde.ftie == NULL || kde.ftie_len < sizeof(*ftie)) { wpa_printf(MSG_INFO, "TDLS: No FTIE in TPK M3"); goto error; } wpa_hexdump(MSG_DEBUG, "TDLS: FTIE Received from TPK M3", kde.ftie, sizeof(*ftie)); ftie = (struct wpa_tdls_ftie *) kde.ftie; if (kde.rsn_ie == NULL) { wpa_printf(MSG_INFO, "TDLS: No RSN IE in TPK M3"); goto error; } wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE Received from TPK M3", kde.rsn_ie, kde.rsn_ie_len); if (kde.rsn_ie_len != peer->rsnie_p_len || os_memcmp(kde.rsn_ie, peer->rsnie_p, peer->rsnie_p_len) != 0) { wpa_printf(MSG_INFO, "TDLS: RSN IE in TPK M3 does not match " "with the one sent in TPK M2"); goto error; } if (!os_memcmp(peer->rnonce, ftie->Anonce, WPA_NONCE_LEN) == 0) { wpa_printf(MSG_INFO, "TDLS: FTIE ANonce in TPK M3 does " "not match with FTIE ANonce used in TPK M2"); goto error; } if (!os_memcmp(peer->inonce, ftie->Snonce, WPA_NONCE_LEN) == 0) { wpa_printf(MSG_INFO, "TDLS: FTIE SNonce in TPK M3 does not " "match with FTIE SNonce used in TPK M1"); goto error; } if (kde.key_lifetime == NULL) { wpa_printf(MSG_INFO, "TDLS: No Key Lifetime IE in TPK M3"); goto error; } timeoutie = (struct wpa_tdls_timeoutie *) kde.key_lifetime; wpa_hexdump(MSG_DEBUG, "TDLS: Timeout IE Received from TPK M3", (u8 *) timeoutie, sizeof(*timeoutie)); lifetime = WPA_GET_LE32(timeoutie->value); wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds in TPK M3", lifetime); if (lifetime != peer->lifetime) { wpa_printf(MSG_INFO, "TDLS: Unexpected TPK lifetime %u in " "TPK M3 (expected %u)", lifetime, peer->lifetime); goto error; } if (wpa_supplicant_verify_tdls_mic(3, peer, (u8 *) lnkid, (u8 *) timeoutie, ftie) < 0) { wpa_tdls_del_key(sm, peer); goto error; } if (wpa_tdls_set_key(sm, peer) < 0) { /* * Some drivers may not be able to config the key prior to full * STA entry having been configured. */ wpa_printf(MSG_DEBUG, "TDLS: Try to configure TPK again after " "STA entry is complete"); peer->reconfig_key = 1; } skip_rsn: if (!peer->tpk_success) { /* * Enable Link only when tpk_success is 0, signifying that this * processing of TPK M3 frame is not because of a retransmission * during TDLS setup handshake. */ ret = wpa_tdls_enable_link(sm, peer); if (ret < 0) { wpa_printf(MSG_DEBUG, "TDLS: Could not enable link"); wpa_tdls_do_teardown( sm, peer, WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED); } } return ret; error: wpa_tdls_disable_peer_link(sm, peer); return -1; } static u8 * wpa_add_tdls_timeoutie(u8 *pos, u8 *ie, size_t ie_len, u32 tsecs) { struct wpa_tdls_timeoutie *lifetime = (struct wpa_tdls_timeoutie *) ie; os_memset(lifetime, 0, ie_len); lifetime->ie_type = WLAN_EID_TIMEOUT_INTERVAL; lifetime->ie_len = sizeof(struct wpa_tdls_timeoutie) - 2; lifetime->interval_type = WLAN_TIMEOUT_KEY_LIFETIME; WPA_PUT_LE32(lifetime->value, tsecs); os_memcpy(pos, ie, ie_len); return pos + ie_len; } /** * wpa_tdls_start - Initiate TDLS handshake (send TPK Handshake Message 1) * @sm: Pointer to WPA state machine data from wpa_sm_init() * @peer: MAC address of the peer STA * Returns: 0 on success, or -1 on failure * * Send TPK Handshake Message 1 info to driver to start TDLS * handshake with the peer. */ int wpa_tdls_start(struct wpa_sm *sm, const u8 *addr) { struct wpa_tdls_peer *peer; int tdls_prohibited = sm->tdls_prohibited; if (sm->tdls_disabled || !sm->tdls_supported) return -1; #ifdef CONFIG_TDLS_TESTING if ((tdls_testing & TDLS_TESTING_IGNORE_AP_PROHIBIT) && tdls_prohibited) { wpa_printf(MSG_DEBUG, "TDLS: Testing - ignore AP prohibition " "on TDLS"); tdls_prohibited = 0; } #endif /* CONFIG_TDLS_TESTING */ if (tdls_prohibited) { wpa_printf(MSG_DEBUG, "TDLS: TDLS is prohibited in this BSS - " "reject request to start setup"); return -1; } peer = wpa_tdls_add_peer(sm, addr, NULL); if (peer == NULL) return -1; if (peer->tpk_in_progress) { wpa_printf(MSG_DEBUG, "TDLS: Setup is already in progress with the peer"); return 0; } peer->initiator = 1; /* add the peer to the driver as a "setup in progress" peer */ wpa_sm_tdls_peer_addset(sm, peer->addr, 1, 0, 0, NULL, 0, NULL, NULL, 0, NULL, 0, NULL, 0, NULL, 0); peer->tpk_in_progress = 1; if (wpa_tdls_send_tpk_m1(sm, peer) < 0) { wpa_tdls_disable_peer_link(sm, peer); return -1; } return 0; } void wpa_tdls_remove(struct wpa_sm *sm, const u8 *addr) { struct wpa_tdls_peer *peer; if (sm->tdls_disabled || !sm->tdls_supported) return; for (peer = sm->tdls; peer; peer = peer->next) { if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0) break; } if (peer == NULL || !peer->tpk_success) return; if (sm->tdls_external_setup) { /* * Disable previous link to allow renegotiation to be completed * on AP path. */ wpa_tdls_disable_peer_link(sm, peer); } } /** * wpa_supplicant_rx_tdls - Receive TDLS data frame * * This function is called to receive TDLS (ethertype = 0x890d) data frames. */ static void wpa_supplicant_rx_tdls(void *ctx, const u8 *src_addr, const u8 *buf, size_t len) { struct wpa_sm *sm = ctx; struct wpa_tdls_frame *tf; wpa_hexdump(MSG_DEBUG, "TDLS: Received Data frame encapsulation", buf, len); if (sm->tdls_disabled || !sm->tdls_supported) { wpa_printf(MSG_DEBUG, "TDLS: Discard message - TDLS disabled " "or unsupported by driver"); return; } if (os_memcmp(src_addr, sm->own_addr, ETH_ALEN) == 0) { wpa_printf(MSG_DEBUG, "TDLS: Discard copy of own message"); return; } if (len < sizeof(*tf)) { wpa_printf(MSG_INFO, "TDLS: Drop too short frame"); return; } /* Check to make sure its a valid encapsulated TDLS frame */ tf = (struct wpa_tdls_frame *) buf; if (tf->payloadtype != 2 /* TDLS_RFTYPE */ || tf->category != WLAN_ACTION_TDLS) { wpa_printf(MSG_INFO, "TDLS: Invalid frame - payloadtype=%u " "category=%u action=%u", tf->payloadtype, tf->category, tf->action); return; } switch (tf->action) { case WLAN_TDLS_SETUP_REQUEST: wpa_tdls_process_tpk_m1(sm, src_addr, buf, len); break; case WLAN_TDLS_SETUP_RESPONSE: wpa_tdls_process_tpk_m2(sm, src_addr, buf, len); break; case WLAN_TDLS_SETUP_CONFIRM: wpa_tdls_process_tpk_m3(sm, src_addr, buf, len); break; case WLAN_TDLS_TEARDOWN: wpa_tdls_recv_teardown(sm, src_addr, buf, len); break; case WLAN_TDLS_DISCOVERY_REQUEST: wpa_tdls_process_discovery_request(sm, src_addr, buf, len); break; default: /* Kernel code will process remaining frames */ wpa_printf(MSG_DEBUG, "TDLS: Ignore TDLS frame action code %u", tf->action); break; } } /** * wpa_tdls_init - Initialize driver interface parameters for TDLS * @wpa_s: Pointer to wpa_supplicant data * Returns: 0 on success, -1 on failure * * This function is called to initialize driver interface parameters for TDLS. * wpa_drv_init() must have been called before this function to initialize the * driver interface. */ int wpa_tdls_init(struct wpa_sm *sm) { if (sm == NULL) return -1; sm->l2_tdls = l2_packet_init(sm->bridge_ifname ? sm->bridge_ifname : sm->ifname, sm->own_addr, ETH_P_80211_ENCAP, wpa_supplicant_rx_tdls, sm, 0); if (sm->l2_tdls == NULL) { wpa_printf(MSG_ERROR, "TDLS: Failed to open l2_packet " "connection"); return -1; } /* * Drivers that support TDLS but don't implement the get_capa callback * are assumed to perform everything internally */ if (wpa_sm_tdls_get_capa(sm, &sm->tdls_supported, &sm->tdls_external_setup) < 0) { sm->tdls_supported = 1; sm->tdls_external_setup = 0; } wpa_printf(MSG_DEBUG, "TDLS: TDLS operation%s supported by " "driver", sm->tdls_supported ? "" : " not"); wpa_printf(MSG_DEBUG, "TDLS: Driver uses %s link setup", sm->tdls_external_setup ? "external" : "internal"); return 0; } void wpa_tdls_teardown_peers(struct wpa_sm *sm) { struct wpa_tdls_peer *peer; peer = sm->tdls; wpa_printf(MSG_DEBUG, "TDLS: Tear down peers"); while (peer) { wpa_printf(MSG_DEBUG, "TDLS: Tear down peer " MACSTR, MAC2STR(peer->addr)); if (sm->tdls_external_setup) wpa_tdls_send_teardown(sm, peer->addr, WLAN_REASON_DEAUTH_LEAVING); else wpa_sm_tdls_oper(sm, TDLS_TEARDOWN, peer->addr); peer = peer->next; } } static void wpa_tdls_remove_peers(struct wpa_sm *sm) { struct wpa_tdls_peer *peer, *tmp; peer = sm->tdls; sm->tdls = NULL; while (peer) { int res; tmp = peer->next; res = wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, peer->addr); wpa_printf(MSG_DEBUG, "TDLS: Remove peer " MACSTR " (res=%d)", MAC2STR(peer->addr), res); wpa_tdls_peer_free(sm, peer); os_free(peer); peer = tmp; } } /** * wpa_tdls_deinit - Deinitialize driver interface parameters for TDLS * * This function is called to recover driver interface parameters for TDLS * and frees resources allocated for it. */ void wpa_tdls_deinit(struct wpa_sm *sm) { if (sm == NULL) return; if (sm->l2_tdls) l2_packet_deinit(sm->l2_tdls); sm->l2_tdls = NULL; wpa_tdls_remove_peers(sm); } void wpa_tdls_assoc(struct wpa_sm *sm) { wpa_printf(MSG_DEBUG, "TDLS: Remove peers on association"); wpa_tdls_remove_peers(sm); } void wpa_tdls_disassoc(struct wpa_sm *sm) { wpa_printf(MSG_DEBUG, "TDLS: Remove peers on disassociation"); wpa_tdls_remove_peers(sm); } static int wpa_tdls_prohibited(const u8 *ies, size_t len) { struct wpa_eapol_ie_parse elems; if (ies == NULL) return 0; if (wpa_supplicant_parse_ies(ies, len, &elems) < 0) return 0; if (elems.ext_capab == NULL || elems.ext_capab_len < 2 + 5) return 0; /* bit 38 - TDLS Prohibited */ return !!(elems.ext_capab[2 + 4] & 0x40); } void wpa_tdls_ap_ies(struct wpa_sm *sm, const u8 *ies, size_t len) { sm->tdls_prohibited = wpa_tdls_prohibited(ies, len); wpa_printf(MSG_DEBUG, "TDLS: TDLS is %s in the target BSS", sm->tdls_prohibited ? "prohibited" : "allowed"); } void wpa_tdls_assoc_resp_ies(struct wpa_sm *sm, const u8 *ies, size_t len) { if (!sm->tdls_prohibited && wpa_tdls_prohibited(ies, len)) { wpa_printf(MSG_DEBUG, "TDLS: TDLS prohibited based on " "(Re)Association Response IEs"); sm->tdls_prohibited = 1; } } void wpa_tdls_enable(struct wpa_sm *sm, int enabled) { wpa_printf(MSG_DEBUG, "TDLS: %s", enabled ? "enabled" : "disabled"); sm->tdls_disabled = !enabled; } int wpa_tdls_is_external_setup(struct wpa_sm *sm) { return sm->tdls_external_setup; } wpa-2.1/src/rsn_supp/wpa.c000066400000000000000000002263441227414666700155700ustar00rootroot00000000000000/* * WPA Supplicant - WPA state machine and EAPOL-Key processing * Copyright (c) 2003-2012, 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/aes_wrap.h" #include "crypto/crypto.h" #include "crypto/random.h" #include "common/ieee802_11_defs.h" #include "eapol_supp/eapol_supp_sm.h" #include "wpa.h" #include "eloop.h" #include "preauth.h" #include "pmksa_cache.h" #include "wpa_i.h" #include "wpa_ie.h" #include "peerkey.h" /** * wpa_eapol_key_send - Send WPA/RSN EAPOL-Key message * @sm: Pointer to WPA state machine data from wpa_sm_init() * @kck: Key Confirmation Key (KCK, part of PTK) * @ver: Version field from Key Info * @dest: Destination address for the frame * @proto: Ethertype (usually ETH_P_EAPOL) * @msg: EAPOL-Key message * @msg_len: Length of message * @key_mic: Pointer to the buffer to which the EAPOL-Key MIC is written */ void wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck, int ver, const u8 *dest, u16 proto, u8 *msg, size_t msg_len, u8 *key_mic) { if (is_zero_ether_addr(dest) && is_zero_ether_addr(sm->bssid)) { /* * Association event was not yet received; try to fetch * BSSID from the driver. */ if (wpa_sm_get_bssid(sm, sm->bssid) < 0) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Failed to read BSSID for " "EAPOL-Key destination address"); } else { dest = sm->bssid; wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Use BSSID (" MACSTR ") as the destination for EAPOL-Key", MAC2STR(dest)); } } if (key_mic && wpa_eapol_key_mic(kck, ver, msg, msg_len, key_mic)) { wpa_msg(sm->ctx->msg_ctx, MSG_ERROR, "WPA: Failed to generate EAPOL-Key " "version %d MIC", ver); goto out; } wpa_hexdump_key(MSG_DEBUG, "WPA: KCK", kck, 16); wpa_hexdump(MSG_DEBUG, "WPA: Derived Key MIC", key_mic, 16); wpa_hexdump(MSG_MSGDUMP, "WPA: TX EAPOL-Key", msg, msg_len); wpa_sm_ether_send(sm, dest, proto, msg, msg_len); eapol_sm_notify_tx_eapol_key(sm->eapol); out: os_free(msg); } /** * wpa_sm_key_request - Send EAPOL-Key Request * @sm: Pointer to WPA state machine data from wpa_sm_init() * @error: Indicate whether this is an Michael MIC error report * @pairwise: 1 = error report for pairwise packet, 0 = for group packet * * Send an EAPOL-Key Request to the current authenticator. This function is * used to request rekeying and it is usually called when a local Michael MIC * failure is detected. */ void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise) { size_t rlen; struct wpa_eapol_key *reply; int key_info, ver; u8 bssid[ETH_ALEN], *rbuf; if (wpa_key_mgmt_ft(sm->key_mgmt) || wpa_key_mgmt_sha256(sm->key_mgmt)) ver = WPA_KEY_INFO_TYPE_AES_128_CMAC; else if (sm->pairwise_cipher != WPA_CIPHER_TKIP) ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES; else ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4; if (wpa_sm_get_bssid(sm, bssid) < 0) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "Failed to read BSSID for EAPOL-Key request"); return; } rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL, sizeof(*reply), &rlen, (void *) &reply); if (rbuf == NULL) return; reply->type = sm->proto == WPA_PROTO_RSN ? EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA; key_info = WPA_KEY_INFO_REQUEST | ver; if (sm->ptk_set) key_info |= WPA_KEY_INFO_MIC; if (error) key_info |= WPA_KEY_INFO_ERROR; if (pairwise) key_info |= WPA_KEY_INFO_KEY_TYPE; WPA_PUT_BE16(reply->key_info, key_info); WPA_PUT_BE16(reply->key_length, 0); os_memcpy(reply->replay_counter, sm->request_counter, WPA_REPLAY_COUNTER_LEN); inc_byte_array(sm->request_counter, WPA_REPLAY_COUNTER_LEN); WPA_PUT_BE16(reply->key_data_length, 0); wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: Sending EAPOL-Key Request (error=%d " "pairwise=%d ptk_set=%d len=%lu)", error, pairwise, sm->ptk_set, (unsigned long) rlen); wpa_eapol_key_send(sm, sm->ptk.kck, ver, bssid, ETH_P_EAPOL, rbuf, rlen, key_info & WPA_KEY_INFO_MIC ? reply->key_mic : NULL); } static int wpa_supplicant_get_pmk(struct wpa_sm *sm, const unsigned char *src_addr, const u8 *pmkid) { int abort_cached = 0; if (pmkid && !sm->cur_pmksa) { /* When using drivers that generate RSN IE, wpa_supplicant may * not have enough time to get the association information * event before receiving this 1/4 message, so try to find a * matching PMKSA cache entry here. */ sm->cur_pmksa = pmksa_cache_get(sm->pmksa, src_addr, pmkid, NULL); if (sm->cur_pmksa) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: found matching PMKID from PMKSA cache"); } else { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: no matching PMKID found"); abort_cached = 1; } } if (pmkid && sm->cur_pmksa && os_memcmp(pmkid, sm->cur_pmksa->pmkid, PMKID_LEN) == 0) { wpa_hexdump(MSG_DEBUG, "RSN: matched PMKID", pmkid, PMKID_LEN); wpa_sm_set_pmk_from_pmksa(sm); wpa_hexdump_key(MSG_DEBUG, "RSN: PMK from PMKSA cache", sm->pmk, sm->pmk_len); eapol_sm_notify_cached(sm->eapol); #ifdef CONFIG_IEEE80211R sm->xxkey_len = 0; #endif /* CONFIG_IEEE80211R */ } else if (wpa_key_mgmt_wpa_ieee8021x(sm->key_mgmt) && sm->eapol) { int res, pmk_len; pmk_len = PMK_LEN; res = eapol_sm_get_key(sm->eapol, sm->pmk, PMK_LEN); if (res) { /* * EAP-LEAP is an exception from other EAP methods: it * uses only 16-byte PMK. */ res = eapol_sm_get_key(sm->eapol, sm->pmk, 16); pmk_len = 16; } else { #ifdef CONFIG_IEEE80211R u8 buf[2 * PMK_LEN]; if (eapol_sm_get_key(sm->eapol, buf, 2 * PMK_LEN) == 0) { os_memcpy(sm->xxkey, buf + PMK_LEN, PMK_LEN); sm->xxkey_len = PMK_LEN; os_memset(buf, 0, sizeof(buf)); } #endif /* CONFIG_IEEE80211R */ } if (res == 0) { struct rsn_pmksa_cache_entry *sa = NULL; wpa_hexdump_key(MSG_DEBUG, "WPA: PMK from EAPOL state " "machines", sm->pmk, pmk_len); sm->pmk_len = pmk_len; if (sm->proto == WPA_PROTO_RSN && !wpa_key_mgmt_ft(sm->key_mgmt)) { sa = pmksa_cache_add(sm->pmksa, sm->pmk, pmk_len, src_addr, sm->own_addr, sm->network_ctx, sm->key_mgmt); } if (!sm->cur_pmksa && pmkid && pmksa_cache_get(sm->pmksa, src_addr, pmkid, NULL)) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: the new PMK matches with the " "PMKID"); abort_cached = 0; } if (!sm->cur_pmksa) sm->cur_pmksa = sa; } else { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Failed to get master session key from " "EAPOL state machines - key handshake " "aborted"); if (sm->cur_pmksa) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: Cancelled PMKSA caching " "attempt"); sm->cur_pmksa = NULL; abort_cached = 1; } else if (!abort_cached) { return -1; } } } if (abort_cached && wpa_key_mgmt_wpa_ieee8021x(sm->key_mgmt) && !wpa_key_mgmt_ft(sm->key_mgmt)) { /* Send EAPOL-Start to trigger full EAP authentication. */ u8 *buf; size_t buflen; wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: no PMKSA entry found - trigger " "full EAP authentication"); buf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_START, NULL, 0, &buflen, NULL); if (buf) { wpa_sm_ether_send(sm, sm->bssid, ETH_P_EAPOL, buf, buflen); os_free(buf); return -2; } return -1; } return 0; } /** * wpa_supplicant_send_2_of_4 - Send message 2 of WPA/RSN 4-Way Handshake * @sm: Pointer to WPA state machine data from wpa_sm_init() * @dst: Destination address for the frame * @key: Pointer to the EAPOL-Key frame header * @ver: Version bits from EAPOL-Key Key Info * @nonce: Nonce value for the EAPOL-Key frame * @wpa_ie: WPA/RSN IE * @wpa_ie_len: Length of the WPA/RSN IE * @ptk: PTK to use for keyed hash and encryption * Returns: 0 on success, -1 on failure */ int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst, const struct wpa_eapol_key *key, int ver, const u8 *nonce, const u8 *wpa_ie, size_t wpa_ie_len, struct wpa_ptk *ptk) { size_t rlen; struct wpa_eapol_key *reply; u8 *rbuf; u8 *rsn_ie_buf = NULL; if (wpa_ie == NULL) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: No wpa_ie set - " "cannot generate msg 2/4"); return -1; } #ifdef CONFIG_IEEE80211R if (wpa_key_mgmt_ft(sm->key_mgmt)) { int res; /* * Add PMKR1Name into RSN IE (PMKID-List) and add MDIE and * FTIE from (Re)Association Response. */ rsn_ie_buf = os_malloc(wpa_ie_len + 2 + 2 + PMKID_LEN + sm->assoc_resp_ies_len); if (rsn_ie_buf == NULL) return -1; os_memcpy(rsn_ie_buf, wpa_ie, wpa_ie_len); res = wpa_insert_pmkid(rsn_ie_buf, wpa_ie_len, sm->pmk_r1_name); if (res < 0) { os_free(rsn_ie_buf); return -1; } wpa_ie_len += res; if (sm->assoc_resp_ies) { os_memcpy(rsn_ie_buf + wpa_ie_len, sm->assoc_resp_ies, sm->assoc_resp_ies_len); wpa_ie_len += sm->assoc_resp_ies_len; } wpa_ie = rsn_ie_buf; } #endif /* CONFIG_IEEE80211R */ wpa_hexdump(MSG_DEBUG, "WPA: WPA IE for msg 2/4", wpa_ie, wpa_ie_len); rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL, sizeof(*reply) + wpa_ie_len, &rlen, (void *) &reply); if (rbuf == NULL) { os_free(rsn_ie_buf); return -1; } reply->type = sm->proto == WPA_PROTO_RSN ? EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA; WPA_PUT_BE16(reply->key_info, ver | WPA_KEY_INFO_KEY_TYPE | WPA_KEY_INFO_MIC); if (sm->proto == WPA_PROTO_RSN) WPA_PUT_BE16(reply->key_length, 0); else os_memcpy(reply->key_length, key->key_length, 2); os_memcpy(reply->replay_counter, key->replay_counter, WPA_REPLAY_COUNTER_LEN); wpa_hexdump(MSG_DEBUG, "WPA: Replay Counter", reply->replay_counter, WPA_REPLAY_COUNTER_LEN); WPA_PUT_BE16(reply->key_data_length, wpa_ie_len); os_memcpy(reply + 1, wpa_ie, wpa_ie_len); os_free(rsn_ie_buf); os_memcpy(reply->key_nonce, nonce, WPA_NONCE_LEN); wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 2/4"); wpa_eapol_key_send(sm, ptk->kck, ver, dst, ETH_P_EAPOL, rbuf, rlen, reply->key_mic); return 0; } static int wpa_derive_ptk(struct wpa_sm *sm, const unsigned char *src_addr, const struct wpa_eapol_key *key, struct wpa_ptk *ptk) { size_t ptk_len = sm->pairwise_cipher != WPA_CIPHER_TKIP ? 48 : 64; #ifdef CONFIG_IEEE80211R if (wpa_key_mgmt_ft(sm->key_mgmt)) return wpa_derive_ptk_ft(sm, src_addr, key, ptk, ptk_len); #endif /* CONFIG_IEEE80211R */ wpa_pmk_to_ptk(sm->pmk, sm->pmk_len, "Pairwise key expansion", sm->own_addr, sm->bssid, sm->snonce, key->key_nonce, (u8 *) ptk, ptk_len, wpa_key_mgmt_sha256(sm->key_mgmt)); return 0; } static void wpa_supplicant_process_1_of_4(struct wpa_sm *sm, const unsigned char *src_addr, const struct wpa_eapol_key *key, u16 ver) { struct wpa_eapol_ie_parse ie; struct wpa_ptk *ptk; u8 buf[8]; int res; u8 *kde, *kde_buf = NULL; size_t kde_len; if (wpa_sm_get_network_ctx(sm) == NULL) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: No SSID info " "found (msg 1 of 4)"); return; } wpa_sm_set_state(sm, WPA_4WAY_HANDSHAKE); wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: RX message 1 of 4-Way " "Handshake from " MACSTR " (ver=%d)", MAC2STR(src_addr), ver); os_memset(&ie, 0, sizeof(ie)); if (sm->proto == WPA_PROTO_RSN) { /* RSN: msg 1/4 should contain PMKID for the selected PMK */ const u8 *_buf = (const u8 *) (key + 1); size_t len = WPA_GET_BE16(key->key_data_length); wpa_hexdump(MSG_DEBUG, "RSN: msg 1/4 key data", _buf, len); if (wpa_supplicant_parse_ies(_buf, len, &ie) < 0) goto failed; if (ie.pmkid) { wpa_hexdump(MSG_DEBUG, "RSN: PMKID from " "Authenticator", ie.pmkid, PMKID_LEN); } } res = wpa_supplicant_get_pmk(sm, src_addr, ie.pmkid); if (res == -2) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: Do not reply to " "msg 1/4 - requesting full EAP authentication"); return; } if (res) goto failed; if (sm->renew_snonce) { if (random_get_bytes(sm->snonce, WPA_NONCE_LEN)) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Failed to get random data for SNonce"); goto failed; } sm->renew_snonce = 0; wpa_hexdump(MSG_DEBUG, "WPA: Renewed SNonce", sm->snonce, WPA_NONCE_LEN); } /* Calculate PTK which will be stored as a temporary PTK until it has * been verified when processing message 3/4. */ ptk = &sm->tptk; wpa_derive_ptk(sm, src_addr, key, ptk); /* Supplicant: swap tx/rx Mic keys */ os_memcpy(buf, ptk->u.auth.tx_mic_key, 8); os_memcpy(ptk->u.auth.tx_mic_key, ptk->u.auth.rx_mic_key, 8); os_memcpy(ptk->u.auth.rx_mic_key, buf, 8); sm->tptk_set = 1; kde = sm->assoc_wpa_ie; kde_len = sm->assoc_wpa_ie_len; #ifdef CONFIG_P2P if (sm->p2p) { kde_buf = os_malloc(kde_len + 2 + RSN_SELECTOR_LEN + 1); if (kde_buf) { u8 *pos; wpa_printf(MSG_DEBUG, "P2P: Add IP Address Request KDE " "into EAPOL-Key 2/4"); os_memcpy(kde_buf, kde, kde_len); kde = kde_buf; pos = kde + kde_len; *pos++ = WLAN_EID_VENDOR_SPECIFIC; *pos++ = RSN_SELECTOR_LEN + 1; RSN_SELECTOR_PUT(pos, WFA_KEY_DATA_IP_ADDR_REQ); pos += RSN_SELECTOR_LEN; *pos++ = 0x01; kde_len = pos - kde; } } #endif /* CONFIG_P2P */ if (wpa_supplicant_send_2_of_4(sm, sm->bssid, key, ver, sm->snonce, kde, kde_len, ptk)) goto failed; os_free(kde_buf); os_memcpy(sm->anonce, key->key_nonce, WPA_NONCE_LEN); return; failed: os_free(kde_buf); wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED); } static void wpa_sm_start_preauth(void *eloop_ctx, void *timeout_ctx) { struct wpa_sm *sm = eloop_ctx; rsn_preauth_candidate_process(sm); } static void wpa_supplicant_key_neg_complete(struct wpa_sm *sm, const u8 *addr, int secure) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: Key negotiation completed with " MACSTR " [PTK=%s GTK=%s]", MAC2STR(addr), wpa_cipher_txt(sm->pairwise_cipher), wpa_cipher_txt(sm->group_cipher)); wpa_sm_cancel_auth_timeout(sm); wpa_sm_set_state(sm, WPA_COMPLETED); if (secure) { wpa_sm_mlme_setprotection( sm, addr, MLME_SETPROTECTION_PROTECT_TYPE_RX_TX, MLME_SETPROTECTION_KEY_TYPE_PAIRWISE); eapol_sm_notify_portValid(sm->eapol, TRUE); if (wpa_key_mgmt_wpa_psk(sm->key_mgmt)) eapol_sm_notify_eap_success(sm->eapol, TRUE); /* * Start preauthentication after a short wait to avoid a * possible race condition between the data receive and key * configuration after the 4-Way Handshake. This increases the * likelihood of the first preauth EAPOL-Start frame getting to * the target AP. */ eloop_register_timeout(1, 0, wpa_sm_start_preauth, sm, NULL); } if (sm->cur_pmksa && sm->cur_pmksa->opportunistic) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: Authenticator accepted " "opportunistic PMKSA entry - marking it valid"); sm->cur_pmksa->opportunistic = 0; } #ifdef CONFIG_IEEE80211R if (wpa_key_mgmt_ft(sm->key_mgmt)) { /* Prepare for the next transition */ wpa_ft_prepare_auth_request(sm, NULL); } #endif /* CONFIG_IEEE80211R */ } static void wpa_sm_rekey_ptk(void *eloop_ctx, void *timeout_ctx) { struct wpa_sm *sm = eloop_ctx; wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Request PTK rekeying"); wpa_sm_key_request(sm, 0, 1); } static int wpa_supplicant_install_ptk(struct wpa_sm *sm, const struct wpa_eapol_key *key) { int keylen, rsclen; enum wpa_alg alg; const u8 *key_rsc; u8 null_rsc[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Installing PTK to the driver"); if (sm->pairwise_cipher == WPA_CIPHER_NONE) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Pairwise Cipher " "Suite: NONE - do not use pairwise keys"); return 0; } if (!wpa_cipher_valid_pairwise(sm->pairwise_cipher)) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Unsupported pairwise cipher %d", sm->pairwise_cipher); return -1; } alg = wpa_cipher_to_alg(sm->pairwise_cipher); keylen = wpa_cipher_key_len(sm->pairwise_cipher); rsclen = wpa_cipher_rsc_len(sm->pairwise_cipher); if (sm->proto == WPA_PROTO_RSN) { key_rsc = null_rsc; } else { key_rsc = key->key_rsc; wpa_hexdump(MSG_DEBUG, "WPA: RSC", key_rsc, rsclen); } if (wpa_sm_set_key(sm, alg, sm->bssid, 0, 1, key_rsc, rsclen, (u8 *) sm->ptk.tk1, keylen) < 0) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Failed to set PTK to the " "driver (alg=%d keylen=%d bssid=" MACSTR ")", alg, keylen, MAC2STR(sm->bssid)); return -1; } if (sm->wpa_ptk_rekey) { eloop_cancel_timeout(wpa_sm_rekey_ptk, sm, NULL); eloop_register_timeout(sm->wpa_ptk_rekey, 0, wpa_sm_rekey_ptk, sm, NULL); } return 0; } static int wpa_supplicant_check_group_cipher(struct wpa_sm *sm, int group_cipher, int keylen, int maxkeylen, int *key_rsc_len, enum wpa_alg *alg) { int klen; *alg = wpa_cipher_to_alg(group_cipher); if (*alg == WPA_ALG_NONE) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Unsupported Group Cipher %d", group_cipher); return -1; } *key_rsc_len = wpa_cipher_rsc_len(group_cipher); klen = wpa_cipher_key_len(group_cipher); if (keylen != klen || maxkeylen < klen) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Unsupported %s Group Cipher key length %d (%d)", wpa_cipher_txt(group_cipher), keylen, maxkeylen); return -1; } return 0; } struct wpa_gtk_data { enum wpa_alg alg; int tx, key_rsc_len, keyidx; u8 gtk[32]; int gtk_len; }; static int wpa_supplicant_install_gtk(struct wpa_sm *sm, const struct wpa_gtk_data *gd, const u8 *key_rsc) { const u8 *_gtk = gd->gtk; u8 gtk_buf[32]; wpa_hexdump_key(MSG_DEBUG, "WPA: Group Key", gd->gtk, gd->gtk_len); wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Installing GTK to the driver (keyidx=%d tx=%d len=%d)", gd->keyidx, gd->tx, gd->gtk_len); wpa_hexdump(MSG_DEBUG, "WPA: RSC", key_rsc, gd->key_rsc_len); if (sm->group_cipher == WPA_CIPHER_TKIP) { /* Swap Tx/Rx keys for Michael MIC */ os_memcpy(gtk_buf, gd->gtk, 16); os_memcpy(gtk_buf + 16, gd->gtk + 24, 8); os_memcpy(gtk_buf + 24, gd->gtk + 16, 8); _gtk = gtk_buf; } if (sm->pairwise_cipher == WPA_CIPHER_NONE) { if (wpa_sm_set_key(sm, gd->alg, NULL, gd->keyidx, 1, key_rsc, gd->key_rsc_len, _gtk, gd->gtk_len) < 0) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Failed to set GTK to the driver " "(Group only)"); return -1; } } else if (wpa_sm_set_key(sm, gd->alg, broadcast_ether_addr, gd->keyidx, gd->tx, key_rsc, gd->key_rsc_len, _gtk, gd->gtk_len) < 0) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Failed to set GTK to " "the driver (alg=%d keylen=%d keyidx=%d)", gd->alg, gd->gtk_len, gd->keyidx); return -1; } return 0; } static int wpa_supplicant_gtk_tx_bit_workaround(const struct wpa_sm *sm, int tx) { if (tx && sm->pairwise_cipher != WPA_CIPHER_NONE) { /* Ignore Tx bit for GTK if a pairwise key is used. One AP * seemed to set this bit (incorrectly, since Tx is only when * doing Group Key only APs) and without this workaround, the * data connection does not work because wpa_supplicant * configured non-zero keyidx to be used for unicast. */ wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: Tx bit set for GTK, but pairwise " "keys are used - ignore Tx bit"); return 0; } return tx; } static int wpa_supplicant_pairwise_gtk(struct wpa_sm *sm, const struct wpa_eapol_key *key, const u8 *gtk, size_t gtk_len, int key_info) { struct wpa_gtk_data gd; /* * IEEE Std 802.11i-2004 - 8.5.2 EAPOL-Key frames - Figure 43x * GTK KDE format: * KeyID[bits 0-1], Tx [bit 2], Reserved [bits 3-7] * Reserved [bits 0-7] * GTK */ os_memset(&gd, 0, sizeof(gd)); wpa_hexdump_key(MSG_DEBUG, "RSN: received GTK in pairwise handshake", gtk, gtk_len); if (gtk_len < 2 || gtk_len - 2 > sizeof(gd.gtk)) return -1; gd.keyidx = gtk[0] & 0x3; gd.tx = wpa_supplicant_gtk_tx_bit_workaround(sm, !!(gtk[0] & BIT(2))); gtk += 2; gtk_len -= 2; os_memcpy(gd.gtk, gtk, gtk_len); gd.gtk_len = gtk_len; if (sm->group_cipher != WPA_CIPHER_GTK_NOT_USED && (wpa_supplicant_check_group_cipher(sm, sm->group_cipher, gtk_len, gtk_len, &gd.key_rsc_len, &gd.alg) || wpa_supplicant_install_gtk(sm, &gd, key->key_rsc))) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: Failed to install GTK"); return -1; } wpa_supplicant_key_neg_complete(sm, sm->bssid, key_info & WPA_KEY_INFO_SECURE); return 0; } static int ieee80211w_set_keys(struct wpa_sm *sm, struct wpa_eapol_ie_parse *ie) { #ifdef CONFIG_IEEE80211W if (sm->mgmt_group_cipher != WPA_CIPHER_AES_128_CMAC) return 0; if (ie->igtk) { const struct wpa_igtk_kde *igtk; u16 keyidx; if (ie->igtk_len != sizeof(*igtk)) return -1; igtk = (const struct wpa_igtk_kde *) ie->igtk; keyidx = WPA_GET_LE16(igtk->keyid); wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: IGTK keyid %d " "pn %02x%02x%02x%02x%02x%02x", keyidx, MAC2STR(igtk->pn)); wpa_hexdump_key(MSG_DEBUG, "WPA: IGTK", igtk->igtk, WPA_IGTK_LEN); if (keyidx > 4095) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Invalid IGTK KeyID %d", keyidx); return -1; } if (wpa_sm_set_key(sm, WPA_ALG_IGTK, broadcast_ether_addr, keyidx, 0, igtk->pn, sizeof(igtk->pn), igtk->igtk, WPA_IGTK_LEN) < 0) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Failed to configure IGTK to the driver"); return -1; } } return 0; #else /* CONFIG_IEEE80211W */ return 0; #endif /* CONFIG_IEEE80211W */ } static void wpa_report_ie_mismatch(struct wpa_sm *sm, const char *reason, const u8 *src_addr, const u8 *wpa_ie, size_t wpa_ie_len, const u8 *rsn_ie, size_t rsn_ie_len) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: %s (src=" MACSTR ")", reason, MAC2STR(src_addr)); if (sm->ap_wpa_ie) { wpa_hexdump(MSG_INFO, "WPA: WPA IE in Beacon/ProbeResp", sm->ap_wpa_ie, sm->ap_wpa_ie_len); } if (wpa_ie) { if (!sm->ap_wpa_ie) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: No WPA IE in Beacon/ProbeResp"); } wpa_hexdump(MSG_INFO, "WPA: WPA IE in 3/4 msg", wpa_ie, wpa_ie_len); } if (sm->ap_rsn_ie) { wpa_hexdump(MSG_INFO, "WPA: RSN IE in Beacon/ProbeResp", sm->ap_rsn_ie, sm->ap_rsn_ie_len); } if (rsn_ie) { if (!sm->ap_rsn_ie) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: No RSN IE in Beacon/ProbeResp"); } wpa_hexdump(MSG_INFO, "WPA: RSN IE in 3/4 msg", rsn_ie, rsn_ie_len); } wpa_sm_deauthenticate(sm, WLAN_REASON_IE_IN_4WAY_DIFFERS); } #ifdef CONFIG_IEEE80211R static int ft_validate_mdie(struct wpa_sm *sm, const unsigned char *src_addr, struct wpa_eapol_ie_parse *ie, const u8 *assoc_resp_mdie) { struct rsn_mdie *mdie; mdie = (struct rsn_mdie *) (ie->mdie + 2); if (ie->mdie == NULL || ie->mdie_len < 2 + sizeof(*mdie) || os_memcmp(mdie->mobility_domain, sm->mobility_domain, MOBILITY_DOMAIN_ID_LEN) != 0) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "FT: MDIE in msg 3/4 did " "not match with the current mobility domain"); return -1; } if (assoc_resp_mdie && (assoc_resp_mdie[1] != ie->mdie[1] || os_memcmp(assoc_resp_mdie, ie->mdie, 2 + ie->mdie[1]) != 0)) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "FT: MDIE mismatch"); wpa_hexdump(MSG_DEBUG, "FT: MDIE in EAPOL-Key msg 3/4", ie->mdie, 2 + ie->mdie[1]); wpa_hexdump(MSG_DEBUG, "FT: MDIE in (Re)Association Response", assoc_resp_mdie, 2 + assoc_resp_mdie[1]); return -1; } return 0; } static int ft_validate_ftie(struct wpa_sm *sm, const unsigned char *src_addr, struct wpa_eapol_ie_parse *ie, const u8 *assoc_resp_ftie) { if (ie->ftie == NULL) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "FT: No FTIE in EAPOL-Key msg 3/4"); return -1; } if (assoc_resp_ftie == NULL) return 0; if (assoc_resp_ftie[1] != ie->ftie[1] || os_memcmp(assoc_resp_ftie, ie->ftie, 2 + ie->ftie[1]) != 0) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "FT: FTIE mismatch"); wpa_hexdump(MSG_DEBUG, "FT: FTIE in EAPOL-Key msg 3/4", ie->ftie, 2 + ie->ftie[1]); wpa_hexdump(MSG_DEBUG, "FT: FTIE in (Re)Association Response", assoc_resp_ftie, 2 + assoc_resp_ftie[1]); return -1; } return 0; } static int ft_validate_rsnie(struct wpa_sm *sm, const unsigned char *src_addr, struct wpa_eapol_ie_parse *ie) { struct wpa_ie_data rsn; if (!ie->rsn_ie) return 0; /* * Verify that PMKR1Name from EAPOL-Key message 3/4 * matches with the value we derived. */ if (wpa_parse_wpa_ie_rsn(ie->rsn_ie, ie->rsn_ie_len, &rsn) < 0 || rsn.num_pmkid != 1 || rsn.pmkid == NULL) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "FT: No PMKR1Name in " "FT 4-way handshake message 3/4"); return -1; } if (os_memcmp(rsn.pmkid, sm->pmk_r1_name, WPA_PMK_NAME_LEN) != 0) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "FT: PMKR1Name mismatch in " "FT 4-way handshake message 3/4"); wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name from Authenticator", rsn.pmkid, WPA_PMK_NAME_LEN); wpa_hexdump(MSG_DEBUG, "FT: Derived PMKR1Name", sm->pmk_r1_name, WPA_PMK_NAME_LEN); return -1; } return 0; } static int wpa_supplicant_validate_ie_ft(struct wpa_sm *sm, const unsigned char *src_addr, struct wpa_eapol_ie_parse *ie) { const u8 *pos, *end, *mdie = NULL, *ftie = NULL; if (sm->assoc_resp_ies) { pos = sm->assoc_resp_ies; end = pos + sm->assoc_resp_ies_len; while (pos + 2 < end) { if (pos + 2 + pos[1] > end) break; switch (*pos) { case WLAN_EID_MOBILITY_DOMAIN: mdie = pos; break; case WLAN_EID_FAST_BSS_TRANSITION: ftie = pos; break; } pos += 2 + pos[1]; } } if (ft_validate_mdie(sm, src_addr, ie, mdie) < 0 || ft_validate_ftie(sm, src_addr, ie, ftie) < 0 || ft_validate_rsnie(sm, src_addr, ie) < 0) return -1; return 0; } #endif /* CONFIG_IEEE80211R */ static int wpa_supplicant_validate_ie(struct wpa_sm *sm, const unsigned char *src_addr, struct wpa_eapol_ie_parse *ie) { if (sm->ap_wpa_ie == NULL && sm->ap_rsn_ie == NULL) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: No WPA/RSN IE for this AP known. " "Trying to get from scan results"); if (wpa_sm_get_beacon_ie(sm) < 0) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Could not find AP from " "the scan results"); } else { wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Found the current AP from " "updated scan results"); } } if (ie->wpa_ie == NULL && ie->rsn_ie == NULL && (sm->ap_wpa_ie || sm->ap_rsn_ie)) { wpa_report_ie_mismatch(sm, "IE in 3/4 msg does not match " "with IE in Beacon/ProbeResp (no IE?)", src_addr, ie->wpa_ie, ie->wpa_ie_len, ie->rsn_ie, ie->rsn_ie_len); return -1; } if ((ie->wpa_ie && sm->ap_wpa_ie && (ie->wpa_ie_len != sm->ap_wpa_ie_len || os_memcmp(ie->wpa_ie, sm->ap_wpa_ie, ie->wpa_ie_len) != 0)) || (ie->rsn_ie && sm->ap_rsn_ie && wpa_compare_rsn_ie(wpa_key_mgmt_ft(sm->key_mgmt), sm->ap_rsn_ie, sm->ap_rsn_ie_len, ie->rsn_ie, ie->rsn_ie_len))) { wpa_report_ie_mismatch(sm, "IE in 3/4 msg does not match " "with IE in Beacon/ProbeResp", src_addr, ie->wpa_ie, ie->wpa_ie_len, ie->rsn_ie, ie->rsn_ie_len); return -1; } if (sm->proto == WPA_PROTO_WPA && ie->rsn_ie && sm->ap_rsn_ie == NULL && sm->rsn_enabled) { wpa_report_ie_mismatch(sm, "Possible downgrade attack " "detected - RSN was enabled and RSN IE " "was in msg 3/4, but not in " "Beacon/ProbeResp", src_addr, ie->wpa_ie, ie->wpa_ie_len, ie->rsn_ie, ie->rsn_ie_len); return -1; } #ifdef CONFIG_IEEE80211R if (wpa_key_mgmt_ft(sm->key_mgmt) && wpa_supplicant_validate_ie_ft(sm, src_addr, ie) < 0) return -1; #endif /* CONFIG_IEEE80211R */ return 0; } /** * wpa_supplicant_send_4_of_4 - Send message 4 of WPA/RSN 4-Way Handshake * @sm: Pointer to WPA state machine data from wpa_sm_init() * @dst: Destination address for the frame * @key: Pointer to the EAPOL-Key frame header * @ver: Version bits from EAPOL-Key Key Info * @key_info: Key Info * @kde: KDEs to include the EAPOL-Key frame * @kde_len: Length of KDEs * @ptk: PTK to use for keyed hash and encryption * Returns: 0 on success, -1 on failure */ int wpa_supplicant_send_4_of_4(struct wpa_sm *sm, const unsigned char *dst, const struct wpa_eapol_key *key, u16 ver, u16 key_info, const u8 *kde, size_t kde_len, struct wpa_ptk *ptk) { size_t rlen; struct wpa_eapol_key *reply; u8 *rbuf; if (kde) wpa_hexdump(MSG_DEBUG, "WPA: KDE for msg 4/4", kde, kde_len); rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL, sizeof(*reply) + kde_len, &rlen, (void *) &reply); if (rbuf == NULL) return -1; reply->type = sm->proto == WPA_PROTO_RSN ? EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA; key_info &= WPA_KEY_INFO_SECURE; key_info |= ver | WPA_KEY_INFO_KEY_TYPE | WPA_KEY_INFO_MIC; WPA_PUT_BE16(reply->key_info, key_info); if (sm->proto == WPA_PROTO_RSN) WPA_PUT_BE16(reply->key_length, 0); else os_memcpy(reply->key_length, key->key_length, 2); os_memcpy(reply->replay_counter, key->replay_counter, WPA_REPLAY_COUNTER_LEN); WPA_PUT_BE16(reply->key_data_length, kde_len); if (kde) os_memcpy(reply + 1, kde, kde_len); wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 4/4"); wpa_eapol_key_send(sm, ptk->kck, ver, dst, ETH_P_EAPOL, rbuf, rlen, reply->key_mic); return 0; } static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm, const struct wpa_eapol_key *key, u16 ver) { u16 key_info, keylen, len; const u8 *pos; struct wpa_eapol_ie_parse ie; wpa_sm_set_state(sm, WPA_4WAY_HANDSHAKE); wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: RX message 3 of 4-Way " "Handshake from " MACSTR " (ver=%d)", MAC2STR(sm->bssid), ver); key_info = WPA_GET_BE16(key->key_info); pos = (const u8 *) (key + 1); len = WPA_GET_BE16(key->key_data_length); wpa_hexdump(MSG_DEBUG, "WPA: IE KeyData", pos, len); if (wpa_supplicant_parse_ies(pos, len, &ie) < 0) goto failed; if (ie.gtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: GTK IE in unencrypted key data"); goto failed; } #ifdef CONFIG_IEEE80211W if (ie.igtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: IGTK KDE in unencrypted key data"); goto failed; } if (ie.igtk && ie.igtk_len != sizeof(struct wpa_igtk_kde)) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Invalid IGTK KDE length %lu", (unsigned long) ie.igtk_len); goto failed; } #endif /* CONFIG_IEEE80211W */ if (wpa_supplicant_validate_ie(sm, sm->bssid, &ie) < 0) goto failed; if (os_memcmp(sm->anonce, key->key_nonce, WPA_NONCE_LEN) != 0) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: ANonce from message 1 of 4-Way Handshake " "differs from 3 of 4-Way Handshake - drop packet (src=" MACSTR ")", MAC2STR(sm->bssid)); goto failed; } keylen = WPA_GET_BE16(key->key_length); if (keylen != wpa_cipher_key_len(sm->pairwise_cipher)) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Invalid %s key length %d (src=" MACSTR ")", wpa_cipher_txt(sm->pairwise_cipher), keylen, MAC2STR(sm->bssid)); goto failed; } #ifdef CONFIG_P2P if (ie.ip_addr_alloc) { os_memcpy(sm->p2p_ip_addr, ie.ip_addr_alloc, 3 * 4); wpa_hexdump(MSG_DEBUG, "P2P: IP address info", sm->p2p_ip_addr, sizeof(sm->p2p_ip_addr)); } #endif /* CONFIG_P2P */ if (wpa_supplicant_send_4_of_4(sm, sm->bssid, key, ver, key_info, NULL, 0, &sm->ptk)) { goto failed; } /* SNonce was successfully used in msg 3/4, so mark it to be renewed * for the next 4-Way Handshake. If msg 3 is received again, the old * SNonce will still be used to avoid changing PTK. */ sm->renew_snonce = 1; if (key_info & WPA_KEY_INFO_INSTALL) { if (wpa_supplicant_install_ptk(sm, key)) goto failed; } if (key_info & WPA_KEY_INFO_SECURE) { wpa_sm_mlme_setprotection( sm, sm->bssid, MLME_SETPROTECTION_PROTECT_TYPE_RX, MLME_SETPROTECTION_KEY_TYPE_PAIRWISE); eapol_sm_notify_portValid(sm->eapol, TRUE); } wpa_sm_set_state(sm, WPA_GROUP_HANDSHAKE); if (sm->group_cipher == WPA_CIPHER_GTK_NOT_USED) { wpa_supplicant_key_neg_complete(sm, sm->bssid, key_info & WPA_KEY_INFO_SECURE); } else if (ie.gtk && wpa_supplicant_pairwise_gtk(sm, key, ie.gtk, ie.gtk_len, key_info) < 0) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "RSN: Failed to configure GTK"); goto failed; } if (ieee80211w_set_keys(sm, &ie) < 0) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "RSN: Failed to configure IGTK"); goto failed; } if (ie.gtk) wpa_sm_set_rekey_offload(sm); return; failed: wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED); } static int wpa_supplicant_process_1_of_2_rsn(struct wpa_sm *sm, const u8 *keydata, size_t keydatalen, u16 key_info, struct wpa_gtk_data *gd) { int maxkeylen; struct wpa_eapol_ie_parse ie; wpa_hexdump(MSG_DEBUG, "RSN: msg 1/2 key data", keydata, keydatalen); if (wpa_supplicant_parse_ies(keydata, keydatalen, &ie) < 0) return -1; if (ie.gtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: GTK IE in unencrypted key data"); return -1; } if (ie.gtk == NULL) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: No GTK IE in Group Key msg 1/2"); return -1; } maxkeylen = gd->gtk_len = ie.gtk_len - 2; if (wpa_supplicant_check_group_cipher(sm, sm->group_cipher, gd->gtk_len, maxkeylen, &gd->key_rsc_len, &gd->alg)) return -1; wpa_hexdump(MSG_DEBUG, "RSN: received GTK in group key handshake", ie.gtk, ie.gtk_len); gd->keyidx = ie.gtk[0] & 0x3; gd->tx = wpa_supplicant_gtk_tx_bit_workaround(sm, !!(ie.gtk[0] & BIT(2))); if (ie.gtk_len - 2 > sizeof(gd->gtk)) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "RSN: Too long GTK in GTK IE (len=%lu)", (unsigned long) ie.gtk_len - 2); return -1; } os_memcpy(gd->gtk, ie.gtk + 2, ie.gtk_len - 2); if (ieee80211w_set_keys(sm, &ie) < 0) wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "RSN: Failed to configure IGTK"); return 0; } static int wpa_supplicant_process_1_of_2_wpa(struct wpa_sm *sm, const struct wpa_eapol_key *key, size_t keydatalen, int key_info, size_t extra_len, u16 ver, struct wpa_gtk_data *gd) { size_t maxkeylen; u8 ek[32]; gd->gtk_len = WPA_GET_BE16(key->key_length); maxkeylen = keydatalen; if (keydatalen > extra_len) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: Truncated EAPOL-Key packet: " "key_data_length=%lu > extra_len=%lu", (unsigned long) keydatalen, (unsigned long) extra_len); return -1; } if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { if (maxkeylen < 8) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: Too short maxkeylen (%lu)", (unsigned long) maxkeylen); return -1; } maxkeylen -= 8; } if (wpa_supplicant_check_group_cipher(sm, sm->group_cipher, gd->gtk_len, maxkeylen, &gd->key_rsc_len, &gd->alg)) return -1; gd->keyidx = (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) >> WPA_KEY_INFO_KEY_INDEX_SHIFT; if (ver == WPA_KEY_INFO_TYPE_HMAC_MD5_RC4) { os_memcpy(ek, key->key_iv, 16); os_memcpy(ek + 16, sm->ptk.kek, 16); if (keydatalen > sizeof(gd->gtk)) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: RC4 key data too long (%lu)", (unsigned long) keydatalen); return -1; } os_memcpy(gd->gtk, key + 1, keydatalen); if (rc4_skip(ek, 32, 256, gd->gtk, keydatalen)) { wpa_msg(sm->ctx->msg_ctx, MSG_ERROR, "WPA: RC4 failed"); return -1; } } else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { if (keydatalen % 8) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Unsupported AES-WRAP len %lu", (unsigned long) keydatalen); return -1; } if (maxkeylen > sizeof(gd->gtk)) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: AES-WRAP key data " "too long (keydatalen=%lu maxkeylen=%lu)", (unsigned long) keydatalen, (unsigned long) maxkeylen); return -1; } if (aes_unwrap(sm->ptk.kek, maxkeylen / 8, (const u8 *) (key + 1), gd->gtk)) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: AES unwrap failed - could not decrypt " "GTK"); return -1; } } else { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Unsupported key_info type %d", ver); return -1; } gd->tx = wpa_supplicant_gtk_tx_bit_workaround( sm, !!(key_info & WPA_KEY_INFO_TXRX)); return 0; } static int wpa_supplicant_send_2_of_2(struct wpa_sm *sm, const struct wpa_eapol_key *key, int ver, u16 key_info) { size_t rlen; struct wpa_eapol_key *reply; u8 *rbuf; rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL, sizeof(*reply), &rlen, (void *) &reply); if (rbuf == NULL) return -1; reply->type = sm->proto == WPA_PROTO_RSN ? EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA; key_info &= WPA_KEY_INFO_KEY_INDEX_MASK; key_info |= ver | WPA_KEY_INFO_MIC | WPA_KEY_INFO_SECURE; WPA_PUT_BE16(reply->key_info, key_info); if (sm->proto == WPA_PROTO_RSN) WPA_PUT_BE16(reply->key_length, 0); else os_memcpy(reply->key_length, key->key_length, 2); os_memcpy(reply->replay_counter, key->replay_counter, WPA_REPLAY_COUNTER_LEN); WPA_PUT_BE16(reply->key_data_length, 0); wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 2/2"); wpa_eapol_key_send(sm, sm->ptk.kck, ver, sm->bssid, ETH_P_EAPOL, rbuf, rlen, reply->key_mic); return 0; } static void wpa_supplicant_process_1_of_2(struct wpa_sm *sm, const unsigned char *src_addr, const struct wpa_eapol_key *key, int extra_len, u16 ver) { u16 key_info, keydatalen; int rekey, ret; struct wpa_gtk_data gd; os_memset(&gd, 0, sizeof(gd)); rekey = wpa_sm_get_state(sm) == WPA_COMPLETED; wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: RX message 1 of Group Key " "Handshake from " MACSTR " (ver=%d)", MAC2STR(src_addr), ver); key_info = WPA_GET_BE16(key->key_info); keydatalen = WPA_GET_BE16(key->key_data_length); if (sm->proto == WPA_PROTO_RSN) { ret = wpa_supplicant_process_1_of_2_rsn(sm, (const u8 *) (key + 1), keydatalen, key_info, &gd); } else { ret = wpa_supplicant_process_1_of_2_wpa(sm, key, keydatalen, key_info, extra_len, ver, &gd); } wpa_sm_set_state(sm, WPA_GROUP_HANDSHAKE); if (ret) goto failed; if (wpa_supplicant_install_gtk(sm, &gd, key->key_rsc) || wpa_supplicant_send_2_of_2(sm, key, ver, key_info)) goto failed; if (rekey) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: Group rekeying " "completed with " MACSTR " [GTK=%s]", MAC2STR(sm->bssid), wpa_cipher_txt(sm->group_cipher)); wpa_sm_cancel_auth_timeout(sm); wpa_sm_set_state(sm, WPA_COMPLETED); } else { wpa_supplicant_key_neg_complete(sm, sm->bssid, key_info & WPA_KEY_INFO_SECURE); } wpa_sm_set_rekey_offload(sm); return; failed: wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED); } static int wpa_supplicant_verify_eapol_key_mic(struct wpa_sm *sm, struct wpa_eapol_key *key, u16 ver, const u8 *buf, size_t len) { u8 mic[16]; int ok = 0; os_memcpy(mic, key->key_mic, 16); if (sm->tptk_set) { os_memset(key->key_mic, 0, 16); wpa_eapol_key_mic(sm->tptk.kck, ver, buf, len, key->key_mic); if (os_memcmp(mic, key->key_mic, 16) != 0) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Invalid EAPOL-Key MIC " "when using TPTK - ignoring TPTK"); } else { ok = 1; sm->tptk_set = 0; sm->ptk_set = 1; os_memcpy(&sm->ptk, &sm->tptk, sizeof(sm->ptk)); } } if (!ok && sm->ptk_set) { os_memset(key->key_mic, 0, 16); wpa_eapol_key_mic(sm->ptk.kck, ver, buf, len, key->key_mic); if (os_memcmp(mic, key->key_mic, 16) != 0) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Invalid EAPOL-Key MIC - " "dropping packet"); return -1; } ok = 1; } if (!ok) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Could not verify EAPOL-Key MIC - " "dropping packet"); return -1; } os_memcpy(sm->rx_replay_counter, key->replay_counter, WPA_REPLAY_COUNTER_LEN); sm->rx_replay_counter_set = 1; return 0; } /* Decrypt RSN EAPOL-Key key data (RC4 or AES-WRAP) */ static int wpa_supplicant_decrypt_key_data(struct wpa_sm *sm, struct wpa_eapol_key *key, u16 ver) { u16 keydatalen = WPA_GET_BE16(key->key_data_length); wpa_hexdump(MSG_DEBUG, "RSN: encrypted key data", (u8 *) (key + 1), keydatalen); if (!sm->ptk_set) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: PTK not available, cannot decrypt EAPOL-Key Key " "Data"); return -1; } /* Decrypt key data here so that this operation does not need * to be implemented separately for each message type. */ if (ver == WPA_KEY_INFO_TYPE_HMAC_MD5_RC4) { u8 ek[32]; os_memcpy(ek, key->key_iv, 16); os_memcpy(ek + 16, sm->ptk.kek, 16); if (rc4_skip(ek, 32, 256, (u8 *) (key + 1), keydatalen)) { wpa_msg(sm->ctx->msg_ctx, MSG_ERROR, "WPA: RC4 failed"); return -1; } } else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES || ver == WPA_KEY_INFO_TYPE_AES_128_CMAC) { u8 *buf; if (keydatalen % 8) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Unsupported AES-WRAP len %d", keydatalen); return -1; } keydatalen -= 8; /* AES-WRAP adds 8 bytes */ buf = os_malloc(keydatalen); if (buf == NULL) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: No memory for AES-UNWRAP buffer"); return -1; } if (aes_unwrap(sm->ptk.kek, keydatalen / 8, (u8 *) (key + 1), buf)) { os_free(buf); wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: AES unwrap failed - " "could not decrypt EAPOL-Key key data"); return -1; } os_memcpy(key + 1, buf, keydatalen); os_free(buf); WPA_PUT_BE16(key->key_data_length, keydatalen); } else { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Unsupported key_info type %d", ver); return -1; } wpa_hexdump_key(MSG_DEBUG, "WPA: decrypted EAPOL-Key key data", (u8 *) (key + 1), keydatalen); return 0; } /** * wpa_sm_aborted_cached - Notify WPA that PMKSA caching was aborted * @sm: Pointer to WPA state machine data from wpa_sm_init() */ void wpa_sm_aborted_cached(struct wpa_sm *sm) { if (sm && sm->cur_pmksa) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: Cancelling PMKSA caching attempt"); sm->cur_pmksa = NULL; } } static void wpa_eapol_key_dump(struct wpa_sm *sm, const struct wpa_eapol_key *key) { #ifndef CONFIG_NO_STDOUT_DEBUG u16 key_info = WPA_GET_BE16(key->key_info); wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, " EAPOL-Key type=%d", key->type); wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, " key_info 0x%x (ver=%d keyidx=%d rsvd=%d %s%s%s%s%s%s%s%s)", key_info, key_info & WPA_KEY_INFO_TYPE_MASK, (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) >> WPA_KEY_INFO_KEY_INDEX_SHIFT, (key_info & (BIT(13) | BIT(14) | BIT(15))) >> 13, key_info & WPA_KEY_INFO_KEY_TYPE ? "Pairwise" : "Group", key_info & WPA_KEY_INFO_INSTALL ? " Install" : "", key_info & WPA_KEY_INFO_ACK ? " Ack" : "", key_info & WPA_KEY_INFO_MIC ? " MIC" : "", key_info & WPA_KEY_INFO_SECURE ? " Secure" : "", key_info & WPA_KEY_INFO_ERROR ? " Error" : "", key_info & WPA_KEY_INFO_REQUEST ? " Request" : "", key_info & WPA_KEY_INFO_ENCR_KEY_DATA ? " Encr" : ""); wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, " key_length=%u key_data_length=%u", WPA_GET_BE16(key->key_length), WPA_GET_BE16(key->key_data_length)); wpa_hexdump(MSG_DEBUG, " replay_counter", key->replay_counter, WPA_REPLAY_COUNTER_LEN); wpa_hexdump(MSG_DEBUG, " key_nonce", key->key_nonce, WPA_NONCE_LEN); wpa_hexdump(MSG_DEBUG, " key_iv", key->key_iv, 16); wpa_hexdump(MSG_DEBUG, " key_rsc", key->key_rsc, 8); wpa_hexdump(MSG_DEBUG, " key_id (reserved)", key->key_id, 8); wpa_hexdump(MSG_DEBUG, " key_mic", key->key_mic, 16); #endif /* CONFIG_NO_STDOUT_DEBUG */ } /** * wpa_sm_rx_eapol - Process received WPA EAPOL frames * @sm: Pointer to WPA state machine data from wpa_sm_init() * @src_addr: Source MAC address of the EAPOL packet * @buf: Pointer to the beginning of the EAPOL data (EAPOL header) * @len: Length of the EAPOL frame * Returns: 1 = WPA EAPOL-Key processed, 0 = not a WPA EAPOL-Key, -1 failure * * This function is called for each received EAPOL frame. Other than EAPOL-Key * frames can be skipped if filtering is done elsewhere. wpa_sm_rx_eapol() is * only processing WPA and WPA2 EAPOL-Key frames. * * The received EAPOL-Key packets are validated and valid packets are replied * to. In addition, key material (PTK, GTK) is configured at the end of a * successful key handshake. */ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, const u8 *buf, size_t len) { size_t plen, data_len, extra_len; struct ieee802_1x_hdr *hdr; struct wpa_eapol_key *key; u16 key_info, ver; u8 *tmp; int ret = -1; struct wpa_peerkey *peerkey = NULL; #ifdef CONFIG_IEEE80211R sm->ft_completed = 0; #endif /* CONFIG_IEEE80211R */ if (len < sizeof(*hdr) + sizeof(*key)) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: EAPOL frame too short to be a WPA " "EAPOL-Key (len %lu, expecting at least %lu)", (unsigned long) len, (unsigned long) sizeof(*hdr) + sizeof(*key)); return 0; } tmp = os_malloc(len); if (tmp == NULL) return -1; os_memcpy(tmp, buf, len); hdr = (struct ieee802_1x_hdr *) tmp; key = (struct wpa_eapol_key *) (hdr + 1); plen = be_to_host16(hdr->length); data_len = plen + sizeof(*hdr); wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "IEEE 802.1X RX: version=%d type=%d length=%lu", hdr->version, hdr->type, (unsigned long) plen); if (hdr->version < EAPOL_VERSION) { /* TODO: backwards compatibility */ } if (hdr->type != IEEE802_1X_TYPE_EAPOL_KEY) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: EAPOL frame (type %u) discarded, " "not a Key frame", hdr->type); ret = 0; goto out; } if (plen > len - sizeof(*hdr) || plen < sizeof(*key)) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: EAPOL frame payload size %lu " "invalid (frame size %lu)", (unsigned long) plen, (unsigned long) len); ret = 0; goto out; } if (key->type != EAPOL_KEY_TYPE_WPA && key->type != EAPOL_KEY_TYPE_RSN) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: EAPOL-Key type (%d) unknown, discarded", key->type); ret = 0; goto out; } wpa_eapol_key_dump(sm, key); eapol_sm_notify_lower_layer_success(sm->eapol, 0); wpa_hexdump(MSG_MSGDUMP, "WPA: RX EAPOL-Key", tmp, len); if (data_len < len) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: ignoring %lu bytes after the IEEE 802.1X data", (unsigned long) len - data_len); } key_info = WPA_GET_BE16(key->key_info); ver = key_info & WPA_KEY_INFO_TYPE_MASK; if (ver != WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 && #if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W) ver != WPA_KEY_INFO_TYPE_AES_128_CMAC && #endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */ ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: Unsupported EAPOL-Key descriptor version %d", ver); goto out; } #ifdef CONFIG_IEEE80211R if (wpa_key_mgmt_ft(sm->key_mgmt)) { /* IEEE 802.11r uses a new key_info type (AES-128-CMAC). */ if (ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "FT: AP did not use AES-128-CMAC"); goto out; } } else #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_IEEE80211W if (wpa_key_mgmt_sha256(sm->key_mgmt)) { if (ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: AP did not use the " "negotiated AES-128-CMAC"); goto out; } } else #endif /* CONFIG_IEEE80211W */ if (sm->pairwise_cipher == WPA_CIPHER_CCMP && ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: CCMP is used, but EAPOL-Key " "descriptor version (%d) is not 2", ver); if (sm->group_cipher != WPA_CIPHER_CCMP && !(key_info & WPA_KEY_INFO_KEY_TYPE)) { /* Earlier versions of IEEE 802.11i did not explicitly * require version 2 descriptor for all EAPOL-Key * packets, so allow group keys to use version 1 if * CCMP is not used for them. */ wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: Backwards compatibility: allow invalid " "version for non-CCMP group keys"); } else goto out; } if (sm->pairwise_cipher == WPA_CIPHER_GCMP && ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: GCMP is used, but EAPOL-Key " "descriptor version (%d) is not 2", ver); goto out; } #ifdef CONFIG_PEERKEY for (peerkey = sm->peerkey; peerkey; peerkey = peerkey->next) { if (os_memcmp(peerkey->addr, src_addr, ETH_ALEN) == 0) break; } if (!(key_info & WPA_KEY_INFO_SMK_MESSAGE) && peerkey) { if (!peerkey->initiator && peerkey->replay_counter_set && os_memcmp(key->replay_counter, peerkey->replay_counter, WPA_REPLAY_COUNTER_LEN) <= 0) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "RSN: EAPOL-Key Replay Counter did not " "increase (STK) - dropping packet"); goto out; } else if (peerkey->initiator) { u8 _tmp[WPA_REPLAY_COUNTER_LEN]; os_memcpy(_tmp, key->replay_counter, WPA_REPLAY_COUNTER_LEN); inc_byte_array(_tmp, WPA_REPLAY_COUNTER_LEN); if (os_memcmp(_tmp, peerkey->replay_counter, WPA_REPLAY_COUNTER_LEN) != 0) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: EAPOL-Key Replay " "Counter did not match (STK) - " "dropping packet"); goto out; } } } if (peerkey && peerkey->initiator && (key_info & WPA_KEY_INFO_ACK)) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "RSN: Ack bit in key_info from STK peer"); goto out; } #endif /* CONFIG_PEERKEY */ if (!peerkey && sm->rx_replay_counter_set && os_memcmp(key->replay_counter, sm->rx_replay_counter, WPA_REPLAY_COUNTER_LEN) <= 0) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: EAPOL-Key Replay Counter did not increase - " "dropping packet"); goto out; } if (!(key_info & (WPA_KEY_INFO_ACK | WPA_KEY_INFO_SMK_MESSAGE)) #ifdef CONFIG_PEERKEY && (peerkey == NULL || !peerkey->initiator) #endif /* CONFIG_PEERKEY */ ) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: No Ack bit in key_info"); goto out; } if (key_info & WPA_KEY_INFO_REQUEST) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: EAPOL-Key with Request bit - dropped"); goto out; } if ((key_info & WPA_KEY_INFO_MIC) && !peerkey && wpa_supplicant_verify_eapol_key_mic(sm, key, ver, tmp, data_len)) goto out; #ifdef CONFIG_PEERKEY if ((key_info & WPA_KEY_INFO_MIC) && peerkey && peerkey_verify_eapol_key_mic(sm, peerkey, key, ver, tmp, data_len)) goto out; #endif /* CONFIG_PEERKEY */ extra_len = data_len - sizeof(*hdr) - sizeof(*key); if (WPA_GET_BE16(key->key_data_length) > extra_len) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: Invalid EAPOL-Key " "frame - key_data overflow (%d > %lu)", WPA_GET_BE16(key->key_data_length), (unsigned long) extra_len); goto out; } extra_len = WPA_GET_BE16(key->key_data_length); if (sm->proto == WPA_PROTO_RSN && (key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) { if (wpa_supplicant_decrypt_key_data(sm, key, ver)) goto out; extra_len = WPA_GET_BE16(key->key_data_length); } if (key_info & WPA_KEY_INFO_KEY_TYPE) { if (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Ignored EAPOL-Key (Pairwise) with " "non-zero key index"); goto out; } if (peerkey) { /* PeerKey 4-Way Handshake */ peerkey_rx_eapol_4way(sm, peerkey, key, key_info, ver); } else if (key_info & WPA_KEY_INFO_MIC) { /* 3/4 4-Way Handshake */ wpa_supplicant_process_3_of_4(sm, key, ver); } else { /* 1/4 4-Way Handshake */ wpa_supplicant_process_1_of_4(sm, src_addr, key, ver); } } else if (key_info & WPA_KEY_INFO_SMK_MESSAGE) { /* PeerKey SMK Handshake */ peerkey_rx_eapol_smk(sm, src_addr, key, extra_len, key_info, ver); } else { if (key_info & WPA_KEY_INFO_MIC) { /* 1/2 Group Key Handshake */ wpa_supplicant_process_1_of_2(sm, src_addr, key, extra_len, ver); } else { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: EAPOL-Key (Group) without Mic bit - " "dropped"); } } ret = 1; out: os_free(tmp); return ret; } #ifdef CONFIG_CTRL_IFACE static u32 wpa_key_mgmt_suite(struct wpa_sm *sm) { switch (sm->key_mgmt) { case WPA_KEY_MGMT_IEEE8021X: return (sm->proto == WPA_PROTO_RSN ? RSN_AUTH_KEY_MGMT_UNSPEC_802_1X : WPA_AUTH_KEY_MGMT_UNSPEC_802_1X); case WPA_KEY_MGMT_PSK: return (sm->proto == WPA_PROTO_RSN ? RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X : WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X); #ifdef CONFIG_IEEE80211R case WPA_KEY_MGMT_FT_IEEE8021X: return RSN_AUTH_KEY_MGMT_FT_802_1X; case WPA_KEY_MGMT_FT_PSK: return RSN_AUTH_KEY_MGMT_FT_PSK; #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_IEEE80211W case WPA_KEY_MGMT_IEEE8021X_SHA256: return RSN_AUTH_KEY_MGMT_802_1X_SHA256; case WPA_KEY_MGMT_PSK_SHA256: return RSN_AUTH_KEY_MGMT_PSK_SHA256; #endif /* CONFIG_IEEE80211W */ case WPA_KEY_MGMT_CCKM: return (sm->proto == WPA_PROTO_RSN ? RSN_AUTH_KEY_MGMT_CCKM: WPA_AUTH_KEY_MGMT_CCKM); case WPA_KEY_MGMT_WPA_NONE: return WPA_AUTH_KEY_MGMT_NONE; default: return 0; } } #define RSN_SUITE "%02x-%02x-%02x-%d" #define RSN_SUITE_ARG(s) \ ((s) >> 24) & 0xff, ((s) >> 16) & 0xff, ((s) >> 8) & 0xff, (s) & 0xff /** * wpa_sm_get_mib - Dump text list of MIB entries * @sm: Pointer to WPA state machine data from wpa_sm_init() * @buf: Buffer for the list * @buflen: Length of the buffer * Returns: Number of bytes written to buffer * * This function is used fetch dot11 MIB variables. */ int wpa_sm_get_mib(struct wpa_sm *sm, char *buf, size_t buflen) { char pmkid_txt[PMKID_LEN * 2 + 1]; int rsna, ret; size_t len; if (sm->cur_pmksa) { wpa_snprintf_hex(pmkid_txt, sizeof(pmkid_txt), sm->cur_pmksa->pmkid, PMKID_LEN); } else pmkid_txt[0] = '\0'; if ((wpa_key_mgmt_wpa_psk(sm->key_mgmt) || wpa_key_mgmt_wpa_ieee8021x(sm->key_mgmt)) && sm->proto == WPA_PROTO_RSN) rsna = 1; else rsna = 0; ret = os_snprintf(buf, buflen, "dot11RSNAOptionImplemented=TRUE\n" "dot11RSNAPreauthenticationImplemented=TRUE\n" "dot11RSNAEnabled=%s\n" "dot11RSNAPreauthenticationEnabled=%s\n" "dot11RSNAConfigVersion=%d\n" "dot11RSNAConfigPairwiseKeysSupported=5\n" "dot11RSNAConfigGroupCipherSize=%d\n" "dot11RSNAConfigPMKLifetime=%d\n" "dot11RSNAConfigPMKReauthThreshold=%d\n" "dot11RSNAConfigNumberOfPTKSAReplayCounters=1\n" "dot11RSNAConfigSATimeout=%d\n", rsna ? "TRUE" : "FALSE", rsna ? "TRUE" : "FALSE", RSN_VERSION, wpa_cipher_key_len(sm->group_cipher) * 8, sm->dot11RSNAConfigPMKLifetime, sm->dot11RSNAConfigPMKReauthThreshold, sm->dot11RSNAConfigSATimeout); if (ret < 0 || (size_t) ret >= buflen) return 0; len = ret; ret = os_snprintf( buf + len, buflen - len, "dot11RSNAAuthenticationSuiteSelected=" RSN_SUITE "\n" "dot11RSNAPairwiseCipherSelected=" RSN_SUITE "\n" "dot11RSNAGroupCipherSelected=" RSN_SUITE "\n" "dot11RSNAPMKIDUsed=%s\n" "dot11RSNAAuthenticationSuiteRequested=" RSN_SUITE "\n" "dot11RSNAPairwiseCipherRequested=" RSN_SUITE "\n" "dot11RSNAGroupCipherRequested=" RSN_SUITE "\n" "dot11RSNAConfigNumberOfGTKSAReplayCounters=0\n" "dot11RSNA4WayHandshakeFailures=%u\n", RSN_SUITE_ARG(wpa_key_mgmt_suite(sm)), RSN_SUITE_ARG(wpa_cipher_to_suite(sm->proto, sm->pairwise_cipher)), RSN_SUITE_ARG(wpa_cipher_to_suite(sm->proto, sm->group_cipher)), pmkid_txt, RSN_SUITE_ARG(wpa_key_mgmt_suite(sm)), RSN_SUITE_ARG(wpa_cipher_to_suite(sm->proto, sm->pairwise_cipher)), RSN_SUITE_ARG(wpa_cipher_to_suite(sm->proto, sm->group_cipher)), sm->dot11RSNA4WayHandshakeFailures); if (ret >= 0 && (size_t) ret < buflen) len += ret; return (int) len; } #endif /* CONFIG_CTRL_IFACE */ static void wpa_sm_pmksa_free_cb(struct rsn_pmksa_cache_entry *entry, void *ctx, enum pmksa_free_reason reason) { struct wpa_sm *sm = ctx; int deauth = 0; wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: PMKSA cache entry free_cb: " MACSTR " reason=%d", MAC2STR(entry->aa), reason); if (sm->cur_pmksa == entry) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: %s current PMKSA entry", reason == PMKSA_REPLACE ? "replaced" : "removed"); pmksa_cache_clear_current(sm); /* * If an entry is simply being replaced, there's no need to * deauthenticate because it will be immediately re-added. * This happens when EAP authentication is completed again * (reauth or failed PMKSA caching attempt). */ if (reason != PMKSA_REPLACE) deauth = 1; } if (reason == PMKSA_EXPIRE && (sm->pmk_len == entry->pmk_len && os_memcmp(sm->pmk, entry->pmk, sm->pmk_len) == 0)) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: deauthenticating due to expired PMK"); pmksa_cache_clear_current(sm); deauth = 1; } if (deauth) { os_memset(sm->pmk, 0, sizeof(sm->pmk)); wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED); } } /** * wpa_sm_init - Initialize WPA state machine * @ctx: Context pointer for callbacks; this needs to be an allocated buffer * Returns: Pointer to the allocated WPA state machine data * * This function is used to allocate a new WPA state machine and the returned * value is passed to all WPA state machine calls. */ struct wpa_sm * wpa_sm_init(struct wpa_sm_ctx *ctx) { struct wpa_sm *sm; sm = os_zalloc(sizeof(*sm)); if (sm == NULL) return NULL; dl_list_init(&sm->pmksa_candidates); sm->renew_snonce = 1; sm->ctx = ctx; sm->dot11RSNAConfigPMKLifetime = 43200; sm->dot11RSNAConfigPMKReauthThreshold = 70; sm->dot11RSNAConfigSATimeout = 60; sm->pmksa = pmksa_cache_init(wpa_sm_pmksa_free_cb, sm, sm); if (sm->pmksa == NULL) { wpa_msg(sm->ctx->msg_ctx, MSG_ERROR, "RSN: PMKSA cache initialization failed"); os_free(sm); return NULL; } return sm; } /** * wpa_sm_deinit - Deinitialize WPA state machine * @sm: Pointer to WPA state machine data from wpa_sm_init() */ void wpa_sm_deinit(struct wpa_sm *sm) { if (sm == NULL) return; pmksa_cache_deinit(sm->pmksa); eloop_cancel_timeout(wpa_sm_start_preauth, sm, NULL); eloop_cancel_timeout(wpa_sm_rekey_ptk, sm, NULL); os_free(sm->assoc_wpa_ie); os_free(sm->ap_wpa_ie); os_free(sm->ap_rsn_ie); os_free(sm->ctx); peerkey_deinit(sm); #ifdef CONFIG_IEEE80211R os_free(sm->assoc_resp_ies); #endif /* CONFIG_IEEE80211R */ os_free(sm); } /** * wpa_sm_notify_assoc - Notify WPA state machine about association * @sm: Pointer to WPA state machine data from wpa_sm_init() * @bssid: The BSSID of the new association * * This function is called to let WPA state machine know that the connection * was established. */ void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid) { int clear_ptk = 1; if (sm == NULL) return; wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Association event - clear replay counter"); os_memcpy(sm->bssid, bssid, ETH_ALEN); os_memset(sm->rx_replay_counter, 0, WPA_REPLAY_COUNTER_LEN); sm->rx_replay_counter_set = 0; sm->renew_snonce = 1; if (os_memcmp(sm->preauth_bssid, bssid, ETH_ALEN) == 0) rsn_preauth_deinit(sm); #ifdef CONFIG_IEEE80211R if (wpa_ft_is_completed(sm)) { /* * Clear portValid to kick EAPOL state machine to re-enter * AUTHENTICATED state to get the EAPOL port Authorized. */ eapol_sm_notify_portValid(sm->eapol, FALSE); wpa_supplicant_key_neg_complete(sm, sm->bssid, 1); /* Prepare for the next transition */ wpa_ft_prepare_auth_request(sm, NULL); clear_ptk = 0; } #endif /* CONFIG_IEEE80211R */ if (clear_ptk) { /* * IEEE 802.11, 8.4.10: Delete PTK SA on (re)association if * this is not part of a Fast BSS Transition. */ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Clear old PTK"); sm->ptk_set = 0; sm->tptk_set = 0; } #ifdef CONFIG_TDLS wpa_tdls_assoc(sm); #endif /* CONFIG_TDLS */ #ifdef CONFIG_P2P os_memset(sm->p2p_ip_addr, 0, sizeof(sm->p2p_ip_addr)); #endif /* CONFIG_P2P */ } /** * wpa_sm_notify_disassoc - Notify WPA state machine about disassociation * @sm: Pointer to WPA state machine data from wpa_sm_init() * * This function is called to let WPA state machine know that the connection * was lost. This will abort any existing pre-authentication session. */ void wpa_sm_notify_disassoc(struct wpa_sm *sm) { peerkey_deinit(sm); rsn_preauth_deinit(sm); pmksa_cache_clear_current(sm); if (wpa_sm_get_state(sm) == WPA_4WAY_HANDSHAKE) sm->dot11RSNA4WayHandshakeFailures++; #ifdef CONFIG_TDLS wpa_tdls_disassoc(sm); #endif /* CONFIG_TDLS */ } /** * wpa_sm_set_pmk - Set PMK * @sm: Pointer to WPA state machine data from wpa_sm_init() * @pmk: The new PMK * @pmk_len: The length of the new PMK in bytes * * Configure the PMK for WPA state machine. */ void wpa_sm_set_pmk(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len) { if (sm == NULL) return; sm->pmk_len = pmk_len; os_memcpy(sm->pmk, pmk, pmk_len); #ifdef CONFIG_IEEE80211R /* Set XXKey to be PSK for FT key derivation */ sm->xxkey_len = pmk_len; os_memcpy(sm->xxkey, pmk, pmk_len); #endif /* CONFIG_IEEE80211R */ } /** * wpa_sm_set_pmk_from_pmksa - Set PMK based on the current PMKSA * @sm: Pointer to WPA state machine data from wpa_sm_init() * * Take the PMK from the current PMKSA into use. If no PMKSA is active, the PMK * will be cleared. */ void wpa_sm_set_pmk_from_pmksa(struct wpa_sm *sm) { if (sm == NULL) return; if (sm->cur_pmksa) { sm->pmk_len = sm->cur_pmksa->pmk_len; os_memcpy(sm->pmk, sm->cur_pmksa->pmk, sm->pmk_len); } else { sm->pmk_len = PMK_LEN; os_memset(sm->pmk, 0, PMK_LEN); } } /** * wpa_sm_set_fast_reauth - Set fast reauthentication (EAP) enabled/disabled * @sm: Pointer to WPA state machine data from wpa_sm_init() * @fast_reauth: Whether fast reauthentication (EAP) is allowed */ void wpa_sm_set_fast_reauth(struct wpa_sm *sm, int fast_reauth) { if (sm) sm->fast_reauth = fast_reauth; } /** * wpa_sm_set_scard_ctx - Set context pointer for smartcard callbacks * @sm: Pointer to WPA state machine data from wpa_sm_init() * @scard_ctx: Context pointer for smartcard related callback functions */ void wpa_sm_set_scard_ctx(struct wpa_sm *sm, void *scard_ctx) { if (sm == NULL) return; sm->scard_ctx = scard_ctx; if (sm->preauth_eapol) eapol_sm_register_scard_ctx(sm->preauth_eapol, scard_ctx); } /** * wpa_sm_set_config - Notification of current configration change * @sm: Pointer to WPA state machine data from wpa_sm_init() * @config: Pointer to current network configuration * * Notify WPA state machine that configuration has changed. config will be * stored as a backpointer to network configuration. This can be %NULL to clear * the stored pointed. */ void wpa_sm_set_config(struct wpa_sm *sm, struct rsn_supp_config *config) { if (!sm) return; if (config) { sm->network_ctx = config->network_ctx; sm->peerkey_enabled = config->peerkey_enabled; sm->allowed_pairwise_cipher = config->allowed_pairwise_cipher; sm->proactive_key_caching = config->proactive_key_caching; sm->eap_workaround = config->eap_workaround; sm->eap_conf_ctx = config->eap_conf_ctx; if (config->ssid) { os_memcpy(sm->ssid, config->ssid, config->ssid_len); sm->ssid_len = config->ssid_len; } else sm->ssid_len = 0; sm->wpa_ptk_rekey = config->wpa_ptk_rekey; sm->p2p = config->p2p; } else { sm->network_ctx = NULL; sm->peerkey_enabled = 0; sm->allowed_pairwise_cipher = 0; sm->proactive_key_caching = 0; sm->eap_workaround = 0; sm->eap_conf_ctx = NULL; sm->ssid_len = 0; sm->wpa_ptk_rekey = 0; sm->p2p = 0; } } /** * wpa_sm_set_own_addr - Set own MAC address * @sm: Pointer to WPA state machine data from wpa_sm_init() * @addr: Own MAC address */ void wpa_sm_set_own_addr(struct wpa_sm *sm, const u8 *addr) { if (sm) os_memcpy(sm->own_addr, addr, ETH_ALEN); } /** * wpa_sm_set_ifname - Set network interface name * @sm: Pointer to WPA state machine data from wpa_sm_init() * @ifname: Interface name * @bridge_ifname: Optional bridge interface name (for pre-auth) */ void wpa_sm_set_ifname(struct wpa_sm *sm, const char *ifname, const char *bridge_ifname) { if (sm) { sm->ifname = ifname; sm->bridge_ifname = bridge_ifname; } } /** * wpa_sm_set_eapol - Set EAPOL state machine pointer * @sm: Pointer to WPA state machine data from wpa_sm_init() * @eapol: Pointer to EAPOL state machine allocated with eapol_sm_init() */ void wpa_sm_set_eapol(struct wpa_sm *sm, struct eapol_sm *eapol) { if (sm) sm->eapol = eapol; } /** * wpa_sm_set_param - Set WPA state machine parameters * @sm: Pointer to WPA state machine data from wpa_sm_init() * @param: Parameter field * @value: Parameter value * Returns: 0 on success, -1 on failure */ int wpa_sm_set_param(struct wpa_sm *sm, enum wpa_sm_conf_params param, unsigned int value) { int ret = 0; if (sm == NULL) return -1; switch (param) { case RSNA_PMK_LIFETIME: if (value > 0) sm->dot11RSNAConfigPMKLifetime = value; else ret = -1; break; case RSNA_PMK_REAUTH_THRESHOLD: if (value > 0 && value <= 100) sm->dot11RSNAConfigPMKReauthThreshold = value; else ret = -1; break; case RSNA_SA_TIMEOUT: if (value > 0) sm->dot11RSNAConfigSATimeout = value; else ret = -1; break; case WPA_PARAM_PROTO: sm->proto = value; break; case WPA_PARAM_PAIRWISE: sm->pairwise_cipher = value; break; case WPA_PARAM_GROUP: sm->group_cipher = value; break; case WPA_PARAM_KEY_MGMT: sm->key_mgmt = value; break; #ifdef CONFIG_IEEE80211W case WPA_PARAM_MGMT_GROUP: sm->mgmt_group_cipher = value; break; #endif /* CONFIG_IEEE80211W */ case WPA_PARAM_RSN_ENABLED: sm->rsn_enabled = value; break; case WPA_PARAM_MFP: sm->mfp = value; break; default: break; } return ret; } /** * wpa_sm_get_param - Get WPA state machine parameters * @sm: Pointer to WPA state machine data from wpa_sm_init() * @param: Parameter field * Returns: Parameter value */ unsigned int wpa_sm_get_param(struct wpa_sm *sm, enum wpa_sm_conf_params param) { if (sm == NULL) return 0; switch (param) { case RSNA_PMK_LIFETIME: return sm->dot11RSNAConfigPMKLifetime; case RSNA_PMK_REAUTH_THRESHOLD: return sm->dot11RSNAConfigPMKReauthThreshold; case RSNA_SA_TIMEOUT: return sm->dot11RSNAConfigSATimeout; case WPA_PARAM_PROTO: return sm->proto; case WPA_PARAM_PAIRWISE: return sm->pairwise_cipher; case WPA_PARAM_GROUP: return sm->group_cipher; case WPA_PARAM_KEY_MGMT: return sm->key_mgmt; #ifdef CONFIG_IEEE80211W case WPA_PARAM_MGMT_GROUP: return sm->mgmt_group_cipher; #endif /* CONFIG_IEEE80211W */ case WPA_PARAM_RSN_ENABLED: return sm->rsn_enabled; default: return 0; } } /** * wpa_sm_get_status - Get WPA state machine * @sm: Pointer to WPA state machine data from wpa_sm_init() * @buf: Buffer for status information * @buflen: Maximum buffer length * @verbose: Whether to include verbose status information * Returns: Number of bytes written to buf. * * Query WPA state machine for status information. This function fills in * a text area with current status information. If the buffer (buf) is not * large enough, status information will be truncated to fit the buffer. */ int wpa_sm_get_status(struct wpa_sm *sm, char *buf, size_t buflen, int verbose) { char *pos = buf, *end = buf + buflen; int ret; ret = os_snprintf(pos, end - pos, "pairwise_cipher=%s\n" "group_cipher=%s\n" "key_mgmt=%s\n", wpa_cipher_txt(sm->pairwise_cipher), wpa_cipher_txt(sm->group_cipher), wpa_key_mgmt_txt(sm->key_mgmt, sm->proto)); if (ret < 0 || ret >= end - pos) return pos - buf; pos += ret; if (sm->mfp != NO_MGMT_FRAME_PROTECTION && sm->ap_rsn_ie) { struct wpa_ie_data rsn; if (wpa_parse_wpa_ie_rsn(sm->ap_rsn_ie, sm->ap_rsn_ie_len, &rsn) >= 0 && rsn.capabilities & (WPA_CAPABILITY_MFPR | WPA_CAPABILITY_MFPC)) { ret = os_snprintf(pos, end - pos, "pmf=%d\n", (rsn.capabilities & WPA_CAPABILITY_MFPR) ? 2 : 1); if (ret < 0 || ret >= end - pos) return pos - buf; pos += ret; } } return pos - buf; } int wpa_sm_pmf_enabled(struct wpa_sm *sm) { struct wpa_ie_data rsn; if (sm->mfp == NO_MGMT_FRAME_PROTECTION || !sm->ap_rsn_ie) return 0; if (wpa_parse_wpa_ie_rsn(sm->ap_rsn_ie, sm->ap_rsn_ie_len, &rsn) >= 0 && rsn.capabilities & (WPA_CAPABILITY_MFPR | WPA_CAPABILITY_MFPC)) return 1; return 0; } /** * wpa_sm_set_assoc_wpa_ie_default - Generate own WPA/RSN IE from configuration * @sm: Pointer to WPA state machine data from wpa_sm_init() * @wpa_ie: Pointer to buffer for WPA/RSN IE * @wpa_ie_len: Pointer to the length of the wpa_ie buffer * Returns: 0 on success, -1 on failure */ int wpa_sm_set_assoc_wpa_ie_default(struct wpa_sm *sm, u8 *wpa_ie, size_t *wpa_ie_len) { int res; if (sm == NULL) return -1; res = wpa_gen_wpa_ie(sm, wpa_ie, *wpa_ie_len); if (res < 0) return -1; *wpa_ie_len = res; wpa_hexdump(MSG_DEBUG, "WPA: Set own WPA IE default", wpa_ie, *wpa_ie_len); if (sm->assoc_wpa_ie == NULL) { /* * Make a copy of the WPA/RSN IE so that 4-Way Handshake gets * the correct version of the IE even if PMKSA caching is * aborted (which would remove PMKID from IE generation). */ sm->assoc_wpa_ie = os_malloc(*wpa_ie_len); if (sm->assoc_wpa_ie == NULL) return -1; os_memcpy(sm->assoc_wpa_ie, wpa_ie, *wpa_ie_len); sm->assoc_wpa_ie_len = *wpa_ie_len; } return 0; } /** * wpa_sm_set_assoc_wpa_ie - Set own WPA/RSN IE from (Re)AssocReq * @sm: Pointer to WPA state machine data from wpa_sm_init() * @ie: Pointer to IE data (starting from id) * @len: IE length * Returns: 0 on success, -1 on failure * * Inform WPA state machine about the WPA/RSN IE used in (Re)Association * Request frame. The IE will be used to override the default value generated * with wpa_sm_set_assoc_wpa_ie_default(). */ int wpa_sm_set_assoc_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len) { if (sm == NULL) return -1; os_free(sm->assoc_wpa_ie); if (ie == NULL || len == 0) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: clearing own WPA/RSN IE"); sm->assoc_wpa_ie = NULL; sm->assoc_wpa_ie_len = 0; } else { wpa_hexdump(MSG_DEBUG, "WPA: set own WPA/RSN IE", ie, len); sm->assoc_wpa_ie = os_malloc(len); if (sm->assoc_wpa_ie == NULL) return -1; os_memcpy(sm->assoc_wpa_ie, ie, len); sm->assoc_wpa_ie_len = len; } return 0; } /** * wpa_sm_set_ap_wpa_ie - Set AP WPA IE from Beacon/ProbeResp * @sm: Pointer to WPA state machine data from wpa_sm_init() * @ie: Pointer to IE data (starting from id) * @len: IE length * Returns: 0 on success, -1 on failure * * Inform WPA state machine about the WPA IE used in Beacon / Probe Response * frame. */ int wpa_sm_set_ap_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len) { if (sm == NULL) return -1; os_free(sm->ap_wpa_ie); if (ie == NULL || len == 0) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: clearing AP WPA IE"); sm->ap_wpa_ie = NULL; sm->ap_wpa_ie_len = 0; } else { wpa_hexdump(MSG_DEBUG, "WPA: set AP WPA IE", ie, len); sm->ap_wpa_ie = os_malloc(len); if (sm->ap_wpa_ie == NULL) return -1; os_memcpy(sm->ap_wpa_ie, ie, len); sm->ap_wpa_ie_len = len; } return 0; } /** * wpa_sm_set_ap_rsn_ie - Set AP RSN IE from Beacon/ProbeResp * @sm: Pointer to WPA state machine data from wpa_sm_init() * @ie: Pointer to IE data (starting from id) * @len: IE length * Returns: 0 on success, -1 on failure * * Inform WPA state machine about the RSN IE used in Beacon / Probe Response * frame. */ int wpa_sm_set_ap_rsn_ie(struct wpa_sm *sm, const u8 *ie, size_t len) { if (sm == NULL) return -1; os_free(sm->ap_rsn_ie); if (ie == NULL || len == 0) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: clearing AP RSN IE"); sm->ap_rsn_ie = NULL; sm->ap_rsn_ie_len = 0; } else { wpa_hexdump(MSG_DEBUG, "WPA: set AP RSN IE", ie, len); sm->ap_rsn_ie = os_malloc(len); if (sm->ap_rsn_ie == NULL) return -1; os_memcpy(sm->ap_rsn_ie, ie, len); sm->ap_rsn_ie_len = len; } return 0; } /** * wpa_sm_parse_own_wpa_ie - Parse own WPA/RSN IE * @sm: Pointer to WPA state machine data from wpa_sm_init() * @data: Pointer to data area for parsing results * Returns: 0 on success, -1 if IE is not known, or -2 on parsing failure * * Parse the contents of the own WPA or RSN IE from (Re)AssocReq and write the * parsed data into data. */ int wpa_sm_parse_own_wpa_ie(struct wpa_sm *sm, struct wpa_ie_data *data) { if (sm == NULL) return -1; if (sm->assoc_wpa_ie == NULL) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: No WPA/RSN IE available from association info"); return -1; } if (wpa_parse_wpa_ie(sm->assoc_wpa_ie, sm->assoc_wpa_ie_len, data)) return -2; return 0; } int wpa_sm_pmksa_cache_list(struct wpa_sm *sm, char *buf, size_t len) { return pmksa_cache_list(sm->pmksa, buf, len); } void wpa_sm_drop_sa(struct wpa_sm *sm) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Clear old PMK and PTK"); sm->ptk_set = 0; sm->tptk_set = 0; os_memset(sm->pmk, 0, sizeof(sm->pmk)); os_memset(&sm->ptk, 0, sizeof(sm->ptk)); os_memset(&sm->tptk, 0, sizeof(sm->tptk)); } int wpa_sm_has_ptk(struct wpa_sm *sm) { if (sm == NULL) return 0; return sm->ptk_set; } void wpa_sm_update_replay_ctr(struct wpa_sm *sm, const u8 *replay_ctr) { os_memcpy(sm->rx_replay_counter, replay_ctr, WPA_REPLAY_COUNTER_LEN); } void wpa_sm_pmksa_cache_flush(struct wpa_sm *sm, void *network_ctx) { pmksa_cache_flush(sm->pmksa, network_ctx, NULL, 0); } #ifdef CONFIG_WNM int wpa_wnmsleep_install_key(struct wpa_sm *sm, u8 subelem_id, u8 *buf) { struct wpa_gtk_data gd; #ifdef CONFIG_IEEE80211W struct wpa_igtk_kde igd; u16 keyidx; #endif /* CONFIG_IEEE80211W */ u16 keyinfo; u8 keylen; /* plaintext key len */ u8 *key_rsc; os_memset(&gd, 0, sizeof(gd)); #ifdef CONFIG_IEEE80211W os_memset(&igd, 0, sizeof(igd)); #endif /* CONFIG_IEEE80211W */ keylen = wpa_cipher_key_len(sm->group_cipher); gd.key_rsc_len = wpa_cipher_rsc_len(sm->group_cipher); gd.alg = wpa_cipher_to_alg(sm->group_cipher); if (gd.alg == WPA_ALG_NONE) { wpa_printf(MSG_DEBUG, "Unsupported group cipher suite"); return -1; } if (subelem_id == WNM_SLEEP_SUBELEM_GTK) { key_rsc = buf + 5; keyinfo = WPA_GET_LE16(buf + 2); gd.gtk_len = keylen; if (gd.gtk_len != buf[4]) { wpa_printf(MSG_DEBUG, "GTK len mismatch len %d vs %d", gd.gtk_len, buf[4]); return -1; } gd.keyidx = keyinfo & 0x03; /* B0 - B1 */ gd.tx = wpa_supplicant_gtk_tx_bit_workaround( sm, !!(keyinfo & WPA_KEY_INFO_TXRX)); os_memcpy(gd.gtk, buf + 13, gd.gtk_len); wpa_hexdump_key(MSG_DEBUG, "Install GTK (WNM SLEEP)", gd.gtk, gd.gtk_len); if (wpa_supplicant_install_gtk(sm, &gd, key_rsc)) { wpa_printf(MSG_DEBUG, "Failed to install the GTK in " "WNM mode"); return -1; } #ifdef CONFIG_IEEE80211W } else if (subelem_id == WNM_SLEEP_SUBELEM_IGTK) { os_memcpy(igd.keyid, buf + 2, 2); os_memcpy(igd.pn, buf + 4, 6); keyidx = WPA_GET_LE16(igd.keyid); os_memcpy(igd.igtk, buf + 10, WPA_IGTK_LEN); wpa_hexdump_key(MSG_DEBUG, "Install IGTK (WNM SLEEP)", igd.igtk, WPA_IGTK_LEN); if (wpa_sm_set_key(sm, WPA_ALG_IGTK, broadcast_ether_addr, keyidx, 0, igd.pn, sizeof(igd.pn), igd.igtk, WPA_IGTK_LEN) < 0) { wpa_printf(MSG_DEBUG, "Failed to install the IGTK in " "WNM mode"); return -1; } #endif /* CONFIG_IEEE80211W */ } else { wpa_printf(MSG_DEBUG, "Unknown element id"); return -1; } return 0; } #endif /* CONFIG_WNM */ #ifdef CONFIG_PEERKEY int wpa_sm_rx_eapol_peerkey(struct wpa_sm *sm, const u8 *src_addr, const u8 *buf, size_t len) { struct wpa_peerkey *peerkey; for (peerkey = sm->peerkey; peerkey; peerkey = peerkey->next) { if (os_memcmp(peerkey->addr, src_addr, ETH_ALEN) == 0) break; } if (!peerkey) return 0; wpa_sm_rx_eapol(sm, src_addr, buf, len); return 1; } #endif /* CONFIG_PEERKEY */ #ifdef CONFIG_P2P int wpa_sm_get_p2p_ip_addr(struct wpa_sm *sm, u8 *buf) { if (sm == NULL || WPA_GET_BE32(sm->p2p_ip_addr) == 0) return -1; os_memcpy(buf, sm->p2p_ip_addr, 3 * 4); return 0; } #endif /* CONFIG_P2P */ wpa-2.1/src/rsn_supp/wpa.h000066400000000000000000000261031227414666700155640ustar00rootroot00000000000000/* * wpa_supplicant - WPA definitions * Copyright (c) 2003-2007, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef WPA_H #define WPA_H #include "common/defs.h" #include "common/eapol_common.h" #include "common/wpa_common.h" #include "common/ieee802_11_defs.h" struct wpa_sm; struct eapol_sm; struct wpa_config_blob; struct wpa_sm_ctx { void *ctx; /* pointer to arbitrary upper level context */ void *msg_ctx; /* upper level context for wpa_msg() calls */ void (*set_state)(void *ctx, enum wpa_states state); enum wpa_states (*get_state)(void *ctx); void (*deauthenticate)(void * ctx, int reason_code); int (*set_key)(void *ctx, enum wpa_alg alg, const u8 *addr, int key_idx, int set_tx, const u8 *seq, size_t seq_len, const u8 *key, size_t key_len); void * (*get_network_ctx)(void *ctx); int (*get_bssid)(void *ctx, u8 *bssid); int (*ether_send)(void *ctx, const u8 *dest, u16 proto, const u8 *buf, size_t len); int (*get_beacon_ie)(void *ctx); void (*cancel_auth_timeout)(void *ctx); u8 * (*alloc_eapol)(void *ctx, u8 type, const void *data, u16 data_len, size_t *msg_len, void **data_pos); int (*add_pmkid)(void *ctx, const u8 *bssid, const u8 *pmkid); int (*remove_pmkid)(void *ctx, const u8 *bssid, const u8 *pmkid); void (*set_config_blob)(void *ctx, struct wpa_config_blob *blob); const struct wpa_config_blob * (*get_config_blob)(void *ctx, const char *name); int (*mlme_setprotection)(void *ctx, const u8 *addr, int protection_type, int key_type); int (*update_ft_ies)(void *ctx, const u8 *md, const u8 *ies, size_t ies_len); int (*send_ft_action)(void *ctx, u8 action, const u8 *target_ap, const u8 *ies, size_t ies_len); int (*mark_authenticated)(void *ctx, const u8 *target_ap); #ifdef CONFIG_TDLS int (*tdls_get_capa)(void *ctx, int *tdls_supported, int *tdls_ext_setup); int (*send_tdls_mgmt)(void *ctx, const u8 *dst, u8 action_code, u8 dialog_token, u16 status_code, const u8 *buf, size_t len); int (*tdls_oper)(void *ctx, int oper, const u8 *peer); int (*tdls_peer_addset)(void *ctx, const u8 *addr, int add, u16 aid, u16 capability, const u8 *supp_rates, size_t supp_rates_len, const struct ieee80211_ht_capabilities *ht_capab, const struct ieee80211_vht_capabilities *vht_capab, u8 qosinfo, const u8 *ext_capab, size_t ext_capab_len, const u8 *supp_channels, size_t supp_channels_len, const u8 *supp_oper_classes, size_t supp_oper_classes_len); #endif /* CONFIG_TDLS */ void (*set_rekey_offload)(void *ctx, const u8 *kek, const u8 *kck, const u8 *replay_ctr); }; enum wpa_sm_conf_params { RSNA_PMK_LIFETIME /* dot11RSNAConfigPMKLifetime */, RSNA_PMK_REAUTH_THRESHOLD /* dot11RSNAConfigPMKReauthThreshold */, RSNA_SA_TIMEOUT /* dot11RSNAConfigSATimeout */, WPA_PARAM_PROTO, WPA_PARAM_PAIRWISE, WPA_PARAM_GROUP, WPA_PARAM_KEY_MGMT, WPA_PARAM_MGMT_GROUP, WPA_PARAM_RSN_ENABLED, WPA_PARAM_MFP }; struct rsn_supp_config { void *network_ctx; int peerkey_enabled; int allowed_pairwise_cipher; /* bitfield of WPA_CIPHER_* */ int proactive_key_caching; int eap_workaround; void *eap_conf_ctx; const u8 *ssid; size_t ssid_len; int wpa_ptk_rekey; int p2p; }; #ifndef CONFIG_NO_WPA struct wpa_sm * wpa_sm_init(struct wpa_sm_ctx *ctx); void wpa_sm_deinit(struct wpa_sm *sm); void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid); void wpa_sm_notify_disassoc(struct wpa_sm *sm); void wpa_sm_set_pmk(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len); void wpa_sm_set_pmk_from_pmksa(struct wpa_sm *sm); void wpa_sm_set_fast_reauth(struct wpa_sm *sm, int fast_reauth); void wpa_sm_set_scard_ctx(struct wpa_sm *sm, void *scard_ctx); void wpa_sm_set_config(struct wpa_sm *sm, struct rsn_supp_config *config); void wpa_sm_set_own_addr(struct wpa_sm *sm, const u8 *addr); void wpa_sm_set_ifname(struct wpa_sm *sm, const char *ifname, const char *bridge_ifname); void wpa_sm_set_eapol(struct wpa_sm *sm, struct eapol_sm *eapol); int wpa_sm_set_assoc_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len); int wpa_sm_set_assoc_wpa_ie_default(struct wpa_sm *sm, u8 *wpa_ie, size_t *wpa_ie_len); int wpa_sm_set_ap_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len); int wpa_sm_set_ap_rsn_ie(struct wpa_sm *sm, const u8 *ie, size_t len); int wpa_sm_get_mib(struct wpa_sm *sm, char *buf, size_t buflen); int wpa_sm_set_param(struct wpa_sm *sm, enum wpa_sm_conf_params param, unsigned int value); unsigned int wpa_sm_get_param(struct wpa_sm *sm, enum wpa_sm_conf_params param); int wpa_sm_get_status(struct wpa_sm *sm, char *buf, size_t buflen, int verbose); int wpa_sm_pmf_enabled(struct wpa_sm *sm); void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise); int wpa_parse_wpa_ie(const u8 *wpa_ie, size_t wpa_ie_len, struct wpa_ie_data *data); void wpa_sm_aborted_cached(struct wpa_sm *sm); int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, const u8 *buf, size_t len); int wpa_sm_parse_own_wpa_ie(struct wpa_sm *sm, struct wpa_ie_data *data); int wpa_sm_pmksa_cache_list(struct wpa_sm *sm, char *buf, size_t len); void wpa_sm_drop_sa(struct wpa_sm *sm); int wpa_sm_has_ptk(struct wpa_sm *sm); void wpa_sm_update_replay_ctr(struct wpa_sm *sm, const u8 *replay_ctr); void wpa_sm_pmksa_cache_flush(struct wpa_sm *sm, void *network_ctx); int wpa_sm_get_p2p_ip_addr(struct wpa_sm *sm, u8 *buf); #else /* CONFIG_NO_WPA */ static inline struct wpa_sm * wpa_sm_init(struct wpa_sm_ctx *ctx) { return (struct wpa_sm *) 1; } static inline void wpa_sm_deinit(struct wpa_sm *sm) { } static inline void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid) { } static inline void wpa_sm_notify_disassoc(struct wpa_sm *sm) { } static inline void wpa_sm_set_pmk(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len) { } static inline void wpa_sm_set_pmk_from_pmksa(struct wpa_sm *sm) { } static inline void wpa_sm_set_fast_reauth(struct wpa_sm *sm, int fast_reauth) { } static inline void wpa_sm_set_scard_ctx(struct wpa_sm *sm, void *scard_ctx) { } static inline void wpa_sm_set_config(struct wpa_sm *sm, struct rsn_supp_config *config) { } static inline void wpa_sm_set_own_addr(struct wpa_sm *sm, const u8 *addr) { } static inline void wpa_sm_set_ifname(struct wpa_sm *sm, const char *ifname, const char *bridge_ifname) { } static inline void wpa_sm_set_eapol(struct wpa_sm *sm, struct eapol_sm *eapol) { } static inline int wpa_sm_set_assoc_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len) { return -1; } static inline int wpa_sm_set_assoc_wpa_ie_default(struct wpa_sm *sm, u8 *wpa_ie, size_t *wpa_ie_len) { return -1; } static inline int wpa_sm_set_ap_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len) { return -1; } static inline int wpa_sm_set_ap_rsn_ie(struct wpa_sm *sm, const u8 *ie, size_t len) { return -1; } static inline int wpa_sm_get_mib(struct wpa_sm *sm, char *buf, size_t buflen) { return 0; } static inline int wpa_sm_set_param(struct wpa_sm *sm, enum wpa_sm_conf_params param, unsigned int value) { return -1; } static inline unsigned int wpa_sm_get_param(struct wpa_sm *sm, enum wpa_sm_conf_params param) { return 0; } static inline int wpa_sm_get_status(struct wpa_sm *sm, char *buf, size_t buflen, int verbose) { return 0; } static inline int wpa_sm_pmf_enabled(struct wpa_sm *sm) { return 0; } static inline void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise) { } static inline int wpa_parse_wpa_ie(const u8 *wpa_ie, size_t wpa_ie_len, struct wpa_ie_data *data) { return -1; } static inline void wpa_sm_aborted_cached(struct wpa_sm *sm) { } static inline int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, const u8 *buf, size_t len) { return -1; } static inline int wpa_sm_parse_own_wpa_ie(struct wpa_sm *sm, struct wpa_ie_data *data) { return -1; } static inline int wpa_sm_pmksa_cache_list(struct wpa_sm *sm, char *buf, size_t len) { return -1; } static inline void wpa_sm_drop_sa(struct wpa_sm *sm) { } static inline int wpa_sm_has_ptk(struct wpa_sm *sm) { return 0; } static inline void wpa_sm_update_replay_ctr(struct wpa_sm *sm, const u8 *replay_ctr) { } static inline void wpa_sm_pmksa_cache_flush(struct wpa_sm *sm, void *network_ctx) { } #endif /* CONFIG_NO_WPA */ #ifdef CONFIG_PEERKEY int wpa_sm_stkstart(struct wpa_sm *sm, const u8 *peer); int wpa_sm_rx_eapol_peerkey(struct wpa_sm *sm, const u8 *src_addr, const u8 *buf, size_t len); #else /* CONFIG_PEERKEY */ static inline int wpa_sm_stkstart(struct wpa_sm *sm, const u8 *peer) { return -1; } static inline int wpa_sm_rx_eapol_peerkey(struct wpa_sm *sm, const u8 *src_addr, const u8 *buf, size_t len) { return 0; } #endif /* CONFIG_PEERKEY */ #ifdef CONFIG_IEEE80211R int wpa_sm_set_ft_params(struct wpa_sm *sm, const u8 *ies, size_t ies_len); int wpa_ft_prepare_auth_request(struct wpa_sm *sm, const u8 *mdie); int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len, int ft_action, const u8 *target_ap, const u8 *ric_ies, size_t ric_ies_len); int wpa_ft_is_completed(struct wpa_sm *sm); void wpa_reset_ft_completed(struct wpa_sm *sm); int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, size_t ies_len, const u8 *src_addr); int wpa_ft_start_over_ds(struct wpa_sm *sm, const u8 *target_ap, const u8 *mdie); #else /* CONFIG_IEEE80211R */ static inline int wpa_sm_set_ft_params(struct wpa_sm *sm, const u8 *ies, size_t ies_len) { return 0; } static inline int wpa_ft_prepare_auth_request(struct wpa_sm *sm, const u8 *mdie) { return 0; } static inline int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len, int ft_action, const u8 *target_ap) { return 0; } static inline int wpa_ft_is_completed(struct wpa_sm *sm) { return 0; } static inline void wpa_reset_ft_completed(struct wpa_sm *sm) { } static inline int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, size_t ies_len, const u8 *src_addr) { return -1; } #endif /* CONFIG_IEEE80211R */ /* tdls.c */ void wpa_tdls_ap_ies(struct wpa_sm *sm, const u8 *ies, size_t len); void wpa_tdls_assoc_resp_ies(struct wpa_sm *sm, const u8 *ies, size_t len); int wpa_tdls_start(struct wpa_sm *sm, const u8 *addr); void wpa_tdls_remove(struct wpa_sm *sm, const u8 *addr); int wpa_tdls_send_teardown(struct wpa_sm *sm, const u8 *addr, u16 reason_code); int wpa_tdls_teardown_link(struct wpa_sm *sm, const u8 *addr, u16 reason_code); int wpa_tdls_send_discovery_request(struct wpa_sm *sm, const u8 *addr); int wpa_tdls_init(struct wpa_sm *sm); void wpa_tdls_teardown_peers(struct wpa_sm *sm); void wpa_tdls_deinit(struct wpa_sm *sm); void wpa_tdls_enable(struct wpa_sm *sm, int enabled); void wpa_tdls_disable_link(struct wpa_sm *sm, const u8 *addr); const char * wpa_tdls_get_link_status(struct wpa_sm *sm, const u8 *addr); int wpa_tdls_is_external_setup(struct wpa_sm *sm); int wpa_wnmsleep_install_key(struct wpa_sm *sm, u8 subelem_id, u8 *buf); #endif /* WPA_H */ wpa-2.1/src/rsn_supp/wpa_ft.c000066400000000000000000000562471227414666700162640ustar00rootroot00000000000000/* * WPA Supplicant - IEEE 802.11r - Fast BSS Transition * Copyright (c) 2006-2007, 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/aes_wrap.h" #include "crypto/random.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" #include "wpa.h" #include "wpa_i.h" #ifdef CONFIG_IEEE80211R int wpa_derive_ptk_ft(struct wpa_sm *sm, const unsigned char *src_addr, const struct wpa_eapol_key *key, struct wpa_ptk *ptk, size_t ptk_len) { u8 ptk_name[WPA_PMK_NAME_LEN]; const u8 *anonce = key->key_nonce; if (sm->xxkey_len == 0) { wpa_printf(MSG_DEBUG, "FT: XXKey not available for key " "derivation"); return -1; } wpa_derive_pmk_r0(sm->xxkey, sm->xxkey_len, sm->ssid, sm->ssid_len, sm->mobility_domain, sm->r0kh_id, sm->r0kh_id_len, sm->own_addr, sm->pmk_r0, sm->pmk_r0_name); wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0", sm->pmk_r0, PMK_LEN); wpa_hexdump(MSG_DEBUG, "FT: PMKR0Name", sm->pmk_r0_name, WPA_PMK_NAME_LEN); wpa_derive_pmk_r1(sm->pmk_r0, sm->pmk_r0_name, sm->r1kh_id, sm->own_addr, sm->pmk_r1, sm->pmk_r1_name); wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", sm->pmk_r1, PMK_LEN); wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", sm->pmk_r1_name, WPA_PMK_NAME_LEN); wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->snonce, anonce, sm->own_addr, sm->bssid, sm->pmk_r1_name, (u8 *) ptk, ptk_len, ptk_name); wpa_hexdump_key(MSG_DEBUG, "FT: PTK", (u8 *) ptk, ptk_len); wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN); return 0; } /** * wpa_sm_set_ft_params - Set FT (IEEE 802.11r) parameters * @sm: Pointer to WPA state machine data from wpa_sm_init() * @ies: Association Response IEs or %NULL to clear FT parameters * @ies_len: Length of ies buffer in octets * Returns: 0 on success, -1 on failure */ int wpa_sm_set_ft_params(struct wpa_sm *sm, const u8 *ies, size_t ies_len) { struct wpa_ft_ies ft; if (sm == NULL) return 0; if (wpa_ft_parse_ies(ies, ies_len, &ft) < 0) return -1; if (ft.mdie && ft.mdie_len < MOBILITY_DOMAIN_ID_LEN + 1) return -1; if (ft.mdie) { wpa_hexdump(MSG_DEBUG, "FT: Mobility domain", ft.mdie, MOBILITY_DOMAIN_ID_LEN); os_memcpy(sm->mobility_domain, ft.mdie, MOBILITY_DOMAIN_ID_LEN); sm->mdie_ft_capab = ft.mdie[MOBILITY_DOMAIN_ID_LEN]; wpa_printf(MSG_DEBUG, "FT: Capability and Policy: 0x%02x", sm->mdie_ft_capab); } else os_memset(sm->mobility_domain, 0, MOBILITY_DOMAIN_ID_LEN); if (ft.r0kh_id) { wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID", ft.r0kh_id, ft.r0kh_id_len); os_memcpy(sm->r0kh_id, ft.r0kh_id, ft.r0kh_id_len); sm->r0kh_id_len = ft.r0kh_id_len; } else { /* FIX: When should R0KH-ID be cleared? We need to keep the * old R0KH-ID in order to be able to use this during FT. */ /* * os_memset(sm->r0kh_id, 0, FT_R0KH_ID_LEN); * sm->r0kh_id_len = 0; */ } if (ft.r1kh_id) { wpa_hexdump(MSG_DEBUG, "FT: R1KH-ID", ft.r1kh_id, FT_R1KH_ID_LEN); os_memcpy(sm->r1kh_id, ft.r1kh_id, FT_R1KH_ID_LEN); } else os_memset(sm->r1kh_id, 0, FT_R1KH_ID_LEN); os_free(sm->assoc_resp_ies); sm->assoc_resp_ies = os_malloc(ft.mdie_len + 2 + ft.ftie_len + 2); if (sm->assoc_resp_ies) { u8 *pos = sm->assoc_resp_ies; if (ft.mdie) { os_memcpy(pos, ft.mdie - 2, ft.mdie_len + 2); pos += ft.mdie_len + 2; } if (ft.ftie) { os_memcpy(pos, ft.ftie - 2, ft.ftie_len + 2); pos += ft.ftie_len + 2; } sm->assoc_resp_ies_len = pos - sm->assoc_resp_ies; wpa_hexdump(MSG_DEBUG, "FT: Stored MDIE and FTIE from " "(Re)Association Response", sm->assoc_resp_ies, sm->assoc_resp_ies_len); } return 0; } /** * wpa_ft_gen_req_ies - Generate FT (IEEE 802.11r) IEs for Auth/ReAssoc Request * @sm: Pointer to WPA state machine data from wpa_sm_init() * @len: Buffer for returning the length of the IEs * @anonce: ANonce or %NULL if not yet available * @pmk_name: PMKR0Name or PMKR1Name to be added into the RSN IE PMKID List * @kck: 128-bit KCK for MIC or %NULL if no MIC is used * @target_ap: Target AP address * @ric_ies: Optional IE(s), e.g., WMM TSPEC(s), for RIC-Request or %NULL * @ric_ies_len: Length of ric_ies buffer in octets * @ap_mdie: Mobility Domain IE from the target AP * Returns: Pointer to buffer with IEs or %NULL on failure * * Caller is responsible for freeing the returned buffer with os_free(); */ static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len, const u8 *anonce, const u8 *pmk_name, const u8 *kck, const u8 *target_ap, const u8 *ric_ies, size_t ric_ies_len, const u8 *ap_mdie) { size_t buf_len; u8 *buf, *pos, *ftie_len, *ftie_pos; struct rsn_mdie *mdie; struct rsn_ftie *ftie; struct rsn_ie_hdr *rsnie; u16 capab; sm->ft_completed = 0; buf_len = 2 + sizeof(struct rsn_mdie) + 2 + sizeof(struct rsn_ftie) + 2 + sm->r0kh_id_len + ric_ies_len + 100; buf = os_zalloc(buf_len); if (buf == NULL) return NULL; pos = buf; /* RSNIE[PMKR0Name/PMKR1Name] */ rsnie = (struct rsn_ie_hdr *) pos; rsnie->elem_id = WLAN_EID_RSN; WPA_PUT_LE16(rsnie->version, RSN_VERSION); pos = (u8 *) (rsnie + 1); /* Group Suite Selector */ if (sm->group_cipher != WPA_CIPHER_CCMP && sm->group_cipher != WPA_CIPHER_GCMP && sm->group_cipher != WPA_CIPHER_TKIP) { wpa_printf(MSG_WARNING, "FT: Invalid group cipher (%d)", sm->group_cipher); os_free(buf); return NULL; } RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN, sm->group_cipher)); pos += RSN_SELECTOR_LEN; /* Pairwise Suite Count */ WPA_PUT_LE16(pos, 1); pos += 2; /* Pairwise Suite List */ if (!wpa_cipher_valid_pairwise(sm->pairwise_cipher)) { wpa_printf(MSG_WARNING, "FT: Invalid pairwise cipher (%d)", sm->pairwise_cipher); os_free(buf); return NULL; } RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN, sm->pairwise_cipher)); pos += RSN_SELECTOR_LEN; /* Authenticated Key Management Suite Count */ WPA_PUT_LE16(pos, 1); pos += 2; /* Authenticated Key Management Suite List */ if (sm->key_mgmt == WPA_KEY_MGMT_FT_IEEE8021X) RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X); else if (sm->key_mgmt == WPA_KEY_MGMT_FT_PSK) RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_PSK); else if (sm->key_mgmt == WPA_KEY_MGMT_FT_SAE) RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_SAE); else { wpa_printf(MSG_WARNING, "FT: Invalid key management type (%d)", sm->key_mgmt); os_free(buf); return NULL; } pos += RSN_SELECTOR_LEN; /* RSN Capabilities */ capab = 0; #ifdef CONFIG_IEEE80211W if (sm->mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC) capab |= WPA_CAPABILITY_MFPC; #endif /* CONFIG_IEEE80211W */ WPA_PUT_LE16(pos, capab); pos += 2; /* PMKID Count */ WPA_PUT_LE16(pos, 1); pos += 2; /* PMKID List [PMKR0Name/PMKR1Name] */ os_memcpy(pos, pmk_name, WPA_PMK_NAME_LEN); pos += WPA_PMK_NAME_LEN; #ifdef CONFIG_IEEE80211W if (sm->mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC) { /* Management Group Cipher Suite */ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_AES_128_CMAC); pos += RSN_SELECTOR_LEN; } #endif /* CONFIG_IEEE80211W */ rsnie->len = (pos - (u8 *) rsnie) - 2; /* MDIE */ *pos++ = WLAN_EID_MOBILITY_DOMAIN; *pos++ = sizeof(*mdie); mdie = (struct rsn_mdie *) pos; pos += sizeof(*mdie); os_memcpy(mdie->mobility_domain, sm->mobility_domain, MOBILITY_DOMAIN_ID_LEN); mdie->ft_capab = ap_mdie && ap_mdie[1] >= 3 ? ap_mdie[4] : sm->mdie_ft_capab; /* FTIE[SNonce, [R1KH-ID,] R0KH-ID ] */ ftie_pos = pos; *pos++ = WLAN_EID_FAST_BSS_TRANSITION; ftie_len = pos++; ftie = (struct rsn_ftie *) pos; pos += sizeof(*ftie); os_memcpy(ftie->snonce, sm->snonce, WPA_NONCE_LEN); if (anonce) os_memcpy(ftie->anonce, anonce, WPA_NONCE_LEN); if (kck) { /* R1KH-ID sub-element in third FT message */ *pos++ = FTIE_SUBELEM_R1KH_ID; *pos++ = FT_R1KH_ID_LEN; os_memcpy(pos, sm->r1kh_id, FT_R1KH_ID_LEN); pos += FT_R1KH_ID_LEN; } /* R0KH-ID sub-element */ *pos++ = FTIE_SUBELEM_R0KH_ID; *pos++ = sm->r0kh_id_len; os_memcpy(pos, sm->r0kh_id, sm->r0kh_id_len); pos += sm->r0kh_id_len; *ftie_len = pos - ftie_len - 1; if (ric_ies) { /* RIC Request */ os_memcpy(pos, ric_ies, ric_ies_len); pos += ric_ies_len; } if (kck) { /* * IEEE Std 802.11r-2008, 11A.8.4 * MIC shall be calculated over: * non-AP STA MAC address * Target AP MAC address * Transaction seq number (5 for ReassocReq, 3 otherwise) * RSN IE * MDIE * FTIE (with MIC field set to 0) * RIC-Request (if present) */ /* Information element count */ ftie->mic_control[1] = 3 + ieee802_11_ie_count(ric_ies, ric_ies_len); if (wpa_ft_mic(kck, sm->own_addr, target_ap, 5, ((u8 *) mdie) - 2, 2 + sizeof(*mdie), ftie_pos, 2 + *ftie_len, (u8 *) rsnie, 2 + rsnie->len, ric_ies, ric_ies_len, ftie->mic) < 0) { wpa_printf(MSG_INFO, "FT: Failed to calculate MIC"); os_free(buf); return NULL; } } *len = pos - buf; return buf; } static int wpa_ft_install_ptk(struct wpa_sm *sm, const u8 *bssid) { int keylen; enum wpa_alg alg; u8 null_rsc[6] = { 0, 0, 0, 0, 0, 0 }; wpa_printf(MSG_DEBUG, "FT: Installing PTK to the driver."); if (!wpa_cipher_valid_pairwise(sm->pairwise_cipher)) { wpa_printf(MSG_WARNING, "FT: Unsupported pairwise cipher %d", sm->pairwise_cipher); return -1; } alg = wpa_cipher_to_alg(sm->pairwise_cipher); keylen = wpa_cipher_key_len(sm->pairwise_cipher); if (wpa_sm_set_key(sm, alg, bssid, 0, 1, null_rsc, sizeof(null_rsc), (u8 *) sm->ptk.tk1, keylen) < 0) { wpa_printf(MSG_WARNING, "FT: Failed to set PTK to the driver"); return -1; } return 0; } /** * wpa_ft_prepare_auth_request - Generate over-the-air auth request * @sm: Pointer to WPA state machine data from wpa_sm_init() * @mdie: Target AP MDIE * Returns: 0 on success, -1 on failure */ int wpa_ft_prepare_auth_request(struct wpa_sm *sm, const u8 *mdie) { u8 *ft_ies; size_t ft_ies_len; /* Generate a new SNonce */ if (random_get_bytes(sm->snonce, WPA_NONCE_LEN)) { wpa_printf(MSG_INFO, "FT: Failed to generate a new SNonce"); return -1; } ft_ies = wpa_ft_gen_req_ies(sm, &ft_ies_len, NULL, sm->pmk_r0_name, NULL, sm->bssid, NULL, 0, mdie); if (ft_ies) { wpa_sm_update_ft_ies(sm, sm->mobility_domain, ft_ies, ft_ies_len); os_free(ft_ies); } return 0; } int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len, int ft_action, const u8 *target_ap, const u8 *ric_ies, size_t ric_ies_len) { u8 *ft_ies; size_t ft_ies_len, ptk_len; struct wpa_ft_ies parse; struct rsn_mdie *mdie; struct rsn_ftie *ftie; u8 ptk_name[WPA_PMK_NAME_LEN]; int ret; const u8 *bssid; wpa_hexdump(MSG_DEBUG, "FT: Response IEs", ies, ies_len); wpa_hexdump(MSG_DEBUG, "FT: RIC IEs", ric_ies, ric_ies_len); if (ft_action) { if (!sm->over_the_ds_in_progress) { wpa_printf(MSG_DEBUG, "FT: No over-the-DS in progress " "- drop FT Action Response"); return -1; } if (os_memcmp(target_ap, sm->target_ap, ETH_ALEN) != 0) { wpa_printf(MSG_DEBUG, "FT: No over-the-DS in progress " "with this Target AP - drop FT Action " "Response"); return -1; } } if (!wpa_key_mgmt_ft(sm->key_mgmt)) { wpa_printf(MSG_DEBUG, "FT: Reject FT IEs since FT is not " "enabled for this connection"); return -1; } if (wpa_ft_parse_ies(ies, ies_len, &parse) < 0) { wpa_printf(MSG_DEBUG, "FT: Failed to parse IEs"); return -1; } mdie = (struct rsn_mdie *) parse.mdie; if (mdie == NULL || parse.mdie_len < sizeof(*mdie) || os_memcmp(mdie->mobility_domain, sm->mobility_domain, MOBILITY_DOMAIN_ID_LEN) != 0) { wpa_printf(MSG_DEBUG, "FT: Invalid MDIE"); return -1; } ftie = (struct rsn_ftie *) parse.ftie; if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) { wpa_printf(MSG_DEBUG, "FT: Invalid FTIE"); return -1; } if (os_memcmp(ftie->snonce, sm->snonce, WPA_NONCE_LEN) != 0) { wpa_printf(MSG_DEBUG, "FT: SNonce mismatch in FTIE"); wpa_hexdump(MSG_DEBUG, "FT: Received SNonce", ftie->snonce, WPA_NONCE_LEN); wpa_hexdump(MSG_DEBUG, "FT: Expected SNonce", sm->snonce, WPA_NONCE_LEN); return -1; } if (parse.r0kh_id == NULL) { wpa_printf(MSG_DEBUG, "FT: No R0KH-ID subelem in FTIE"); return -1; } if (parse.r0kh_id_len != sm->r0kh_id_len || os_memcmp(parse.r0kh_id, sm->r0kh_id, parse.r0kh_id_len) != 0) { wpa_printf(MSG_DEBUG, "FT: R0KH-ID in FTIE did not match with " "the current R0KH-ID"); wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID in FTIE", parse.r0kh_id, parse.r0kh_id_len); wpa_hexdump(MSG_DEBUG, "FT: The current R0KH-ID", sm->r0kh_id, sm->r0kh_id_len); return -1; } if (parse.r1kh_id == NULL) { wpa_printf(MSG_DEBUG, "FT: No R1KH-ID subelem in FTIE"); return -1; } if (parse.rsn_pmkid == NULL || os_memcmp(parse.rsn_pmkid, sm->pmk_r0_name, WPA_PMK_NAME_LEN)) { wpa_printf(MSG_DEBUG, "FT: No matching PMKR0Name (PMKID) in " "RSNIE"); return -1; } os_memcpy(sm->r1kh_id, parse.r1kh_id, FT_R1KH_ID_LEN); wpa_hexdump(MSG_DEBUG, "FT: R1KH-ID", sm->r1kh_id, FT_R1KH_ID_LEN); wpa_hexdump(MSG_DEBUG, "FT: SNonce", sm->snonce, WPA_NONCE_LEN); wpa_hexdump(MSG_DEBUG, "FT: ANonce", ftie->anonce, WPA_NONCE_LEN); os_memcpy(sm->anonce, ftie->anonce, WPA_NONCE_LEN); wpa_derive_pmk_r1(sm->pmk_r0, sm->pmk_r0_name, sm->r1kh_id, sm->own_addr, sm->pmk_r1, sm->pmk_r1_name); wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", sm->pmk_r1, PMK_LEN); wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", sm->pmk_r1_name, WPA_PMK_NAME_LEN); bssid = target_ap; ptk_len = sm->pairwise_cipher != WPA_CIPHER_TKIP ? 48 : 64; wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->snonce, ftie->anonce, sm->own_addr, bssid, sm->pmk_r1_name, (u8 *) &sm->ptk, ptk_len, ptk_name); wpa_hexdump_key(MSG_DEBUG, "FT: PTK", (u8 *) &sm->ptk, ptk_len); wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN); ft_ies = wpa_ft_gen_req_ies(sm, &ft_ies_len, ftie->anonce, sm->pmk_r1_name, sm->ptk.kck, bssid, ric_ies, ric_ies_len, parse.mdie ? parse.mdie - 2 : NULL); if (ft_ies) { wpa_sm_update_ft_ies(sm, sm->mobility_domain, ft_ies, ft_ies_len); os_free(ft_ies); } wpa_sm_mark_authenticated(sm, bssid); ret = wpa_ft_install_ptk(sm, bssid); if (ret) { /* * Some drivers do not support key configuration when we are * not associated with the target AP. Work around this by * trying again after the following reassociation gets * completed. */ wpa_printf(MSG_DEBUG, "FT: Failed to set PTK prior to " "association - try again after reassociation"); sm->set_ptk_after_assoc = 1; } else sm->set_ptk_after_assoc = 0; sm->ft_completed = 1; if (ft_action) { /* * The caller is expected trigger re-association with the * Target AP. */ os_memcpy(sm->bssid, target_ap, ETH_ALEN); } return 0; } int wpa_ft_is_completed(struct wpa_sm *sm) { if (sm == NULL) return 0; if (!wpa_key_mgmt_ft(sm->key_mgmt)) return 0; return sm->ft_completed; } void wpa_reset_ft_completed(struct wpa_sm *sm) { if (sm != NULL) sm->ft_completed = 0; } static int wpa_ft_process_gtk_subelem(struct wpa_sm *sm, const u8 *gtk_elem, size_t gtk_elem_len) { u8 gtk[32]; int keyidx; enum wpa_alg alg; size_t gtk_len, keylen, rsc_len; if (gtk_elem == NULL) { wpa_printf(MSG_DEBUG, "FT: No GTK included in FTIE"); return 0; } wpa_hexdump_key(MSG_DEBUG, "FT: Received GTK in Reassoc Resp", gtk_elem, gtk_elem_len); if (gtk_elem_len < 11 + 24 || (gtk_elem_len - 11) % 8 || gtk_elem_len - 19 > sizeof(gtk)) { wpa_printf(MSG_DEBUG, "FT: Invalid GTK sub-elem " "length %lu", (unsigned long) gtk_elem_len); return -1; } gtk_len = gtk_elem_len - 19; if (aes_unwrap(sm->ptk.kek, gtk_len / 8, gtk_elem + 11, gtk)) { wpa_printf(MSG_WARNING, "FT: AES unwrap failed - could not " "decrypt GTK"); return -1; } keylen = wpa_cipher_key_len(sm->group_cipher); rsc_len = wpa_cipher_rsc_len(sm->group_cipher); alg = wpa_cipher_to_alg(sm->group_cipher); if (alg == WPA_ALG_NONE) { wpa_printf(MSG_WARNING, "WPA: Unsupported Group Cipher %d", sm->group_cipher); return -1; } if (gtk_len < keylen) { wpa_printf(MSG_DEBUG, "FT: Too short GTK in FTIE"); return -1; } /* Key Info[2] | Key Length[1] | RSC[8] | Key[5..32]. */ keyidx = WPA_GET_LE16(gtk_elem) & 0x03; if (gtk_elem[2] != keylen) { wpa_printf(MSG_DEBUG, "FT: GTK length mismatch: received %d " "negotiated %lu", gtk_elem[2], (unsigned long) keylen); return -1; } wpa_hexdump_key(MSG_DEBUG, "FT: GTK from Reassoc Resp", gtk, keylen); if (sm->group_cipher == WPA_CIPHER_TKIP) { /* Swap Tx/Rx keys for Michael MIC */ u8 tmp[8]; os_memcpy(tmp, gtk + 16, 8); os_memcpy(gtk + 16, gtk + 24, 8); os_memcpy(gtk + 24, tmp, 8); } if (wpa_sm_set_key(sm, alg, broadcast_ether_addr, keyidx, 0, gtk_elem + 3, rsc_len, gtk, keylen) < 0) { wpa_printf(MSG_WARNING, "WPA: Failed to set GTK to the " "driver."); return -1; } return 0; } #ifdef CONFIG_IEEE80211W static int wpa_ft_process_igtk_subelem(struct wpa_sm *sm, const u8 *igtk_elem, size_t igtk_elem_len) { u8 igtk[WPA_IGTK_LEN]; u16 keyidx; if (sm->mgmt_group_cipher != WPA_CIPHER_AES_128_CMAC) return 0; if (igtk_elem == NULL) { wpa_printf(MSG_DEBUG, "FT: No IGTK included in FTIE"); return 0; } wpa_hexdump_key(MSG_DEBUG, "FT: Received IGTK in Reassoc Resp", igtk_elem, igtk_elem_len); if (igtk_elem_len != 2 + 6 + 1 + WPA_IGTK_LEN + 8) { wpa_printf(MSG_DEBUG, "FT: Invalid IGTK sub-elem " "length %lu", (unsigned long) igtk_elem_len); return -1; } if (igtk_elem[8] != WPA_IGTK_LEN) { wpa_printf(MSG_DEBUG, "FT: Invalid IGTK sub-elem Key Length " "%d", igtk_elem[8]); return -1; } if (aes_unwrap(sm->ptk.kek, WPA_IGTK_LEN / 8, igtk_elem + 9, igtk)) { wpa_printf(MSG_WARNING, "FT: AES unwrap failed - could not " "decrypt IGTK"); return -1; } /* KeyID[2] | IPN[6] | Key Length[1] | Key[16+8] */ keyidx = WPA_GET_LE16(igtk_elem); wpa_hexdump_key(MSG_DEBUG, "FT: IGTK from Reassoc Resp", igtk, WPA_IGTK_LEN); if (wpa_sm_set_key(sm, WPA_ALG_IGTK, broadcast_ether_addr, keyidx, 0, igtk_elem + 2, 6, igtk, WPA_IGTK_LEN) < 0) { wpa_printf(MSG_WARNING, "WPA: Failed to set IGTK to the " "driver."); return -1; } return 0; } #endif /* CONFIG_IEEE80211W */ int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, size_t ies_len, const u8 *src_addr) { struct wpa_ft_ies parse; struct rsn_mdie *mdie; struct rsn_ftie *ftie; unsigned int count; u8 mic[16]; wpa_hexdump(MSG_DEBUG, "FT: Response IEs", ies, ies_len); if (!wpa_key_mgmt_ft(sm->key_mgmt)) { wpa_printf(MSG_DEBUG, "FT: Reject FT IEs since FT is not " "enabled for this connection"); return -1; } if (wpa_ft_parse_ies(ies, ies_len, &parse) < 0) { wpa_printf(MSG_DEBUG, "FT: Failed to parse IEs"); return -1; } mdie = (struct rsn_mdie *) parse.mdie; if (mdie == NULL || parse.mdie_len < sizeof(*mdie) || os_memcmp(mdie->mobility_domain, sm->mobility_domain, MOBILITY_DOMAIN_ID_LEN) != 0) { wpa_printf(MSG_DEBUG, "FT: Invalid MDIE"); return -1; } ftie = (struct rsn_ftie *) parse.ftie; if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) { wpa_printf(MSG_DEBUG, "FT: Invalid FTIE"); return -1; } if (os_memcmp(ftie->snonce, sm->snonce, WPA_NONCE_LEN) != 0) { wpa_printf(MSG_DEBUG, "FT: SNonce mismatch in FTIE"); wpa_hexdump(MSG_DEBUG, "FT: Received SNonce", ftie->snonce, WPA_NONCE_LEN); wpa_hexdump(MSG_DEBUG, "FT: Expected SNonce", sm->snonce, WPA_NONCE_LEN); return -1; } if (os_memcmp(ftie->anonce, sm->anonce, WPA_NONCE_LEN) != 0) { wpa_printf(MSG_DEBUG, "FT: ANonce mismatch in FTIE"); wpa_hexdump(MSG_DEBUG, "FT: Received ANonce", ftie->anonce, WPA_NONCE_LEN); wpa_hexdump(MSG_DEBUG, "FT: Expected ANonce", sm->anonce, WPA_NONCE_LEN); return -1; } if (parse.r0kh_id == NULL) { wpa_printf(MSG_DEBUG, "FT: No R0KH-ID subelem in FTIE"); return -1; } if (parse.r0kh_id_len != sm->r0kh_id_len || os_memcmp(parse.r0kh_id, sm->r0kh_id, parse.r0kh_id_len) != 0) { wpa_printf(MSG_DEBUG, "FT: R0KH-ID in FTIE did not match with " "the current R0KH-ID"); wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID in FTIE", parse.r0kh_id, parse.r0kh_id_len); wpa_hexdump(MSG_DEBUG, "FT: The current R0KH-ID", sm->r0kh_id, sm->r0kh_id_len); return -1; } if (parse.r1kh_id == NULL) { wpa_printf(MSG_DEBUG, "FT: No R1KH-ID subelem in FTIE"); return -1; } if (os_memcmp(parse.r1kh_id, sm->r1kh_id, FT_R1KH_ID_LEN) != 0) { wpa_printf(MSG_DEBUG, "FT: Unknown R1KH-ID used in " "ReassocResp"); return -1; } if (parse.rsn_pmkid == NULL || os_memcmp(parse.rsn_pmkid, sm->pmk_r1_name, WPA_PMK_NAME_LEN)) { wpa_printf(MSG_DEBUG, "FT: No matching PMKR1Name (PMKID) in " "RSNIE (pmkid=%d)", !!parse.rsn_pmkid); return -1; } count = 3; if (parse.ric) count += ieee802_11_ie_count(parse.ric, parse.ric_len); if (ftie->mic_control[1] != count) { wpa_printf(MSG_DEBUG, "FT: Unexpected IE count in MIC " "Control: received %u expected %u", ftie->mic_control[1], count); return -1; } if (wpa_ft_mic(sm->ptk.kck, sm->own_addr, src_addr, 6, parse.mdie - 2, parse.mdie_len + 2, parse.ftie - 2, parse.ftie_len + 2, parse.rsn - 2, parse.rsn_len + 2, parse.ric, parse.ric_len, mic) < 0) { wpa_printf(MSG_DEBUG, "FT: Failed to calculate MIC"); return -1; } if (os_memcmp(mic, ftie->mic, 16) != 0) { wpa_printf(MSG_DEBUG, "FT: Invalid MIC in FTIE"); wpa_hexdump(MSG_MSGDUMP, "FT: Received MIC", ftie->mic, 16); wpa_hexdump(MSG_MSGDUMP, "FT: Calculated MIC", mic, 16); return -1; } if (wpa_ft_process_gtk_subelem(sm, parse.gtk, parse.gtk_len) < 0) return -1; #ifdef CONFIG_IEEE80211W if (wpa_ft_process_igtk_subelem(sm, parse.igtk, parse.igtk_len) < 0) return -1; #endif /* CONFIG_IEEE80211W */ if (sm->set_ptk_after_assoc) { wpa_printf(MSG_DEBUG, "FT: Try to set PTK again now that we " "are associated"); if (wpa_ft_install_ptk(sm, src_addr) < 0) return -1; sm->set_ptk_after_assoc = 0; } if (parse.ric) { wpa_hexdump(MSG_MSGDUMP, "FT: RIC Response", parse.ric, parse.ric_len); /* TODO: parse response and inform driver about results when * using wpa_supplicant SME */ } wpa_printf(MSG_DEBUG, "FT: Completed successfully"); return 0; } /** * wpa_ft_start_over_ds - Generate over-the-DS auth request * @sm: Pointer to WPA state machine data from wpa_sm_init() * @target_ap: Target AP Address * @mdie: Mobility Domain IE from the target AP * Returns: 0 on success, -1 on failure */ int wpa_ft_start_over_ds(struct wpa_sm *sm, const u8 *target_ap, const u8 *mdie) { u8 *ft_ies; size_t ft_ies_len; wpa_printf(MSG_DEBUG, "FT: Request over-the-DS with " MACSTR, MAC2STR(target_ap)); /* Generate a new SNonce */ if (random_get_bytes(sm->snonce, WPA_NONCE_LEN)) { wpa_printf(MSG_INFO, "FT: Failed to generate a new SNonce"); return -1; } ft_ies = wpa_ft_gen_req_ies(sm, &ft_ies_len, NULL, sm->pmk_r0_name, NULL, target_ap, NULL, 0, mdie); if (ft_ies) { sm->over_the_ds_in_progress = 1; os_memcpy(sm->target_ap, target_ap, ETH_ALEN); wpa_sm_send_ft_action(sm, 1, target_ap, ft_ies, ft_ies_len); os_free(ft_ies); } return 0; } #endif /* CONFIG_IEEE80211R */ wpa-2.1/src/rsn_supp/wpa_i.h000066400000000000000000000223711227414666700160770ustar00rootroot00000000000000/* * Internal WPA/RSN supplicant state machine definitions * Copyright (c) 2004-2010, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef WPA_I_H #define WPA_I_H #include "utils/list.h" struct wpa_peerkey; struct wpa_tdls_peer; struct wpa_eapol_key; /** * struct wpa_sm - Internal WPA state machine data */ struct wpa_sm { u8 pmk[PMK_LEN]; size_t pmk_len; struct wpa_ptk ptk, tptk; int ptk_set, tptk_set; u8 snonce[WPA_NONCE_LEN]; u8 anonce[WPA_NONCE_LEN]; /* ANonce from the last 1/4 msg */ int renew_snonce; u8 rx_replay_counter[WPA_REPLAY_COUNTER_LEN]; int rx_replay_counter_set; u8 request_counter[WPA_REPLAY_COUNTER_LEN]; struct eapol_sm *eapol; /* EAPOL state machine from upper level code */ struct rsn_pmksa_cache *pmksa; /* PMKSA cache */ struct rsn_pmksa_cache_entry *cur_pmksa; /* current PMKSA entry */ struct dl_list pmksa_candidates; struct l2_packet_data *l2_preauth; struct l2_packet_data *l2_preauth_br; struct l2_packet_data *l2_tdls; u8 preauth_bssid[ETH_ALEN]; /* current RSN pre-auth peer or * 00:00:00:00:00:00 if no pre-auth is * in progress */ struct eapol_sm *preauth_eapol; struct wpa_sm_ctx *ctx; void *scard_ctx; /* context for smartcard callbacks */ int fast_reauth; /* whether EAP fast re-authentication is enabled */ void *network_ctx; int peerkey_enabled; int allowed_pairwise_cipher; /* bitfield of WPA_CIPHER_* */ int proactive_key_caching; int eap_workaround; void *eap_conf_ctx; u8 ssid[32]; size_t ssid_len; int wpa_ptk_rekey; int p2p; u8 own_addr[ETH_ALEN]; const char *ifname; const char *bridge_ifname; u8 bssid[ETH_ALEN]; unsigned int dot11RSNAConfigPMKLifetime; unsigned int dot11RSNAConfigPMKReauthThreshold; unsigned int dot11RSNAConfigSATimeout; unsigned int dot11RSNA4WayHandshakeFailures; /* Selected configuration (based on Beacon/ProbeResp WPA IE) */ unsigned int proto; unsigned int pairwise_cipher; unsigned int group_cipher; unsigned int key_mgmt; unsigned int mgmt_group_cipher; int rsn_enabled; /* Whether RSN is enabled in configuration */ int mfp; /* 0 = disabled, 1 = optional, 2 = mandatory */ u8 *assoc_wpa_ie; /* Own WPA/RSN IE from (Re)AssocReq */ size_t assoc_wpa_ie_len; u8 *ap_wpa_ie, *ap_rsn_ie; size_t ap_wpa_ie_len, ap_rsn_ie_len; #ifdef CONFIG_PEERKEY struct wpa_peerkey *peerkey; #endif /* CONFIG_PEERKEY */ #ifdef CONFIG_TDLS struct wpa_tdls_peer *tdls; int tdls_prohibited; int tdls_disabled; /* The driver supports TDLS */ int tdls_supported; /* * The driver requires explicit discovery/setup/teardown frames sent * to it via tdls_mgmt. */ int tdls_external_setup; #endif /* CONFIG_TDLS */ #ifdef CONFIG_IEEE80211R u8 xxkey[PMK_LEN]; /* PSK or the second 256 bits of MSK */ size_t xxkey_len; u8 pmk_r0[PMK_LEN]; u8 pmk_r0_name[WPA_PMK_NAME_LEN]; u8 pmk_r1[PMK_LEN]; u8 pmk_r1_name[WPA_PMK_NAME_LEN]; u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN]; u8 r0kh_id[FT_R0KH_ID_MAX_LEN]; size_t r0kh_id_len; u8 r1kh_id[FT_R1KH_ID_LEN]; int ft_completed; int over_the_ds_in_progress; u8 target_ap[ETH_ALEN]; /* over-the-DS target AP */ int set_ptk_after_assoc; u8 mdie_ft_capab; /* FT Capability and Policy from target AP MDIE */ u8 *assoc_resp_ies; /* MDIE and FTIE from (Re)Association Response */ size_t assoc_resp_ies_len; #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_P2P u8 p2p_ip_addr[3 * 4]; #endif /* CONFIG_P2P */ }; static inline void wpa_sm_set_state(struct wpa_sm *sm, enum wpa_states state) { WPA_ASSERT(sm->ctx->set_state); sm->ctx->set_state(sm->ctx->ctx, state); } static inline enum wpa_states wpa_sm_get_state(struct wpa_sm *sm) { WPA_ASSERT(sm->ctx->get_state); return sm->ctx->get_state(sm->ctx->ctx); } static inline void wpa_sm_deauthenticate(struct wpa_sm *sm, int reason_code) { WPA_ASSERT(sm->ctx->deauthenticate); sm->ctx->deauthenticate(sm->ctx->ctx, reason_code); } static inline int wpa_sm_set_key(struct wpa_sm *sm, enum wpa_alg alg, const u8 *addr, int key_idx, int set_tx, const u8 *seq, size_t seq_len, const u8 *key, size_t key_len) { WPA_ASSERT(sm->ctx->set_key); return sm->ctx->set_key(sm->ctx->ctx, alg, addr, key_idx, set_tx, seq, seq_len, key, key_len); } static inline void * wpa_sm_get_network_ctx(struct wpa_sm *sm) { WPA_ASSERT(sm->ctx->get_network_ctx); return sm->ctx->get_network_ctx(sm->ctx->ctx); } static inline int wpa_sm_get_bssid(struct wpa_sm *sm, u8 *bssid) { WPA_ASSERT(sm->ctx->get_bssid); return sm->ctx->get_bssid(sm->ctx->ctx, bssid); } static inline int wpa_sm_ether_send(struct wpa_sm *sm, const u8 *dest, u16 proto, const u8 *buf, size_t len) { WPA_ASSERT(sm->ctx->ether_send); return sm->ctx->ether_send(sm->ctx->ctx, dest, proto, buf, len); } static inline int wpa_sm_get_beacon_ie(struct wpa_sm *sm) { WPA_ASSERT(sm->ctx->get_beacon_ie); return sm->ctx->get_beacon_ie(sm->ctx->ctx); } static inline void wpa_sm_cancel_auth_timeout(struct wpa_sm *sm) { WPA_ASSERT(sm->ctx->cancel_auth_timeout); sm->ctx->cancel_auth_timeout(sm->ctx->ctx); } static inline u8 * wpa_sm_alloc_eapol(struct wpa_sm *sm, u8 type, const void *data, u16 data_len, size_t *msg_len, void **data_pos) { WPA_ASSERT(sm->ctx->alloc_eapol); return sm->ctx->alloc_eapol(sm->ctx->ctx, type, data, data_len, msg_len, data_pos); } static inline int wpa_sm_add_pmkid(struct wpa_sm *sm, const u8 *bssid, const u8 *pmkid) { WPA_ASSERT(sm->ctx->add_pmkid); return sm->ctx->add_pmkid(sm->ctx->ctx, bssid, pmkid); } static inline int wpa_sm_remove_pmkid(struct wpa_sm *sm, const u8 *bssid, const u8 *pmkid) { WPA_ASSERT(sm->ctx->remove_pmkid); return sm->ctx->remove_pmkid(sm->ctx->ctx, bssid, pmkid); } static inline int wpa_sm_mlme_setprotection(struct wpa_sm *sm, const u8 *addr, int protect_type, int key_type) { WPA_ASSERT(sm->ctx->mlme_setprotection); return sm->ctx->mlme_setprotection(sm->ctx->ctx, addr, protect_type, key_type); } static inline int wpa_sm_update_ft_ies(struct wpa_sm *sm, const u8 *md, const u8 *ies, size_t ies_len) { if (sm->ctx->update_ft_ies) return sm->ctx->update_ft_ies(sm->ctx->ctx, md, ies, ies_len); return -1; } static inline int wpa_sm_send_ft_action(struct wpa_sm *sm, u8 action, const u8 *target_ap, const u8 *ies, size_t ies_len) { if (sm->ctx->send_ft_action) return sm->ctx->send_ft_action(sm->ctx->ctx, action, target_ap, ies, ies_len); return -1; } static inline int wpa_sm_mark_authenticated(struct wpa_sm *sm, const u8 *target_ap) { if (sm->ctx->mark_authenticated) return sm->ctx->mark_authenticated(sm->ctx->ctx, target_ap); return -1; } static inline void wpa_sm_set_rekey_offload(struct wpa_sm *sm) { if (!sm->ctx->set_rekey_offload) return; sm->ctx->set_rekey_offload(sm->ctx->ctx, sm->ptk.kek, sm->ptk.kck, sm->rx_replay_counter); } #ifdef CONFIG_TDLS static inline int wpa_sm_tdls_get_capa(struct wpa_sm *sm, int *tdls_supported, int *tdls_ext_setup) { if (sm->ctx->tdls_get_capa) return sm->ctx->tdls_get_capa(sm->ctx->ctx, tdls_supported, tdls_ext_setup); return -1; } static inline int wpa_sm_send_tdls_mgmt(struct wpa_sm *sm, const u8 *dst, u8 action_code, u8 dialog_token, u16 status_code, const u8 *buf, size_t len) { if (sm->ctx->send_tdls_mgmt) return sm->ctx->send_tdls_mgmt(sm->ctx->ctx, dst, action_code, dialog_token, status_code, buf, len); return -1; } static inline int wpa_sm_tdls_oper(struct wpa_sm *sm, int oper, const u8 *peer) { if (sm->ctx->tdls_oper) return sm->ctx->tdls_oper(sm->ctx->ctx, oper, peer); return -1; } static inline int wpa_sm_tdls_peer_addset(struct wpa_sm *sm, const u8 *addr, int add, u16 aid, u16 capability, const u8 *supp_rates, size_t supp_rates_len, const struct ieee80211_ht_capabilities *ht_capab, const struct ieee80211_vht_capabilities *vht_capab, u8 qosinfo, const u8 *ext_capab, size_t ext_capab_len, const u8 *supp_channels, size_t supp_channels_len, const u8 *supp_oper_classes, size_t supp_oper_classes_len) { if (sm->ctx->tdls_peer_addset) return sm->ctx->tdls_peer_addset(sm->ctx->ctx, addr, add, aid, capability, supp_rates, supp_rates_len, ht_capab, vht_capab, qosinfo, ext_capab, ext_capab_len, supp_channels, supp_channels_len, supp_oper_classes, supp_oper_classes_len); return -1; } #endif /* CONFIG_TDLS */ void wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck, int ver, const u8 *dest, u16 proto, u8 *msg, size_t msg_len, u8 *key_mic); int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst, const struct wpa_eapol_key *key, int ver, const u8 *nonce, const u8 *wpa_ie, size_t wpa_ie_len, struct wpa_ptk *ptk); int wpa_supplicant_send_4_of_4(struct wpa_sm *sm, const unsigned char *dst, const struct wpa_eapol_key *key, u16 ver, u16 key_info, const u8 *kde, size_t kde_len, struct wpa_ptk *ptk); int wpa_derive_ptk_ft(struct wpa_sm *sm, const unsigned char *src_addr, const struct wpa_eapol_key *key, struct wpa_ptk *ptk, size_t ptk_len); void wpa_tdls_assoc(struct wpa_sm *sm); void wpa_tdls_disassoc(struct wpa_sm *sm); #endif /* WPA_I_H */ wpa-2.1/src/rsn_supp/wpa_ie.c000066400000000000000000000327241227414666700162420ustar00rootroot00000000000000/* * wpa_supplicant - WPA/RSN IE and KDE processing * Copyright (c) 2003-2008, 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 "wpa.h" #include "pmksa_cache.h" #include "common/ieee802_11_defs.h" #include "wpa_i.h" #include "wpa_ie.h" /** * wpa_parse_wpa_ie - Parse WPA/RSN IE * @wpa_ie: Pointer to WPA or RSN IE * @wpa_ie_len: Length of the WPA/RSN IE * @data: Pointer to data area for parsing results * Returns: 0 on success, -1 on failure * * Parse the contents of WPA or RSN IE and write the parsed data into data. */ int wpa_parse_wpa_ie(const u8 *wpa_ie, size_t wpa_ie_len, struct wpa_ie_data *data) { if (wpa_ie_len >= 1 && wpa_ie[0] == WLAN_EID_RSN) return wpa_parse_wpa_ie_rsn(wpa_ie, wpa_ie_len, data); else return wpa_parse_wpa_ie_wpa(wpa_ie, wpa_ie_len, data); } static int wpa_gen_wpa_ie_wpa(u8 *wpa_ie, size_t wpa_ie_len, int pairwise_cipher, int group_cipher, int key_mgmt) { u8 *pos; struct wpa_ie_hdr *hdr; u32 suite; if (wpa_ie_len < sizeof(*hdr) + WPA_SELECTOR_LEN + 2 + WPA_SELECTOR_LEN + 2 + WPA_SELECTOR_LEN) return -1; hdr = (struct wpa_ie_hdr *) wpa_ie; hdr->elem_id = WLAN_EID_VENDOR_SPECIFIC; RSN_SELECTOR_PUT(hdr->oui, WPA_OUI_TYPE); WPA_PUT_LE16(hdr->version, WPA_VERSION); pos = (u8 *) (hdr + 1); suite = wpa_cipher_to_suite(WPA_PROTO_WPA, group_cipher); if (suite == 0) { wpa_printf(MSG_WARNING, "Invalid group cipher (%d).", group_cipher); return -1; } RSN_SELECTOR_PUT(pos, suite); pos += WPA_SELECTOR_LEN; *pos++ = 1; *pos++ = 0; suite = wpa_cipher_to_suite(WPA_PROTO_WPA, pairwise_cipher); if (suite == 0 || (!wpa_cipher_valid_pairwise(pairwise_cipher) && pairwise_cipher != WPA_CIPHER_NONE)) { wpa_printf(MSG_WARNING, "Invalid pairwise cipher (%d).", pairwise_cipher); return -1; } RSN_SELECTOR_PUT(pos, suite); pos += WPA_SELECTOR_LEN; *pos++ = 1; *pos++ = 0; if (key_mgmt == WPA_KEY_MGMT_IEEE8021X) { RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_UNSPEC_802_1X); } else if (key_mgmt == WPA_KEY_MGMT_PSK) { RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X); } else if (key_mgmt == WPA_KEY_MGMT_WPA_NONE) { RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_NONE); } else if (key_mgmt == WPA_KEY_MGMT_CCKM) { RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_CCKM); } else { wpa_printf(MSG_WARNING, "Invalid key management type (%d).", key_mgmt); return -1; } pos += WPA_SELECTOR_LEN; /* WPA Capabilities; use defaults, so no need to include it */ hdr->len = (pos - wpa_ie) - 2; WPA_ASSERT((size_t) (pos - wpa_ie) <= wpa_ie_len); return pos - wpa_ie; } static int wpa_gen_wpa_ie_rsn(u8 *rsn_ie, size_t rsn_ie_len, int pairwise_cipher, int group_cipher, int key_mgmt, int mgmt_group_cipher, struct wpa_sm *sm) { u8 *pos; struct rsn_ie_hdr *hdr; u16 capab; u32 suite; if (rsn_ie_len < sizeof(*hdr) + RSN_SELECTOR_LEN + 2 + RSN_SELECTOR_LEN + 2 + RSN_SELECTOR_LEN + 2 + (sm->cur_pmksa ? 2 + PMKID_LEN : 0)) { wpa_printf(MSG_DEBUG, "RSN: Too short IE buffer (%lu bytes)", (unsigned long) rsn_ie_len); return -1; } hdr = (struct rsn_ie_hdr *) rsn_ie; hdr->elem_id = WLAN_EID_RSN; WPA_PUT_LE16(hdr->version, RSN_VERSION); pos = (u8 *) (hdr + 1); suite = wpa_cipher_to_suite(WPA_PROTO_RSN, group_cipher); if (suite == 0) { wpa_printf(MSG_WARNING, "Invalid group cipher (%d).", group_cipher); return -1; } RSN_SELECTOR_PUT(pos, suite); pos += RSN_SELECTOR_LEN; *pos++ = 1; *pos++ = 0; suite = wpa_cipher_to_suite(WPA_PROTO_RSN, pairwise_cipher); if (suite == 0 || (!wpa_cipher_valid_pairwise(pairwise_cipher) && pairwise_cipher != WPA_CIPHER_NONE)) { wpa_printf(MSG_WARNING, "Invalid pairwise cipher (%d).", pairwise_cipher); return -1; } RSN_SELECTOR_PUT(pos, suite); pos += RSN_SELECTOR_LEN; *pos++ = 1; *pos++ = 0; if (key_mgmt == WPA_KEY_MGMT_IEEE8021X) { RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_UNSPEC_802_1X); } else if (key_mgmt == WPA_KEY_MGMT_PSK) { RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X); } else if (key_mgmt == WPA_KEY_MGMT_CCKM) { RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_CCKM); #ifdef CONFIG_IEEE80211R } else if (key_mgmt == WPA_KEY_MGMT_FT_IEEE8021X) { RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X); } else if (key_mgmt == WPA_KEY_MGMT_FT_PSK) { RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_PSK); #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_IEEE80211W } else if (key_mgmt == WPA_KEY_MGMT_IEEE8021X_SHA256) { RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SHA256); } else if (key_mgmt == WPA_KEY_MGMT_PSK_SHA256) { RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PSK_SHA256); #endif /* CONFIG_IEEE80211W */ #ifdef CONFIG_SAE } else if (key_mgmt == WPA_KEY_MGMT_SAE) { RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_SAE); } else if (key_mgmt == WPA_KEY_MGMT_FT_SAE) { RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_SAE); #endif /* CONFIG_SAE */ } else { wpa_printf(MSG_WARNING, "Invalid key management type (%d).", key_mgmt); return -1; } pos += RSN_SELECTOR_LEN; /* RSN Capabilities */ capab = 0; #ifdef CONFIG_IEEE80211W if (sm->mfp) capab |= WPA_CAPABILITY_MFPC; if (sm->mfp == 2) capab |= WPA_CAPABILITY_MFPR; #endif /* CONFIG_IEEE80211W */ WPA_PUT_LE16(pos, capab); pos += 2; if (sm->cur_pmksa) { /* PMKID Count (2 octets, little endian) */ *pos++ = 1; *pos++ = 0; /* PMKID */ os_memcpy(pos, sm->cur_pmksa->pmkid, PMKID_LEN); pos += PMKID_LEN; } #ifdef CONFIG_IEEE80211W if (mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC) { if (!sm->cur_pmksa) { /* PMKID Count */ WPA_PUT_LE16(pos, 0); pos += 2; } /* Management Group Cipher Suite */ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_AES_128_CMAC); pos += RSN_SELECTOR_LEN; } #endif /* CONFIG_IEEE80211W */ hdr->len = (pos - rsn_ie) - 2; WPA_ASSERT((size_t) (pos - rsn_ie) <= rsn_ie_len); return pos - rsn_ie; } /** * wpa_gen_wpa_ie - Generate WPA/RSN IE based on current security policy * @sm: Pointer to WPA state machine data from wpa_sm_init() * @wpa_ie: Pointer to memory area for the generated WPA/RSN IE * @wpa_ie_len: Maximum length of the generated WPA/RSN IE * Returns: Length of the generated WPA/RSN IE or -1 on failure */ int wpa_gen_wpa_ie(struct wpa_sm *sm, u8 *wpa_ie, size_t wpa_ie_len) { if (sm->proto == WPA_PROTO_RSN) return wpa_gen_wpa_ie_rsn(wpa_ie, wpa_ie_len, sm->pairwise_cipher, sm->group_cipher, sm->key_mgmt, sm->mgmt_group_cipher, sm); else return wpa_gen_wpa_ie_wpa(wpa_ie, wpa_ie_len, sm->pairwise_cipher, sm->group_cipher, sm->key_mgmt); } /** * wpa_parse_generic - Parse EAPOL-Key Key Data Generic IEs * @pos: Pointer to the IE header * @end: Pointer to the end of the Key Data buffer * @ie: Pointer to parsed IE data * Returns: 0 on success, 1 if end mark is found, -1 on failure */ static int wpa_parse_generic(const u8 *pos, const u8 *end, struct wpa_eapol_ie_parse *ie) { if (pos[1] == 0) return 1; if (pos[1] >= 6 && RSN_SELECTOR_GET(pos + 2) == WPA_OUI_TYPE && pos[2 + WPA_SELECTOR_LEN] == 1 && pos[2 + WPA_SELECTOR_LEN + 1] == 0) { ie->wpa_ie = pos; ie->wpa_ie_len = pos[1] + 2; wpa_hexdump(MSG_DEBUG, "WPA: WPA IE in EAPOL-Key", ie->wpa_ie, ie->wpa_ie_len); return 0; } if (pos + 1 + RSN_SELECTOR_LEN < end && pos[1] >= RSN_SELECTOR_LEN + PMKID_LEN && RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_PMKID) { ie->pmkid = pos + 2 + RSN_SELECTOR_LEN; wpa_hexdump(MSG_DEBUG, "WPA: PMKID in EAPOL-Key", pos, pos[1] + 2); return 0; } if (pos[1] > RSN_SELECTOR_LEN + 2 && RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_GROUPKEY) { ie->gtk = pos + 2 + RSN_SELECTOR_LEN; ie->gtk_len = pos[1] - RSN_SELECTOR_LEN; wpa_hexdump_key(MSG_DEBUG, "WPA: GTK in EAPOL-Key", pos, pos[1] + 2); return 0; } if (pos[1] > RSN_SELECTOR_LEN + 2 && RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_MAC_ADDR) { ie->mac_addr = pos + 2 + RSN_SELECTOR_LEN; ie->mac_addr_len = pos[1] - RSN_SELECTOR_LEN; wpa_hexdump(MSG_DEBUG, "WPA: MAC Address in EAPOL-Key", pos, pos[1] + 2); return 0; } #ifdef CONFIG_PEERKEY if (pos[1] > RSN_SELECTOR_LEN + 2 && RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_SMK) { ie->smk = pos + 2 + RSN_SELECTOR_LEN; ie->smk_len = pos[1] - RSN_SELECTOR_LEN; wpa_hexdump_key(MSG_DEBUG, "WPA: SMK in EAPOL-Key", pos, pos[1] + 2); return 0; } if (pos[1] > RSN_SELECTOR_LEN + 2 && RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_NONCE) { ie->nonce = pos + 2 + RSN_SELECTOR_LEN; ie->nonce_len = pos[1] - RSN_SELECTOR_LEN; wpa_hexdump(MSG_DEBUG, "WPA: Nonce in EAPOL-Key", pos, pos[1] + 2); return 0; } if (pos[1] > RSN_SELECTOR_LEN + 2 && RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_LIFETIME) { ie->lifetime = pos + 2 + RSN_SELECTOR_LEN; ie->lifetime_len = pos[1] - RSN_SELECTOR_LEN; wpa_hexdump(MSG_DEBUG, "WPA: Lifetime in EAPOL-Key", pos, pos[1] + 2); return 0; } if (pos[1] > RSN_SELECTOR_LEN + 2 && RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_ERROR) { ie->error = pos + 2 + RSN_SELECTOR_LEN; ie->error_len = pos[1] - RSN_SELECTOR_LEN; wpa_hexdump(MSG_DEBUG, "WPA: Error in EAPOL-Key", pos, pos[1] + 2); return 0; } #endif /* CONFIG_PEERKEY */ #ifdef CONFIG_IEEE80211W if (pos[1] > RSN_SELECTOR_LEN + 2 && RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_IGTK) { ie->igtk = pos + 2 + RSN_SELECTOR_LEN; ie->igtk_len = pos[1] - RSN_SELECTOR_LEN; wpa_hexdump_key(MSG_DEBUG, "WPA: IGTK in EAPOL-Key", pos, pos[1] + 2); return 0; } #endif /* CONFIG_IEEE80211W */ #ifdef CONFIG_P2P if (pos[1] >= RSN_SELECTOR_LEN + 1 && RSN_SELECTOR_GET(pos + 2) == WFA_KEY_DATA_IP_ADDR_REQ) { ie->ip_addr_req = pos + 2 + RSN_SELECTOR_LEN; wpa_hexdump(MSG_DEBUG, "WPA: IP Address Request in EAPOL-Key", ie->ip_addr_req, pos[1] - RSN_SELECTOR_LEN); return 0; } if (pos[1] >= RSN_SELECTOR_LEN + 3 * 4 && RSN_SELECTOR_GET(pos + 2) == WFA_KEY_DATA_IP_ADDR_ALLOC) { ie->ip_addr_alloc = pos + 2 + RSN_SELECTOR_LEN; wpa_hexdump(MSG_DEBUG, "WPA: IP Address Allocation in EAPOL-Key", ie->ip_addr_alloc, pos[1] - RSN_SELECTOR_LEN); return 0; } #endif /* CONFIG_P2P */ return 0; } /** * wpa_supplicant_parse_ies - Parse EAPOL-Key Key Data IEs * @buf: Pointer to the Key Data buffer * @len: Key Data Length * @ie: Pointer to parsed IE data * Returns: 0 on success, -1 on failure */ int wpa_supplicant_parse_ies(const u8 *buf, size_t len, struct wpa_eapol_ie_parse *ie) { const u8 *pos, *end; int ret = 0; os_memset(ie, 0, sizeof(*ie)); for (pos = buf, end = pos + len; pos + 1 < end; pos += 2 + pos[1]) { if (pos[0] == 0xdd && ((pos == buf + len - 1) || pos[1] == 0)) { /* Ignore padding */ break; } if (pos + 2 + pos[1] > end) { wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key Key Data " "underflow (ie=%d len=%d pos=%d)", pos[0], pos[1], (int) (pos - buf)); wpa_hexdump_key(MSG_DEBUG, "WPA: Key Data", buf, len); ret = -1; break; } if (*pos == WLAN_EID_RSN) { ie->rsn_ie = pos; ie->rsn_ie_len = pos[1] + 2; wpa_hexdump(MSG_DEBUG, "WPA: RSN IE in EAPOL-Key", ie->rsn_ie, ie->rsn_ie_len); } else if (*pos == WLAN_EID_MOBILITY_DOMAIN) { ie->mdie = pos; ie->mdie_len = pos[1] + 2; wpa_hexdump(MSG_DEBUG, "WPA: MDIE in EAPOL-Key", ie->mdie, ie->mdie_len); } else if (*pos == WLAN_EID_FAST_BSS_TRANSITION) { ie->ftie = pos; ie->ftie_len = pos[1] + 2; wpa_hexdump(MSG_DEBUG, "WPA: FTIE in EAPOL-Key", ie->ftie, ie->ftie_len); } else if (*pos == WLAN_EID_TIMEOUT_INTERVAL && pos[1] >= 5) { if (pos[2] == WLAN_TIMEOUT_REASSOC_DEADLINE) { ie->reassoc_deadline = pos; wpa_hexdump(MSG_DEBUG, "WPA: Reassoc Deadline " "in EAPOL-Key", ie->reassoc_deadline, pos[1] + 2); } else if (pos[2] == WLAN_TIMEOUT_KEY_LIFETIME) { ie->key_lifetime = pos; wpa_hexdump(MSG_DEBUG, "WPA: KeyLifetime " "in EAPOL-Key", ie->key_lifetime, pos[1] + 2); } else { wpa_hexdump(MSG_DEBUG, "WPA: Unrecognized " "EAPOL-Key Key Data IE", pos, 2 + pos[1]); } } else if (*pos == WLAN_EID_LINK_ID) { if (pos[1] >= 18) { ie->lnkid = pos; ie->lnkid_len = pos[1] + 2; } } else if (*pos == WLAN_EID_EXT_CAPAB) { ie->ext_capab = pos; ie->ext_capab_len = pos[1] + 2; } else if (*pos == WLAN_EID_SUPP_RATES) { ie->supp_rates = pos; ie->supp_rates_len = pos[1] + 2; } else if (*pos == WLAN_EID_EXT_SUPP_RATES) { ie->ext_supp_rates = pos; ie->ext_supp_rates_len = pos[1] + 2; } else if (*pos == WLAN_EID_HT_CAP) { ie->ht_capabilities = pos + 2; ie->ht_capabilities_len = pos[1]; } else if (*pos == WLAN_EID_VHT_AID) { if (pos[1] >= 2) ie->aid = WPA_GET_LE16(pos + 2); } else if (*pos == WLAN_EID_VHT_CAP) { ie->vht_capabilities = pos + 2; ie->vht_capabilities_len = pos[1]; } else if (*pos == WLAN_EID_QOS && pos[1] >= 1) { ie->qosinfo = pos[2]; } else if (*pos == WLAN_EID_SUPPORTED_CHANNELS) { ie->supp_channels = pos + 2; ie->supp_channels_len = pos[1]; } else if (*pos == WLAN_EID_SUPPORTED_OPERATING_CLASSES) { ie->supp_oper_classes = pos + 2; ie->supp_oper_classes_len = pos[1]; } else if (*pos == WLAN_EID_VENDOR_SPECIFIC) { ret = wpa_parse_generic(pos, end, ie); if (ret < 0) break; if (ret > 0) { ret = 0; break; } } else { wpa_hexdump(MSG_DEBUG, "WPA: Unrecognized EAPOL-Key " "Key Data IE", pos, 2 + pos[1]); } } return ret; } wpa-2.1/src/rsn_supp/wpa_ie.h000066400000000000000000000031061227414666700162370ustar00rootroot00000000000000/* * wpa_supplicant - WPA/RSN IE and KDE definitions * Copyright (c) 2004-2007, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef WPA_IE_H #define WPA_IE_H struct wpa_sm; struct wpa_eapol_ie_parse { const u8 *wpa_ie; size_t wpa_ie_len; const u8 *rsn_ie; size_t rsn_ie_len; const u8 *pmkid; const u8 *gtk; size_t gtk_len; const u8 *mac_addr; size_t mac_addr_len; #ifdef CONFIG_PEERKEY const u8 *smk; size_t smk_len; const u8 *nonce; size_t nonce_len; const u8 *lifetime; size_t lifetime_len; const u8 *error; size_t error_len; #endif /* CONFIG_PEERKEY */ #ifdef CONFIG_IEEE80211W const u8 *igtk; size_t igtk_len; #endif /* CONFIG_IEEE80211W */ const u8 *mdie; size_t mdie_len; const u8 *ftie; size_t ftie_len; const u8 *reassoc_deadline; const u8 *key_lifetime; const u8 *lnkid; size_t lnkid_len; const u8 *ext_capab; size_t ext_capab_len; const u8 *supp_rates; size_t supp_rates_len; const u8 *ext_supp_rates; size_t ext_supp_rates_len; const u8 *ht_capabilities; size_t ht_capabilities_len; const u8 *vht_capabilities; size_t vht_capabilities_len; const u8 *supp_channels; size_t supp_channels_len; const u8 *supp_oper_classes; size_t supp_oper_classes_len; u8 qosinfo; u16 aid; #ifdef CONFIG_P2P const u8 *ip_addr_req; const u8 *ip_addr_alloc; #endif /* CONFIG_P2P */ }; int wpa_supplicant_parse_ies(const u8 *buf, size_t len, struct wpa_eapol_ie_parse *ie); int wpa_gen_wpa_ie(struct wpa_sm *sm, u8 *wpa_ie, size_t wpa_ie_len); #endif /* WPA_IE_H */ wpa-2.1/src/tls/000077500000000000000000000000001227414666700135535ustar00rootroot00000000000000wpa-2.1/src/tls/.gitignore000066400000000000000000000000111227414666700155330ustar00rootroot00000000000000libtls.a wpa-2.1/src/tls/Makefile000066400000000000000000000010741227414666700152150ustar00rootroot00000000000000all: libtls.a clean: rm -f *~ *.o *.d libtls.a install: @echo Nothing to be made. include ../lib.rules CFLAGS += -DCONFIG_INTERNAL_LIBTOMMATH CFLAGS += -DCONFIG_CRYPTO_INTERNAL CFLAGS += -DCONFIG_TLSV11 CFLAGS += -DCONFIG_TLSV12 LIB_OBJS= \ asn1.o \ bignum.o \ pkcs1.o \ pkcs5.o \ pkcs8.o \ rsa.o \ tlsv1_client.o \ tlsv1_client_read.o \ tlsv1_client_write.o \ tlsv1_common.o \ tlsv1_cred.o \ tlsv1_record.o \ tlsv1_server.o \ tlsv1_server_read.o \ tlsv1_server_write.o \ x509v3.o libtls.a: $(LIB_OBJS) $(AR) crT $@ $? -include $(OBJS:%.o=%.d) wpa-2.1/src/tls/asn1.c000066400000000000000000000077611227414666700145740ustar00rootroot00000000000000/* * ASN.1 DER parsing * Copyright (c) 2006, 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 "asn1.h" int asn1_get_next(const u8 *buf, size_t len, struct asn1_hdr *hdr) { const u8 *pos, *end; u8 tmp; os_memset(hdr, 0, sizeof(*hdr)); pos = buf; end = buf + len; hdr->identifier = *pos++; hdr->class = hdr->identifier >> 6; hdr->constructed = !!(hdr->identifier & (1 << 5)); if ((hdr->identifier & 0x1f) == 0x1f) { hdr->tag = 0; do { if (pos >= end) { wpa_printf(MSG_DEBUG, "ASN.1: Identifier " "underflow"); return -1; } tmp = *pos++; wpa_printf(MSG_MSGDUMP, "ASN.1: Extended tag data: " "0x%02x", tmp); hdr->tag = (hdr->tag << 7) | (tmp & 0x7f); } while (tmp & 0x80); } else hdr->tag = hdr->identifier & 0x1f; tmp = *pos++; if (tmp & 0x80) { if (tmp == 0xff) { wpa_printf(MSG_DEBUG, "ASN.1: Reserved length " "value 0xff used"); return -1; } tmp &= 0x7f; /* number of subsequent octets */ hdr->length = 0; if (tmp > 4) { wpa_printf(MSG_DEBUG, "ASN.1: Too long length field"); return -1; } while (tmp--) { if (pos >= end) { wpa_printf(MSG_DEBUG, "ASN.1: Length " "underflow"); return -1; } hdr->length = (hdr->length << 8) | *pos++; } } else { /* Short form - length 0..127 in one octet */ hdr->length = tmp; } if (end < pos || hdr->length > (unsigned int) (end - pos)) { wpa_printf(MSG_DEBUG, "ASN.1: Contents underflow"); return -1; } hdr->payload = pos; return 0; } int asn1_parse_oid(const u8 *buf, size_t len, struct asn1_oid *oid) { const u8 *pos, *end; unsigned long val; u8 tmp; os_memset(oid, 0, sizeof(*oid)); pos = buf; end = buf + len; while (pos < end) { val = 0; do { if (pos >= end) return -1; tmp = *pos++; val = (val << 7) | (tmp & 0x7f); } while (tmp & 0x80); if (oid->len >= ASN1_MAX_OID_LEN) { wpa_printf(MSG_DEBUG, "ASN.1: Too long OID value"); return -1; } if (oid->len == 0) { /* * The first octet encodes the first two object * identifier components in (X*40) + Y formula. * X = 0..2. */ oid->oid[0] = val / 40; if (oid->oid[0] > 2) oid->oid[0] = 2; oid->oid[1] = val - oid->oid[0] * 40; oid->len = 2; } else oid->oid[oid->len++] = val; } return 0; } int asn1_get_oid(const u8 *buf, size_t len, struct asn1_oid *oid, const u8 **next) { struct asn1_hdr hdr; if (asn1_get_next(buf, len, &hdr) < 0 || hdr.length == 0) return -1; if (hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_OID) { wpa_printf(MSG_DEBUG, "ASN.1: Expected OID - found class %d " "tag 0x%x", hdr.class, hdr.tag); return -1; } *next = hdr.payload + hdr.length; return asn1_parse_oid(hdr.payload, hdr.length, oid); } void asn1_oid_to_str(struct asn1_oid *oid, char *buf, size_t len) { char *pos = buf; size_t i; int ret; if (len == 0) return; buf[0] = '\0'; for (i = 0; i < oid->len; i++) { ret = os_snprintf(pos, buf + len - pos, "%s%lu", i == 0 ? "" : ".", oid->oid[i]); if (ret < 0 || ret >= buf + len - pos) break; pos += ret; } buf[len - 1] = '\0'; } static u8 rotate_bits(u8 octet) { int i; u8 res; res = 0; for (i = 0; i < 8; i++) { res <<= 1; if (octet & 1) res |= 1; octet >>= 1; } return res; } unsigned long asn1_bit_string_to_long(const u8 *buf, size_t len) { unsigned long val = 0; const u8 *pos = buf; /* BER requires that unused bits are zero, so we can ignore the number * of unused bits */ pos++; if (len >= 2) val |= rotate_bits(*pos++); if (len >= 3) val |= ((unsigned long) rotate_bits(*pos++)) << 8; if (len >= 4) val |= ((unsigned long) rotate_bits(*pos++)) << 16; if (len >= 5) val |= ((unsigned long) rotate_bits(*pos++)) << 24; if (len >= 6) wpa_printf(MSG_DEBUG, "X509: %s - some bits ignored " "(BIT STRING length %lu)", __func__, (unsigned long) len); return val; } wpa-2.1/src/tls/asn1.h000066400000000000000000000041431227414666700145700ustar00rootroot00000000000000/* * ASN.1 DER parsing * Copyright (c) 2006, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef ASN1_H #define ASN1_H #define ASN1_TAG_EOC 0x00 /* not used with DER */ #define ASN1_TAG_BOOLEAN 0x01 #define ASN1_TAG_INTEGER 0x02 #define ASN1_TAG_BITSTRING 0x03 #define ASN1_TAG_OCTETSTRING 0x04 #define ASN1_TAG_NULL 0x05 #define ASN1_TAG_OID 0x06 #define ASN1_TAG_OBJECT_DESCRIPTOR 0x07 /* not yet parsed */ #define ASN1_TAG_EXTERNAL 0x08 /* not yet parsed */ #define ASN1_TAG_REAL 0x09 /* not yet parsed */ #define ASN1_TAG_ENUMERATED 0x0A /* not yet parsed */ #define ASN1_TAG_UTF8STRING 0x0C /* not yet parsed */ #define ANS1_TAG_RELATIVE_OID 0x0D #define ASN1_TAG_SEQUENCE 0x10 /* shall be constructed */ #define ASN1_TAG_SET 0x11 #define ASN1_TAG_NUMERICSTRING 0x12 /* not yet parsed */ #define ASN1_TAG_PRINTABLESTRING 0x13 #define ASN1_TAG_TG1STRING 0x14 /* not yet parsed */ #define ASN1_TAG_VIDEOTEXSTRING 0x15 /* not yet parsed */ #define ASN1_TAG_IA5STRING 0x16 #define ASN1_TAG_UTCTIME 0x17 #define ASN1_TAG_GENERALIZEDTIME 0x18 /* not yet parsed */ #define ASN1_TAG_GRAPHICSTRING 0x19 /* not yet parsed */ #define ASN1_TAG_VISIBLESTRING 0x1A #define ASN1_TAG_GENERALSTRING 0x1B /* not yet parsed */ #define ASN1_TAG_UNIVERSALSTRING 0x1C /* not yet parsed */ #define ASN1_TAG_BMPSTRING 0x1D /* not yet parsed */ #define ASN1_CLASS_UNIVERSAL 0 #define ASN1_CLASS_APPLICATION 1 #define ASN1_CLASS_CONTEXT_SPECIFIC 2 #define ASN1_CLASS_PRIVATE 3 struct asn1_hdr { const u8 *payload; u8 identifier, class, constructed; unsigned int tag, length; }; #define ASN1_MAX_OID_LEN 20 struct asn1_oid { unsigned long oid[ASN1_MAX_OID_LEN]; size_t len; }; int asn1_get_next(const u8 *buf, size_t len, struct asn1_hdr *hdr); int asn1_parse_oid(const u8 *buf, size_t len, struct asn1_oid *oid); int asn1_get_oid(const u8 *buf, size_t len, struct asn1_oid *oid, const u8 **next); void asn1_oid_to_str(struct asn1_oid *oid, char *buf, size_t len); unsigned long asn1_bit_string_to_long(const u8 *buf, size_t len); #endif /* ASN1_H */ wpa-2.1/src/tls/bignum.c000066400000000000000000000125541227414666700152070ustar00rootroot00000000000000/* * Big number math * Copyright (c) 2006, 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 "bignum.h" #ifdef CONFIG_INTERNAL_LIBTOMMATH #include "libtommath.c" #else /* CONFIG_INTERNAL_LIBTOMMATH */ #include #endif /* CONFIG_INTERNAL_LIBTOMMATH */ /* * The current version is just a wrapper for LibTomMath library, so * struct bignum is just typecast to mp_int. */ /** * bignum_init - Allocate memory for bignum * Returns: Pointer to allocated bignum or %NULL on failure */ struct bignum * bignum_init(void) { struct bignum *n = os_zalloc(sizeof(mp_int)); if (n == NULL) return NULL; if (mp_init((mp_int *) n) != MP_OKAY) { os_free(n); n = NULL; } return n; } /** * bignum_deinit - Free bignum * @n: Bignum from bignum_init() */ void bignum_deinit(struct bignum *n) { if (n) { mp_clear((mp_int *) n); os_free(n); } } /** * bignum_get_unsigned_bin - Get length of bignum as an unsigned binary buffer * @n: Bignum from bignum_init() * Returns: Length of n if written to a binary buffer */ size_t bignum_get_unsigned_bin_len(struct bignum *n) { return mp_unsigned_bin_size((mp_int *) n); } /** * bignum_get_unsigned_bin - Set binary buffer to unsigned bignum * @n: Bignum from bignum_init() * @buf: Buffer for the binary number * @len: Length of the buffer, can be %NULL if buffer is known to be long * enough. Set to used buffer length on success if not %NULL. * Returns: 0 on success, -1 on failure */ int bignum_get_unsigned_bin(const struct bignum *n, u8 *buf, size_t *len) { size_t need = mp_unsigned_bin_size((mp_int *) n); if (len && need > *len) { *len = need; return -1; } if (mp_to_unsigned_bin((mp_int *) n, buf) != MP_OKAY) { wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__); return -1; } if (len) *len = need; return 0; } /** * bignum_set_unsigned_bin - Set bignum based on unsigned binary buffer * @n: Bignum from bignum_init(); to be set to the given value * @buf: Buffer with unsigned binary value * @len: Length of buf in octets * Returns: 0 on success, -1 on failure */ int bignum_set_unsigned_bin(struct bignum *n, const u8 *buf, size_t len) { if (mp_read_unsigned_bin((mp_int *) n, (u8 *) buf, len) != MP_OKAY) { wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__); return -1; } return 0; } /** * bignum_cmp - Signed comparison * @a: Bignum from bignum_init() * @b: Bignum from bignum_init() * Returns: 0 on success, -1 on failure */ int bignum_cmp(const struct bignum *a, const struct bignum *b) { return mp_cmp((mp_int *) a, (mp_int *) b); } /** * bignum_cmd_d - Compare bignum to standard integer * @a: Bignum from bignum_init() * @b: Small integer * Returns: 0 on success, -1 on failure */ int bignum_cmp_d(const struct bignum *a, unsigned long b) { return mp_cmp_d((mp_int *) a, b); } /** * bignum_add - c = a + b * @a: Bignum from bignum_init() * @b: Bignum from bignum_init() * @c: Bignum from bignum_init(); used to store the result of a + b * Returns: 0 on success, -1 on failure */ int bignum_add(const struct bignum *a, const struct bignum *b, struct bignum *c) { if (mp_add((mp_int *) a, (mp_int *) b, (mp_int *) c) != MP_OKAY) { wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__); return -1; } return 0; } /** * bignum_sub - c = a - b * @a: Bignum from bignum_init() * @b: Bignum from bignum_init() * @c: Bignum from bignum_init(); used to store the result of a - b * Returns: 0 on success, -1 on failure */ int bignum_sub(const struct bignum *a, const struct bignum *b, struct bignum *c) { if (mp_sub((mp_int *) a, (mp_int *) b, (mp_int *) c) != MP_OKAY) { wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__); return -1; } return 0; } /** * bignum_mul - c = a * b * @a: Bignum from bignum_init() * @b: Bignum from bignum_init() * @c: Bignum from bignum_init(); used to store the result of a * b * Returns: 0 on success, -1 on failure */ int bignum_mul(const struct bignum *a, const struct bignum *b, struct bignum *c) { if (mp_mul((mp_int *) a, (mp_int *) b, (mp_int *) c) != MP_OKAY) { wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__); return -1; } return 0; } /** * bignum_mulmod - d = a * b (mod c) * @a: Bignum from bignum_init() * @b: Bignum from bignum_init() * @c: Bignum from bignum_init(); modulus * @d: Bignum from bignum_init(); used to store the result of a * b (mod c) * Returns: 0 on success, -1 on failure */ int bignum_mulmod(const struct bignum *a, const struct bignum *b, const struct bignum *c, struct bignum *d) { if (mp_mulmod((mp_int *) a, (mp_int *) b, (mp_int *) c, (mp_int *) d) != MP_OKAY) { wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__); return -1; } return 0; } /** * bignum_exptmod - Modular exponentiation: d = a^b (mod c) * @a: Bignum from bignum_init(); base * @b: Bignum from bignum_init(); exponent * @c: Bignum from bignum_init(); modulus * @d: Bignum from bignum_init(); used to store the result of a^b (mod c) * Returns: 0 on success, -1 on failure */ int bignum_exptmod(const struct bignum *a, const struct bignum *b, const struct bignum *c, struct bignum *d) { if (mp_exptmod((mp_int *) a, (mp_int *) b, (mp_int *) c, (mp_int *) d) != MP_OKAY) { wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__); return -1; } return 0; } wpa-2.1/src/tls/bignum.h000066400000000000000000000022001227414666700151770ustar00rootroot00000000000000/* * Big number math * Copyright (c) 2006, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef BIGNUM_H #define BIGNUM_H struct bignum; struct bignum * bignum_init(void); void bignum_deinit(struct bignum *n); size_t bignum_get_unsigned_bin_len(struct bignum *n); int bignum_get_unsigned_bin(const struct bignum *n, u8 *buf, size_t *len); int bignum_set_unsigned_bin(struct bignum *n, const u8 *buf, size_t len); int bignum_cmp(const struct bignum *a, const struct bignum *b); int bignum_cmp_d(const struct bignum *a, unsigned long b); int bignum_add(const struct bignum *a, const struct bignum *b, struct bignum *c); int bignum_sub(const struct bignum *a, const struct bignum *b, struct bignum *c); int bignum_mul(const struct bignum *a, const struct bignum *b, struct bignum *c); int bignum_mulmod(const struct bignum *a, const struct bignum *b, const struct bignum *c, struct bignum *d); int bignum_exptmod(const struct bignum *a, const struct bignum *b, const struct bignum *c, struct bignum *d); #endif /* BIGNUM_H */ wpa-2.1/src/tls/libtommath.c000066400000000000000000002306631227414666700160710ustar00rootroot00000000000000/* * Minimal code for RSA support from LibTomMath 0.41 * http://libtom.org/ * http://libtom.org/files/ltm-0.41.tar.bz2 * This library was released in public domain by Tom St Denis. * * The combination in this file may not use all of the optimized algorithms * from LibTomMath and may be considerable slower than the LibTomMath with its * default settings. The main purpose of having this version here is to make it * easier to build bignum.c wrapper without having to install and build an * external library. * * If CONFIG_INTERNAL_LIBTOMMATH is defined, bignum.c includes this * libtommath.c file instead of using the external LibTomMath library. */ #ifndef CHAR_BIT #define CHAR_BIT 8 #endif #define BN_MP_INVMOD_C #define BN_S_MP_EXPTMOD_C /* Note: #undef in tommath_superclass.h; this would * require BN_MP_EXPTMOD_FAST_C instead */ #define BN_S_MP_MUL_DIGS_C #define BN_MP_INVMOD_SLOW_C #define BN_S_MP_SQR_C #define BN_S_MP_MUL_HIGH_DIGS_C /* Note: #undef in tommath_superclass.h; this * would require other than mp_reduce */ #ifdef LTM_FAST /* Use faster div at the cost of about 1 kB */ #define BN_MP_MUL_D_C /* Include faster exptmod (Montgomery) at the cost of about 2.5 kB in code */ #define BN_MP_EXPTMOD_FAST_C #define BN_MP_MONTGOMERY_SETUP_C #define BN_FAST_MP_MONTGOMERY_REDUCE_C #define BN_MP_MONTGOMERY_CALC_NORMALIZATION_C #define BN_MP_MUL_2_C /* Include faster sqr at the cost of about 0.5 kB in code */ #define BN_FAST_S_MP_SQR_C /* About 0.25 kB of code, but ~1.7kB of stack space! */ #define BN_FAST_S_MP_MUL_DIGS_C #else /* LTM_FAST */ #define BN_MP_DIV_SMALL #define BN_MP_INIT_MULTI_C #define BN_MP_CLEAR_MULTI_C #define BN_MP_ABS_C #endif /* LTM_FAST */ /* Current uses do not require support for negative exponent in exptmod, so we * can save about 1.5 kB in leaving out invmod. */ #define LTM_NO_NEG_EXP /* from tommath.h */ #ifndef MIN #define MIN(x,y) ((x)<(y)?(x):(y)) #endif #ifndef MAX #define MAX(x,y) ((x)>(y)?(x):(y)) #endif #define OPT_CAST(x) #ifdef __x86_64__ typedef unsigned long mp_digit; typedef unsigned long mp_word __attribute__((mode(TI))); #define DIGIT_BIT 60 #define MP_64BIT #else typedef unsigned long mp_digit; typedef u64 mp_word; #define DIGIT_BIT 28 #define MP_28BIT #endif #define XMALLOC os_malloc #define XFREE os_free #define XREALLOC os_realloc #define MP_MASK ((((mp_digit)1)<<((mp_digit)DIGIT_BIT))-((mp_digit)1)) #define MP_LT -1 /* less than */ #define MP_EQ 0 /* equal to */ #define MP_GT 1 /* greater than */ #define MP_ZPOS 0 /* positive integer */ #define MP_NEG 1 /* negative */ #define MP_OKAY 0 /* ok result */ #define MP_MEM -2 /* out of mem */ #define MP_VAL -3 /* invalid input */ #define MP_YES 1 /* yes response */ #define MP_NO 0 /* no response */ typedef int mp_err; /* define this to use lower memory usage routines (exptmods mostly) */ #define MP_LOW_MEM /* default precision */ #ifndef MP_PREC #ifndef MP_LOW_MEM #define MP_PREC 32 /* default digits of precision */ #else #define MP_PREC 8 /* default digits of precision */ #endif #endif /* size of comba arrays, should be at least 2 * 2**(BITS_PER_WORD - BITS_PER_DIGIT*2) */ #define MP_WARRAY (1 << (sizeof(mp_word) * CHAR_BIT - 2 * DIGIT_BIT + 1)) /* the infamous mp_int structure */ typedef struct { int used, alloc, sign; mp_digit *dp; } mp_int; /* ---> Basic Manipulations <--- */ #define mp_iszero(a) (((a)->used == 0) ? MP_YES : MP_NO) #define mp_iseven(a) (((a)->used > 0 && (((a)->dp[0] & 1) == 0)) ? MP_YES : MP_NO) #define mp_isodd(a) (((a)->used > 0 && (((a)->dp[0] & 1) == 1)) ? MP_YES : MP_NO) /* prototypes for copied functions */ #define s_mp_mul(a, b, c) s_mp_mul_digs(a, b, c, (a)->used + (b)->used + 1) static int s_mp_exptmod(mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int redmode); static int s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs); static int s_mp_sqr(mp_int * a, mp_int * b); static int s_mp_mul_high_digs(mp_int * a, mp_int * b, mp_int * c, int digs); #ifdef BN_FAST_S_MP_MUL_DIGS_C static int fast_s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs); #endif #ifdef BN_MP_INIT_MULTI_C static int mp_init_multi(mp_int *mp, ...); #endif #ifdef BN_MP_CLEAR_MULTI_C static void mp_clear_multi(mp_int *mp, ...); #endif static int mp_lshd(mp_int * a, int b); static void mp_set(mp_int * a, mp_digit b); static void mp_clamp(mp_int * a); static void mp_exch(mp_int * a, mp_int * b); static void mp_rshd(mp_int * a, int b); static void mp_zero(mp_int * a); static int mp_mod_2d(mp_int * a, int b, mp_int * c); static int mp_div_2d(mp_int * a, int b, mp_int * c, mp_int * d); static int mp_init_copy(mp_int * a, mp_int * b); static int mp_mul_2d(mp_int * a, int b, mp_int * c); #ifndef LTM_NO_NEG_EXP static int mp_div_2(mp_int * a, mp_int * b); static int mp_invmod(mp_int * a, mp_int * b, mp_int * c); static int mp_invmod_slow(mp_int * a, mp_int * b, mp_int * c); #endif /* LTM_NO_NEG_EXP */ static int mp_copy(mp_int * a, mp_int * b); static int mp_count_bits(mp_int * a); static int mp_div(mp_int * a, mp_int * b, mp_int * c, mp_int * d); static int mp_mod(mp_int * a, mp_int * b, mp_int * c); static int mp_grow(mp_int * a, int size); static int mp_cmp_mag(mp_int * a, mp_int * b); #ifdef BN_MP_ABS_C static int mp_abs(mp_int * a, mp_int * b); #endif static int mp_sqr(mp_int * a, mp_int * b); static int mp_reduce_2k_l(mp_int *a, mp_int *n, mp_int *d); static int mp_reduce_2k_setup_l(mp_int *a, mp_int *d); static int mp_2expt(mp_int * a, int b); static int mp_reduce_setup(mp_int * a, mp_int * b); static int mp_reduce(mp_int * x, mp_int * m, mp_int * mu); static int mp_init_size(mp_int * a, int size); #ifdef BN_MP_EXPTMOD_FAST_C static int mp_exptmod_fast (mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int redmode); #endif /* BN_MP_EXPTMOD_FAST_C */ #ifdef BN_FAST_S_MP_SQR_C static int fast_s_mp_sqr (mp_int * a, mp_int * b); #endif /* BN_FAST_S_MP_SQR_C */ #ifdef BN_MP_MUL_D_C static int mp_mul_d (mp_int * a, mp_digit b, mp_int * c); #endif /* BN_MP_MUL_D_C */ /* functions from bn_.c */ /* reverse an array, used for radix code */ static void bn_reverse (unsigned char *s, int len) { int ix, iy; unsigned char t; ix = 0; iy = len - 1; while (ix < iy) { t = s[ix]; s[ix] = s[iy]; s[iy] = t; ++ix; --iy; } } /* low level addition, based on HAC pp.594, Algorithm 14.7 */ static int s_mp_add (mp_int * a, mp_int * b, mp_int * c) { mp_int *x; int olduse, res, min, max; /* find sizes, we let |a| <= |b| which means we have to sort * them. "x" will point to the input with the most digits */ if (a->used > b->used) { min = b->used; max = a->used; x = a; } else { min = a->used; max = b->used; x = b; } /* init result */ if (c->alloc < max + 1) { if ((res = mp_grow (c, max + 1)) != MP_OKAY) { return res; } } /* get old used digit count and set new one */ olduse = c->used; c->used = max + 1; { register mp_digit u, *tmpa, *tmpb, *tmpc; register int i; /* alias for digit pointers */ /* first input */ tmpa = a->dp; /* second input */ tmpb = b->dp; /* destination */ tmpc = c->dp; /* zero the carry */ u = 0; for (i = 0; i < min; i++) { /* Compute the sum at one digit, T[i] = A[i] + B[i] + U */ *tmpc = *tmpa++ + *tmpb++ + u; /* U = carry bit of T[i] */ u = *tmpc >> ((mp_digit)DIGIT_BIT); /* take away carry bit from T[i] */ *tmpc++ &= MP_MASK; } /* now copy higher words if any, that is in A+B * if A or B has more digits add those in */ if (min != max) { for (; i < max; i++) { /* T[i] = X[i] + U */ *tmpc = x->dp[i] + u; /* U = carry bit of T[i] */ u = *tmpc >> ((mp_digit)DIGIT_BIT); /* take away carry bit from T[i] */ *tmpc++ &= MP_MASK; } } /* add carry */ *tmpc++ = u; /* clear digits above oldused */ for (i = c->used; i < olduse; i++) { *tmpc++ = 0; } } mp_clamp (c); return MP_OKAY; } /* low level subtraction (assumes |a| > |b|), HAC pp.595 Algorithm 14.9 */ static int s_mp_sub (mp_int * a, mp_int * b, mp_int * c) { int olduse, res, min, max; /* find sizes */ min = b->used; max = a->used; /* init result */ if (c->alloc < max) { if ((res = mp_grow (c, max)) != MP_OKAY) { return res; } } olduse = c->used; c->used = max; { register mp_digit u, *tmpa, *tmpb, *tmpc; register int i; /* alias for digit pointers */ tmpa = a->dp; tmpb = b->dp; tmpc = c->dp; /* set carry to zero */ u = 0; for (i = 0; i < min; i++) { /* T[i] = A[i] - B[i] - U */ *tmpc = *tmpa++ - *tmpb++ - u; /* U = carry bit of T[i] * Note this saves performing an AND operation since * if a carry does occur it will propagate all the way to the * MSB. As a result a single shift is enough to get the carry */ u = *tmpc >> ((mp_digit)(CHAR_BIT * sizeof (mp_digit) - 1)); /* Clear carry from T[i] */ *tmpc++ &= MP_MASK; } /* now copy higher words if any, e.g. if A has more digits than B */ for (; i < max; i++) { /* T[i] = A[i] - U */ *tmpc = *tmpa++ - u; /* U = carry bit of T[i] */ u = *tmpc >> ((mp_digit)(CHAR_BIT * sizeof (mp_digit) - 1)); /* Clear carry from T[i] */ *tmpc++ &= MP_MASK; } /* clear digits above used (since we may not have grown result above) */ for (i = c->used; i < olduse; i++) { *tmpc++ = 0; } } mp_clamp (c); return MP_OKAY; } /* init a new mp_int */ static int mp_init (mp_int * a) { int i; /* allocate memory required and clear it */ a->dp = OPT_CAST(mp_digit) XMALLOC (sizeof (mp_digit) * MP_PREC); if (a->dp == NULL) { return MP_MEM; } /* set the digits to zero */ for (i = 0; i < MP_PREC; i++) { a->dp[i] = 0; } /* set the used to zero, allocated digits to the default precision * and sign to positive */ a->used = 0; a->alloc = MP_PREC; a->sign = MP_ZPOS; return MP_OKAY; } /* clear one (frees) */ static void mp_clear (mp_int * a) { int i; /* only do anything if a hasn't been freed previously */ if (a->dp != NULL) { /* first zero the digits */ for (i = 0; i < a->used; i++) { a->dp[i] = 0; } /* free ram */ XFREE(a->dp); /* reset members to make debugging easier */ a->dp = NULL; a->alloc = a->used = 0; a->sign = MP_ZPOS; } } /* high level addition (handles signs) */ static int mp_add (mp_int * a, mp_int * b, mp_int * c) { int sa, sb, res; /* get sign of both inputs */ sa = a->sign; sb = b->sign; /* handle two cases, not four */ if (sa == sb) { /* both positive or both negative */ /* add their magnitudes, copy the sign */ c->sign = sa; res = s_mp_add (a, b, c); } else { /* one positive, the other negative */ /* subtract the one with the greater magnitude from */ /* the one of the lesser magnitude. The result gets */ /* the sign of the one with the greater magnitude. */ if (mp_cmp_mag (a, b) == MP_LT) { c->sign = sb; res = s_mp_sub (b, a, c); } else { c->sign = sa; res = s_mp_sub (a, b, c); } } return res; } /* high level subtraction (handles signs) */ static int mp_sub (mp_int * a, mp_int * b, mp_int * c) { int sa, sb, res; sa = a->sign; sb = b->sign; if (sa != sb) { /* subtract a negative from a positive, OR */ /* subtract a positive from a negative. */ /* In either case, ADD their magnitudes, */ /* and use the sign of the first number. */ c->sign = sa; res = s_mp_add (a, b, c); } else { /* subtract a positive from a positive, OR */ /* subtract a negative from a negative. */ /* First, take the difference between their */ /* magnitudes, then... */ if (mp_cmp_mag (a, b) != MP_LT) { /* Copy the sign from the first */ c->sign = sa; /* The first has a larger or equal magnitude */ res = s_mp_sub (a, b, c); } else { /* The result has the *opposite* sign from */ /* the first number. */ c->sign = (sa == MP_ZPOS) ? MP_NEG : MP_ZPOS; /* The second has a larger magnitude */ res = s_mp_sub (b, a, c); } } return res; } /* high level multiplication (handles sign) */ static int mp_mul (mp_int * a, mp_int * b, mp_int * c) { int res, neg; neg = (a->sign == b->sign) ? MP_ZPOS : MP_NEG; /* use Toom-Cook? */ #ifdef BN_MP_TOOM_MUL_C if (MIN (a->used, b->used) >= TOOM_MUL_CUTOFF) { res = mp_toom_mul(a, b, c); } else #endif #ifdef BN_MP_KARATSUBA_MUL_C /* use Karatsuba? */ if (MIN (a->used, b->used) >= KARATSUBA_MUL_CUTOFF) { res = mp_karatsuba_mul (a, b, c); } else #endif { /* can we use the fast multiplier? * * The fast multiplier can be used if the output will * have less than MP_WARRAY digits and the number of * digits won't affect carry propagation */ #ifdef BN_FAST_S_MP_MUL_DIGS_C int digs = a->used + b->used + 1; if ((digs < MP_WARRAY) && MIN(a->used, b->used) <= (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) { res = fast_s_mp_mul_digs (a, b, c, digs); } else #endif #ifdef BN_S_MP_MUL_DIGS_C res = s_mp_mul (a, b, c); /* uses s_mp_mul_digs */ #else #error mp_mul could fail res = MP_VAL; #endif } c->sign = (c->used > 0) ? neg : MP_ZPOS; return res; } /* d = a * b (mod c) */ static int mp_mulmod (mp_int * a, mp_int * b, mp_int * c, mp_int * d) { int res; mp_int t; if ((res = mp_init (&t)) != MP_OKAY) { return res; } if ((res = mp_mul (a, b, &t)) != MP_OKAY) { mp_clear (&t); return res; } res = mp_mod (&t, c, d); mp_clear (&t); return res; } /* c = a mod b, 0 <= c < b */ static int mp_mod (mp_int * a, mp_int * b, mp_int * c) { mp_int t; int res; if ((res = mp_init (&t)) != MP_OKAY) { return res; } if ((res = mp_div (a, b, NULL, &t)) != MP_OKAY) { mp_clear (&t); return res; } if (t.sign != b->sign) { res = mp_add (b, &t, c); } else { res = MP_OKAY; mp_exch (&t, c); } mp_clear (&t); return res; } /* this is a shell function that calls either the normal or Montgomery * exptmod functions. Originally the call to the montgomery code was * embedded in the normal function but that wasted a lot of stack space * for nothing (since 99% of the time the Montgomery code would be called) */ static int mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y) { int dr; /* modulus P must be positive */ if (P->sign == MP_NEG) { return MP_VAL; } /* if exponent X is negative we have to recurse */ if (X->sign == MP_NEG) { #ifdef LTM_NO_NEG_EXP return MP_VAL; #else /* LTM_NO_NEG_EXP */ #ifdef BN_MP_INVMOD_C mp_int tmpG, tmpX; int err; /* first compute 1/G mod P */ if ((err = mp_init(&tmpG)) != MP_OKAY) { return err; } if ((err = mp_invmod(G, P, &tmpG)) != MP_OKAY) { mp_clear(&tmpG); return err; } /* now get |X| */ if ((err = mp_init(&tmpX)) != MP_OKAY) { mp_clear(&tmpG); return err; } if ((err = mp_abs(X, &tmpX)) != MP_OKAY) { mp_clear_multi(&tmpG, &tmpX, NULL); return err; } /* and now compute (1/G)**|X| instead of G**X [X < 0] */ err = mp_exptmod(&tmpG, &tmpX, P, Y); mp_clear_multi(&tmpG, &tmpX, NULL); return err; #else #error mp_exptmod would always fail /* no invmod */ return MP_VAL; #endif #endif /* LTM_NO_NEG_EXP */ } /* modified diminished radix reduction */ #if defined(BN_MP_REDUCE_IS_2K_L_C) && defined(BN_MP_REDUCE_2K_L_C) && defined(BN_S_MP_EXPTMOD_C) if (mp_reduce_is_2k_l(P) == MP_YES) { return s_mp_exptmod(G, X, P, Y, 1); } #endif #ifdef BN_MP_DR_IS_MODULUS_C /* is it a DR modulus? */ dr = mp_dr_is_modulus(P); #else /* default to no */ dr = 0; #endif #ifdef BN_MP_REDUCE_IS_2K_C /* if not, is it a unrestricted DR modulus? */ if (dr == 0) { dr = mp_reduce_is_2k(P) << 1; } #endif /* if the modulus is odd or dr != 0 use the montgomery method */ #ifdef BN_MP_EXPTMOD_FAST_C if (mp_isodd (P) == 1 || dr != 0) { return mp_exptmod_fast (G, X, P, Y, dr); } else { #endif #ifdef BN_S_MP_EXPTMOD_C /* otherwise use the generic Barrett reduction technique */ return s_mp_exptmod (G, X, P, Y, 0); #else #error mp_exptmod could fail /* no exptmod for evens */ return MP_VAL; #endif #ifdef BN_MP_EXPTMOD_FAST_C } #endif if (dr == 0) { /* avoid compiler warnings about possibly unused variable */ } } /* compare two ints (signed)*/ static int mp_cmp (mp_int * a, mp_int * b) { /* compare based on sign */ if (a->sign != b->sign) { if (a->sign == MP_NEG) { return MP_LT; } else { return MP_GT; } } /* compare digits */ if (a->sign == MP_NEG) { /* if negative compare opposite direction */ return mp_cmp_mag(b, a); } else { return mp_cmp_mag(a, b); } } /* compare a digit */ static int mp_cmp_d(mp_int * a, mp_digit b) { /* compare based on sign */ if (a->sign == MP_NEG) { return MP_LT; } /* compare based on magnitude */ if (a->used > 1) { return MP_GT; } /* compare the only digit of a to b */ if (a->dp[0] > b) { return MP_GT; } else if (a->dp[0] < b) { return MP_LT; } else { return MP_EQ; } } #ifndef LTM_NO_NEG_EXP /* hac 14.61, pp608 */ static int mp_invmod (mp_int * a, mp_int * b, mp_int * c) { /* b cannot be negative */ if (b->sign == MP_NEG || mp_iszero(b) == 1) { return MP_VAL; } #ifdef BN_FAST_MP_INVMOD_C /* if the modulus is odd we can use a faster routine instead */ if (mp_isodd (b) == 1) { return fast_mp_invmod (a, b, c); } #endif #ifdef BN_MP_INVMOD_SLOW_C return mp_invmod_slow(a, b, c); #endif #ifndef BN_FAST_MP_INVMOD_C #ifndef BN_MP_INVMOD_SLOW_C #error mp_invmod would always fail #endif #endif return MP_VAL; } #endif /* LTM_NO_NEG_EXP */ /* get the size for an unsigned equivalent */ static int mp_unsigned_bin_size (mp_int * a) { int size = mp_count_bits (a); return (size / 8 + ((size & 7) != 0 ? 1 : 0)); } #ifndef LTM_NO_NEG_EXP /* hac 14.61, pp608 */ static int mp_invmod_slow (mp_int * a, mp_int * b, mp_int * c) { mp_int x, y, u, v, A, B, C, D; int res; /* b cannot be negative */ if (b->sign == MP_NEG || mp_iszero(b) == 1) { return MP_VAL; } /* init temps */ if ((res = mp_init_multi(&x, &y, &u, &v, &A, &B, &C, &D, NULL)) != MP_OKAY) { return res; } /* x = a, y = b */ if ((res = mp_mod(a, b, &x)) != MP_OKAY) { goto LBL_ERR; } if ((res = mp_copy (b, &y)) != MP_OKAY) { goto LBL_ERR; } /* 2. [modified] if x,y are both even then return an error! */ if (mp_iseven (&x) == 1 && mp_iseven (&y) == 1) { res = MP_VAL; goto LBL_ERR; } /* 3. u=x, v=y, A=1, B=0, C=0,D=1 */ if ((res = mp_copy (&x, &u)) != MP_OKAY) { goto LBL_ERR; } if ((res = mp_copy (&y, &v)) != MP_OKAY) { goto LBL_ERR; } mp_set (&A, 1); mp_set (&D, 1); top: /* 4. while u is even do */ while (mp_iseven (&u) == 1) { /* 4.1 u = u/2 */ if ((res = mp_div_2 (&u, &u)) != MP_OKAY) { goto LBL_ERR; } /* 4.2 if A or B is odd then */ if (mp_isodd (&A) == 1 || mp_isodd (&B) == 1) { /* A = (A+y)/2, B = (B-x)/2 */ if ((res = mp_add (&A, &y, &A)) != MP_OKAY) { goto LBL_ERR; } if ((res = mp_sub (&B, &x, &B)) != MP_OKAY) { goto LBL_ERR; } } /* A = A/2, B = B/2 */ if ((res = mp_div_2 (&A, &A)) != MP_OKAY) { goto LBL_ERR; } if ((res = mp_div_2 (&B, &B)) != MP_OKAY) { goto LBL_ERR; } } /* 5. while v is even do */ while (mp_iseven (&v) == 1) { /* 5.1 v = v/2 */ if ((res = mp_div_2 (&v, &v)) != MP_OKAY) { goto LBL_ERR; } /* 5.2 if C or D is odd then */ if (mp_isodd (&C) == 1 || mp_isodd (&D) == 1) { /* C = (C+y)/2, D = (D-x)/2 */ if ((res = mp_add (&C, &y, &C)) != MP_OKAY) { goto LBL_ERR; } if ((res = mp_sub (&D, &x, &D)) != MP_OKAY) { goto LBL_ERR; } } /* C = C/2, D = D/2 */ if ((res = mp_div_2 (&C, &C)) != MP_OKAY) { goto LBL_ERR; } if ((res = mp_div_2 (&D, &D)) != MP_OKAY) { goto LBL_ERR; } } /* 6. if u >= v then */ if (mp_cmp (&u, &v) != MP_LT) { /* u = u - v, A = A - C, B = B - D */ if ((res = mp_sub (&u, &v, &u)) != MP_OKAY) { goto LBL_ERR; } if ((res = mp_sub (&A, &C, &A)) != MP_OKAY) { goto LBL_ERR; } if ((res = mp_sub (&B, &D, &B)) != MP_OKAY) { goto LBL_ERR; } } else { /* v - v - u, C = C - A, D = D - B */ if ((res = mp_sub (&v, &u, &v)) != MP_OKAY) { goto LBL_ERR; } if ((res = mp_sub (&C, &A, &C)) != MP_OKAY) { goto LBL_ERR; } if ((res = mp_sub (&D, &B, &D)) != MP_OKAY) { goto LBL_ERR; } } /* if not zero goto step 4 */ if (mp_iszero (&u) == 0) goto top; /* now a = C, b = D, gcd == g*v */ /* if v != 1 then there is no inverse */ if (mp_cmp_d (&v, 1) != MP_EQ) { res = MP_VAL; goto LBL_ERR; } /* if its too low */ while (mp_cmp_d(&C, 0) == MP_LT) { if ((res = mp_add(&C, b, &C)) != MP_OKAY) { goto LBL_ERR; } } /* too big */ while (mp_cmp_mag(&C, b) != MP_LT) { if ((res = mp_sub(&C, b, &C)) != MP_OKAY) { goto LBL_ERR; } } /* C is now the inverse */ mp_exch (&C, c); res = MP_OKAY; LBL_ERR:mp_clear_multi (&x, &y, &u, &v, &A, &B, &C, &D, NULL); return res; } #endif /* LTM_NO_NEG_EXP */ /* compare maginitude of two ints (unsigned) */ static int mp_cmp_mag (mp_int * a, mp_int * b) { int n; mp_digit *tmpa, *tmpb; /* compare based on # of non-zero digits */ if (a->used > b->used) { return MP_GT; } if (a->used < b->used) { return MP_LT; } /* alias for a */ tmpa = a->dp + (a->used - 1); /* alias for b */ tmpb = b->dp + (a->used - 1); /* compare based on digits */ for (n = 0; n < a->used; ++n, --tmpa, --tmpb) { if (*tmpa > *tmpb) { return MP_GT; } if (*tmpa < *tmpb) { return MP_LT; } } return MP_EQ; } /* reads a unsigned char array, assumes the msb is stored first [big endian] */ static int mp_read_unsigned_bin (mp_int * a, const unsigned char *b, int c) { int res; /* make sure there are at least two digits */ if (a->alloc < 2) { if ((res = mp_grow(a, 2)) != MP_OKAY) { return res; } } /* zero the int */ mp_zero (a); /* read the bytes in */ while (c-- > 0) { if ((res = mp_mul_2d (a, 8, a)) != MP_OKAY) { return res; } #ifndef MP_8BIT a->dp[0] |= *b++; a->used += 1; #else a->dp[0] = (*b & MP_MASK); a->dp[1] |= ((*b++ >> 7U) & 1); a->used += 2; #endif } mp_clamp (a); return MP_OKAY; } /* store in unsigned [big endian] format */ static int mp_to_unsigned_bin (mp_int * a, unsigned char *b) { int x, res; mp_int t; if ((res = mp_init_copy (&t, a)) != MP_OKAY) { return res; } x = 0; while (mp_iszero (&t) == 0) { #ifndef MP_8BIT b[x++] = (unsigned char) (t.dp[0] & 255); #else b[x++] = (unsigned char) (t.dp[0] | ((t.dp[1] & 0x01) << 7)); #endif if ((res = mp_div_2d (&t, 8, &t, NULL)) != MP_OKAY) { mp_clear (&t); return res; } } bn_reverse (b, x); mp_clear (&t); return MP_OKAY; } /* shift right by a certain bit count (store quotient in c, optional remainder in d) */ static int mp_div_2d (mp_int * a, int b, mp_int * c, mp_int * d) { mp_digit D, r, rr; int x, res; mp_int t; /* if the shift count is <= 0 then we do no work */ if (b <= 0) { res = mp_copy (a, c); if (d != NULL) { mp_zero (d); } return res; } if ((res = mp_init (&t)) != MP_OKAY) { return res; } /* get the remainder */ if (d != NULL) { if ((res = mp_mod_2d (a, b, &t)) != MP_OKAY) { mp_clear (&t); return res; } } /* copy */ if ((res = mp_copy (a, c)) != MP_OKAY) { mp_clear (&t); return res; } /* shift by as many digits in the bit count */ if (b >= (int)DIGIT_BIT) { mp_rshd (c, b / DIGIT_BIT); } /* shift any bit count < DIGIT_BIT */ D = (mp_digit) (b % DIGIT_BIT); if (D != 0) { register mp_digit *tmpc, mask, shift; /* mask */ mask = (((mp_digit)1) << D) - 1; /* shift for lsb */ shift = DIGIT_BIT - D; /* alias */ tmpc = c->dp + (c->used - 1); /* carry */ r = 0; for (x = c->used - 1; x >= 0; x--) { /* get the lower bits of this word in a temp */ rr = *tmpc & mask; /* shift the current word and mix in the carry bits from the previous word */ *tmpc = (*tmpc >> D) | (r << shift); --tmpc; /* set the carry to the carry bits of the current word found above */ r = rr; } } mp_clamp (c); if (d != NULL) { mp_exch (&t, d); } mp_clear (&t); return MP_OKAY; } static int mp_init_copy (mp_int * a, mp_int * b) { int res; if ((res = mp_init (a)) != MP_OKAY) { return res; } return mp_copy (b, a); } /* set to zero */ static void mp_zero (mp_int * a) { int n; mp_digit *tmp; a->sign = MP_ZPOS; a->used = 0; tmp = a->dp; for (n = 0; n < a->alloc; n++) { *tmp++ = 0; } } /* copy, b = a */ static int mp_copy (mp_int * a, mp_int * b) { int res, n; /* if dst == src do nothing */ if (a == b) { return MP_OKAY; } /* grow dest */ if (b->alloc < a->used) { if ((res = mp_grow (b, a->used)) != MP_OKAY) { return res; } } /* zero b and copy the parameters over */ { register mp_digit *tmpa, *tmpb; /* pointer aliases */ /* source */ tmpa = a->dp; /* destination */ tmpb = b->dp; /* copy all the digits */ for (n = 0; n < a->used; n++) { *tmpb++ = *tmpa++; } /* clear high digits */ for (; n < b->used; n++) { *tmpb++ = 0; } } /* copy used count and sign */ b->used = a->used; b->sign = a->sign; return MP_OKAY; } /* shift right a certain amount of digits */ static void mp_rshd (mp_int * a, int b) { int x; /* if b <= 0 then ignore it */ if (b <= 0) { return; } /* if b > used then simply zero it and return */ if (a->used <= b) { mp_zero (a); return; } { register mp_digit *bottom, *top; /* shift the digits down */ /* bottom */ bottom = a->dp; /* top [offset into digits] */ top = a->dp + b; /* this is implemented as a sliding window where * the window is b-digits long and digits from * the top of the window are copied to the bottom * * e.g. b-2 | b-1 | b0 | b1 | b2 | ... | bb | ----> /\ | ----> \-------------------/ ----> */ for (x = 0; x < (a->used - b); x++) { *bottom++ = *top++; } /* zero the top digits */ for (; x < a->used; x++) { *bottom++ = 0; } } /* remove excess digits */ a->used -= b; } /* swap the elements of two integers, for cases where you can't simply swap the * mp_int pointers around */ static void mp_exch (mp_int * a, mp_int * b) { mp_int t; t = *a; *a = *b; *b = t; } /* trim unused digits * * This is used to ensure that leading zero digits are * trimed and the leading "used" digit will be non-zero * Typically very fast. Also fixes the sign if there * are no more leading digits */ static void mp_clamp (mp_int * a) { /* decrease used while the most significant digit is * zero. */ while (a->used > 0 && a->dp[a->used - 1] == 0) { --(a->used); } /* reset the sign flag if used == 0 */ if (a->used == 0) { a->sign = MP_ZPOS; } } /* grow as required */ static int mp_grow (mp_int * a, int size) { int i; mp_digit *tmp; /* if the alloc size is smaller alloc more ram */ if (a->alloc < size) { /* ensure there are always at least MP_PREC digits extra on top */ size += (MP_PREC * 2) - (size % MP_PREC); /* reallocate the array a->dp * * We store the return in a temporary variable * in case the operation failed we don't want * to overwrite the dp member of a. */ tmp = OPT_CAST(mp_digit) XREALLOC (a->dp, sizeof (mp_digit) * size); if (tmp == NULL) { /* reallocation failed but "a" is still valid [can be freed] */ return MP_MEM; } /* reallocation succeeded so set a->dp */ a->dp = tmp; /* zero excess digits */ i = a->alloc; a->alloc = size; for (; i < a->alloc; i++) { a->dp[i] = 0; } } return MP_OKAY; } #ifdef BN_MP_ABS_C /* b = |a| * * Simple function copies the input and fixes the sign to positive */ static int mp_abs (mp_int * a, mp_int * b) { int res; /* copy a to b */ if (a != b) { if ((res = mp_copy (a, b)) != MP_OKAY) { return res; } } /* force the sign of b to positive */ b->sign = MP_ZPOS; return MP_OKAY; } #endif /* set to a digit */ static void mp_set (mp_int * a, mp_digit b) { mp_zero (a); a->dp[0] = b & MP_MASK; a->used = (a->dp[0] != 0) ? 1 : 0; } #ifndef LTM_NO_NEG_EXP /* b = a/2 */ static int mp_div_2(mp_int * a, mp_int * b) { int x, res, oldused; /* copy */ if (b->alloc < a->used) { if ((res = mp_grow (b, a->used)) != MP_OKAY) { return res; } } oldused = b->used; b->used = a->used; { register mp_digit r, rr, *tmpa, *tmpb; /* source alias */ tmpa = a->dp + b->used - 1; /* dest alias */ tmpb = b->dp + b->used - 1; /* carry */ r = 0; for (x = b->used - 1; x >= 0; x--) { /* get the carry for the next iteration */ rr = *tmpa & 1; /* shift the current digit, add in carry and store */ *tmpb-- = (*tmpa-- >> 1) | (r << (DIGIT_BIT - 1)); /* forward carry to next iteration */ r = rr; } /* zero excess digits */ tmpb = b->dp + b->used; for (x = b->used; x < oldused; x++) { *tmpb++ = 0; } } b->sign = a->sign; mp_clamp (b); return MP_OKAY; } #endif /* LTM_NO_NEG_EXP */ /* shift left by a certain bit count */ static int mp_mul_2d (mp_int * a, int b, mp_int * c) { mp_digit d; int res; /* copy */ if (a != c) { if ((res = mp_copy (a, c)) != MP_OKAY) { return res; } } if (c->alloc < (int)(c->used + b/DIGIT_BIT + 1)) { if ((res = mp_grow (c, c->used + b / DIGIT_BIT + 1)) != MP_OKAY) { return res; } } /* shift by as many digits in the bit count */ if (b >= (int)DIGIT_BIT) { if ((res = mp_lshd (c, b / DIGIT_BIT)) != MP_OKAY) { return res; } } /* shift any bit count < DIGIT_BIT */ d = (mp_digit) (b % DIGIT_BIT); if (d != 0) { register mp_digit *tmpc, shift, mask, r, rr; register int x; /* bitmask for carries */ mask = (((mp_digit)1) << d) - 1; /* shift for msbs */ shift = DIGIT_BIT - d; /* alias */ tmpc = c->dp; /* carry */ r = 0; for (x = 0; x < c->used; x++) { /* get the higher bits of the current word */ rr = (*tmpc >> shift) & mask; /* shift the current word and OR in the carry */ *tmpc = ((*tmpc << d) | r) & MP_MASK; ++tmpc; /* set the carry to the carry bits of the current word */ r = rr; } /* set final carry */ if (r != 0) { c->dp[(c->used)++] = r; } } mp_clamp (c); return MP_OKAY; } #ifdef BN_MP_INIT_MULTI_C static int mp_init_multi(mp_int *mp, ...) { mp_err res = MP_OKAY; /* Assume ok until proven otherwise */ int n = 0; /* Number of ok inits */ mp_int* cur_arg = mp; va_list args; va_start(args, mp); /* init args to next argument from caller */ while (cur_arg != NULL) { if (mp_init(cur_arg) != MP_OKAY) { /* Oops - error! Back-track and mp_clear what we already succeeded in init-ing, then return error. */ va_list clean_args; /* end the current list */ va_end(args); /* now start cleaning up */ cur_arg = mp; va_start(clean_args, mp); while (n--) { mp_clear(cur_arg); cur_arg = va_arg(clean_args, mp_int*); } va_end(clean_args); res = MP_MEM; break; } n++; cur_arg = va_arg(args, mp_int*); } va_end(args); return res; /* Assumed ok, if error flagged above. */ } #endif #ifdef BN_MP_CLEAR_MULTI_C static void mp_clear_multi(mp_int *mp, ...) { mp_int* next_mp = mp; va_list args; va_start(args, mp); while (next_mp != NULL) { mp_clear(next_mp); next_mp = va_arg(args, mp_int*); } va_end(args); } #endif /* shift left a certain amount of digits */ static int mp_lshd (mp_int * a, int b) { int x, res; /* if its less than zero return */ if (b <= 0) { return MP_OKAY; } /* grow to fit the new digits */ if (a->alloc < a->used + b) { if ((res = mp_grow (a, a->used + b)) != MP_OKAY) { return res; } } { register mp_digit *top, *bottom; /* increment the used by the shift amount then copy upwards */ a->used += b; /* top */ top = a->dp + a->used - 1; /* base */ bottom = a->dp + a->used - 1 - b; /* much like mp_rshd this is implemented using a sliding window * except the window goes the otherway around. Copying from * the bottom to the top. see bn_mp_rshd.c for more info. */ for (x = a->used - 1; x >= b; x--) { *top-- = *bottom--; } /* zero the lower digits */ top = a->dp; for (x = 0; x < b; x++) { *top++ = 0; } } return MP_OKAY; } /* returns the number of bits in an int */ static int mp_count_bits (mp_int * a) { int r; mp_digit q; /* shortcut */ if (a->used == 0) { return 0; } /* get number of digits and add that */ r = (a->used - 1) * DIGIT_BIT; /* take the last digit and count the bits in it */ q = a->dp[a->used - 1]; while (q > ((mp_digit) 0)) { ++r; q >>= ((mp_digit) 1); } return r; } /* calc a value mod 2**b */ static int mp_mod_2d (mp_int * a, int b, mp_int * c) { int x, res; /* if b is <= 0 then zero the int */ if (b <= 0) { mp_zero (c); return MP_OKAY; } /* if the modulus is larger than the value than return */ if (b >= (int) (a->used * DIGIT_BIT)) { res = mp_copy (a, c); return res; } /* copy */ if ((res = mp_copy (a, c)) != MP_OKAY) { return res; } /* zero digits above the last digit of the modulus */ for (x = (b / DIGIT_BIT) + ((b % DIGIT_BIT) == 0 ? 0 : 1); x < c->used; x++) { c->dp[x] = 0; } /* clear the digit that is not completely outside/inside the modulus */ c->dp[b / DIGIT_BIT] &= (mp_digit) ((((mp_digit) 1) << (((mp_digit) b) % DIGIT_BIT)) - ((mp_digit) 1)); mp_clamp (c); return MP_OKAY; } #ifdef BN_MP_DIV_SMALL /* slower bit-bang division... also smaller */ static int mp_div(mp_int * a, mp_int * b, mp_int * c, mp_int * d) { mp_int ta, tb, tq, q; int res, n, n2; /* is divisor zero ? */ if (mp_iszero (b) == 1) { return MP_VAL; } /* if a < b then q=0, r = a */ if (mp_cmp_mag (a, b) == MP_LT) { if (d != NULL) { res = mp_copy (a, d); } else { res = MP_OKAY; } if (c != NULL) { mp_zero (c); } return res; } /* init our temps */ if ((res = mp_init_multi(&ta, &tb, &tq, &q, NULL) != MP_OKAY)) { return res; } mp_set(&tq, 1); n = mp_count_bits(a) - mp_count_bits(b); if (((res = mp_abs(a, &ta)) != MP_OKAY) || ((res = mp_abs(b, &tb)) != MP_OKAY) || ((res = mp_mul_2d(&tb, n, &tb)) != MP_OKAY) || ((res = mp_mul_2d(&tq, n, &tq)) != MP_OKAY)) { goto LBL_ERR; } while (n-- >= 0) { if (mp_cmp(&tb, &ta) != MP_GT) { if (((res = mp_sub(&ta, &tb, &ta)) != MP_OKAY) || ((res = mp_add(&q, &tq, &q)) != MP_OKAY)) { goto LBL_ERR; } } if (((res = mp_div_2d(&tb, 1, &tb, NULL)) != MP_OKAY) || ((res = mp_div_2d(&tq, 1, &tq, NULL)) != MP_OKAY)) { goto LBL_ERR; } } /* now q == quotient and ta == remainder */ n = a->sign; n2 = (a->sign == b->sign ? MP_ZPOS : MP_NEG); if (c != NULL) { mp_exch(c, &q); c->sign = (mp_iszero(c) == MP_YES) ? MP_ZPOS : n2; } if (d != NULL) { mp_exch(d, &ta); d->sign = (mp_iszero(d) == MP_YES) ? MP_ZPOS : n; } LBL_ERR: mp_clear_multi(&ta, &tb, &tq, &q, NULL); return res; } #else /* integer signed division. * c*b + d == a [e.g. a/b, c=quotient, d=remainder] * HAC pp.598 Algorithm 14.20 * * Note that the description in HAC is horribly * incomplete. For example, it doesn't consider * the case where digits are removed from 'x' in * the inner loop. It also doesn't consider the * case that y has fewer than three digits, etc.. * * The overall algorithm is as described as * 14.20 from HAC but fixed to treat these cases. */ static int mp_div (mp_int * a, mp_int * b, mp_int * c, mp_int * d) { mp_int q, x, y, t1, t2; int res, n, t, i, norm, neg; /* is divisor zero ? */ if (mp_iszero (b) == 1) { return MP_VAL; } /* if a < b then q=0, r = a */ if (mp_cmp_mag (a, b) == MP_LT) { if (d != NULL) { res = mp_copy (a, d); } else { res = MP_OKAY; } if (c != NULL) { mp_zero (c); } return res; } if ((res = mp_init_size (&q, a->used + 2)) != MP_OKAY) { return res; } q.used = a->used + 2; if ((res = mp_init (&t1)) != MP_OKAY) { goto LBL_Q; } if ((res = mp_init (&t2)) != MP_OKAY) { goto LBL_T1; } if ((res = mp_init_copy (&x, a)) != MP_OKAY) { goto LBL_T2; } if ((res = mp_init_copy (&y, b)) != MP_OKAY) { goto LBL_X; } /* fix the sign */ neg = (a->sign == b->sign) ? MP_ZPOS : MP_NEG; x.sign = y.sign = MP_ZPOS; /* normalize both x and y, ensure that y >= b/2, [b == 2**DIGIT_BIT] */ norm = mp_count_bits(&y) % DIGIT_BIT; if (norm < (int)(DIGIT_BIT-1)) { norm = (DIGIT_BIT-1) - norm; if ((res = mp_mul_2d (&x, norm, &x)) != MP_OKAY) { goto LBL_Y; } if ((res = mp_mul_2d (&y, norm, &y)) != MP_OKAY) { goto LBL_Y; } } else { norm = 0; } /* note hac does 0 based, so if used==5 then its 0,1,2,3,4, e.g. use 4 */ n = x.used - 1; t = y.used - 1; /* while (x >= y*b**n-t) do { q[n-t] += 1; x -= y*b**{n-t} } */ if ((res = mp_lshd (&y, n - t)) != MP_OKAY) { /* y = y*b**{n-t} */ goto LBL_Y; } while (mp_cmp (&x, &y) != MP_LT) { ++(q.dp[n - t]); if ((res = mp_sub (&x, &y, &x)) != MP_OKAY) { goto LBL_Y; } } /* reset y by shifting it back down */ mp_rshd (&y, n - t); /* step 3. for i from n down to (t + 1) */ for (i = n; i >= (t + 1); i--) { if (i > x.used) { continue; } /* step 3.1 if xi == yt then set q{i-t-1} to b-1, * otherwise set q{i-t-1} to (xi*b + x{i-1})/yt */ if (x.dp[i] == y.dp[t]) { q.dp[i - t - 1] = ((((mp_digit)1) << DIGIT_BIT) - 1); } else { mp_word tmp; tmp = ((mp_word) x.dp[i]) << ((mp_word) DIGIT_BIT); tmp |= ((mp_word) x.dp[i - 1]); tmp /= ((mp_word) y.dp[t]); if (tmp > (mp_word) MP_MASK) tmp = MP_MASK; q.dp[i - t - 1] = (mp_digit) (tmp & (mp_word) (MP_MASK)); } /* while (q{i-t-1} * (yt * b + y{t-1})) > xi * b**2 + xi-1 * b + xi-2 do q{i-t-1} -= 1; */ q.dp[i - t - 1] = (q.dp[i - t - 1] + 1) & MP_MASK; do { q.dp[i - t - 1] = (q.dp[i - t - 1] - 1) & MP_MASK; /* find left hand */ mp_zero (&t1); t1.dp[0] = (t - 1 < 0) ? 0 : y.dp[t - 1]; t1.dp[1] = y.dp[t]; t1.used = 2; if ((res = mp_mul_d (&t1, q.dp[i - t - 1], &t1)) != MP_OKAY) { goto LBL_Y; } /* find right hand */ t2.dp[0] = (i - 2 < 0) ? 0 : x.dp[i - 2]; t2.dp[1] = (i - 1 < 0) ? 0 : x.dp[i - 1]; t2.dp[2] = x.dp[i]; t2.used = 3; } while (mp_cmp_mag(&t1, &t2) == MP_GT); /* step 3.3 x = x - q{i-t-1} * y * b**{i-t-1} */ if ((res = mp_mul_d (&y, q.dp[i - t - 1], &t1)) != MP_OKAY) { goto LBL_Y; } if ((res = mp_lshd (&t1, i - t - 1)) != MP_OKAY) { goto LBL_Y; } if ((res = mp_sub (&x, &t1, &x)) != MP_OKAY) { goto LBL_Y; } /* if x < 0 then { x = x + y*b**{i-t-1}; q{i-t-1} -= 1; } */ if (x.sign == MP_NEG) { if ((res = mp_copy (&y, &t1)) != MP_OKAY) { goto LBL_Y; } if ((res = mp_lshd (&t1, i - t - 1)) != MP_OKAY) { goto LBL_Y; } if ((res = mp_add (&x, &t1, &x)) != MP_OKAY) { goto LBL_Y; } q.dp[i - t - 1] = (q.dp[i - t - 1] - 1UL) & MP_MASK; } } /* now q is the quotient and x is the remainder * [which we have to normalize] */ /* get sign before writing to c */ x.sign = x.used == 0 ? MP_ZPOS : a->sign; if (c != NULL) { mp_clamp (&q); mp_exch (&q, c); c->sign = neg; } if (d != NULL) { mp_div_2d (&x, norm, &x, NULL); mp_exch (&x, d); } res = MP_OKAY; LBL_Y:mp_clear (&y); LBL_X:mp_clear (&x); LBL_T2:mp_clear (&t2); LBL_T1:mp_clear (&t1); LBL_Q:mp_clear (&q); return res; } #endif #ifdef MP_LOW_MEM #define TAB_SIZE 32 #else #define TAB_SIZE 256 #endif static int s_mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int redmode) { mp_int M[TAB_SIZE], res, mu; mp_digit buf; int err, bitbuf, bitcpy, bitcnt, mode, digidx, x, y, winsize; int (*redux)(mp_int*,mp_int*,mp_int*); /* find window size */ x = mp_count_bits (X); if (x <= 7) { winsize = 2; } else if (x <= 36) { winsize = 3; } else if (x <= 140) { winsize = 4; } else if (x <= 450) { winsize = 5; } else if (x <= 1303) { winsize = 6; } else if (x <= 3529) { winsize = 7; } else { winsize = 8; } #ifdef MP_LOW_MEM if (winsize > 5) { winsize = 5; } #endif /* init M array */ /* init first cell */ if ((err = mp_init(&M[1])) != MP_OKAY) { return err; } /* now init the second half of the array */ for (x = 1<<(winsize-1); x < (1 << winsize); x++) { if ((err = mp_init(&M[x])) != MP_OKAY) { for (y = 1<<(winsize-1); y < x; y++) { mp_clear (&M[y]); } mp_clear(&M[1]); return err; } } /* create mu, used for Barrett reduction */ if ((err = mp_init (&mu)) != MP_OKAY) { goto LBL_M; } if (redmode == 0) { if ((err = mp_reduce_setup (&mu, P)) != MP_OKAY) { goto LBL_MU; } redux = mp_reduce; } else { if ((err = mp_reduce_2k_setup_l (P, &mu)) != MP_OKAY) { goto LBL_MU; } redux = mp_reduce_2k_l; } /* create M table * * The M table contains powers of the base, * e.g. M[x] = G**x mod P * * The first half of the table is not * computed though accept for M[0] and M[1] */ if ((err = mp_mod (G, P, &M[1])) != MP_OKAY) { goto LBL_MU; } /* compute the value at M[1<<(winsize-1)] by squaring * M[1] (winsize-1) times */ if ((err = mp_copy (&M[1], &M[1 << (winsize - 1)])) != MP_OKAY) { goto LBL_MU; } for (x = 0; x < (winsize - 1); x++) { /* square it */ if ((err = mp_sqr (&M[1 << (winsize - 1)], &M[1 << (winsize - 1)])) != MP_OKAY) { goto LBL_MU; } /* reduce modulo P */ if ((err = redux (&M[1 << (winsize - 1)], P, &mu)) != MP_OKAY) { goto LBL_MU; } } /* create upper table, that is M[x] = M[x-1] * M[1] (mod P) * for x = (2**(winsize - 1) + 1) to (2**winsize - 1) */ for (x = (1 << (winsize - 1)) + 1; x < (1 << winsize); x++) { if ((err = mp_mul (&M[x - 1], &M[1], &M[x])) != MP_OKAY) { goto LBL_MU; } if ((err = redux (&M[x], P, &mu)) != MP_OKAY) { goto LBL_MU; } } /* setup result */ if ((err = mp_init (&res)) != MP_OKAY) { goto LBL_MU; } mp_set (&res, 1); /* set initial mode and bit cnt */ mode = 0; bitcnt = 1; buf = 0; digidx = X->used - 1; bitcpy = 0; bitbuf = 0; for (;;) { /* grab next digit as required */ if (--bitcnt == 0) { /* if digidx == -1 we are out of digits */ if (digidx == -1) { break; } /* read next digit and reset the bitcnt */ buf = X->dp[digidx--]; bitcnt = (int) DIGIT_BIT; } /* grab the next msb from the exponent */ y = (buf >> (mp_digit)(DIGIT_BIT - 1)) & 1; buf <<= (mp_digit)1; /* if the bit is zero and mode == 0 then we ignore it * These represent the leading zero bits before the first 1 bit * in the exponent. Technically this opt is not required but it * does lower the # of trivial squaring/reductions used */ if (mode == 0 && y == 0) { continue; } /* if the bit is zero and mode == 1 then we square */ if (mode == 1 && y == 0) { if ((err = mp_sqr (&res, &res)) != MP_OKAY) { goto LBL_RES; } if ((err = redux (&res, P, &mu)) != MP_OKAY) { goto LBL_RES; } continue; } /* else we add it to the window */ bitbuf |= (y << (winsize - ++bitcpy)); mode = 2; if (bitcpy == winsize) { /* ok window is filled so square as required and multiply */ /* square first */ for (x = 0; x < winsize; x++) { if ((err = mp_sqr (&res, &res)) != MP_OKAY) { goto LBL_RES; } if ((err = redux (&res, P, &mu)) != MP_OKAY) { goto LBL_RES; } } /* then multiply */ if ((err = mp_mul (&res, &M[bitbuf], &res)) != MP_OKAY) { goto LBL_RES; } if ((err = redux (&res, P, &mu)) != MP_OKAY) { goto LBL_RES; } /* empty window and reset */ bitcpy = 0; bitbuf = 0; mode = 1; } } /* if bits remain then square/multiply */ if (mode == 2 && bitcpy > 0) { /* square then multiply if the bit is set */ for (x = 0; x < bitcpy; x++) { if ((err = mp_sqr (&res, &res)) != MP_OKAY) { goto LBL_RES; } if ((err = redux (&res, P, &mu)) != MP_OKAY) { goto LBL_RES; } bitbuf <<= 1; if ((bitbuf & (1 << winsize)) != 0) { /* then multiply */ if ((err = mp_mul (&res, &M[1], &res)) != MP_OKAY) { goto LBL_RES; } if ((err = redux (&res, P, &mu)) != MP_OKAY) { goto LBL_RES; } } } } mp_exch (&res, Y); err = MP_OKAY; LBL_RES:mp_clear (&res); LBL_MU:mp_clear (&mu); LBL_M: mp_clear(&M[1]); for (x = 1<<(winsize-1); x < (1 << winsize); x++) { mp_clear (&M[x]); } return err; } /* computes b = a*a */ static int mp_sqr (mp_int * a, mp_int * b) { int res; #ifdef BN_MP_TOOM_SQR_C /* use Toom-Cook? */ if (a->used >= TOOM_SQR_CUTOFF) { res = mp_toom_sqr(a, b); /* Karatsuba? */ } else #endif #ifdef BN_MP_KARATSUBA_SQR_C if (a->used >= KARATSUBA_SQR_CUTOFF) { res = mp_karatsuba_sqr (a, b); } else #endif { #ifdef BN_FAST_S_MP_SQR_C /* can we use the fast comba multiplier? */ if ((a->used * 2 + 1) < MP_WARRAY && a->used < (1 << (sizeof(mp_word) * CHAR_BIT - 2*DIGIT_BIT - 1))) { res = fast_s_mp_sqr (a, b); } else #endif #ifdef BN_S_MP_SQR_C res = s_mp_sqr (a, b); #else #error mp_sqr could fail res = MP_VAL; #endif } b->sign = MP_ZPOS; return res; } /* reduces a modulo n where n is of the form 2**p - d This differs from reduce_2k since "d" can be larger than a single digit. */ static int mp_reduce_2k_l(mp_int *a, mp_int *n, mp_int *d) { mp_int q; int p, res; if ((res = mp_init(&q)) != MP_OKAY) { return res; } p = mp_count_bits(n); top: /* q = a/2**p, a = a mod 2**p */ if ((res = mp_div_2d(a, p, &q, a)) != MP_OKAY) { goto ERR; } /* q = q * d */ if ((res = mp_mul(&q, d, &q)) != MP_OKAY) { goto ERR; } /* a = a + q */ if ((res = s_mp_add(a, &q, a)) != MP_OKAY) { goto ERR; } if (mp_cmp_mag(a, n) != MP_LT) { s_mp_sub(a, n, a); goto top; } ERR: mp_clear(&q); return res; } /* determines the setup value */ static int mp_reduce_2k_setup_l(mp_int *a, mp_int *d) { int res; mp_int tmp; if ((res = mp_init(&tmp)) != MP_OKAY) { return res; } if ((res = mp_2expt(&tmp, mp_count_bits(a))) != MP_OKAY) { goto ERR; } if ((res = s_mp_sub(&tmp, a, d)) != MP_OKAY) { goto ERR; } ERR: mp_clear(&tmp); return res; } /* computes a = 2**b * * Simple algorithm which zeroes the int, grows it then just sets one bit * as required. */ static int mp_2expt (mp_int * a, int b) { int res; /* zero a as per default */ mp_zero (a); /* grow a to accommodate the single bit */ if ((res = mp_grow (a, b / DIGIT_BIT + 1)) != MP_OKAY) { return res; } /* set the used count of where the bit will go */ a->used = b / DIGIT_BIT + 1; /* put the single bit in its place */ a->dp[b / DIGIT_BIT] = ((mp_digit)1) << (b % DIGIT_BIT); return MP_OKAY; } /* pre-calculate the value required for Barrett reduction * For a given modulus "b" it calulates the value required in "a" */ static int mp_reduce_setup (mp_int * a, mp_int * b) { int res; if ((res = mp_2expt (a, b->used * 2 * DIGIT_BIT)) != MP_OKAY) { return res; } return mp_div (a, b, a, NULL); } /* reduces x mod m, assumes 0 < x < m**2, mu is * precomputed via mp_reduce_setup. * From HAC pp.604 Algorithm 14.42 */ static int mp_reduce (mp_int * x, mp_int * m, mp_int * mu) { mp_int q; int res, um = m->used; /* q = x */ if ((res = mp_init_copy (&q, x)) != MP_OKAY) { return res; } /* q1 = x / b**(k-1) */ mp_rshd (&q, um - 1); /* according to HAC this optimization is ok */ if (((unsigned long) um) > (((mp_digit)1) << (DIGIT_BIT - 1))) { if ((res = mp_mul (&q, mu, &q)) != MP_OKAY) { goto CLEANUP; } } else { #ifdef BN_S_MP_MUL_HIGH_DIGS_C if ((res = s_mp_mul_high_digs (&q, mu, &q, um)) != MP_OKAY) { goto CLEANUP; } #elif defined(BN_FAST_S_MP_MUL_HIGH_DIGS_C) if ((res = fast_s_mp_mul_high_digs (&q, mu, &q, um)) != MP_OKAY) { goto CLEANUP; } #else { #error mp_reduce would always fail res = MP_VAL; goto CLEANUP; } #endif } /* q3 = q2 / b**(k+1) */ mp_rshd (&q, um + 1); /* x = x mod b**(k+1), quick (no division) */ if ((res = mp_mod_2d (x, DIGIT_BIT * (um + 1), x)) != MP_OKAY) { goto CLEANUP; } /* q = q * m mod b**(k+1), quick (no division) */ if ((res = s_mp_mul_digs (&q, m, &q, um + 1)) != MP_OKAY) { goto CLEANUP; } /* x = x - q */ if ((res = mp_sub (x, &q, x)) != MP_OKAY) { goto CLEANUP; } /* If x < 0, add b**(k+1) to it */ if (mp_cmp_d (x, 0) == MP_LT) { mp_set (&q, 1); if ((res = mp_lshd (&q, um + 1)) != MP_OKAY) { goto CLEANUP; } if ((res = mp_add (x, &q, x)) != MP_OKAY) { goto CLEANUP; } } /* Back off if it's too big */ while (mp_cmp (x, m) != MP_LT) { if ((res = s_mp_sub (x, m, x)) != MP_OKAY) { goto CLEANUP; } } CLEANUP: mp_clear (&q); return res; } /* multiplies |a| * |b| and only computes up to digs digits of result * HAC pp. 595, Algorithm 14.12 Modified so you can control how * many digits of output are created. */ static int s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs) { mp_int t; int res, pa, pb, ix, iy; mp_digit u; mp_word r; mp_digit tmpx, *tmpt, *tmpy; #ifdef BN_FAST_S_MP_MUL_DIGS_C /* can we use the fast multiplier? */ if (((digs) < MP_WARRAY) && MIN (a->used, b->used) < (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) { return fast_s_mp_mul_digs (a, b, c, digs); } #endif if ((res = mp_init_size (&t, digs)) != MP_OKAY) { return res; } t.used = digs; /* compute the digits of the product directly */ pa = a->used; for (ix = 0; ix < pa; ix++) { /* set the carry to zero */ u = 0; /* limit ourselves to making digs digits of output */ pb = MIN (b->used, digs - ix); /* setup some aliases */ /* copy of the digit from a used within the nested loop */ tmpx = a->dp[ix]; /* an alias for the destination shifted ix places */ tmpt = t.dp + ix; /* an alias for the digits of b */ tmpy = b->dp; /* compute the columns of the output and propagate the carry */ for (iy = 0; iy < pb; iy++) { /* compute the column as a mp_word */ r = ((mp_word)*tmpt) + ((mp_word)tmpx) * ((mp_word)*tmpy++) + ((mp_word) u); /* the new column is the lower part of the result */ *tmpt++ = (mp_digit) (r & ((mp_word) MP_MASK)); /* get the carry word from the result */ u = (mp_digit) (r >> ((mp_word) DIGIT_BIT)); } /* set carry if it is placed below digs */ if (ix + iy < digs) { *tmpt = u; } } mp_clamp (&t); mp_exch (&t, c); mp_clear (&t); return MP_OKAY; } #ifdef BN_FAST_S_MP_MUL_DIGS_C /* Fast (comba) multiplier * * This is the fast column-array [comba] multiplier. It is * designed to compute the columns of the product first * then handle the carries afterwards. This has the effect * of making the nested loops that compute the columns very * simple and schedulable on super-scalar processors. * * This has been modified to produce a variable number of * digits of output so if say only a half-product is required * you don't have to compute the upper half (a feature * required for fast Barrett reduction). * * Based on Algorithm 14.12 on pp.595 of HAC. * */ static int fast_s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs) { int olduse, res, pa, ix, iz; mp_digit W[MP_WARRAY]; register mp_word _W; /* grow the destination as required */ if (c->alloc < digs) { if ((res = mp_grow (c, digs)) != MP_OKAY) { return res; } } /* number of output digits to produce */ pa = MIN(digs, a->used + b->used); /* clear the carry */ _W = 0; for (ix = 0; ix < pa; ix++) { int tx, ty; int iy; mp_digit *tmpx, *tmpy; /* get offsets into the two bignums */ ty = MIN(b->used-1, ix); tx = ix - ty; /* setup temp aliases */ tmpx = a->dp + tx; tmpy = b->dp + ty; /* this is the number of times the loop will iterrate, essentially while (tx++ < a->used && ty-- >= 0) { ... } */ iy = MIN(a->used-tx, ty+1); /* execute loop */ for (iz = 0; iz < iy; ++iz) { _W += ((mp_word)*tmpx++)*((mp_word)*tmpy--); } /* store term */ W[ix] = ((mp_digit)_W) & MP_MASK; /* make next carry */ _W = _W >> ((mp_word)DIGIT_BIT); } /* setup dest */ olduse = c->used; c->used = pa; { register mp_digit *tmpc; tmpc = c->dp; for (ix = 0; ix < pa+1; ix++) { /* now extract the previous digit [below the carry] */ *tmpc++ = W[ix]; } /* clear unused digits [that existed in the old copy of c] */ for (; ix < olduse; ix++) { *tmpc++ = 0; } } mp_clamp (c); return MP_OKAY; } #endif /* BN_FAST_S_MP_MUL_DIGS_C */ /* init an mp_init for a given size */ static int mp_init_size (mp_int * a, int size) { int x; /* pad size so there are always extra digits */ size += (MP_PREC * 2) - (size % MP_PREC); /* alloc mem */ a->dp = OPT_CAST(mp_digit) XMALLOC (sizeof (mp_digit) * size); if (a->dp == NULL) { return MP_MEM; } /* set the members */ a->used = 0; a->alloc = size; a->sign = MP_ZPOS; /* zero the digits */ for (x = 0; x < size; x++) { a->dp[x] = 0; } return MP_OKAY; } /* low level squaring, b = a*a, HAC pp.596-597, Algorithm 14.16 */ static int s_mp_sqr (mp_int * a, mp_int * b) { mp_int t; int res, ix, iy, pa; mp_word r; mp_digit u, tmpx, *tmpt; pa = a->used; if ((res = mp_init_size (&t, 2*pa + 1)) != MP_OKAY) { return res; } /* default used is maximum possible size */ t.used = 2*pa + 1; for (ix = 0; ix < pa; ix++) { /* first calculate the digit at 2*ix */ /* calculate double precision result */ r = ((mp_word) t.dp[2*ix]) + ((mp_word)a->dp[ix])*((mp_word)a->dp[ix]); /* store lower part in result */ t.dp[ix+ix] = (mp_digit) (r & ((mp_word) MP_MASK)); /* get the carry */ u = (mp_digit)(r >> ((mp_word) DIGIT_BIT)); /* left hand side of A[ix] * A[iy] */ tmpx = a->dp[ix]; /* alias for where to store the results */ tmpt = t.dp + (2*ix + 1); for (iy = ix + 1; iy < pa; iy++) { /* first calculate the product */ r = ((mp_word)tmpx) * ((mp_word)a->dp[iy]); /* now calculate the double precision result, note we use * addition instead of *2 since it's easier to optimize */ r = ((mp_word) *tmpt) + r + r + ((mp_word) u); /* store lower part */ *tmpt++ = (mp_digit) (r & ((mp_word) MP_MASK)); /* get carry */ u = (mp_digit)(r >> ((mp_word) DIGIT_BIT)); } /* propagate upwards */ while (u != ((mp_digit) 0)) { r = ((mp_word) *tmpt) + ((mp_word) u); *tmpt++ = (mp_digit) (r & ((mp_word) MP_MASK)); u = (mp_digit)(r >> ((mp_word) DIGIT_BIT)); } } mp_clamp (&t); mp_exch (&t, b); mp_clear (&t); return MP_OKAY; } /* multiplies |a| * |b| and does not compute the lower digs digits * [meant to get the higher part of the product] */ static int s_mp_mul_high_digs (mp_int * a, mp_int * b, mp_int * c, int digs) { mp_int t; int res, pa, pb, ix, iy; mp_digit u; mp_word r; mp_digit tmpx, *tmpt, *tmpy; /* can we use the fast multiplier? */ #ifdef BN_FAST_S_MP_MUL_HIGH_DIGS_C if (((a->used + b->used + 1) < MP_WARRAY) && MIN (a->used, b->used) < (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) { return fast_s_mp_mul_high_digs (a, b, c, digs); } #endif if ((res = mp_init_size (&t, a->used + b->used + 1)) != MP_OKAY) { return res; } t.used = a->used + b->used + 1; pa = a->used; pb = b->used; for (ix = 0; ix < pa; ix++) { /* clear the carry */ u = 0; /* left hand side of A[ix] * B[iy] */ tmpx = a->dp[ix]; /* alias to the address of where the digits will be stored */ tmpt = &(t.dp[digs]); /* alias for where to read the right hand side from */ tmpy = b->dp + (digs - ix); for (iy = digs - ix; iy < pb; iy++) { /* calculate the double precision result */ r = ((mp_word)*tmpt) + ((mp_word)tmpx) * ((mp_word)*tmpy++) + ((mp_word) u); /* get the lower part */ *tmpt++ = (mp_digit) (r & ((mp_word) MP_MASK)); /* carry the carry */ u = (mp_digit) (r >> ((mp_word) DIGIT_BIT)); } *tmpt = u; } mp_clamp (&t); mp_exch (&t, c); mp_clear (&t); return MP_OKAY; } #ifdef BN_MP_MONTGOMERY_SETUP_C /* setups the montgomery reduction stuff */ static int mp_montgomery_setup (mp_int * n, mp_digit * rho) { mp_digit x, b; /* fast inversion mod 2**k * * Based on the fact that * * XA = 1 (mod 2**n) => (X(2-XA)) A = 1 (mod 2**2n) * => 2*X*A - X*X*A*A = 1 * => 2*(1) - (1) = 1 */ b = n->dp[0]; if ((b & 1) == 0) { return MP_VAL; } x = (((b + 2) & 4) << 1) + b; /* here x*a==1 mod 2**4 */ x *= 2 - b * x; /* here x*a==1 mod 2**8 */ #if !defined(MP_8BIT) x *= 2 - b * x; /* here x*a==1 mod 2**16 */ #endif #if defined(MP_64BIT) || !(defined(MP_8BIT) || defined(MP_16BIT)) x *= 2 - b * x; /* here x*a==1 mod 2**32 */ #endif #ifdef MP_64BIT x *= 2 - b * x; /* here x*a==1 mod 2**64 */ #endif /* rho = -1/m mod b */ *rho = (unsigned long)(((mp_word)1 << ((mp_word) DIGIT_BIT)) - x) & MP_MASK; return MP_OKAY; } #endif #ifdef BN_FAST_MP_MONTGOMERY_REDUCE_C /* computes xR**-1 == x (mod N) via Montgomery Reduction * * This is an optimized implementation of montgomery_reduce * which uses the comba method to quickly calculate the columns of the * reduction. * * Based on Algorithm 14.32 on pp.601 of HAC. */ static int fast_mp_montgomery_reduce (mp_int * x, mp_int * n, mp_digit rho) { int ix, res, olduse; mp_word W[MP_WARRAY]; /* get old used count */ olduse = x->used; /* grow a as required */ if (x->alloc < n->used + 1) { if ((res = mp_grow (x, n->used + 1)) != MP_OKAY) { return res; } } /* first we have to get the digits of the input into * an array of double precision words W[...] */ { register mp_word *_W; register mp_digit *tmpx; /* alias for the W[] array */ _W = W; /* alias for the digits of x*/ tmpx = x->dp; /* copy the digits of a into W[0..a->used-1] */ for (ix = 0; ix < x->used; ix++) { *_W++ = *tmpx++; } /* zero the high words of W[a->used..m->used*2] */ for (; ix < n->used * 2 + 1; ix++) { *_W++ = 0; } } /* now we proceed to zero successive digits * from the least significant upwards */ for (ix = 0; ix < n->used; ix++) { /* mu = ai * m' mod b * * We avoid a double precision multiplication (which isn't required) * by casting the value down to a mp_digit. Note this requires * that W[ix-1] have the carry cleared (see after the inner loop) */ register mp_digit mu; mu = (mp_digit) (((W[ix] & MP_MASK) * rho) & MP_MASK); /* a = a + mu * m * b**i * * This is computed in place and on the fly. The multiplication * by b**i is handled by offseting which columns the results * are added to. * * Note the comba method normally doesn't handle carries in the * inner loop In this case we fix the carry from the previous * column since the Montgomery reduction requires digits of the * result (so far) [see above] to work. This is * handled by fixing up one carry after the inner loop. The * carry fixups are done in order so after these loops the * first m->used words of W[] have the carries fixed */ { register int iy; register mp_digit *tmpn; register mp_word *_W; /* alias for the digits of the modulus */ tmpn = n->dp; /* Alias for the columns set by an offset of ix */ _W = W + ix; /* inner loop */ for (iy = 0; iy < n->used; iy++) { *_W++ += ((mp_word)mu) * ((mp_word)*tmpn++); } } /* now fix carry for next digit, W[ix+1] */ W[ix + 1] += W[ix] >> ((mp_word) DIGIT_BIT); } /* now we have to propagate the carries and * shift the words downward [all those least * significant digits we zeroed]. */ { register mp_digit *tmpx; register mp_word *_W, *_W1; /* nox fix rest of carries */ /* alias for current word */ _W1 = W + ix; /* alias for next word, where the carry goes */ _W = W + ++ix; for (; ix <= n->used * 2 + 1; ix++) { *_W++ += *_W1++ >> ((mp_word) DIGIT_BIT); } /* copy out, A = A/b**n * * The result is A/b**n but instead of converting from an * array of mp_word to mp_digit than calling mp_rshd * we just copy them in the right order */ /* alias for destination word */ tmpx = x->dp; /* alias for shifted double precision result */ _W = W + n->used; for (ix = 0; ix < n->used + 1; ix++) { *tmpx++ = (mp_digit)(*_W++ & ((mp_word) MP_MASK)); } /* zero oldused digits, if the input a was larger than * m->used+1 we'll have to clear the digits */ for (; ix < olduse; ix++) { *tmpx++ = 0; } } /* set the max used and clamp */ x->used = n->used + 1; mp_clamp (x); /* if A >= m then A = A - m */ if (mp_cmp_mag (x, n) != MP_LT) { return s_mp_sub (x, n, x); } return MP_OKAY; } #endif #ifdef BN_MP_MUL_2_C /* b = a*2 */ static int mp_mul_2(mp_int * a, mp_int * b) { int x, res, oldused; /* grow to accommodate result */ if (b->alloc < a->used + 1) { if ((res = mp_grow (b, a->used + 1)) != MP_OKAY) { return res; } } oldused = b->used; b->used = a->used; { register mp_digit r, rr, *tmpa, *tmpb; /* alias for source */ tmpa = a->dp; /* alias for dest */ tmpb = b->dp; /* carry */ r = 0; for (x = 0; x < a->used; x++) { /* get what will be the *next* carry bit from the * MSB of the current digit */ rr = *tmpa >> ((mp_digit)(DIGIT_BIT - 1)); /* now shift up this digit, add in the carry [from the previous] */ *tmpb++ = ((*tmpa++ << ((mp_digit)1)) | r) & MP_MASK; /* copy the carry that would be from the source * digit into the next iteration */ r = rr; } /* new leading digit? */ if (r != 0) { /* add a MSB which is always 1 at this point */ *tmpb = 1; ++(b->used); } /* now zero any excess digits on the destination * that we didn't write to */ tmpb = b->dp + b->used; for (x = b->used; x < oldused; x++) { *tmpb++ = 0; } } b->sign = a->sign; return MP_OKAY; } #endif #ifdef BN_MP_MONTGOMERY_CALC_NORMALIZATION_C /* * shifts with subtractions when the result is greater than b. * * The method is slightly modified to shift B unconditionally up to just under * the leading bit of b. This saves a lot of multiple precision shifting. */ static int mp_montgomery_calc_normalization (mp_int * a, mp_int * b) { int x, bits, res; /* how many bits of last digit does b use */ bits = mp_count_bits (b) % DIGIT_BIT; if (b->used > 1) { if ((res = mp_2expt (a, (b->used - 1) * DIGIT_BIT + bits - 1)) != MP_OKAY) { return res; } } else { mp_set(a, 1); bits = 1; } /* now compute C = A * B mod b */ for (x = bits - 1; x < (int)DIGIT_BIT; x++) { if ((res = mp_mul_2 (a, a)) != MP_OKAY) { return res; } if (mp_cmp_mag (a, b) != MP_LT) { if ((res = s_mp_sub (a, b, a)) != MP_OKAY) { return res; } } } return MP_OKAY; } #endif #ifdef BN_MP_EXPTMOD_FAST_C /* computes Y == G**X mod P, HAC pp.616, Algorithm 14.85 * * Uses a left-to-right k-ary sliding window to compute the modular exponentiation. * The value of k changes based on the size of the exponent. * * Uses Montgomery or Diminished Radix reduction [whichever appropriate] */ static int mp_exptmod_fast (mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int redmode) { mp_int M[TAB_SIZE], res; mp_digit buf, mp; int err, bitbuf, bitcpy, bitcnt, mode, digidx, x, y, winsize; /* use a pointer to the reduction algorithm. This allows us to use * one of many reduction algorithms without modding the guts of * the code with if statements everywhere. */ int (*redux)(mp_int*,mp_int*,mp_digit); /* find window size */ x = mp_count_bits (X); if (x <= 7) { winsize = 2; } else if (x <= 36) { winsize = 3; } else if (x <= 140) { winsize = 4; } else if (x <= 450) { winsize = 5; } else if (x <= 1303) { winsize = 6; } else if (x <= 3529) { winsize = 7; } else { winsize = 8; } #ifdef MP_LOW_MEM if (winsize > 5) { winsize = 5; } #endif /* init M array */ /* init first cell */ if ((err = mp_init(&M[1])) != MP_OKAY) { return err; } /* now init the second half of the array */ for (x = 1<<(winsize-1); x < (1 << winsize); x++) { if ((err = mp_init(&M[x])) != MP_OKAY) { for (y = 1<<(winsize-1); y < x; y++) { mp_clear (&M[y]); } mp_clear(&M[1]); return err; } } /* determine and setup reduction code */ if (redmode == 0) { #ifdef BN_MP_MONTGOMERY_SETUP_C /* now setup montgomery */ if ((err = mp_montgomery_setup (P, &mp)) != MP_OKAY) { goto LBL_M; } #else err = MP_VAL; goto LBL_M; #endif /* automatically pick the comba one if available (saves quite a few calls/ifs) */ #ifdef BN_FAST_MP_MONTGOMERY_REDUCE_C if (((P->used * 2 + 1) < MP_WARRAY) && P->used < (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) { redux = fast_mp_montgomery_reduce; } else #endif { #ifdef BN_MP_MONTGOMERY_REDUCE_C /* use slower baseline Montgomery method */ redux = mp_montgomery_reduce; #else err = MP_VAL; goto LBL_M; #endif } } else if (redmode == 1) { #if defined(BN_MP_DR_SETUP_C) && defined(BN_MP_DR_REDUCE_C) /* setup DR reduction for moduli of the form B**k - b */ mp_dr_setup(P, &mp); redux = mp_dr_reduce; #else err = MP_VAL; goto LBL_M; #endif } else { #if defined(BN_MP_REDUCE_2K_SETUP_C) && defined(BN_MP_REDUCE_2K_C) /* setup DR reduction for moduli of the form 2**k - b */ if ((err = mp_reduce_2k_setup(P, &mp)) != MP_OKAY) { goto LBL_M; } redux = mp_reduce_2k; #else err = MP_VAL; goto LBL_M; #endif } /* setup result */ if ((err = mp_init (&res)) != MP_OKAY) { goto LBL_M; } /* create M table * * * The first half of the table is not computed though accept for M[0] and M[1] */ if (redmode == 0) { #ifdef BN_MP_MONTGOMERY_CALC_NORMALIZATION_C /* now we need R mod m */ if ((err = mp_montgomery_calc_normalization (&res, P)) != MP_OKAY) { goto LBL_RES; } #else err = MP_VAL; goto LBL_RES; #endif /* now set M[1] to G * R mod m */ if ((err = mp_mulmod (G, &res, P, &M[1])) != MP_OKAY) { goto LBL_RES; } } else { mp_set(&res, 1); if ((err = mp_mod(G, P, &M[1])) != MP_OKAY) { goto LBL_RES; } } /* compute the value at M[1<<(winsize-1)] by squaring M[1] (winsize-1) times */ if ((err = mp_copy (&M[1], &M[1 << (winsize - 1)])) != MP_OKAY) { goto LBL_RES; } for (x = 0; x < (winsize - 1); x++) { if ((err = mp_sqr (&M[1 << (winsize - 1)], &M[1 << (winsize - 1)])) != MP_OKAY) { goto LBL_RES; } if ((err = redux (&M[1 << (winsize - 1)], P, mp)) != MP_OKAY) { goto LBL_RES; } } /* create upper table */ for (x = (1 << (winsize - 1)) + 1; x < (1 << winsize); x++) { if ((err = mp_mul (&M[x - 1], &M[1], &M[x])) != MP_OKAY) { goto LBL_RES; } if ((err = redux (&M[x], P, mp)) != MP_OKAY) { goto LBL_RES; } } /* set initial mode and bit cnt */ mode = 0; bitcnt = 1; buf = 0; digidx = X->used - 1; bitcpy = 0; bitbuf = 0; for (;;) { /* grab next digit as required */ if (--bitcnt == 0) { /* if digidx == -1 we are out of digits so break */ if (digidx == -1) { break; } /* read next digit and reset bitcnt */ buf = X->dp[digidx--]; bitcnt = (int)DIGIT_BIT; } /* grab the next msb from the exponent */ y = (mp_digit)(buf >> (DIGIT_BIT - 1)) & 1; buf <<= (mp_digit)1; /* if the bit is zero and mode == 0 then we ignore it * These represent the leading zero bits before the first 1 bit * in the exponent. Technically this opt is not required but it * does lower the # of trivial squaring/reductions used */ if (mode == 0 && y == 0) { continue; } /* if the bit is zero and mode == 1 then we square */ if (mode == 1 && y == 0) { if ((err = mp_sqr (&res, &res)) != MP_OKAY) { goto LBL_RES; } if ((err = redux (&res, P, mp)) != MP_OKAY) { goto LBL_RES; } continue; } /* else we add it to the window */ bitbuf |= (y << (winsize - ++bitcpy)); mode = 2; if (bitcpy == winsize) { /* ok window is filled so square as required and multiply */ /* square first */ for (x = 0; x < winsize; x++) { if ((err = mp_sqr (&res, &res)) != MP_OKAY) { goto LBL_RES; } if ((err = redux (&res, P, mp)) != MP_OKAY) { goto LBL_RES; } } /* then multiply */ if ((err = mp_mul (&res, &M[bitbuf], &res)) != MP_OKAY) { goto LBL_RES; } if ((err = redux (&res, P, mp)) != MP_OKAY) { goto LBL_RES; } /* empty window and reset */ bitcpy = 0; bitbuf = 0; mode = 1; } } /* if bits remain then square/multiply */ if (mode == 2 && bitcpy > 0) { /* square then multiply if the bit is set */ for (x = 0; x < bitcpy; x++) { if ((err = mp_sqr (&res, &res)) != MP_OKAY) { goto LBL_RES; } if ((err = redux (&res, P, mp)) != MP_OKAY) { goto LBL_RES; } /* get next bit of the window */ bitbuf <<= 1; if ((bitbuf & (1 << winsize)) != 0) { /* then multiply */ if ((err = mp_mul (&res, &M[1], &res)) != MP_OKAY) { goto LBL_RES; } if ((err = redux (&res, P, mp)) != MP_OKAY) { goto LBL_RES; } } } } if (redmode == 0) { /* fixup result if Montgomery reduction is used * recall that any value in a Montgomery system is * actually multiplied by R mod n. So we have * to reduce one more time to cancel out the factor * of R. */ if ((err = redux(&res, P, mp)) != MP_OKAY) { goto LBL_RES; } } /* swap res with Y */ mp_exch (&res, Y); err = MP_OKAY; LBL_RES:mp_clear (&res); LBL_M: mp_clear(&M[1]); for (x = 1<<(winsize-1); x < (1 << winsize); x++) { mp_clear (&M[x]); } return err; } #endif #ifdef BN_FAST_S_MP_SQR_C /* the jist of squaring... * you do like mult except the offset of the tmpx [one that * starts closer to zero] can't equal the offset of tmpy. * So basically you set up iy like before then you min it with * (ty-tx) so that it never happens. You double all those * you add in the inner loop After that loop you do the squares and add them in. */ static int fast_s_mp_sqr (mp_int * a, mp_int * b) { int olduse, res, pa, ix, iz; mp_digit W[MP_WARRAY], *tmpx; mp_word W1; /* grow the destination as required */ pa = a->used + a->used; if (b->alloc < pa) { if ((res = mp_grow (b, pa)) != MP_OKAY) { return res; } } /* number of output digits to produce */ W1 = 0; for (ix = 0; ix < pa; ix++) { int tx, ty, iy; mp_word _W; mp_digit *tmpy; /* clear counter */ _W = 0; /* get offsets into the two bignums */ ty = MIN(a->used-1, ix); tx = ix - ty; /* setup temp aliases */ tmpx = a->dp + tx; tmpy = a->dp + ty; /* this is the number of times the loop will iterrate, essentially while (tx++ < a->used && ty-- >= 0) { ... } */ iy = MIN(a->used-tx, ty+1); /* now for squaring tx can never equal ty * we halve the distance since they approach at a rate of 2x * and we have to round because odd cases need to be executed */ iy = MIN(iy, (ty-tx+1)>>1); /* execute loop */ for (iz = 0; iz < iy; iz++) { _W += ((mp_word)*tmpx++)*((mp_word)*tmpy--); } /* double the inner product and add carry */ _W = _W + _W + W1; /* even columns have the square term in them */ if ((ix&1) == 0) { _W += ((mp_word)a->dp[ix>>1])*((mp_word)a->dp[ix>>1]); } /* store it */ W[ix] = (mp_digit)(_W & MP_MASK); /* make next carry */ W1 = _W >> ((mp_word)DIGIT_BIT); } /* setup dest */ olduse = b->used; b->used = a->used+a->used; { mp_digit *tmpb; tmpb = b->dp; for (ix = 0; ix < pa; ix++) { *tmpb++ = W[ix] & MP_MASK; } /* clear unused digits [that existed in the old copy of c] */ for (; ix < olduse; ix++) { *tmpb++ = 0; } } mp_clamp (b); return MP_OKAY; } #endif #ifdef BN_MP_MUL_D_C /* multiply by a digit */ static int mp_mul_d (mp_int * a, mp_digit b, mp_int * c) { mp_digit u, *tmpa, *tmpc; mp_word r; int ix, res, olduse; /* make sure c is big enough to hold a*b */ if (c->alloc < a->used + 1) { if ((res = mp_grow (c, a->used + 1)) != MP_OKAY) { return res; } } /* get the original destinations used count */ olduse = c->used; /* set the sign */ c->sign = a->sign; /* alias for a->dp [source] */ tmpa = a->dp; /* alias for c->dp [dest] */ tmpc = c->dp; /* zero carry */ u = 0; /* compute columns */ for (ix = 0; ix < a->used; ix++) { /* compute product and carry sum for this term */ r = ((mp_word) u) + ((mp_word)*tmpa++) * ((mp_word)b); /* mask off higher bits to get a single digit */ *tmpc++ = (mp_digit) (r & ((mp_word) MP_MASK)); /* send carry into next iteration */ u = (mp_digit) (r >> ((mp_word) DIGIT_BIT)); } /* store final carry [if any] and increment ix offset */ *tmpc++ = u; ++ix; /* now zero digits above the top */ while (ix++ < olduse) { *tmpc++ = 0; } /* set used count */ c->used = a->used + 1; mp_clamp(c); return MP_OKAY; } #endif wpa-2.1/src/tls/pkcs1.c000066400000000000000000000102401227414666700147350ustar00rootroot00000000000000/* * PKCS #1 (RSA Encryption) * Copyright (c) 2006-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 "rsa.h" #include "pkcs1.h" static int pkcs1_generate_encryption_block(u8 block_type, size_t modlen, const u8 *in, size_t inlen, u8 *out, size_t *outlen) { size_t ps_len; u8 *pos; /* * PKCS #1 v1.5, 8.1: * * EB = 00 || BT || PS || 00 || D * BT = 00 or 01 for private-key operation; 02 for public-key operation * PS = k-3-||D||; at least eight octets * (BT=0: PS=0x00, BT=1: PS=0xff, BT=2: PS=pseudorandom non-zero) * k = length of modulus in octets (modlen) */ if (modlen < 12 || modlen > *outlen || inlen > modlen - 11) { wpa_printf(MSG_DEBUG, "PKCS #1: %s - Invalid buffer " "lengths (modlen=%lu outlen=%lu inlen=%lu)", __func__, (unsigned long) modlen, (unsigned long) *outlen, (unsigned long) inlen); return -1; } pos = out; *pos++ = 0x00; *pos++ = block_type; /* BT */ ps_len = modlen - inlen - 3; switch (block_type) { case 0: os_memset(pos, 0x00, ps_len); pos += ps_len; break; case 1: os_memset(pos, 0xff, ps_len); pos += ps_len; break; case 2: if (os_get_random(pos, ps_len) < 0) { wpa_printf(MSG_DEBUG, "PKCS #1: %s - Failed to get " "random data for PS", __func__); return -1; } while (ps_len--) { if (*pos == 0x00) *pos = 0x01; pos++; } break; default: wpa_printf(MSG_DEBUG, "PKCS #1: %s - Unsupported block type " "%d", __func__, block_type); return -1; } *pos++ = 0x00; os_memcpy(pos, in, inlen); /* D */ return 0; } int pkcs1_encrypt(int block_type, struct crypto_rsa_key *key, int use_private, const u8 *in, size_t inlen, u8 *out, size_t *outlen) { size_t modlen; modlen = crypto_rsa_get_modulus_len(key); if (pkcs1_generate_encryption_block(block_type, modlen, in, inlen, out, outlen) < 0) return -1; return crypto_rsa_exptmod(out, modlen, out, outlen, key, use_private); } int pkcs1_v15_private_key_decrypt(struct crypto_rsa_key *key, const u8 *in, size_t inlen, u8 *out, size_t *outlen) { int res; u8 *pos, *end; res = crypto_rsa_exptmod(in, inlen, out, outlen, key, 1); if (res) return res; if (*outlen < 2 || out[0] != 0 || out[1] != 2) return -1; /* Skip PS (pseudorandom non-zero octets) */ pos = out + 2; end = out + *outlen; while (*pos && pos < end) pos++; if (pos == end) return -1; pos++; *outlen -= pos - out; /* Strip PKCS #1 header */ os_memmove(out, pos, *outlen); return 0; } int pkcs1_decrypt_public_key(struct crypto_rsa_key *key, const u8 *crypt, size_t crypt_len, u8 *plain, size_t *plain_len) { size_t len; u8 *pos; len = *plain_len; if (crypto_rsa_exptmod(crypt, crypt_len, plain, &len, key, 0) < 0) return -1; /* * PKCS #1 v1.5, 8.1: * * EB = 00 || BT || PS || 00 || D * BT = 00 or 01 * PS = k-3-||D|| times (00 if BT=00) or (FF if BT=01) * k = length of modulus in octets */ if (len < 3 + 8 + 16 /* min hash len */ || plain[0] != 0x00 || (plain[1] != 0x00 && plain[1] != 0x01)) { wpa_printf(MSG_INFO, "LibTomCrypt: Invalid signature EB " "structure"); return -1; } pos = plain + 3; if (plain[1] == 0x00) { /* BT = 00 */ if (plain[2] != 0x00) { wpa_printf(MSG_INFO, "LibTomCrypt: Invalid signature " "PS (BT=00)"); return -1; } while (pos + 1 < plain + len && *pos == 0x00 && pos[1] == 0x00) pos++; } else { /* BT = 01 */ if (plain[2] != 0xff) { wpa_printf(MSG_INFO, "LibTomCrypt: Invalid signature " "PS (BT=01)"); return -1; } while (pos < plain + len && *pos == 0xff) pos++; } if (pos - plain - 2 < 8) { /* PKCS #1 v1.5, 8.1: At least eight octets long PS */ wpa_printf(MSG_INFO, "LibTomCrypt: Too short signature " "padding"); return -1; } if (pos + 16 /* min hash len */ >= plain + len || *pos != 0x00) { wpa_printf(MSG_INFO, "LibTomCrypt: Invalid signature EB " "structure (2)"); return -1; } pos++; len -= pos - plain; /* Strip PKCS #1 header */ os_memmove(plain, pos, len); *plain_len = len; return 0; } wpa-2.1/src/tls/pkcs1.h000066400000000000000000000012221227414666700147420ustar00rootroot00000000000000/* * PKCS #1 (RSA Encryption) * Copyright (c) 2006-2009, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef PKCS1_H #define PKCS1_H int pkcs1_encrypt(int block_type, struct crypto_rsa_key *key, int use_private, const u8 *in, size_t inlen, u8 *out, size_t *outlen); int pkcs1_v15_private_key_decrypt(struct crypto_rsa_key *key, const u8 *in, size_t inlen, u8 *out, size_t *outlen); int pkcs1_decrypt_public_key(struct crypto_rsa_key *key, const u8 *crypt, size_t crypt_len, u8 *plain, size_t *plain_len); #endif /* PKCS1_H */ wpa-2.1/src/tls/pkcs5.c000066400000000000000000000135041227414666700147470ustar00rootroot00000000000000/* * PKCS #5 (Password-based Encryption) * Copyright (c) 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 "crypto/crypto.h" #include "crypto/md5.h" #include "asn1.h" #include "pkcs5.h" struct pkcs5_params { enum pkcs5_alg { PKCS5_ALG_UNKNOWN, PKCS5_ALG_MD5_DES_CBC } alg; u8 salt[8]; size_t salt_len; unsigned int iter_count; }; static enum pkcs5_alg pkcs5_get_alg(struct asn1_oid *oid) { if (oid->len == 7 && oid->oid[0] == 1 /* iso */ && oid->oid[1] == 2 /* member-body */ && oid->oid[2] == 840 /* us */ && oid->oid[3] == 113549 /* rsadsi */ && oid->oid[4] == 1 /* pkcs */ && oid->oid[5] == 5 /* pkcs-5 */ && oid->oid[6] == 3 /* pbeWithMD5AndDES-CBC */) return PKCS5_ALG_MD5_DES_CBC; return PKCS5_ALG_UNKNOWN; } static int pkcs5_get_params(const u8 *enc_alg, size_t enc_alg_len, struct pkcs5_params *params) { struct asn1_hdr hdr; const u8 *enc_alg_end, *pos, *end; struct asn1_oid oid; char obuf[80]; /* AlgorithmIdentifier */ enc_alg_end = enc_alg + enc_alg_len; os_memset(params, 0, sizeof(*params)); if (asn1_get_oid(enc_alg, enc_alg_end - enc_alg, &oid, &pos)) { wpa_printf(MSG_DEBUG, "PKCS #5: Failed to parse OID " "(algorithm)"); return -1; } asn1_oid_to_str(&oid, obuf, sizeof(obuf)); wpa_printf(MSG_DEBUG, "PKCS #5: encryption algorithm %s", obuf); params->alg = pkcs5_get_alg(&oid); if (params->alg == PKCS5_ALG_UNKNOWN) { wpa_printf(MSG_INFO, "PKCS #5: unsupported encryption " "algorithm %s", obuf); return -1; } /* * PKCS#5, Section 8 * PBEParameter ::= SEQUENCE { * salt OCTET STRING SIZE(8), * iterationCount INTEGER } */ if (asn1_get_next(pos, enc_alg_end - pos, &hdr) < 0 || hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_SEQUENCE) { wpa_printf(MSG_DEBUG, "PKCS #5: Expected SEQUENCE " "(PBEParameter) - found class %d tag 0x%x", hdr.class, hdr.tag); return -1; } pos = hdr.payload; end = hdr.payload + hdr.length; /* salt OCTET STRING SIZE(8) */ if (asn1_get_next(pos, end - pos, &hdr) < 0 || hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_OCTETSTRING || hdr.length != 8) { wpa_printf(MSG_DEBUG, "PKCS #5: Expected OCTETSTRING SIZE(8) " "(salt) - found class %d tag 0x%x size %d", hdr.class, hdr.tag, hdr.length); return -1; } pos = hdr.payload + hdr.length; os_memcpy(params->salt, hdr.payload, hdr.length); params->salt_len = hdr.length; wpa_hexdump(MSG_DEBUG, "PKCS #5: salt", params->salt, params->salt_len); /* iterationCount INTEGER */ if (asn1_get_next(pos, end - pos, &hdr) < 0 || hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_INTEGER) { wpa_printf(MSG_DEBUG, "PKCS #5: Expected INTEGER - found " "class %d tag 0x%x", hdr.class, hdr.tag); return -1; } if (hdr.length == 1) params->iter_count = *hdr.payload; else if (hdr.length == 2) params->iter_count = WPA_GET_BE16(hdr.payload); else if (hdr.length == 4) params->iter_count = WPA_GET_BE32(hdr.payload); else { wpa_hexdump(MSG_DEBUG, "PKCS #5: Unsupported INTEGER value " " (iterationCount)", hdr.payload, hdr.length); return -1; } wpa_printf(MSG_DEBUG, "PKCS #5: iterationCount=0x%x", params->iter_count); if (params->iter_count == 0 || params->iter_count > 0xffff) { wpa_printf(MSG_INFO, "PKCS #5: Unsupported " "iterationCount=0x%x", params->iter_count); return -1; } return 0; } static struct crypto_cipher * pkcs5_crypto_init(struct pkcs5_params *params, const char *passwd) { unsigned int i; u8 hash[MD5_MAC_LEN]; const u8 *addr[2]; size_t len[2]; if (params->alg != PKCS5_ALG_MD5_DES_CBC) return NULL; addr[0] = (const u8 *) passwd; len[0] = os_strlen(passwd); addr[1] = params->salt; len[1] = params->salt_len; if (md5_vector(2, addr, len, hash) < 0) return NULL; addr[0] = hash; len[0] = MD5_MAC_LEN; for (i = 1; i < params->iter_count; i++) { if (md5_vector(1, addr, len, hash) < 0) return NULL; } /* TODO: DES key parity bits(?) */ wpa_hexdump_key(MSG_DEBUG, "PKCS #5: DES key", hash, 8); wpa_hexdump_key(MSG_DEBUG, "PKCS #5: DES IV", hash + 8, 8); return crypto_cipher_init(CRYPTO_CIPHER_ALG_DES, hash + 8, hash, 8); } u8 * pkcs5_decrypt(const u8 *enc_alg, size_t enc_alg_len, const u8 *enc_data, size_t enc_data_len, const char *passwd, size_t *data_len) { struct crypto_cipher *ctx; u8 *eb, pad; struct pkcs5_params params; unsigned int i; if (pkcs5_get_params(enc_alg, enc_alg_len, ¶ms) < 0) { wpa_printf(MSG_DEBUG, "PKCS #5: Unsupported parameters"); return NULL; } ctx = pkcs5_crypto_init(¶ms, passwd); if (ctx == NULL) { wpa_printf(MSG_DEBUG, "PKCS #5: Failed to initialize crypto"); return NULL; } /* PKCS #5, Section 7 - Decryption process */ if (enc_data_len < 16 || enc_data_len % 8) { wpa_printf(MSG_INFO, "PKCS #5: invalid length of ciphertext " "%d", (int) enc_data_len); crypto_cipher_deinit(ctx); return NULL; } eb = os_malloc(enc_data_len); if (eb == NULL) { crypto_cipher_deinit(ctx); return NULL; } if (crypto_cipher_decrypt(ctx, enc_data, eb, enc_data_len) < 0) { wpa_printf(MSG_DEBUG, "PKCS #5: Failed to decrypt EB"); crypto_cipher_deinit(ctx); os_free(eb); return NULL; } crypto_cipher_deinit(ctx); pad = eb[enc_data_len - 1]; if (pad > 8) { wpa_printf(MSG_INFO, "PKCS #5: Invalid PS octet 0x%x", pad); os_free(eb); return NULL; } for (i = enc_data_len - pad; i < enc_data_len; i++) { if (eb[i] != pad) { wpa_hexdump(MSG_INFO, "PKCS #5: Invalid PS", eb + enc_data_len - pad, pad); os_free(eb); return NULL; } } wpa_hexdump_key(MSG_MSGDUMP, "PKCS #5: message M (encrypted key)", eb, enc_data_len - pad); *data_len = enc_data_len - pad; return eb; } wpa-2.1/src/tls/pkcs5.h000066400000000000000000000006241227414666700147530ustar00rootroot00000000000000/* * PKCS #5 (Password-based Encryption) * Copyright (c) 2009, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef PKCS5_H #define PKCS5_H u8 * pkcs5_decrypt(const u8 *enc_alg, size_t enc_alg_len, const u8 *enc_data, size_t enc_data_len, const char *passwd, size_t *data_len); #endif /* PKCS5_H */ wpa-2.1/src/tls/pkcs8.c000066400000000000000000000121361227414666700147520ustar00rootroot00000000000000/* * PKCS #8 (Private-key information syntax) * Copyright (c) 2006-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 "asn1.h" #include "bignum.h" #include "rsa.h" #include "pkcs5.h" #include "pkcs8.h" struct crypto_private_key * pkcs8_key_import(const u8 *buf, size_t len) { struct asn1_hdr hdr; const u8 *pos, *end; struct bignum *zero; struct asn1_oid oid; char obuf[80]; /* PKCS #8, Chapter 6 */ /* PrivateKeyInfo ::= SEQUENCE */ if (asn1_get_next(buf, len, &hdr) < 0 || hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_SEQUENCE) { wpa_printf(MSG_DEBUG, "PKCS #8: Does not start with PKCS #8 " "header (SEQUENCE); assume PKCS #8 not used"); return NULL; } pos = hdr.payload; end = pos + hdr.length; /* version Version (Version ::= INTEGER) */ if (asn1_get_next(pos, end - pos, &hdr) < 0 || hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_INTEGER) { wpa_printf(MSG_DEBUG, "PKCS #8: Expected INTEGER - found " "class %d tag 0x%x; assume PKCS #8 not used", hdr.class, hdr.tag); return NULL; } zero = bignum_init(); if (zero == NULL) return NULL; if (bignum_set_unsigned_bin(zero, hdr.payload, hdr.length) < 0) { wpa_printf(MSG_DEBUG, "PKCS #8: Failed to parse INTEGER"); bignum_deinit(zero); return NULL; } pos = hdr.payload + hdr.length; if (bignum_cmp_d(zero, 0) != 0) { wpa_printf(MSG_DEBUG, "PKCS #8: Expected zero INTEGER in the " "beginning of private key; not found; assume " "PKCS #8 not used"); bignum_deinit(zero); return NULL; } bignum_deinit(zero); /* privateKeyAlgorithm PrivateKeyAlgorithmIdentifier * (PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier) */ if (asn1_get_next(pos, len, &hdr) < 0 || hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_SEQUENCE) { wpa_printf(MSG_DEBUG, "PKCS #8: Expected SEQUENCE " "(AlgorithmIdentifier) - found class %d tag 0x%x; " "assume PKCS #8 not used", hdr.class, hdr.tag); return NULL; } if (asn1_get_oid(hdr.payload, hdr.length, &oid, &pos)) { wpa_printf(MSG_DEBUG, "PKCS #8: Failed to parse OID " "(algorithm); assume PKCS #8 not used"); return NULL; } asn1_oid_to_str(&oid, obuf, sizeof(obuf)); wpa_printf(MSG_DEBUG, "PKCS #8: algorithm=%s", obuf); if (oid.len != 7 || oid.oid[0] != 1 /* iso */ || oid.oid[1] != 2 /* member-body */ || oid.oid[2] != 840 /* us */ || oid.oid[3] != 113549 /* rsadsi */ || oid.oid[4] != 1 /* pkcs */ || oid.oid[5] != 1 /* pkcs-1 */ || oid.oid[6] != 1 /* rsaEncryption */) { wpa_printf(MSG_DEBUG, "PKCS #8: Unsupported private key " "algorithm %s", obuf); return NULL; } pos = hdr.payload + hdr.length; /* privateKey PrivateKey (PrivateKey ::= OCTET STRING) */ if (asn1_get_next(pos, end - pos, &hdr) < 0 || hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_OCTETSTRING) { wpa_printf(MSG_DEBUG, "PKCS #8: Expected OCTETSTRING " "(privateKey) - found class %d tag 0x%x", hdr.class, hdr.tag); return NULL; } wpa_printf(MSG_DEBUG, "PKCS #8: Try to parse RSAPrivateKey"); return (struct crypto_private_key *) crypto_rsa_import_private_key(hdr.payload, hdr.length); } struct crypto_private_key * pkcs8_enc_key_import(const u8 *buf, size_t len, const char *passwd) { struct asn1_hdr hdr; const u8 *pos, *end, *enc_alg; size_t enc_alg_len; u8 *data; size_t data_len; if (passwd == NULL) return NULL; /* * PKCS #8, Chapter 7 * EncryptedPrivateKeyInfo ::= SEQUENCE { * encryptionAlgorithm EncryptionAlgorithmIdentifier, * encryptedData EncryptedData } * EncryptionAlgorithmIdentifier ::= AlgorithmIdentifier * EncryptedData ::= OCTET STRING */ if (asn1_get_next(buf, len, &hdr) < 0 || hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_SEQUENCE) { wpa_printf(MSG_DEBUG, "PKCS #8: Does not start with PKCS #8 " "header (SEQUENCE); assume encrypted PKCS #8 not " "used"); return NULL; } pos = hdr.payload; end = pos + hdr.length; /* encryptionAlgorithm EncryptionAlgorithmIdentifier */ if (asn1_get_next(pos, end - pos, &hdr) < 0 || hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_SEQUENCE) { wpa_printf(MSG_DEBUG, "PKCS #8: Expected SEQUENCE " "(AlgorithmIdentifier) - found class %d tag 0x%x; " "assume encrypted PKCS #8 not used", hdr.class, hdr.tag); return NULL; } enc_alg = hdr.payload; enc_alg_len = hdr.length; pos = hdr.payload + hdr.length; /* encryptedData EncryptedData */ if (asn1_get_next(pos, end - pos, &hdr) < 0 || hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_OCTETSTRING) { wpa_printf(MSG_DEBUG, "PKCS #8: Expected OCTETSTRING " "(encryptedData) - found class %d tag 0x%x", hdr.class, hdr.tag); return NULL; } data = pkcs5_decrypt(enc_alg, enc_alg_len, hdr.payload, hdr.length, passwd, &data_len); if (data) { struct crypto_private_key *key; key = pkcs8_key_import(data, data_len); os_free(data); return key; } return NULL; } wpa-2.1/src/tls/pkcs8.h000066400000000000000000000006641227414666700147620ustar00rootroot00000000000000/* * PKCS #8 (Private-key information syntax) * Copyright (c) 2006-2009, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef PKCS8_H #define PKCS8_H struct crypto_private_key * pkcs8_key_import(const u8 *buf, size_t len); struct crypto_private_key * pkcs8_enc_key_import(const u8 *buf, size_t len, const char *passwd); #endif /* PKCS8_H */ wpa-2.1/src/tls/rsa.c000066400000000000000000000204521227414666700145070ustar00rootroot00000000000000/* * RSA * Copyright (c) 2006, 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 "asn1.h" #include "bignum.h" #include "rsa.h" struct crypto_rsa_key { int private_key; /* whether private key is set */ struct bignum *n; /* modulus (p * q) */ struct bignum *e; /* public exponent */ /* The following parameters are available only if private_key is set */ struct bignum *d; /* private exponent */ struct bignum *p; /* prime p (factor of n) */ struct bignum *q; /* prime q (factor of n) */ struct bignum *dmp1; /* d mod (p - 1); CRT exponent */ struct bignum *dmq1; /* d mod (q - 1); CRT exponent */ struct bignum *iqmp; /* 1 / q mod p; CRT coefficient */ }; static const u8 * crypto_rsa_parse_integer(const u8 *pos, const u8 *end, struct bignum *num) { struct asn1_hdr hdr; if (pos == NULL) return NULL; if (asn1_get_next(pos, end - pos, &hdr) < 0 || hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_INTEGER) { wpa_printf(MSG_DEBUG, "RSA: Expected INTEGER - found class %d " "tag 0x%x", hdr.class, hdr.tag); return NULL; } if (bignum_set_unsigned_bin(num, hdr.payload, hdr.length) < 0) { wpa_printf(MSG_DEBUG, "RSA: Failed to parse INTEGER"); return NULL; } return hdr.payload + hdr.length; } /** * crypto_rsa_import_public_key - Import an RSA public key * @buf: Key buffer (DER encoded RSA public key) * @len: Key buffer length in bytes * Returns: Pointer to the public key or %NULL on failure */ struct crypto_rsa_key * crypto_rsa_import_public_key(const u8 *buf, size_t len) { struct crypto_rsa_key *key; struct asn1_hdr hdr; const u8 *pos, *end; key = os_zalloc(sizeof(*key)); if (key == NULL) return NULL; key->n = bignum_init(); key->e = bignum_init(); if (key->n == NULL || key->e == NULL) { crypto_rsa_free(key); return NULL; } /* * PKCS #1, 7.1: * RSAPublicKey ::= SEQUENCE { * modulus INTEGER, -- n * publicExponent INTEGER -- e * } */ if (asn1_get_next(buf, len, &hdr) < 0 || hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_SEQUENCE) { wpa_printf(MSG_DEBUG, "RSA: Expected SEQUENCE " "(public key) - found class %d tag 0x%x", hdr.class, hdr.tag); goto error; } pos = hdr.payload; end = pos + hdr.length; pos = crypto_rsa_parse_integer(pos, end, key->n); pos = crypto_rsa_parse_integer(pos, end, key->e); if (pos == NULL) goto error; if (pos != end) { wpa_hexdump(MSG_DEBUG, "RSA: Extra data in public key SEQUENCE", pos, end - pos); goto error; } return key; error: crypto_rsa_free(key); return NULL; } /** * crypto_rsa_import_private_key - Import an RSA private key * @buf: Key buffer (DER encoded RSA private key) * @len: Key buffer length in bytes * Returns: Pointer to the private key or %NULL on failure */ struct crypto_rsa_key * crypto_rsa_import_private_key(const u8 *buf, size_t len) { struct crypto_rsa_key *key; struct bignum *zero; struct asn1_hdr hdr; const u8 *pos, *end; key = os_zalloc(sizeof(*key)); if (key == NULL) return NULL; key->private_key = 1; key->n = bignum_init(); key->e = bignum_init(); key->d = bignum_init(); key->p = bignum_init(); key->q = bignum_init(); key->dmp1 = bignum_init(); key->dmq1 = bignum_init(); key->iqmp = bignum_init(); if (key->n == NULL || key->e == NULL || key->d == NULL || key->p == NULL || key->q == NULL || key->dmp1 == NULL || key->dmq1 == NULL || key->iqmp == NULL) { crypto_rsa_free(key); return NULL; } /* * PKCS #1, 7.2: * RSAPrivateKey ::= SEQUENCE { * version Version, * modulus INTEGER, -- n * publicExponent INTEGER, -- e * privateExponent INTEGER, -- d * prime1 INTEGER, -- p * prime2 INTEGER, -- q * exponent1 INTEGER, -- d mod (p-1) * exponent2 INTEGER, -- d mod (q-1) * coefficient INTEGER -- (inverse of q) mod p * } * * Version ::= INTEGER -- shall be 0 for this version of the standard */ if (asn1_get_next(buf, len, &hdr) < 0 || hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_SEQUENCE) { wpa_printf(MSG_DEBUG, "RSA: Expected SEQUENCE " "(public key) - found class %d tag 0x%x", hdr.class, hdr.tag); goto error; } pos = hdr.payload; end = pos + hdr.length; zero = bignum_init(); if (zero == NULL) goto error; pos = crypto_rsa_parse_integer(pos, end, zero); if (pos == NULL || bignum_cmp_d(zero, 0) != 0) { wpa_printf(MSG_DEBUG, "RSA: Expected zero INTEGER in the " "beginning of private key; not found"); bignum_deinit(zero); goto error; } bignum_deinit(zero); pos = crypto_rsa_parse_integer(pos, end, key->n); pos = crypto_rsa_parse_integer(pos, end, key->e); pos = crypto_rsa_parse_integer(pos, end, key->d); pos = crypto_rsa_parse_integer(pos, end, key->p); pos = crypto_rsa_parse_integer(pos, end, key->q); pos = crypto_rsa_parse_integer(pos, end, key->dmp1); pos = crypto_rsa_parse_integer(pos, end, key->dmq1); pos = crypto_rsa_parse_integer(pos, end, key->iqmp); if (pos == NULL) goto error; if (pos != end) { wpa_hexdump(MSG_DEBUG, "RSA: Extra data in public key SEQUENCE", pos, end - pos); goto error; } return key; error: crypto_rsa_free(key); return NULL; } /** * crypto_rsa_get_modulus_len - Get the modulus length of the RSA key * @key: RSA key * Returns: Modulus length of the key */ size_t crypto_rsa_get_modulus_len(struct crypto_rsa_key *key) { return bignum_get_unsigned_bin_len(key->n); } /** * crypto_rsa_exptmod - RSA modular exponentiation * @in: Input data * @inlen: Input data length * @out: Buffer for output data * @outlen: Maximum size of the output buffer and used size on success * @key: RSA key * @use_private: 1 = Use RSA private key, 0 = Use RSA public key * Returns: 0 on success, -1 on failure */ int crypto_rsa_exptmod(const u8 *in, size_t inlen, u8 *out, size_t *outlen, struct crypto_rsa_key *key, int use_private) { struct bignum *tmp, *a = NULL, *b = NULL; int ret = -1; size_t modlen; if (use_private && !key->private_key) return -1; tmp = bignum_init(); if (tmp == NULL) return -1; if (bignum_set_unsigned_bin(tmp, in, inlen) < 0) goto error; if (bignum_cmp(key->n, tmp) < 0) { /* Too large input value for the RSA key modulus */ goto error; } if (use_private) { /* * Decrypt (or sign) using Chinese remainer theorem to speed * up calculation. This is equivalent to tmp = tmp^d mod n * (which would require more CPU to calculate directly). * * dmp1 = (1/e) mod (p-1) * dmq1 = (1/e) mod (q-1) * iqmp = (1/q) mod p, where p > q * m1 = c^dmp1 mod p * m2 = c^dmq1 mod q * h = q^-1 (m1 - m2) mod p * m = m2 + hq */ a = bignum_init(); b = bignum_init(); if (a == NULL || b == NULL) goto error; /* a = tmp^dmp1 mod p */ if (bignum_exptmod(tmp, key->dmp1, key->p, a) < 0) goto error; /* b = tmp^dmq1 mod q */ if (bignum_exptmod(tmp, key->dmq1, key->q, b) < 0) goto error; /* tmp = (a - b) * (1/q mod p) (mod p) */ if (bignum_sub(a, b, tmp) < 0 || bignum_mulmod(tmp, key->iqmp, key->p, tmp) < 0) goto error; /* tmp = b + q * tmp */ if (bignum_mul(tmp, key->q, tmp) < 0 || bignum_add(tmp, b, tmp) < 0) goto error; } else { /* Encrypt (or verify signature) */ /* tmp = tmp^e mod N */ if (bignum_exptmod(tmp, key->e, key->n, tmp) < 0) goto error; } modlen = crypto_rsa_get_modulus_len(key); if (modlen > *outlen) { *outlen = modlen; goto error; } if (bignum_get_unsigned_bin_len(tmp) > modlen) goto error; /* should never happen */ *outlen = modlen; os_memset(out, 0, modlen); if (bignum_get_unsigned_bin( tmp, out + (modlen - bignum_get_unsigned_bin_len(tmp)), NULL) < 0) goto error; ret = 0; error: bignum_deinit(tmp); bignum_deinit(a); bignum_deinit(b); return ret; } /** * crypto_rsa_free - Free RSA key * @key: RSA key to be freed * * This function frees an RSA key imported with either * crypto_rsa_import_public_key() or crypto_rsa_import_private_key(). */ void crypto_rsa_free(struct crypto_rsa_key *key) { if (key) { bignum_deinit(key->n); bignum_deinit(key->e); bignum_deinit(key->d); bignum_deinit(key->p); bignum_deinit(key->q); bignum_deinit(key->dmp1); bignum_deinit(key->dmq1); bignum_deinit(key->iqmp); os_free(key); } } wpa-2.1/src/tls/rsa.h000066400000000000000000000012111227414666700145040ustar00rootroot00000000000000/* * RSA * Copyright (c) 2006, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef RSA_H #define RSA_H struct crypto_rsa_key; struct crypto_rsa_key * crypto_rsa_import_public_key(const u8 *buf, size_t len); struct crypto_rsa_key * crypto_rsa_import_private_key(const u8 *buf, size_t len); size_t crypto_rsa_get_modulus_len(struct crypto_rsa_key *key); int crypto_rsa_exptmod(const u8 *in, size_t inlen, u8 *out, size_t *outlen, struct crypto_rsa_key *key, int use_private); void crypto_rsa_free(struct crypto_rsa_key *key); #endif /* RSA_H */ wpa-2.1/src/tls/tlsv1_client.c000066400000000000000000000520471227414666700163360ustar00rootroot00000000000000/* * TLS v1.0/v1.1/v1.2 client (RFC 2246, RFC 4346, RFC 5246) * Copyright (c) 2006-2011, 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/sha1.h" #include "crypto/tls.h" #include "tlsv1_common.h" #include "tlsv1_record.h" #include "tlsv1_client.h" #include "tlsv1_client_i.h" /* TODO: * Support for a message fragmented across several records (RFC 2246, 6.2.1) */ void tls_alert(struct tlsv1_client *conn, u8 level, u8 description) { conn->alert_level = level; conn->alert_description = description; } void tlsv1_client_free_dh(struct tlsv1_client *conn) { os_free(conn->dh_p); os_free(conn->dh_g); os_free(conn->dh_ys); conn->dh_p = conn->dh_g = conn->dh_ys = NULL; } int tls_derive_pre_master_secret(u8 *pre_master_secret) { WPA_PUT_BE16(pre_master_secret, TLS_VERSION); if (os_get_random(pre_master_secret + 2, TLS_PRE_MASTER_SECRET_LEN - 2)) return -1; return 0; } int tls_derive_keys(struct tlsv1_client *conn, const u8 *pre_master_secret, size_t pre_master_secret_len) { u8 seed[2 * TLS_RANDOM_LEN]; u8 key_block[TLS_MAX_KEY_BLOCK_LEN]; u8 *pos; size_t key_block_len; if (pre_master_secret) { wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: pre_master_secret", pre_master_secret, pre_master_secret_len); os_memcpy(seed, conn->client_random, TLS_RANDOM_LEN); os_memcpy(seed + TLS_RANDOM_LEN, conn->server_random, TLS_RANDOM_LEN); if (tls_prf(conn->rl.tls_version, pre_master_secret, pre_master_secret_len, "master secret", seed, 2 * TLS_RANDOM_LEN, conn->master_secret, TLS_MASTER_SECRET_LEN)) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive " "master_secret"); return -1; } wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: master_secret", conn->master_secret, TLS_MASTER_SECRET_LEN); } os_memcpy(seed, conn->server_random, TLS_RANDOM_LEN); os_memcpy(seed + TLS_RANDOM_LEN, conn->client_random, TLS_RANDOM_LEN); key_block_len = 2 * (conn->rl.hash_size + conn->rl.key_material_len); if (conn->rl.tls_version == TLS_VERSION_1) key_block_len += 2 * conn->rl.iv_size; if (tls_prf(conn->rl.tls_version, conn->master_secret, TLS_MASTER_SECRET_LEN, "key expansion", seed, 2 * TLS_RANDOM_LEN, key_block, key_block_len)) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive key_block"); return -1; } wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: key_block", key_block, key_block_len); pos = key_block; /* client_write_MAC_secret */ os_memcpy(conn->rl.write_mac_secret, pos, conn->rl.hash_size); pos += conn->rl.hash_size; /* server_write_MAC_secret */ os_memcpy(conn->rl.read_mac_secret, pos, conn->rl.hash_size); pos += conn->rl.hash_size; /* client_write_key */ os_memcpy(conn->rl.write_key, pos, conn->rl.key_material_len); pos += conn->rl.key_material_len; /* server_write_key */ os_memcpy(conn->rl.read_key, pos, conn->rl.key_material_len); pos += conn->rl.key_material_len; if (conn->rl.tls_version == TLS_VERSION_1) { /* client_write_IV */ os_memcpy(conn->rl.write_iv, pos, conn->rl.iv_size); pos += conn->rl.iv_size; /* server_write_IV */ os_memcpy(conn->rl.read_iv, pos, conn->rl.iv_size); pos += conn->rl.iv_size; } else { /* * Use IV field to set the mask value for TLS v1.1. A fixed * mask of zero is used per the RFC 4346, 6.2.3.2 CBC Block * Cipher option 2a. */ os_memset(conn->rl.write_iv, 0, conn->rl.iv_size); } return 0; } /** * tlsv1_client_handshake - Process TLS handshake * @conn: TLSv1 client connection data from tlsv1_client_init() * @in_data: Input data from TLS peer * @in_len: Input data length * @out_len: Length of the output buffer. * @appl_data: Pointer to application data pointer, or %NULL if dropped * @appl_data_len: Pointer to variable that is set to appl_data length * @need_more_data: Set to 1 if more data would be needed to complete * processing * Returns: Pointer to output data, %NULL on failure */ u8 * tlsv1_client_handshake(struct tlsv1_client *conn, const u8 *in_data, size_t in_len, size_t *out_len, u8 **appl_data, size_t *appl_data_len, int *need_more_data) { const u8 *pos, *end; u8 *msg = NULL, *in_msg = NULL, *in_pos, *in_end, alert, ct; size_t in_msg_len; int no_appl_data; int used; if (need_more_data) *need_more_data = 0; if (conn->state == CLIENT_HELLO) { if (in_len) return NULL; return tls_send_client_hello(conn, out_len); } if (conn->partial_input) { if (wpabuf_resize(&conn->partial_input, in_len) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate " "memory for pending record"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); goto failed; } wpabuf_put_data(conn->partial_input, in_data, in_len); in_data = wpabuf_head(conn->partial_input); in_len = wpabuf_len(conn->partial_input); } if (in_data == NULL || in_len == 0) return NULL; pos = in_data; end = in_data + in_len; in_msg = os_malloc(in_len); if (in_msg == NULL) return NULL; /* Each received packet may include multiple records */ while (pos < end) { in_msg_len = in_len; used = tlsv1_record_receive(&conn->rl, pos, end - pos, in_msg, &in_msg_len, &alert); if (used < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Processing received " "record failed"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, alert); goto failed; } if (used == 0) { struct wpabuf *partial; wpa_printf(MSG_DEBUG, "TLSv1: Need more data"); partial = wpabuf_alloc_copy(pos, end - pos); wpabuf_free(conn->partial_input); conn->partial_input = partial; if (conn->partial_input == NULL) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to " "allocate memory for pending " "record"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); goto failed; } os_free(in_msg); if (need_more_data) *need_more_data = 1; return NULL; } ct = pos[0]; in_pos = in_msg; in_end = in_msg + in_msg_len; /* Each received record may include multiple messages of the * same ContentType. */ while (in_pos < in_end) { in_msg_len = in_end - in_pos; if (tlsv1_client_process_handshake(conn, ct, in_pos, &in_msg_len, appl_data, appl_data_len) < 0) goto failed; in_pos += in_msg_len; } pos += used; } os_free(in_msg); in_msg = NULL; no_appl_data = appl_data == NULL || *appl_data == NULL; msg = tlsv1_client_handshake_write(conn, out_len, no_appl_data); failed: os_free(in_msg); if (conn->alert_level) { wpabuf_free(conn->partial_input); conn->partial_input = NULL; conn->state = FAILED; os_free(msg); msg = tlsv1_client_send_alert(conn, conn->alert_level, conn->alert_description, out_len); } else if (msg == NULL) { msg = os_zalloc(1); *out_len = 0; } if (need_more_data == NULL || !(*need_more_data)) { wpabuf_free(conn->partial_input); conn->partial_input = NULL; } return msg; } /** * tlsv1_client_encrypt - Encrypt data into TLS tunnel * @conn: TLSv1 client connection data from tlsv1_client_init() * @in_data: Pointer to plaintext data to be encrypted * @in_len: Input buffer length * @out_data: Pointer to output buffer (encrypted TLS data) * @out_len: Maximum out_data length * Returns: Number of bytes written to out_data, -1 on failure * * This function is used after TLS handshake has been completed successfully to * send data in the encrypted tunnel. */ int tlsv1_client_encrypt(struct tlsv1_client *conn, const u8 *in_data, size_t in_len, u8 *out_data, size_t out_len) { size_t rlen; wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Plaintext AppData", in_data, in_len); if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_APPLICATION_DATA, out_data, out_len, in_data, in_len, &rlen) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } return rlen; } /** * tlsv1_client_decrypt - Decrypt data from TLS tunnel * @conn: TLSv1 client connection data from tlsv1_client_init() * @in_data: Pointer to input buffer (encrypted TLS data) * @in_len: Input buffer length * @need_more_data: Set to 1 if more data would be needed to complete * processing * Returns: Decrypted data or %NULL on failure * * This function is used after TLS handshake has been completed successfully to * receive data from the encrypted tunnel. */ struct wpabuf * tlsv1_client_decrypt(struct tlsv1_client *conn, const u8 *in_data, size_t in_len, int *need_more_data) { const u8 *in_end, *pos; int used; u8 alert, *out_pos, ct; size_t olen; struct wpabuf *buf = NULL; if (need_more_data) *need_more_data = 0; if (conn->partial_input) { if (wpabuf_resize(&conn->partial_input, in_len) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate " "memory for pending record"); alert = TLS_ALERT_INTERNAL_ERROR; goto fail; } wpabuf_put_data(conn->partial_input, in_data, in_len); in_data = wpabuf_head(conn->partial_input); in_len = wpabuf_len(conn->partial_input); } pos = in_data; in_end = in_data + in_len; while (pos < in_end) { ct = pos[0]; if (wpabuf_resize(&buf, in_end - pos) < 0) { alert = TLS_ALERT_INTERNAL_ERROR; goto fail; } out_pos = wpabuf_put(buf, 0); olen = wpabuf_tailroom(buf); used = tlsv1_record_receive(&conn->rl, pos, in_end - pos, out_pos, &olen, &alert); if (used < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Record layer processing " "failed"); goto fail; } if (used == 0) { struct wpabuf *partial; wpa_printf(MSG_DEBUG, "TLSv1: Need more data"); partial = wpabuf_alloc_copy(pos, in_end - pos); wpabuf_free(conn->partial_input); conn->partial_input = partial; if (conn->partial_input == NULL) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to " "allocate memory for pending " "record"); alert = TLS_ALERT_INTERNAL_ERROR; goto fail; } if (need_more_data) *need_more_data = 1; return buf; } if (ct == TLS_CONTENT_TYPE_ALERT) { if (olen < 2) { wpa_printf(MSG_DEBUG, "TLSv1: Alert " "underflow"); alert = TLS_ALERT_DECODE_ERROR; goto fail; } wpa_printf(MSG_DEBUG, "TLSv1: Received alert %d:%d", out_pos[0], out_pos[1]); if (out_pos[0] == TLS_ALERT_LEVEL_WARNING) { /* Continue processing */ pos += used; continue; } alert = out_pos[1]; goto fail; } if (ct != TLS_CONTENT_TYPE_APPLICATION_DATA) { wpa_printf(MSG_DEBUG, "TLSv1: Unexpected content type " "0x%x when decrypting application data", pos[0]); alert = TLS_ALERT_UNEXPECTED_MESSAGE; goto fail; } wpabuf_put(buf, olen); pos += used; } wpabuf_free(conn->partial_input); conn->partial_input = NULL; return buf; fail: wpabuf_free(buf); wpabuf_free(conn->partial_input); conn->partial_input = NULL; tls_alert(conn, TLS_ALERT_LEVEL_FATAL, alert); return NULL; } /** * tlsv1_client_global_init - Initialize TLSv1 client * Returns: 0 on success, -1 on failure * * This function must be called before using any other TLSv1 client functions. */ int tlsv1_client_global_init(void) { return crypto_global_init(); } /** * tlsv1_client_global_deinit - Deinitialize TLSv1 client * * This function can be used to deinitialize the TLSv1 client that was * initialized by calling tlsv1_client_global_init(). No TLSv1 client functions * can be called after this before calling tlsv1_client_global_init() again. */ void tlsv1_client_global_deinit(void) { crypto_global_deinit(); } /** * tlsv1_client_init - Initialize TLSv1 client connection * Returns: Pointer to TLSv1 client connection data or %NULL on failure */ struct tlsv1_client * tlsv1_client_init(void) { struct tlsv1_client *conn; size_t count; u16 *suites; conn = os_zalloc(sizeof(*conn)); if (conn == NULL) return NULL; conn->state = CLIENT_HELLO; if (tls_verify_hash_init(&conn->verify) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to initialize verify " "hash"); os_free(conn); return NULL; } count = 0; suites = conn->cipher_suites; suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA256; suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA; suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA256; suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA; suites[count++] = TLS_RSA_WITH_3DES_EDE_CBC_SHA; suites[count++] = TLS_RSA_WITH_RC4_128_SHA; suites[count++] = TLS_RSA_WITH_RC4_128_MD5; conn->num_cipher_suites = count; conn->rl.tls_version = TLS_VERSION; return conn; } /** * tlsv1_client_deinit - Deinitialize TLSv1 client connection * @conn: TLSv1 client connection data from tlsv1_client_init() */ void tlsv1_client_deinit(struct tlsv1_client *conn) { crypto_public_key_free(conn->server_rsa_key); tlsv1_record_set_cipher_suite(&conn->rl, TLS_NULL_WITH_NULL_NULL); tlsv1_record_change_write_cipher(&conn->rl); tlsv1_record_change_read_cipher(&conn->rl); tls_verify_hash_free(&conn->verify); os_free(conn->client_hello_ext); tlsv1_client_free_dh(conn); tlsv1_cred_free(conn->cred); wpabuf_free(conn->partial_input); os_free(conn); } /** * tlsv1_client_established - Check whether connection has been established * @conn: TLSv1 client connection data from tlsv1_client_init() * Returns: 1 if connection is established, 0 if not */ int tlsv1_client_established(struct tlsv1_client *conn) { return conn->state == ESTABLISHED; } /** * tlsv1_client_prf - Use TLS-PRF to derive keying material * @conn: TLSv1 client connection data from tlsv1_client_init() * @label: Label (e.g., description of the key) for PRF * @server_random_first: seed is 0 = client_random|server_random, * 1 = server_random|client_random * @out: Buffer for output data from TLS-PRF * @out_len: Length of the output buffer * Returns: 0 on success, -1 on failure */ int tlsv1_client_prf(struct tlsv1_client *conn, const char *label, int server_random_first, u8 *out, size_t out_len) { u8 seed[2 * TLS_RANDOM_LEN]; if (conn->state != ESTABLISHED) return -1; if (server_random_first) { os_memcpy(seed, conn->server_random, TLS_RANDOM_LEN); os_memcpy(seed + TLS_RANDOM_LEN, conn->client_random, TLS_RANDOM_LEN); } else { os_memcpy(seed, conn->client_random, TLS_RANDOM_LEN); os_memcpy(seed + TLS_RANDOM_LEN, conn->server_random, TLS_RANDOM_LEN); } return tls_prf(conn->rl.tls_version, conn->master_secret, TLS_MASTER_SECRET_LEN, label, seed, 2 * TLS_RANDOM_LEN, out, out_len); } /** * tlsv1_client_get_cipher - Get current cipher name * @conn: TLSv1 client connection data from tlsv1_client_init() * @buf: Buffer for the cipher name * @buflen: buf size * Returns: 0 on success, -1 on failure * * Get the name of the currently used cipher. */ int tlsv1_client_get_cipher(struct tlsv1_client *conn, char *buf, size_t buflen) { char *cipher; switch (conn->rl.cipher_suite) { case TLS_RSA_WITH_RC4_128_MD5: cipher = "RC4-MD5"; break; case TLS_RSA_WITH_RC4_128_SHA: cipher = "RC4-SHA"; break; case TLS_RSA_WITH_DES_CBC_SHA: cipher = "DES-CBC-SHA"; break; case TLS_RSA_WITH_3DES_EDE_CBC_SHA: cipher = "DES-CBC3-SHA"; break; case TLS_DH_anon_WITH_AES_128_CBC_SHA256: cipher = "ADH-AES-128-SHA256"; break; case TLS_DH_anon_WITH_AES_128_CBC_SHA: cipher = "ADH-AES-128-SHA"; break; case TLS_RSA_WITH_AES_256_CBC_SHA: cipher = "AES-256-SHA"; break; case TLS_RSA_WITH_AES_256_CBC_SHA256: cipher = "AES-256-SHA256"; break; case TLS_RSA_WITH_AES_128_CBC_SHA: cipher = "AES-128-SHA"; break; case TLS_RSA_WITH_AES_128_CBC_SHA256: cipher = "AES-128-SHA256"; break; default: return -1; } if (os_strlcpy(buf, cipher, buflen) >= buflen) return -1; return 0; } /** * tlsv1_client_shutdown - Shutdown TLS connection * @conn: TLSv1 client connection data from tlsv1_client_init() * Returns: 0 on success, -1 on failure */ int tlsv1_client_shutdown(struct tlsv1_client *conn) { conn->state = CLIENT_HELLO; if (tls_verify_hash_init(&conn->verify) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to re-initialize verify " "hash"); return -1; } tlsv1_record_set_cipher_suite(&conn->rl, TLS_NULL_WITH_NULL_NULL); tlsv1_record_change_write_cipher(&conn->rl); tlsv1_record_change_read_cipher(&conn->rl); conn->certificate_requested = 0; crypto_public_key_free(conn->server_rsa_key); conn->server_rsa_key = NULL; conn->session_resumed = 0; return 0; } /** * tlsv1_client_resumed - Was session resumption used * @conn: TLSv1 client connection data from tlsv1_client_init() * Returns: 1 if current session used session resumption, 0 if not */ int tlsv1_client_resumed(struct tlsv1_client *conn) { return !!conn->session_resumed; } /** * tlsv1_client_hello_ext - Set TLS extension for ClientHello * @conn: TLSv1 client connection data from tlsv1_client_init() * @ext_type: Extension type * @data: Extension payload (%NULL to remove extension) * @data_len: Extension payload length * Returns: 0 on success, -1 on failure */ int tlsv1_client_hello_ext(struct tlsv1_client *conn, int ext_type, const u8 *data, size_t data_len) { u8 *pos; conn->session_ticket_included = 0; os_free(conn->client_hello_ext); conn->client_hello_ext = NULL; conn->client_hello_ext_len = 0; if (data == NULL || data_len == 0) return 0; pos = conn->client_hello_ext = os_malloc(6 + data_len); if (pos == NULL) return -1; WPA_PUT_BE16(pos, 4 + data_len); pos += 2; WPA_PUT_BE16(pos, ext_type); pos += 2; WPA_PUT_BE16(pos, data_len); pos += 2; os_memcpy(pos, data, data_len); conn->client_hello_ext_len = 6 + data_len; if (ext_type == TLS_EXT_PAC_OPAQUE) { conn->session_ticket_included = 1; wpa_printf(MSG_DEBUG, "TLSv1: Using session ticket"); } return 0; } /** * tlsv1_client_get_keys - Get master key and random data from TLS connection * @conn: TLSv1 client connection data from tlsv1_client_init() * @keys: Structure of key/random data (filled on success) * Returns: 0 on success, -1 on failure */ int tlsv1_client_get_keys(struct tlsv1_client *conn, struct tls_keys *keys) { os_memset(keys, 0, sizeof(*keys)); if (conn->state == CLIENT_HELLO) return -1; keys->client_random = conn->client_random; keys->client_random_len = TLS_RANDOM_LEN; if (conn->state != SERVER_HELLO) { keys->server_random = conn->server_random; keys->server_random_len = TLS_RANDOM_LEN; keys->master_key = conn->master_secret; keys->master_key_len = TLS_MASTER_SECRET_LEN; } return 0; } /** * tlsv1_client_get_keyblock_size - Get TLS key_block size * @conn: TLSv1 client connection data from tlsv1_client_init() * Returns: Size of the key_block for the negotiated cipher suite or -1 on * failure */ int tlsv1_client_get_keyblock_size(struct tlsv1_client *conn) { if (conn->state == CLIENT_HELLO || conn->state == SERVER_HELLO) return -1; return 2 * (conn->rl.hash_size + conn->rl.key_material_len + conn->rl.iv_size); } /** * tlsv1_client_set_cipher_list - Configure acceptable cipher suites * @conn: TLSv1 client connection data from tlsv1_client_init() * @ciphers: Zero (TLS_CIPHER_NONE) terminated list of allowed ciphers * (TLS_CIPHER_*). * Returns: 0 on success, -1 on failure */ int tlsv1_client_set_cipher_list(struct tlsv1_client *conn, u8 *ciphers) { size_t count; u16 *suites; /* TODO: implement proper configuration of cipher suites */ if (ciphers[0] == TLS_CIPHER_ANON_DH_AES128_SHA) { count = 0; suites = conn->cipher_suites; suites[count++] = TLS_DH_anon_WITH_AES_256_CBC_SHA256; suites[count++] = TLS_DH_anon_WITH_AES_256_CBC_SHA; suites[count++] = TLS_DH_anon_WITH_AES_128_CBC_SHA256; suites[count++] = TLS_DH_anon_WITH_AES_128_CBC_SHA; suites[count++] = TLS_DH_anon_WITH_3DES_EDE_CBC_SHA; suites[count++] = TLS_DH_anon_WITH_RC4_128_MD5; suites[count++] = TLS_DH_anon_WITH_DES_CBC_SHA; /* * Cisco AP (at least 350 and 1200 series) local authentication * server does not know how to search cipher suites from the * list and seem to require that the last entry in the list is * the one that it wants to use. However, TLS specification * requires the list to be in the client preference order. As a * workaround, add anon-DH AES-128-SHA1 again at the end of the * list to allow the Cisco code to find it. */ suites[count++] = TLS_DH_anon_WITH_AES_128_CBC_SHA; conn->num_cipher_suites = count; } return 0; } /** * tlsv1_client_set_cred - Set client credentials * @conn: TLSv1 client connection data from tlsv1_client_init() * @cred: Credentials from tlsv1_cred_alloc() * Returns: 0 on success, -1 on failure * * On success, the client takes ownership of the credentials block and caller * must not free it. On failure, caller is responsible for freeing the * credential block. */ int tlsv1_client_set_cred(struct tlsv1_client *conn, struct tlsv1_credentials *cred) { tlsv1_cred_free(conn->cred); conn->cred = cred; return 0; } void tlsv1_client_set_time_checks(struct tlsv1_client *conn, int enabled) { conn->disable_time_checks = !enabled; } void tlsv1_client_set_session_ticket_cb(struct tlsv1_client *conn, tlsv1_client_session_ticket_cb cb, void *ctx) { wpa_printf(MSG_DEBUG, "TLSv1: SessionTicket callback set %p (ctx %p)", cb, ctx); conn->session_ticket_cb = cb; conn->session_ticket_cb_ctx = ctx; } wpa-2.1/src/tls/tlsv1_client.h000066400000000000000000000041171227414666700163360ustar00rootroot00000000000000/* * TLS v1.0/v1.1/v1.2 client (RFC 2246, RFC 4346, RFC 5246) * Copyright (c) 2006-2011, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef TLSV1_CLIENT_H #define TLSV1_CLIENT_H #include "tlsv1_cred.h" struct tlsv1_client; int tlsv1_client_global_init(void); void tlsv1_client_global_deinit(void); struct tlsv1_client * tlsv1_client_init(void); void tlsv1_client_deinit(struct tlsv1_client *conn); int tlsv1_client_established(struct tlsv1_client *conn); int tlsv1_client_prf(struct tlsv1_client *conn, const char *label, int server_random_first, u8 *out, size_t out_len); u8 * tlsv1_client_handshake(struct tlsv1_client *conn, const u8 *in_data, size_t in_len, size_t *out_len, u8 **appl_data, size_t *appl_data_len, int *need_more_data); int tlsv1_client_encrypt(struct tlsv1_client *conn, const u8 *in_data, size_t in_len, u8 *out_data, size_t out_len); struct wpabuf * tlsv1_client_decrypt(struct tlsv1_client *conn, const u8 *in_data, size_t in_len, int *need_more_data); int tlsv1_client_get_cipher(struct tlsv1_client *conn, char *buf, size_t buflen); int tlsv1_client_shutdown(struct tlsv1_client *conn); int tlsv1_client_resumed(struct tlsv1_client *conn); int tlsv1_client_hello_ext(struct tlsv1_client *conn, int ext_type, const u8 *data, size_t data_len); int tlsv1_client_get_keys(struct tlsv1_client *conn, struct tls_keys *keys); int tlsv1_client_get_keyblock_size(struct tlsv1_client *conn); int tlsv1_client_set_cipher_list(struct tlsv1_client *conn, u8 *ciphers); int tlsv1_client_set_cred(struct tlsv1_client *conn, struct tlsv1_credentials *cred); void tlsv1_client_set_time_checks(struct tlsv1_client *conn, int enabled); typedef int (*tlsv1_client_session_ticket_cb) (void *ctx, const u8 *ticket, size_t len, const u8 *client_random, const u8 *server_random, u8 *master_secret); void tlsv1_client_set_session_ticket_cb(struct tlsv1_client *conn, tlsv1_client_session_ticket_cb cb, void *ctx); #endif /* TLSV1_CLIENT_H */ wpa-2.1/src/tls/tlsv1_client_i.h000066400000000000000000000044741227414666700166540ustar00rootroot00000000000000/* * TLSv1 client - internal structures * Copyright (c) 2006-2011, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef TLSV1_CLIENT_I_H #define TLSV1_CLIENT_I_H struct tlsv1_client { enum { CLIENT_HELLO, SERVER_HELLO, SERVER_CERTIFICATE, SERVER_KEY_EXCHANGE, SERVER_CERTIFICATE_REQUEST, SERVER_HELLO_DONE, CLIENT_KEY_EXCHANGE, CHANGE_CIPHER_SPEC, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, ACK_FINISHED, ESTABLISHED, FAILED } state; struct tlsv1_record_layer rl; u8 session_id[TLS_SESSION_ID_MAX_LEN]; size_t session_id_len; u8 client_random[TLS_RANDOM_LEN]; u8 server_random[TLS_RANDOM_LEN]; u8 master_secret[TLS_MASTER_SECRET_LEN]; u8 alert_level; u8 alert_description; unsigned int certificate_requested:1; unsigned int session_resumed:1; unsigned int session_ticket_included:1; unsigned int use_session_ticket:1; unsigned int disable_time_checks:1; struct crypto_public_key *server_rsa_key; struct tls_verify_hash verify; #define MAX_CIPHER_COUNT 30 u16 cipher_suites[MAX_CIPHER_COUNT]; size_t num_cipher_suites; u16 prev_cipher_suite; u8 *client_hello_ext; size_t client_hello_ext_len; /* The prime modulus used for Diffie-Hellman */ u8 *dh_p; size_t dh_p_len; /* The generator used for Diffie-Hellman */ u8 *dh_g; size_t dh_g_len; /* The server's Diffie-Hellman public value */ u8 *dh_ys; size_t dh_ys_len; struct tlsv1_credentials *cred; tlsv1_client_session_ticket_cb session_ticket_cb; void *session_ticket_cb_ctx; struct wpabuf *partial_input; }; void tls_alert(struct tlsv1_client *conn, u8 level, u8 description); void tlsv1_client_free_dh(struct tlsv1_client *conn); int tls_derive_pre_master_secret(u8 *pre_master_secret); int tls_derive_keys(struct tlsv1_client *conn, const u8 *pre_master_secret, size_t pre_master_secret_len); u8 * tls_send_client_hello(struct tlsv1_client *conn, size_t *out_len); u8 * tlsv1_client_send_alert(struct tlsv1_client *conn, u8 level, u8 description, size_t *out_len); u8 * tlsv1_client_handshake_write(struct tlsv1_client *conn, size_t *out_len, int no_appl_data); int tlsv1_client_process_handshake(struct tlsv1_client *conn, u8 ct, const u8 *buf, size_t *len, u8 **out_data, size_t *out_len); #endif /* TLSV1_CLIENT_I_H */ wpa-2.1/src/tls/tlsv1_client_read.c000066400000000000000000000636511227414666700173340ustar00rootroot00000000000000/* * TLSv1 client - read handshake message * Copyright (c) 2006-2011, 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/md5.h" #include "crypto/sha1.h" #include "crypto/sha256.h" #include "crypto/tls.h" #include "x509v3.h" #include "tlsv1_common.h" #include "tlsv1_record.h" #include "tlsv1_client.h" #include "tlsv1_client_i.h" static int tls_process_server_key_exchange(struct tlsv1_client *conn, u8 ct, const u8 *in_data, size_t *in_len); static int tls_process_certificate_request(struct tlsv1_client *conn, u8 ct, const u8 *in_data, size_t *in_len); static int tls_process_server_hello_done(struct tlsv1_client *conn, u8 ct, const u8 *in_data, size_t *in_len); static int tls_process_server_hello(struct tlsv1_client *conn, u8 ct, const u8 *in_data, size_t *in_len) { const u8 *pos, *end; size_t left, len, i; u16 cipher_suite; u16 tls_version; if (ct != TLS_CONTENT_TYPE_HANDSHAKE) { wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; " "received content type 0x%x", ct); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; } pos = in_data; left = *in_len; if (left < 4) goto decode_error; /* HandshakeType msg_type */ if (*pos != TLS_HANDSHAKE_TYPE_SERVER_HELLO) { wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake " "message %d (expected ServerHello)", *pos); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; } wpa_printf(MSG_DEBUG, "TLSv1: Received ServerHello"); pos++; /* uint24 length */ len = WPA_GET_BE24(pos); pos += 3; left -= 4; if (len > left) goto decode_error; /* body - ServerHello */ wpa_hexdump(MSG_MSGDUMP, "TLSv1: ServerHello", pos, len); end = pos + len; /* ProtocolVersion server_version */ if (end - pos < 2) goto decode_error; tls_version = WPA_GET_BE16(pos); if (!tls_version_ok(tls_version)) { wpa_printf(MSG_DEBUG, "TLSv1: Unexpected protocol version in " "ServerHello %u.%u", pos[0], pos[1]); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_PROTOCOL_VERSION); return -1; } pos += 2; wpa_printf(MSG_DEBUG, "TLSv1: Using TLS v%s", tls_version_str(tls_version)); conn->rl.tls_version = tls_version; /* Random random */ if (end - pos < TLS_RANDOM_LEN) goto decode_error; os_memcpy(conn->server_random, pos, TLS_RANDOM_LEN); pos += TLS_RANDOM_LEN; wpa_hexdump(MSG_MSGDUMP, "TLSv1: server_random", conn->server_random, TLS_RANDOM_LEN); /* SessionID session_id */ if (end - pos < 1) goto decode_error; if (end - pos < 1 + *pos || *pos > TLS_SESSION_ID_MAX_LEN) goto decode_error; if (conn->session_id_len && conn->session_id_len == *pos && os_memcmp(conn->session_id, pos + 1, conn->session_id_len) == 0) { pos += 1 + conn->session_id_len; wpa_printf(MSG_DEBUG, "TLSv1: Resuming old session"); conn->session_resumed = 1; } else { conn->session_id_len = *pos; pos++; os_memcpy(conn->session_id, pos, conn->session_id_len); pos += conn->session_id_len; } wpa_hexdump(MSG_MSGDUMP, "TLSv1: session_id", conn->session_id, conn->session_id_len); /* CipherSuite cipher_suite */ if (end - pos < 2) goto decode_error; cipher_suite = WPA_GET_BE16(pos); pos += 2; for (i = 0; i < conn->num_cipher_suites; i++) { if (cipher_suite == conn->cipher_suites[i]) break; } if (i == conn->num_cipher_suites) { wpa_printf(MSG_INFO, "TLSv1: Server selected unexpected " "cipher suite 0x%04x", cipher_suite); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_ILLEGAL_PARAMETER); return -1; } if (conn->session_resumed && cipher_suite != conn->prev_cipher_suite) { wpa_printf(MSG_DEBUG, "TLSv1: Server selected a different " "cipher suite for a resumed connection (0x%04x != " "0x%04x)", cipher_suite, conn->prev_cipher_suite); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_ILLEGAL_PARAMETER); return -1; } if (tlsv1_record_set_cipher_suite(&conn->rl, cipher_suite) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to set CipherSuite for " "record layer"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } conn->prev_cipher_suite = cipher_suite; /* CompressionMethod compression_method */ if (end - pos < 1) goto decode_error; if (*pos != TLS_COMPRESSION_NULL) { wpa_printf(MSG_INFO, "TLSv1: Server selected unexpected " "compression 0x%02x", *pos); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_ILLEGAL_PARAMETER); return -1; } pos++; if (end != pos) { /* TODO: ServerHello extensions */ wpa_hexdump(MSG_DEBUG, "TLSv1: Unexpected extra data in the " "end of ServerHello", pos, end - pos); goto decode_error; } if (conn->session_ticket_included && conn->session_ticket_cb) { /* TODO: include SessionTicket extension if one was included in * ServerHello */ int res = conn->session_ticket_cb( conn->session_ticket_cb_ctx, NULL, 0, conn->client_random, conn->server_random, conn->master_secret); if (res < 0) { wpa_printf(MSG_DEBUG, "TLSv1: SessionTicket callback " "indicated failure"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_HANDSHAKE_FAILURE); return -1; } conn->use_session_ticket = !!res; } if ((conn->session_resumed || conn->use_session_ticket) && tls_derive_keys(conn, NULL, 0)) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive keys"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } *in_len = end - in_data; conn->state = (conn->session_resumed || conn->use_session_ticket) ? SERVER_CHANGE_CIPHER_SPEC : SERVER_CERTIFICATE; return 0; decode_error: wpa_printf(MSG_DEBUG, "TLSv1: Failed to decode ServerHello"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; } static int tls_process_certificate(struct tlsv1_client *conn, u8 ct, const u8 *in_data, size_t *in_len) { const u8 *pos, *end; size_t left, len, list_len, cert_len, idx; u8 type; struct x509_certificate *chain = NULL, *last = NULL, *cert; int reason; if (ct != TLS_CONTENT_TYPE_HANDSHAKE) { wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; " "received content type 0x%x", ct); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; } pos = in_data; left = *in_len; if (left < 4) { wpa_printf(MSG_DEBUG, "TLSv1: Too short Certificate message " "(len=%lu)", (unsigned long) left); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; } type = *pos++; len = WPA_GET_BE24(pos); pos += 3; left -= 4; if (len > left) { wpa_printf(MSG_DEBUG, "TLSv1: Unexpected Certificate message " "length (len=%lu != left=%lu)", (unsigned long) len, (unsigned long) left); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; } if (type == TLS_HANDSHAKE_TYPE_SERVER_KEY_EXCHANGE) return tls_process_server_key_exchange(conn, ct, in_data, in_len); if (type == TLS_HANDSHAKE_TYPE_CERTIFICATE_REQUEST) return tls_process_certificate_request(conn, ct, in_data, in_len); if (type == TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE) return tls_process_server_hello_done(conn, ct, in_data, in_len); if (type != TLS_HANDSHAKE_TYPE_CERTIFICATE) { wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake " "message %d (expected Certificate/" "ServerKeyExchange/CertificateRequest/" "ServerHelloDone)", type); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; } wpa_printf(MSG_DEBUG, "TLSv1: Received Certificate (certificate_list len %lu)", (unsigned long) len); /* * opaque ASN.1Cert<2^24-1>; * * struct { * ASN.1Cert certificate_list<1..2^24-1>; * } Certificate; */ end = pos + len; if (end - pos < 3) { wpa_printf(MSG_DEBUG, "TLSv1: Too short Certificate " "(left=%lu)", (unsigned long) left); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; } list_len = WPA_GET_BE24(pos); pos += 3; if ((size_t) (end - pos) != list_len) { wpa_printf(MSG_DEBUG, "TLSv1: Unexpected certificate_list " "length (len=%lu left=%lu)", (unsigned long) list_len, (unsigned long) (end - pos)); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; } idx = 0; while (pos < end) { if (end - pos < 3) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse " "certificate_list"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); x509_certificate_chain_free(chain); return -1; } cert_len = WPA_GET_BE24(pos); pos += 3; if ((size_t) (end - pos) < cert_len) { wpa_printf(MSG_DEBUG, "TLSv1: Unexpected certificate " "length (len=%lu left=%lu)", (unsigned long) cert_len, (unsigned long) (end - pos)); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); x509_certificate_chain_free(chain); return -1; } wpa_printf(MSG_DEBUG, "TLSv1: Certificate %lu (len %lu)", (unsigned long) idx, (unsigned long) cert_len); if (idx == 0) { crypto_public_key_free(conn->server_rsa_key); if (tls_parse_cert(pos, cert_len, &conn->server_rsa_key)) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse " "the certificate"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_BAD_CERTIFICATE); x509_certificate_chain_free(chain); return -1; } } cert = x509_certificate_parse(pos, cert_len); if (cert == NULL) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse " "the certificate"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_BAD_CERTIFICATE); x509_certificate_chain_free(chain); return -1; } if (last == NULL) chain = cert; else last->next = cert; last = cert; idx++; pos += cert_len; } if (conn->cred && x509_certificate_chain_validate(conn->cred->trusted_certs, chain, &reason, conn->disable_time_checks) < 0) { int tls_reason; wpa_printf(MSG_DEBUG, "TLSv1: Server certificate chain " "validation failed (reason=%d)", reason); switch (reason) { case X509_VALIDATE_BAD_CERTIFICATE: tls_reason = TLS_ALERT_BAD_CERTIFICATE; break; case X509_VALIDATE_UNSUPPORTED_CERTIFICATE: tls_reason = TLS_ALERT_UNSUPPORTED_CERTIFICATE; break; case X509_VALIDATE_CERTIFICATE_REVOKED: tls_reason = TLS_ALERT_CERTIFICATE_REVOKED; break; case X509_VALIDATE_CERTIFICATE_EXPIRED: tls_reason = TLS_ALERT_CERTIFICATE_EXPIRED; break; case X509_VALIDATE_CERTIFICATE_UNKNOWN: tls_reason = TLS_ALERT_CERTIFICATE_UNKNOWN; break; case X509_VALIDATE_UNKNOWN_CA: tls_reason = TLS_ALERT_UNKNOWN_CA; break; default: tls_reason = TLS_ALERT_BAD_CERTIFICATE; break; } tls_alert(conn, TLS_ALERT_LEVEL_FATAL, tls_reason); x509_certificate_chain_free(chain); return -1; } x509_certificate_chain_free(chain); *in_len = end - in_data; conn->state = SERVER_KEY_EXCHANGE; return 0; } static int tlsv1_process_diffie_hellman(struct tlsv1_client *conn, const u8 *buf, size_t len) { const u8 *pos, *end; tlsv1_client_free_dh(conn); pos = buf; end = buf + len; if (end - pos < 3) goto fail; conn->dh_p_len = WPA_GET_BE16(pos); pos += 2; if (conn->dh_p_len == 0 || end - pos < (int) conn->dh_p_len) { wpa_printf(MSG_DEBUG, "TLSv1: Invalid dh_p length %lu", (unsigned long) conn->dh_p_len); goto fail; } conn->dh_p = os_malloc(conn->dh_p_len); if (conn->dh_p == NULL) goto fail; os_memcpy(conn->dh_p, pos, conn->dh_p_len); pos += conn->dh_p_len; wpa_hexdump(MSG_DEBUG, "TLSv1: DH p (prime)", conn->dh_p, conn->dh_p_len); if (end - pos < 3) goto fail; conn->dh_g_len = WPA_GET_BE16(pos); pos += 2; if (conn->dh_g_len == 0 || end - pos < (int) conn->dh_g_len) goto fail; conn->dh_g = os_malloc(conn->dh_g_len); if (conn->dh_g == NULL) goto fail; os_memcpy(conn->dh_g, pos, conn->dh_g_len); pos += conn->dh_g_len; wpa_hexdump(MSG_DEBUG, "TLSv1: DH g (generator)", conn->dh_g, conn->dh_g_len); if (conn->dh_g_len == 1 && conn->dh_g[0] < 2) goto fail; if (end - pos < 3) goto fail; conn->dh_ys_len = WPA_GET_BE16(pos); pos += 2; if (conn->dh_ys_len == 0 || end - pos < (int) conn->dh_ys_len) goto fail; conn->dh_ys = os_malloc(conn->dh_ys_len); if (conn->dh_ys == NULL) goto fail; os_memcpy(conn->dh_ys, pos, conn->dh_ys_len); pos += conn->dh_ys_len; wpa_hexdump(MSG_DEBUG, "TLSv1: DH Ys (server's public value)", conn->dh_ys, conn->dh_ys_len); return 0; fail: wpa_printf(MSG_DEBUG, "TLSv1: Processing DH params failed"); tlsv1_client_free_dh(conn); return -1; } static int tls_process_server_key_exchange(struct tlsv1_client *conn, u8 ct, const u8 *in_data, size_t *in_len) { const u8 *pos, *end; size_t left, len; u8 type; const struct tls_cipher_suite *suite; if (ct != TLS_CONTENT_TYPE_HANDSHAKE) { wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; " "received content type 0x%x", ct); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; } pos = in_data; left = *in_len; if (left < 4) { wpa_printf(MSG_DEBUG, "TLSv1: Too short ServerKeyExchange " "(Left=%lu)", (unsigned long) left); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; } type = *pos++; len = WPA_GET_BE24(pos); pos += 3; left -= 4; if (len > left) { wpa_printf(MSG_DEBUG, "TLSv1: Mismatch in ServerKeyExchange " "length (len=%lu != left=%lu)", (unsigned long) len, (unsigned long) left); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; } end = pos + len; if (type == TLS_HANDSHAKE_TYPE_CERTIFICATE_REQUEST) return tls_process_certificate_request(conn, ct, in_data, in_len); if (type == TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE) return tls_process_server_hello_done(conn, ct, in_data, in_len); if (type != TLS_HANDSHAKE_TYPE_SERVER_KEY_EXCHANGE) { wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake " "message %d (expected ServerKeyExchange/" "CertificateRequest/ServerHelloDone)", type); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; } wpa_printf(MSG_DEBUG, "TLSv1: Received ServerKeyExchange"); if (!tls_server_key_exchange_allowed(conn->rl.cipher_suite)) { wpa_printf(MSG_DEBUG, "TLSv1: ServerKeyExchange not allowed " "with the selected cipher suite"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; } wpa_hexdump(MSG_DEBUG, "TLSv1: ServerKeyExchange", pos, len); suite = tls_get_cipher_suite(conn->rl.cipher_suite); if (suite && suite->key_exchange == TLS_KEY_X_DH_anon) { if (tlsv1_process_diffie_hellman(conn, pos, len) < 0) { tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; } } else { wpa_printf(MSG_DEBUG, "TLSv1: UnexpectedServerKeyExchange"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; } *in_len = end - in_data; conn->state = SERVER_CERTIFICATE_REQUEST; return 0; } static int tls_process_certificate_request(struct tlsv1_client *conn, u8 ct, const u8 *in_data, size_t *in_len) { const u8 *pos, *end; size_t left, len; u8 type; if (ct != TLS_CONTENT_TYPE_HANDSHAKE) { wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; " "received content type 0x%x", ct); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; } pos = in_data; left = *in_len; if (left < 4) { wpa_printf(MSG_DEBUG, "TLSv1: Too short CertificateRequest " "(left=%lu)", (unsigned long) left); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; } type = *pos++; len = WPA_GET_BE24(pos); pos += 3; left -= 4; if (len > left) { wpa_printf(MSG_DEBUG, "TLSv1: Mismatch in CertificateRequest " "length (len=%lu != left=%lu)", (unsigned long) len, (unsigned long) left); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; } end = pos + len; if (type == TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE) return tls_process_server_hello_done(conn, ct, in_data, in_len); if (type != TLS_HANDSHAKE_TYPE_CERTIFICATE_REQUEST) { wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake " "message %d (expected CertificateRequest/" "ServerHelloDone)", type); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; } wpa_printf(MSG_DEBUG, "TLSv1: Received CertificateRequest"); conn->certificate_requested = 1; *in_len = end - in_data; conn->state = SERVER_HELLO_DONE; return 0; } static int tls_process_server_hello_done(struct tlsv1_client *conn, u8 ct, const u8 *in_data, size_t *in_len) { const u8 *pos, *end; size_t left, len; u8 type; if (ct != TLS_CONTENT_TYPE_HANDSHAKE) { wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; " "received content type 0x%x", ct); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; } pos = in_data; left = *in_len; if (left < 4) { wpa_printf(MSG_DEBUG, "TLSv1: Too short ServerHelloDone " "(left=%lu)", (unsigned long) left); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; } type = *pos++; len = WPA_GET_BE24(pos); pos += 3; left -= 4; if (len > left) { wpa_printf(MSG_DEBUG, "TLSv1: Mismatch in ServerHelloDone " "length (len=%lu != left=%lu)", (unsigned long) len, (unsigned long) left); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; } end = pos + len; if (type != TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE) { wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake " "message %d (expected ServerHelloDone)", type); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; } wpa_printf(MSG_DEBUG, "TLSv1: Received ServerHelloDone"); *in_len = end - in_data; conn->state = CLIENT_KEY_EXCHANGE; return 0; } static int tls_process_server_change_cipher_spec(struct tlsv1_client *conn, u8 ct, const u8 *in_data, size_t *in_len) { const u8 *pos; size_t left; if (ct != TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC) { wpa_printf(MSG_DEBUG, "TLSv1: Expected ChangeCipherSpec; " "received content type 0x%x", ct); if (conn->use_session_ticket) { int res; wpa_printf(MSG_DEBUG, "TLSv1: Server may have " "rejected SessionTicket"); conn->use_session_ticket = 0; /* Notify upper layers that SessionTicket failed */ res = conn->session_ticket_cb( conn->session_ticket_cb_ctx, NULL, 0, NULL, NULL, NULL); if (res < 0) { wpa_printf(MSG_DEBUG, "TLSv1: SessionTicket " "callback indicated failure"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_HANDSHAKE_FAILURE); return -1; } conn->state = SERVER_CERTIFICATE; return tls_process_certificate(conn, ct, in_data, in_len); } tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; } pos = in_data; left = *in_len; if (left < 1) { wpa_printf(MSG_DEBUG, "TLSv1: Too short ChangeCipherSpec"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; } if (*pos != TLS_CHANGE_CIPHER_SPEC) { wpa_printf(MSG_DEBUG, "TLSv1: Expected ChangeCipherSpec; " "received data 0x%x", *pos); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; } wpa_printf(MSG_DEBUG, "TLSv1: Received ChangeCipherSpec"); if (tlsv1_record_change_read_cipher(&conn->rl) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to change read cipher " "for record layer"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } *in_len = pos + 1 - in_data; conn->state = SERVER_FINISHED; return 0; } static int tls_process_server_finished(struct tlsv1_client *conn, u8 ct, const u8 *in_data, size_t *in_len) { const u8 *pos, *end; size_t left, len, hlen; u8 verify_data[TLS_VERIFY_DATA_LEN]; u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN]; if (ct != TLS_CONTENT_TYPE_HANDSHAKE) { wpa_printf(MSG_DEBUG, "TLSv1: Expected Finished; " "received content type 0x%x", ct); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; } pos = in_data; left = *in_len; if (left < 4) { wpa_printf(MSG_DEBUG, "TLSv1: Too short record (left=%lu) for " "Finished", (unsigned long) left); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; } if (pos[0] != TLS_HANDSHAKE_TYPE_FINISHED) { wpa_printf(MSG_DEBUG, "TLSv1: Expected Finished; received " "type 0x%x", pos[0]); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; } len = WPA_GET_BE24(pos + 1); pos += 4; left -= 4; if (len > left) { wpa_printf(MSG_DEBUG, "TLSv1: Too short buffer for Finished " "(len=%lu > left=%lu)", (unsigned long) len, (unsigned long) left); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; } end = pos + len; if (len != TLS_VERIFY_DATA_LEN) { wpa_printf(MSG_DEBUG, "TLSv1: Unexpected verify_data length " "in Finished: %lu (expected %d)", (unsigned long) len, TLS_VERIFY_DATA_LEN); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; } wpa_hexdump(MSG_MSGDUMP, "TLSv1: verify_data in Finished", pos, TLS_VERIFY_DATA_LEN); #ifdef CONFIG_TLSV12 if (conn->rl.tls_version >= TLS_VERSION_1_2) { hlen = SHA256_MAC_LEN; if (conn->verify.sha256_server == NULL || crypto_hash_finish(conn->verify.sha256_server, hash, &hlen) < 0) { tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); conn->verify.sha256_server = NULL; return -1; } conn->verify.sha256_server = NULL; } else { #endif /* CONFIG_TLSV12 */ hlen = MD5_MAC_LEN; if (conn->verify.md5_server == NULL || crypto_hash_finish(conn->verify.md5_server, hash, &hlen) < 0) { tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); conn->verify.md5_server = NULL; crypto_hash_finish(conn->verify.sha1_server, NULL, NULL); conn->verify.sha1_server = NULL; return -1; } conn->verify.md5_server = NULL; hlen = SHA1_MAC_LEN; if (conn->verify.sha1_server == NULL || crypto_hash_finish(conn->verify.sha1_server, hash + MD5_MAC_LEN, &hlen) < 0) { conn->verify.sha1_server = NULL; tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } conn->verify.sha1_server = NULL; hlen = MD5_MAC_LEN + SHA1_MAC_LEN; #ifdef CONFIG_TLSV12 } #endif /* CONFIG_TLSV12 */ if (tls_prf(conn->rl.tls_version, conn->master_secret, TLS_MASTER_SECRET_LEN, "server finished", hash, hlen, verify_data, TLS_VERIFY_DATA_LEN)) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive verify_data"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECRYPT_ERROR); return -1; } wpa_hexdump_key(MSG_DEBUG, "TLSv1: verify_data (server)", verify_data, TLS_VERIFY_DATA_LEN); if (os_memcmp(pos, verify_data, TLS_VERIFY_DATA_LEN) != 0) { wpa_printf(MSG_INFO, "TLSv1: Mismatch in verify_data"); return -1; } wpa_printf(MSG_DEBUG, "TLSv1: Received Finished"); *in_len = end - in_data; conn->state = (conn->session_resumed || conn->use_session_ticket) ? CHANGE_CIPHER_SPEC : ACK_FINISHED; return 0; } static int tls_process_application_data(struct tlsv1_client *conn, u8 ct, const u8 *in_data, size_t *in_len, u8 **out_data, size_t *out_len) { const u8 *pos; size_t left; if (ct != TLS_CONTENT_TYPE_APPLICATION_DATA) { wpa_printf(MSG_DEBUG, "TLSv1: Expected Application Data; " "received content type 0x%x", ct); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; } pos = in_data; left = *in_len; wpa_hexdump(MSG_DEBUG, "TLSv1: Application Data included in Handshake", pos, left); *out_data = os_malloc(left); if (*out_data) { os_memcpy(*out_data, pos, left); *out_len = left; } return 0; } int tlsv1_client_process_handshake(struct tlsv1_client *conn, u8 ct, const u8 *buf, size_t *len, u8 **out_data, size_t *out_len) { if (ct == TLS_CONTENT_TYPE_ALERT) { if (*len < 2) { wpa_printf(MSG_DEBUG, "TLSv1: Alert underflow"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; } wpa_printf(MSG_DEBUG, "TLSv1: Received alert %d:%d", buf[0], buf[1]); *len = 2; conn->state = FAILED; return -1; } if (ct == TLS_CONTENT_TYPE_HANDSHAKE && *len >= 4 && buf[0] == TLS_HANDSHAKE_TYPE_HELLO_REQUEST) { size_t hr_len = WPA_GET_BE24(buf + 1); if (hr_len > *len - 4) { wpa_printf(MSG_DEBUG, "TLSv1: HelloRequest underflow"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; } wpa_printf(MSG_DEBUG, "TLSv1: Ignored HelloRequest"); *len = 4 + hr_len; return 0; } switch (conn->state) { case SERVER_HELLO: if (tls_process_server_hello(conn, ct, buf, len)) return -1; break; case SERVER_CERTIFICATE: if (tls_process_certificate(conn, ct, buf, len)) return -1; break; case SERVER_KEY_EXCHANGE: if (tls_process_server_key_exchange(conn, ct, buf, len)) return -1; break; case SERVER_CERTIFICATE_REQUEST: if (tls_process_certificate_request(conn, ct, buf, len)) return -1; break; case SERVER_HELLO_DONE: if (tls_process_server_hello_done(conn, ct, buf, len)) return -1; break; case SERVER_CHANGE_CIPHER_SPEC: if (tls_process_server_change_cipher_spec(conn, ct, buf, len)) return -1; break; case SERVER_FINISHED: if (tls_process_server_finished(conn, ct, buf, len)) return -1; break; case ACK_FINISHED: if (out_data && tls_process_application_data(conn, ct, buf, len, out_data, out_len)) return -1; break; default: wpa_printf(MSG_DEBUG, "TLSv1: Unexpected state %d " "while processing received message", conn->state); return -1; } if (ct == TLS_CONTENT_TYPE_HANDSHAKE) tls_verify_hash_add(&conn->verify, buf, *len); return 0; } wpa-2.1/src/tls/tlsv1_client_write.c000066400000000000000000000533031227414666700175440ustar00rootroot00000000000000/* * TLSv1 client - write handshake message * Copyright (c) 2006-2011, 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/md5.h" #include "crypto/sha1.h" #include "crypto/sha256.h" #include "crypto/tls.h" #include "crypto/random.h" #include "x509v3.h" #include "tlsv1_common.h" #include "tlsv1_record.h" #include "tlsv1_client.h" #include "tlsv1_client_i.h" static size_t tls_client_cert_chain_der_len(struct tlsv1_client *conn) { size_t len = 0; struct x509_certificate *cert; if (conn->cred == NULL) return 0; cert = conn->cred->cert; while (cert) { len += 3 + cert->cert_len; if (x509_certificate_self_signed(cert)) break; cert = x509_certificate_get_subject(conn->cred->trusted_certs, &cert->issuer); } return len; } u8 * tls_send_client_hello(struct tlsv1_client *conn, size_t *out_len) { u8 *hello, *end, *pos, *hs_length, *hs_start, *rhdr; struct os_time now; size_t len, i; wpa_printf(MSG_DEBUG, "TLSv1: Send ClientHello"); *out_len = 0; os_get_time(&now); WPA_PUT_BE32(conn->client_random, now.sec); if (random_get_bytes(conn->client_random + 4, TLS_RANDOM_LEN - 4)) { wpa_printf(MSG_ERROR, "TLSv1: Could not generate " "client_random"); return NULL; } wpa_hexdump(MSG_MSGDUMP, "TLSv1: client_random", conn->client_random, TLS_RANDOM_LEN); len = 100 + conn->num_cipher_suites * 2 + conn->client_hello_ext_len; hello = os_malloc(len); if (hello == NULL) return NULL; end = hello + len; rhdr = hello; pos = rhdr + TLS_RECORD_HEADER_LEN; /* opaque fragment[TLSPlaintext.length] */ /* Handshake */ hs_start = pos; /* HandshakeType msg_type */ *pos++ = TLS_HANDSHAKE_TYPE_CLIENT_HELLO; /* uint24 length (to be filled) */ hs_length = pos; pos += 3; /* body - ClientHello */ /* ProtocolVersion client_version */ WPA_PUT_BE16(pos, TLS_VERSION); pos += 2; /* Random random: uint32 gmt_unix_time, opaque random_bytes */ os_memcpy(pos, conn->client_random, TLS_RANDOM_LEN); pos += TLS_RANDOM_LEN; /* SessionID session_id */ *pos++ = conn->session_id_len; os_memcpy(pos, conn->session_id, conn->session_id_len); pos += conn->session_id_len; /* CipherSuite cipher_suites<2..2^16-1> */ WPA_PUT_BE16(pos, 2 * conn->num_cipher_suites); pos += 2; for (i = 0; i < conn->num_cipher_suites; i++) { WPA_PUT_BE16(pos, conn->cipher_suites[i]); pos += 2; } /* CompressionMethod compression_methods<1..2^8-1> */ *pos++ = 1; *pos++ = TLS_COMPRESSION_NULL; if (conn->client_hello_ext) { os_memcpy(pos, conn->client_hello_ext, conn->client_hello_ext_len); pos += conn->client_hello_ext_len; } WPA_PUT_BE24(hs_length, pos - hs_length - 3); tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start); if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, rhdr, end - rhdr, hs_start, pos - hs_start, out_len) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to create TLS record"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); os_free(hello); return NULL; } conn->state = SERVER_HELLO; return hello; } static int tls_write_client_certificate(struct tlsv1_client *conn, u8 **msgpos, u8 *end) { u8 *pos, *rhdr, *hs_start, *hs_length, *cert_start; size_t rlen; struct x509_certificate *cert; pos = *msgpos; wpa_printf(MSG_DEBUG, "TLSv1: Send Certificate"); rhdr = pos; pos += TLS_RECORD_HEADER_LEN; /* opaque fragment[TLSPlaintext.length] */ /* Handshake */ hs_start = pos; /* HandshakeType msg_type */ *pos++ = TLS_HANDSHAKE_TYPE_CERTIFICATE; /* uint24 length (to be filled) */ hs_length = pos; pos += 3; /* body - Certificate */ /* uint24 length (to be filled) */ cert_start = pos; pos += 3; cert = conn->cred ? conn->cred->cert : NULL; while (cert) { if (pos + 3 + cert->cert_len > end) { wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space " "for Certificate (cert_len=%lu left=%lu)", (unsigned long) cert->cert_len, (unsigned long) (end - pos)); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } WPA_PUT_BE24(pos, cert->cert_len); pos += 3; os_memcpy(pos, cert->cert_start, cert->cert_len); pos += cert->cert_len; if (x509_certificate_self_signed(cert)) break; cert = x509_certificate_get_subject(conn->cred->trusted_certs, &cert->issuer); } if (conn->cred == NULL || cert == conn->cred->cert || cert == NULL) { /* * Client was not configured with all the needed certificates * to form a full certificate chain. The server may fail to * validate the chain unless it is configured with all the * missing CA certificates. */ wpa_printf(MSG_DEBUG, "TLSv1: Full client certificate chain " "not configured - validation may fail"); } WPA_PUT_BE24(cert_start, pos - cert_start - 3); WPA_PUT_BE24(hs_length, pos - hs_length - 3); if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, rhdr, end - rhdr, hs_start, pos - hs_start, &rlen) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } pos = rhdr + rlen; tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start); *msgpos = pos; return 0; } static int tlsv1_key_x_anon_dh(struct tlsv1_client *conn, u8 **pos, u8 *end) { /* ClientDiffieHellmanPublic */ u8 *csecret, *csecret_start, *dh_yc, *shared; size_t csecret_len, dh_yc_len, shared_len; csecret_len = conn->dh_p_len; csecret = os_malloc(csecret_len); if (csecret == NULL) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate " "memory for Yc (Diffie-Hellman)"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } if (random_get_bytes(csecret, csecret_len)) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to get random " "data for Diffie-Hellman"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); os_free(csecret); return -1; } if (os_memcmp(csecret, conn->dh_p, csecret_len) > 0) csecret[0] = 0; /* make sure Yc < p */ csecret_start = csecret; while (csecret_len > 1 && *csecret_start == 0) { csecret_start++; csecret_len--; } wpa_hexdump_key(MSG_DEBUG, "TLSv1: DH client's secret value", csecret_start, csecret_len); /* Yc = g^csecret mod p */ dh_yc_len = conn->dh_p_len; dh_yc = os_malloc(dh_yc_len); if (dh_yc == NULL) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate " "memory for Diffie-Hellman"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); os_free(csecret); return -1; } if (crypto_mod_exp(conn->dh_g, conn->dh_g_len, csecret_start, csecret_len, conn->dh_p, conn->dh_p_len, dh_yc, &dh_yc_len)) { tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); os_free(csecret); os_free(dh_yc); return -1; } wpa_hexdump(MSG_DEBUG, "TLSv1: DH Yc (client's public value)", dh_yc, dh_yc_len); WPA_PUT_BE16(*pos, dh_yc_len); *pos += 2; if (*pos + dh_yc_len > end) { wpa_printf(MSG_DEBUG, "TLSv1: Not enough room in the " "message buffer for Yc"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); os_free(csecret); os_free(dh_yc); return -1; } os_memcpy(*pos, dh_yc, dh_yc_len); *pos += dh_yc_len; os_free(dh_yc); shared_len = conn->dh_p_len; shared = os_malloc(shared_len); if (shared == NULL) { wpa_printf(MSG_DEBUG, "TLSv1: Could not allocate memory for " "DH"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); os_free(csecret); return -1; } /* shared = Ys^csecret mod p */ if (crypto_mod_exp(conn->dh_ys, conn->dh_ys_len, csecret_start, csecret_len, conn->dh_p, conn->dh_p_len, shared, &shared_len)) { tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); os_free(csecret); os_free(shared); return -1; } wpa_hexdump_key(MSG_DEBUG, "TLSv1: Shared secret from DH key exchange", shared, shared_len); os_memset(csecret_start, 0, csecret_len); os_free(csecret); if (tls_derive_keys(conn, shared, shared_len)) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive keys"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); os_free(shared); return -1; } os_memset(shared, 0, shared_len); os_free(shared); tlsv1_client_free_dh(conn); return 0; } static int tlsv1_key_x_rsa(struct tlsv1_client *conn, u8 **pos, u8 *end) { u8 pre_master_secret[TLS_PRE_MASTER_SECRET_LEN]; size_t clen; int res; if (tls_derive_pre_master_secret(pre_master_secret) < 0 || tls_derive_keys(conn, pre_master_secret, TLS_PRE_MASTER_SECRET_LEN)) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive keys"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } /* EncryptedPreMasterSecret */ if (conn->server_rsa_key == NULL) { wpa_printf(MSG_DEBUG, "TLSv1: No server RSA key to " "use for encrypting pre-master secret"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } /* RSA encrypted value is encoded with PKCS #1 v1.5 block type 2. */ *pos += 2; clen = end - *pos; res = crypto_public_key_encrypt_pkcs1_v15( conn->server_rsa_key, pre_master_secret, TLS_PRE_MASTER_SECRET_LEN, *pos, &clen); os_memset(pre_master_secret, 0, TLS_PRE_MASTER_SECRET_LEN); if (res < 0) { wpa_printf(MSG_DEBUG, "TLSv1: RSA encryption failed"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } WPA_PUT_BE16(*pos - 2, clen); wpa_hexdump(MSG_MSGDUMP, "TLSv1: Encrypted pre_master_secret", *pos, clen); *pos += clen; return 0; } static int tls_write_client_key_exchange(struct tlsv1_client *conn, u8 **msgpos, u8 *end) { u8 *pos, *rhdr, *hs_start, *hs_length; size_t rlen; tls_key_exchange keyx; const struct tls_cipher_suite *suite; suite = tls_get_cipher_suite(conn->rl.cipher_suite); if (suite == NULL) keyx = TLS_KEY_X_NULL; else keyx = suite->key_exchange; pos = *msgpos; wpa_printf(MSG_DEBUG, "TLSv1: Send ClientKeyExchange"); rhdr = pos; pos += TLS_RECORD_HEADER_LEN; /* opaque fragment[TLSPlaintext.length] */ /* Handshake */ hs_start = pos; /* HandshakeType msg_type */ *pos++ = TLS_HANDSHAKE_TYPE_CLIENT_KEY_EXCHANGE; /* uint24 length (to be filled) */ hs_length = pos; pos += 3; /* body - ClientKeyExchange */ if (keyx == TLS_KEY_X_DH_anon) { if (tlsv1_key_x_anon_dh(conn, &pos, end) < 0) return -1; } else { if (tlsv1_key_x_rsa(conn, &pos, end) < 0) return -1; } WPA_PUT_BE24(hs_length, pos - hs_length - 3); if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, rhdr, end - rhdr, hs_start, pos - hs_start, &rlen) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } pos = rhdr + rlen; tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start); *msgpos = pos; return 0; } static int tls_write_client_certificate_verify(struct tlsv1_client *conn, u8 **msgpos, u8 *end) { u8 *pos, *rhdr, *hs_start, *hs_length, *signed_start; size_t rlen, hlen, clen; u8 hash[100], *hpos; enum { SIGN_ALG_RSA, SIGN_ALG_DSA } alg = SIGN_ALG_RSA; pos = *msgpos; wpa_printf(MSG_DEBUG, "TLSv1: Send CertificateVerify"); rhdr = pos; pos += TLS_RECORD_HEADER_LEN; /* Handshake */ hs_start = pos; /* HandshakeType msg_type */ *pos++ = TLS_HANDSHAKE_TYPE_CERTIFICATE_VERIFY; /* uint24 length (to be filled) */ hs_length = pos; pos += 3; /* * RFC 2246: 7.4.3 and 7.4.8: * Signature signature * * RSA: * digitally-signed struct { * opaque md5_hash[16]; * opaque sha_hash[20]; * }; * * DSA: * digitally-signed struct { * opaque sha_hash[20]; * }; * * The hash values are calculated over all handshake messages sent or * received starting at ClientHello up to, but not including, this * CertificateVerify message, including the type and length fields of * the handshake messages. */ hpos = hash; #ifdef CONFIG_TLSV12 if (conn->rl.tls_version == TLS_VERSION_1_2) { hlen = SHA256_MAC_LEN; if (conn->verify.sha256_cert == NULL || crypto_hash_finish(conn->verify.sha256_cert, hpos, &hlen) < 0) { conn->verify.sha256_cert = NULL; tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } conn->verify.sha256_cert = NULL; /* * RFC 3447, A.2.4 RSASSA-PKCS1-v1_5 * * DigestInfo ::= SEQUENCE { * digestAlgorithm DigestAlgorithm, * digest OCTET STRING * } * * SHA-256 OID: sha256WithRSAEncryption ::= {pkcs-1 11} * * DER encoded DigestInfo for SHA256 per RFC 3447: * 30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00 04 20 || * H */ os_memmove(hash + 19, hash, hlen); hlen += 19; os_memcpy(hash, "\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65" "\x03\x04\x02\x01\x05\x00\x04\x20", 19); } else { #endif /* CONFIG_TLSV12 */ if (alg == SIGN_ALG_RSA) { hlen = MD5_MAC_LEN; if (conn->verify.md5_cert == NULL || crypto_hash_finish(conn->verify.md5_cert, hpos, &hlen) < 0) { tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); conn->verify.md5_cert = NULL; crypto_hash_finish(conn->verify.sha1_cert, NULL, NULL); conn->verify.sha1_cert = NULL; return -1; } hpos += MD5_MAC_LEN; } else crypto_hash_finish(conn->verify.md5_cert, NULL, NULL); conn->verify.md5_cert = NULL; hlen = SHA1_MAC_LEN; if (conn->verify.sha1_cert == NULL || crypto_hash_finish(conn->verify.sha1_cert, hpos, &hlen) < 0) { conn->verify.sha1_cert = NULL; tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } conn->verify.sha1_cert = NULL; if (alg == SIGN_ALG_RSA) hlen += MD5_MAC_LEN; #ifdef CONFIG_TLSV12 } #endif /* CONFIG_TLSV12 */ wpa_hexdump(MSG_MSGDUMP, "TLSv1: CertificateVerify hash", hash, hlen); #ifdef CONFIG_TLSV12 if (conn->rl.tls_version >= TLS_VERSION_1_2) { /* * RFC 5246, 4.7: * TLS v1.2 adds explicit indication of the used signature and * hash algorithms. * * struct { * HashAlgorithm hash; * SignatureAlgorithm signature; * } SignatureAndHashAlgorithm; */ *pos++ = TLS_HASH_ALG_SHA256; *pos++ = TLS_SIGN_ALG_RSA; } #endif /* CONFIG_TLSV12 */ /* * RFC 2246, 4.7: * In digital signing, one-way hash functions are used as input for a * signing algorithm. A digitally-signed element is encoded as an * opaque vector <0..2^16-1>, where the length is specified by the * signing algorithm and key. * * In RSA signing, a 36-byte structure of two hashes (one SHA and one * MD5) is signed (encrypted with the private key). It is encoded with * PKCS #1 block type 0 or type 1 as described in [PKCS1]. */ signed_start = pos; /* length to be filled */ pos += 2; clen = end - pos; if (conn->cred == NULL || crypto_private_key_sign_pkcs1(conn->cred->key, hash, hlen, pos, &clen) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to sign hash (PKCS #1)"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } WPA_PUT_BE16(signed_start, clen); pos += clen; WPA_PUT_BE24(hs_length, pos - hs_length - 3); if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, rhdr, end - rhdr, hs_start, pos - hs_start, &rlen) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } pos = rhdr + rlen; tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start); *msgpos = pos; return 0; } static int tls_write_client_change_cipher_spec(struct tlsv1_client *conn, u8 **msgpos, u8 *end) { size_t rlen; u8 payload[1]; wpa_printf(MSG_DEBUG, "TLSv1: Send ChangeCipherSpec"); payload[0] = TLS_CHANGE_CIPHER_SPEC; if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC, *msgpos, end - *msgpos, payload, sizeof(payload), &rlen) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } if (tlsv1_record_change_write_cipher(&conn->rl) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to set write cipher for " "record layer"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } *msgpos += rlen; return 0; } static int tls_write_client_finished(struct tlsv1_client *conn, u8 **msgpos, u8 *end) { u8 *pos, *hs_start; size_t rlen, hlen; u8 verify_data[1 + 3 + TLS_VERIFY_DATA_LEN]; u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN]; wpa_printf(MSG_DEBUG, "TLSv1: Send Finished"); /* Encrypted Handshake Message: Finished */ #ifdef CONFIG_TLSV12 if (conn->rl.tls_version >= TLS_VERSION_1_2) { hlen = SHA256_MAC_LEN; if (conn->verify.sha256_client == NULL || crypto_hash_finish(conn->verify.sha256_client, hash, &hlen) < 0) { tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); conn->verify.sha256_client = NULL; return -1; } conn->verify.sha256_client = NULL; } else { #endif /* CONFIG_TLSV12 */ hlen = MD5_MAC_LEN; if (conn->verify.md5_client == NULL || crypto_hash_finish(conn->verify.md5_client, hash, &hlen) < 0) { tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); conn->verify.md5_client = NULL; crypto_hash_finish(conn->verify.sha1_client, NULL, NULL); conn->verify.sha1_client = NULL; return -1; } conn->verify.md5_client = NULL; hlen = SHA1_MAC_LEN; if (conn->verify.sha1_client == NULL || crypto_hash_finish(conn->verify.sha1_client, hash + MD5_MAC_LEN, &hlen) < 0) { conn->verify.sha1_client = NULL; tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } conn->verify.sha1_client = NULL; hlen = MD5_MAC_LEN + SHA1_MAC_LEN; #ifdef CONFIG_TLSV12 } #endif /* CONFIG_TLSV12 */ if (tls_prf(conn->rl.tls_version, conn->master_secret, TLS_MASTER_SECRET_LEN, "client finished", hash, hlen, verify_data + 1 + 3, TLS_VERIFY_DATA_LEN)) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate verify_data"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } wpa_hexdump_key(MSG_DEBUG, "TLSv1: verify_data (client)", verify_data + 1 + 3, TLS_VERIFY_DATA_LEN); /* Handshake */ pos = hs_start = verify_data; /* HandshakeType msg_type */ *pos++ = TLS_HANDSHAKE_TYPE_FINISHED; /* uint24 length */ WPA_PUT_BE24(pos, TLS_VERIFY_DATA_LEN); pos += 3; pos += TLS_VERIFY_DATA_LEN; /* verify_data already in place */ tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start); if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, *msgpos, end - *msgpos, hs_start, pos - hs_start, &rlen) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } *msgpos += rlen; return 0; } static u8 * tls_send_client_key_exchange(struct tlsv1_client *conn, size_t *out_len) { u8 *msg, *end, *pos; size_t msglen; *out_len = 0; msglen = 2000; if (conn->certificate_requested) msglen += tls_client_cert_chain_der_len(conn); msg = os_malloc(msglen); if (msg == NULL) return NULL; pos = msg; end = msg + msglen; if (conn->certificate_requested) { if (tls_write_client_certificate(conn, &pos, end) < 0) { os_free(msg); return NULL; } } if (tls_write_client_key_exchange(conn, &pos, end) < 0 || (conn->certificate_requested && conn->cred && conn->cred->key && tls_write_client_certificate_verify(conn, &pos, end) < 0) || tls_write_client_change_cipher_spec(conn, &pos, end) < 0 || tls_write_client_finished(conn, &pos, end) < 0) { os_free(msg); return NULL; } *out_len = pos - msg; conn->state = SERVER_CHANGE_CIPHER_SPEC; return msg; } static u8 * tls_send_change_cipher_spec(struct tlsv1_client *conn, size_t *out_len) { u8 *msg, *end, *pos; *out_len = 0; msg = os_malloc(1000); if (msg == NULL) return NULL; pos = msg; end = msg + 1000; if (tls_write_client_change_cipher_spec(conn, &pos, end) < 0 || tls_write_client_finished(conn, &pos, end) < 0) { os_free(msg); return NULL; } *out_len = pos - msg; wpa_printf(MSG_DEBUG, "TLSv1: Session resumption completed " "successfully"); conn->state = ESTABLISHED; return msg; } u8 * tlsv1_client_handshake_write(struct tlsv1_client *conn, size_t *out_len, int no_appl_data) { switch (conn->state) { case CLIENT_KEY_EXCHANGE: return tls_send_client_key_exchange(conn, out_len); case CHANGE_CIPHER_SPEC: return tls_send_change_cipher_spec(conn, out_len); case ACK_FINISHED: wpa_printf(MSG_DEBUG, "TLSv1: Handshake completed " "successfully"); conn->state = ESTABLISHED; *out_len = 0; if (no_appl_data) { /* Need to return something to get final TLS ACK. */ return os_malloc(1); } return NULL; default: wpa_printf(MSG_DEBUG, "TLSv1: Unexpected state %d while " "generating reply", conn->state); return NULL; } } u8 * tlsv1_client_send_alert(struct tlsv1_client *conn, u8 level, u8 description, size_t *out_len) { u8 *alert, *pos, *length; wpa_printf(MSG_DEBUG, "TLSv1: Send Alert(%d:%d)", level, description); *out_len = 0; alert = os_malloc(10); if (alert == NULL) return NULL; pos = alert; /* TLSPlaintext */ /* ContentType type */ *pos++ = TLS_CONTENT_TYPE_ALERT; /* ProtocolVersion version */ WPA_PUT_BE16(pos, conn->rl.tls_version ? conn->rl.tls_version : TLS_VERSION); pos += 2; /* uint16 length (to be filled) */ length = pos; pos += 2; /* opaque fragment[TLSPlaintext.length] */ /* Alert */ /* AlertLevel level */ *pos++ = level; /* AlertDescription description */ *pos++ = description; WPA_PUT_BE16(length, pos - length - 2); *out_len = pos - alert; return alert; } wpa-2.1/src/tls/tlsv1_common.c000066400000000000000000000226141227414666700163450ustar00rootroot00000000000000/* * TLSv1 common routines * Copyright (c) 2006-2011, 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/sha1.h" #include "crypto/sha256.h" #include "x509v3.h" #include "tlsv1_common.h" /* * TODO: * RFC 2246 Section 9: Mandatory to implement TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA * Add support for commonly used cipher suites; don't bother with exportable * suites. */ static const struct tls_cipher_suite tls_cipher_suites[] = { { TLS_NULL_WITH_NULL_NULL, TLS_KEY_X_NULL, TLS_CIPHER_NULL, TLS_HASH_NULL }, { TLS_RSA_WITH_RC4_128_MD5, TLS_KEY_X_RSA, TLS_CIPHER_RC4_128, TLS_HASH_MD5 }, { TLS_RSA_WITH_RC4_128_SHA, TLS_KEY_X_RSA, TLS_CIPHER_RC4_128, TLS_HASH_SHA }, { TLS_RSA_WITH_DES_CBC_SHA, TLS_KEY_X_RSA, TLS_CIPHER_DES_CBC, TLS_HASH_SHA }, { TLS_RSA_WITH_3DES_EDE_CBC_SHA, TLS_KEY_X_RSA, TLS_CIPHER_3DES_EDE_CBC, TLS_HASH_SHA }, { TLS_DH_anon_WITH_RC4_128_MD5, TLS_KEY_X_DH_anon, TLS_CIPHER_RC4_128, TLS_HASH_MD5 }, { TLS_DH_anon_WITH_DES_CBC_SHA, TLS_KEY_X_DH_anon, TLS_CIPHER_DES_CBC, TLS_HASH_SHA }, { TLS_DH_anon_WITH_3DES_EDE_CBC_SHA, TLS_KEY_X_DH_anon, TLS_CIPHER_3DES_EDE_CBC, TLS_HASH_SHA }, { TLS_RSA_WITH_AES_128_CBC_SHA, TLS_KEY_X_RSA, TLS_CIPHER_AES_128_CBC, TLS_HASH_SHA }, { TLS_DH_anon_WITH_AES_128_CBC_SHA, TLS_KEY_X_DH_anon, TLS_CIPHER_AES_128_CBC, TLS_HASH_SHA }, { TLS_RSA_WITH_AES_256_CBC_SHA, TLS_KEY_X_RSA, TLS_CIPHER_AES_256_CBC, TLS_HASH_SHA }, { TLS_DH_anon_WITH_AES_256_CBC_SHA, TLS_KEY_X_DH_anon, TLS_CIPHER_AES_256_CBC, TLS_HASH_SHA }, { TLS_RSA_WITH_AES_128_CBC_SHA256, TLS_KEY_X_RSA, TLS_CIPHER_AES_128_CBC, TLS_HASH_SHA256 }, { TLS_RSA_WITH_AES_256_CBC_SHA256, TLS_KEY_X_RSA, TLS_CIPHER_AES_256_CBC, TLS_HASH_SHA256 }, { TLS_DH_anon_WITH_AES_128_CBC_SHA256, TLS_KEY_X_DH_anon, TLS_CIPHER_AES_128_CBC, TLS_HASH_SHA256 }, { TLS_DH_anon_WITH_AES_256_CBC_SHA256, TLS_KEY_X_DH_anon, TLS_CIPHER_AES_256_CBC, TLS_HASH_SHA256 } }; #define NUM_TLS_CIPHER_SUITES ARRAY_SIZE(tls_cipher_suites) static const struct tls_cipher_data tls_ciphers[] = { { TLS_CIPHER_NULL, TLS_CIPHER_STREAM, 0, 0, 0, CRYPTO_CIPHER_NULL }, { TLS_CIPHER_IDEA_CBC, TLS_CIPHER_BLOCK, 16, 16, 8, CRYPTO_CIPHER_NULL }, { TLS_CIPHER_RC2_CBC_40, TLS_CIPHER_BLOCK, 5, 16, 0, CRYPTO_CIPHER_ALG_RC2 }, { TLS_CIPHER_RC4_40, TLS_CIPHER_STREAM, 5, 16, 0, CRYPTO_CIPHER_ALG_RC4 }, { TLS_CIPHER_RC4_128, TLS_CIPHER_STREAM, 16, 16, 0, CRYPTO_CIPHER_ALG_RC4 }, { TLS_CIPHER_DES40_CBC, TLS_CIPHER_BLOCK, 5, 8, 8, CRYPTO_CIPHER_ALG_DES }, { TLS_CIPHER_DES_CBC, TLS_CIPHER_BLOCK, 8, 8, 8, CRYPTO_CIPHER_ALG_DES }, { TLS_CIPHER_3DES_EDE_CBC, TLS_CIPHER_BLOCK, 24, 24, 8, CRYPTO_CIPHER_ALG_3DES }, { TLS_CIPHER_AES_128_CBC, TLS_CIPHER_BLOCK, 16, 16, 16, CRYPTO_CIPHER_ALG_AES }, { TLS_CIPHER_AES_256_CBC, TLS_CIPHER_BLOCK, 32, 32, 16, CRYPTO_CIPHER_ALG_AES } }; #define NUM_TLS_CIPHER_DATA ARRAY_SIZE(tls_ciphers) /** * tls_get_cipher_suite - Get TLS cipher suite * @suite: Cipher suite identifier * Returns: Pointer to the cipher data or %NULL if not found */ const struct tls_cipher_suite * tls_get_cipher_suite(u16 suite) { size_t i; for (i = 0; i < NUM_TLS_CIPHER_SUITES; i++) if (tls_cipher_suites[i].suite == suite) return &tls_cipher_suites[i]; return NULL; } const struct tls_cipher_data * tls_get_cipher_data(tls_cipher cipher) { size_t i; for (i = 0; i < NUM_TLS_CIPHER_DATA; i++) if (tls_ciphers[i].cipher == cipher) return &tls_ciphers[i]; return NULL; } int tls_server_key_exchange_allowed(tls_cipher cipher) { const struct tls_cipher_suite *suite; /* RFC 2246, Section 7.4.3 */ suite = tls_get_cipher_suite(cipher); if (suite == NULL) return 0; switch (suite->key_exchange) { case TLS_KEY_X_DHE_DSS: case TLS_KEY_X_DHE_DSS_EXPORT: case TLS_KEY_X_DHE_RSA: case TLS_KEY_X_DHE_RSA_EXPORT: case TLS_KEY_X_DH_anon_EXPORT: case TLS_KEY_X_DH_anon: return 1; case TLS_KEY_X_RSA_EXPORT: return 1 /* FIX: public key len > 512 bits */; default: return 0; } } /** * tls_parse_cert - Parse DER encoded X.509 certificate and get public key * @buf: ASN.1 DER encoded certificate * @len: Length of the buffer * @pk: Buffer for returning the allocated public key * Returns: 0 on success, -1 on failure * * This functions parses an ASN.1 DER encoded X.509 certificate and retrieves * the public key from it. The caller is responsible for freeing the public key * by calling crypto_public_key_free(). */ int tls_parse_cert(const u8 *buf, size_t len, struct crypto_public_key **pk) { struct x509_certificate *cert; wpa_hexdump(MSG_MSGDUMP, "TLSv1: Parse ASN.1 DER certificate", buf, len); *pk = crypto_public_key_from_cert(buf, len); if (*pk) return 0; cert = x509_certificate_parse(buf, len); if (cert == NULL) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse X.509 " "certificate"); return -1; } /* TODO * verify key usage (must allow encryption) * * All certificate profiles, key and cryptographic formats are * defined by the IETF PKIX working group [PKIX]. When a key * usage extension is present, the digitalSignature bit must be * set for the key to be eligible for signing, as described * above, and the keyEncipherment bit must be present to allow * encryption, as described above. The keyAgreement bit must be * set on Diffie-Hellman certificates. (PKIX: RFC 3280) */ *pk = crypto_public_key_import(cert->public_key, cert->public_key_len); x509_certificate_free(cert); if (*pk == NULL) { wpa_printf(MSG_ERROR, "TLSv1: Failed to import " "server public key"); return -1; } return 0; } int tls_verify_hash_init(struct tls_verify_hash *verify) { tls_verify_hash_free(verify); verify->md5_client = crypto_hash_init(CRYPTO_HASH_ALG_MD5, NULL, 0); verify->md5_server = crypto_hash_init(CRYPTO_HASH_ALG_MD5, NULL, 0); verify->md5_cert = crypto_hash_init(CRYPTO_HASH_ALG_MD5, NULL, 0); verify->sha1_client = crypto_hash_init(CRYPTO_HASH_ALG_SHA1, NULL, 0); verify->sha1_server = crypto_hash_init(CRYPTO_HASH_ALG_SHA1, NULL, 0); verify->sha1_cert = crypto_hash_init(CRYPTO_HASH_ALG_SHA1, NULL, 0); if (verify->md5_client == NULL || verify->md5_server == NULL || verify->md5_cert == NULL || verify->sha1_client == NULL || verify->sha1_server == NULL || verify->sha1_cert == NULL) { tls_verify_hash_free(verify); return -1; } #ifdef CONFIG_TLSV12 verify->sha256_client = crypto_hash_init(CRYPTO_HASH_ALG_SHA256, NULL, 0); verify->sha256_server = crypto_hash_init(CRYPTO_HASH_ALG_SHA256, NULL, 0); verify->sha256_cert = crypto_hash_init(CRYPTO_HASH_ALG_SHA256, NULL, 0); if (verify->sha256_client == NULL || verify->sha256_server == NULL || verify->sha256_cert == NULL) { tls_verify_hash_free(verify); return -1; } #endif /* CONFIG_TLSV12 */ return 0; } void tls_verify_hash_add(struct tls_verify_hash *verify, const u8 *buf, size_t len) { if (verify->md5_client && verify->sha1_client) { crypto_hash_update(verify->md5_client, buf, len); crypto_hash_update(verify->sha1_client, buf, len); } if (verify->md5_server && verify->sha1_server) { crypto_hash_update(verify->md5_server, buf, len); crypto_hash_update(verify->sha1_server, buf, len); } if (verify->md5_cert && verify->sha1_cert) { crypto_hash_update(verify->md5_cert, buf, len); crypto_hash_update(verify->sha1_cert, buf, len); } #ifdef CONFIG_TLSV12 if (verify->sha256_client) crypto_hash_update(verify->sha256_client, buf, len); if (verify->sha256_server) crypto_hash_update(verify->sha256_server, buf, len); if (verify->sha256_cert) crypto_hash_update(verify->sha256_cert, buf, len); #endif /* CONFIG_TLSV12 */ } void tls_verify_hash_free(struct tls_verify_hash *verify) { crypto_hash_finish(verify->md5_client, NULL, NULL); crypto_hash_finish(verify->md5_server, NULL, NULL); crypto_hash_finish(verify->md5_cert, NULL, NULL); crypto_hash_finish(verify->sha1_client, NULL, NULL); crypto_hash_finish(verify->sha1_server, NULL, NULL); crypto_hash_finish(verify->sha1_cert, NULL, NULL); verify->md5_client = NULL; verify->md5_server = NULL; verify->md5_cert = NULL; verify->sha1_client = NULL; verify->sha1_server = NULL; verify->sha1_cert = NULL; #ifdef CONFIG_TLSV12 crypto_hash_finish(verify->sha256_client, NULL, NULL); crypto_hash_finish(verify->sha256_server, NULL, NULL); crypto_hash_finish(verify->sha256_cert, NULL, NULL); verify->sha256_client = NULL; verify->sha256_server = NULL; verify->sha256_cert = NULL; #endif /* CONFIG_TLSV12 */ } int tls_version_ok(u16 ver) { if (ver == TLS_VERSION_1) return 1; #ifdef CONFIG_TLSV11 if (ver == TLS_VERSION_1_1) return 1; #endif /* CONFIG_TLSV11 */ #ifdef CONFIG_TLSV12 if (ver == TLS_VERSION_1_2) return 1; #endif /* CONFIG_TLSV12 */ return 0; } const char * tls_version_str(u16 ver) { switch (ver) { case TLS_VERSION_1: return "1.0"; case TLS_VERSION_1_1: return "1.1"; case TLS_VERSION_1_2: return "1.2"; } return "?"; } int tls_prf(u16 ver, const u8 *secret, size_t secret_len, const char *label, const u8 *seed, size_t seed_len, u8 *out, size_t outlen) { #ifdef CONFIG_TLSV12 if (ver >= TLS_VERSION_1_2) { tls_prf_sha256(secret, secret_len, label, seed, seed_len, out, outlen); return 0; } #endif /* CONFIG_TLSV12 */ return tls_prf_sha1_md5(secret, secret_len, label, seed, seed_len, out, outlen); } wpa-2.1/src/tls/tlsv1_common.h000066400000000000000000000216141227414666700163510ustar00rootroot00000000000000/* * TLSv1 common definitions * Copyright (c) 2006-2011, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef TLSV1_COMMON_H #define TLSV1_COMMON_H #include "crypto/crypto.h" #define TLS_VERSION_1 0x0301 /* TLSv1 */ #define TLS_VERSION_1_1 0x0302 /* TLSv1.1 */ #define TLS_VERSION_1_2 0x0303 /* TLSv1.2 */ #ifdef CONFIG_TLSV12 #define TLS_VERSION TLS_VERSION_1_2 #else /* CONFIG_TLSV12 */ #ifdef CONFIG_TLSV11 #define TLS_VERSION TLS_VERSION_1_1 #else /* CONFIG_TLSV11 */ #define TLS_VERSION TLS_VERSION_1 #endif /* CONFIG_TLSV11 */ #endif /* CONFIG_TLSV12 */ #define TLS_RANDOM_LEN 32 #define TLS_PRE_MASTER_SECRET_LEN 48 #define TLS_MASTER_SECRET_LEN 48 #define TLS_SESSION_ID_MAX_LEN 32 #define TLS_VERIFY_DATA_LEN 12 /* HandshakeType */ enum { TLS_HANDSHAKE_TYPE_HELLO_REQUEST = 0, TLS_HANDSHAKE_TYPE_CLIENT_HELLO = 1, TLS_HANDSHAKE_TYPE_SERVER_HELLO = 2, TLS_HANDSHAKE_TYPE_NEW_SESSION_TICKET = 4 /* RFC 4507 */, TLS_HANDSHAKE_TYPE_CERTIFICATE = 11, TLS_HANDSHAKE_TYPE_SERVER_KEY_EXCHANGE = 12, TLS_HANDSHAKE_TYPE_CERTIFICATE_REQUEST = 13, TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE = 14, TLS_HANDSHAKE_TYPE_CERTIFICATE_VERIFY = 15, TLS_HANDSHAKE_TYPE_CLIENT_KEY_EXCHANGE = 16, TLS_HANDSHAKE_TYPE_FINISHED = 20, TLS_HANDSHAKE_TYPE_CERTIFICATE_URL = 21 /* RFC 4366 */, TLS_HANDSHAKE_TYPE_CERTIFICATE_STATUS = 22 /* RFC 4366 */ }; /* CipherSuite */ #define TLS_NULL_WITH_NULL_NULL 0x0000 /* RFC 2246 */ #define TLS_RSA_WITH_NULL_MD5 0x0001 /* RFC 2246 */ #define TLS_RSA_WITH_NULL_SHA 0x0002 /* RFC 2246 */ #define TLS_RSA_EXPORT_WITH_RC4_40_MD5 0x0003 /* RFC 2246 */ #define TLS_RSA_WITH_RC4_128_MD5 0x0004 /* RFC 2246 */ #define TLS_RSA_WITH_RC4_128_SHA 0x0005 /* RFC 2246 */ #define TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5 0x0006 /* RFC 2246 */ #define TLS_RSA_WITH_IDEA_CBC_SHA 0x0007 /* RFC 2246 */ #define TLS_RSA_EXPORT_WITH_DES40_CBC_SHA 0x0008 /* RFC 2246 */ #define TLS_RSA_WITH_DES_CBC_SHA 0x0009 /* RFC 2246 */ #define TLS_RSA_WITH_3DES_EDE_CBC_SHA 0x000A /* RFC 2246 */ #define TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA 0x000B /* RFC 2246 */ #define TLS_DH_DSS_WITH_DES_CBC_SHA 0x000C /* RFC 2246 */ #define TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA 0x000D /* RFC 2246 */ #define TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA 0x000E /* RFC 2246 */ #define TLS_DH_RSA_WITH_DES_CBC_SHA 0x000F /* RFC 2246 */ #define TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA 0x0010 /* RFC 2246 */ #define TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA 0x0011 /* RFC 2246 */ #define TLS_DHE_DSS_WITH_DES_CBC_SHA 0x0012 /* RFC 2246 */ #define TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA 0x0013 /* RFC 2246 */ #define TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA 0x0014 /* RFC 2246 */ #define TLS_DHE_RSA_WITH_DES_CBC_SHA 0x0015 /* RFC 2246 */ #define TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA 0x0016 /* RFC 2246 */ #define TLS_DH_anon_EXPORT_WITH_RC4_40_MD5 0x0017 /* RFC 2246 */ #define TLS_DH_anon_WITH_RC4_128_MD5 0x0018 /* RFC 2246 */ #define TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA 0x0019 /* RFC 2246 */ #define TLS_DH_anon_WITH_DES_CBC_SHA 0x001A /* RFC 2246 */ #define TLS_DH_anon_WITH_3DES_EDE_CBC_SHA 0x001B /* RFC 2246 */ #define TLS_RSA_WITH_AES_128_CBC_SHA 0x002F /* RFC 3268 */ #define TLS_DH_DSS_WITH_AES_128_CBC_SHA 0x0030 /* RFC 3268 */ #define TLS_DH_RSA_WITH_AES_128_CBC_SHA 0x0031 /* RFC 3268 */ #define TLS_DHE_DSS_WITH_AES_128_CBC_SHA 0x0032 /* RFC 3268 */ #define TLS_DHE_RSA_WITH_AES_128_CBC_SHA 0x0033 /* RFC 3268 */ #define TLS_DH_anon_WITH_AES_128_CBC_SHA 0x0034 /* RFC 3268 */ #define TLS_RSA_WITH_AES_256_CBC_SHA 0x0035 /* RFC 3268 */ #define TLS_DH_DSS_WITH_AES_256_CBC_SHA 0x0036 /* RFC 3268 */ #define TLS_DH_RSA_WITH_AES_256_CBC_SHA 0x0037 /* RFC 3268 */ #define TLS_DHE_DSS_WITH_AES_256_CBC_SHA 0x0038 /* RFC 3268 */ #define TLS_DHE_RSA_WITH_AES_256_CBC_SHA 0x0039 /* RFC 3268 */ #define TLS_DH_anon_WITH_AES_256_CBC_SHA 0x003A /* RFC 3268 */ #define TLS_RSA_WITH_NULL_SHA256 0x003B /* RFC 5246 */ #define TLS_RSA_WITH_AES_128_CBC_SHA256 0x003C /* RFC 5246 */ #define TLS_RSA_WITH_AES_256_CBC_SHA256 0x003D /* RFC 5246 */ #define TLS_DH_DSS_WITH_AES_128_CBC_SHA256 0x003E /* RFC 5246 */ #define TLS_DH_RSA_WITH_AES_128_CBC_SHA256 0x003F /* RFC 5246 */ #define TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 0x0040 /* RFC 5246 */ #define TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 0x0067 /* RFC 5246 */ #define TLS_DH_DSS_WITH_AES_256_CBC_SHA256 0x0068 /* RFC 5246 */ #define TLS_DH_RSA_WITH_AES_256_CBC_SHA256 0x0069 /* RFC 5246 */ #define TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 0x006A /* RFC 5246 */ #define TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 0x006B /* RFC 5246 */ #define TLS_DH_anon_WITH_AES_128_CBC_SHA256 0x006C /* RFC 5246 */ #define TLS_DH_anon_WITH_AES_256_CBC_SHA256 0x006D /* RFC 5246 */ /* CompressionMethod */ #define TLS_COMPRESSION_NULL 0 /* HashAlgorithm */ enum { TLS_HASH_ALG_NONE = 0, TLS_HASH_ALG_MD5 = 1, TLS_HASH_ALG_SHA1 = 2, TLS_HASH_ALG_SHA224 = 3, TLS_HASH_ALG_SHA256 = 4, TLS_HASH_ALG_SHA384 = 5, TLS_HASH_ALG_SHA512 = 6 }; /* SignatureAlgorithm */ enum { TLS_SIGN_ALG_ANONYMOUS = 0, TLS_SIGN_ALG_RSA = 1, TLS_SIGN_ALG_DSA = 2, TLS_SIGN_ALG_ECDSA = 3, }; /* AlertLevel */ #define TLS_ALERT_LEVEL_WARNING 1 #define TLS_ALERT_LEVEL_FATAL 2 /* AlertDescription */ #define TLS_ALERT_CLOSE_NOTIFY 0 #define TLS_ALERT_UNEXPECTED_MESSAGE 10 #define TLS_ALERT_BAD_RECORD_MAC 20 #define TLS_ALERT_DECRYPTION_FAILED 21 #define TLS_ALERT_RECORD_OVERFLOW 22 #define TLS_ALERT_DECOMPRESSION_FAILURE 30 #define TLS_ALERT_HANDSHAKE_FAILURE 40 #define TLS_ALERT_BAD_CERTIFICATE 42 #define TLS_ALERT_UNSUPPORTED_CERTIFICATE 43 #define TLS_ALERT_CERTIFICATE_REVOKED 44 #define TLS_ALERT_CERTIFICATE_EXPIRED 45 #define TLS_ALERT_CERTIFICATE_UNKNOWN 46 #define TLS_ALERT_ILLEGAL_PARAMETER 47 #define TLS_ALERT_UNKNOWN_CA 48 #define TLS_ALERT_ACCESS_DENIED 49 #define TLS_ALERT_DECODE_ERROR 50 #define TLS_ALERT_DECRYPT_ERROR 51 #define TLS_ALERT_EXPORT_RESTRICTION 60 #define TLS_ALERT_PROTOCOL_VERSION 70 #define TLS_ALERT_INSUFFICIENT_SECURITY 71 #define TLS_ALERT_INTERNAL_ERROR 80 #define TLS_ALERT_USER_CANCELED 90 #define TLS_ALERT_NO_RENEGOTIATION 100 #define TLS_ALERT_UNSUPPORTED_EXTENSION 110 /* RFC 4366 */ #define TLS_ALERT_CERTIFICATE_UNOBTAINABLE 111 /* RFC 4366 */ #define TLS_ALERT_UNRECOGNIZED_NAME 112 /* RFC 4366 */ #define TLS_ALERT_BAD_CERTIFICATE_STATUS_RESPONSE 113 /* RFC 4366 */ #define TLS_ALERT_BAD_CERTIFICATE_HASH_VALUE 114 /* RFC 4366 */ /* ChangeCipherSpec */ enum { TLS_CHANGE_CIPHER_SPEC = 1 }; /* TLS Extensions */ #define TLS_EXT_SERVER_NAME 0 /* RFC 4366 */ #define TLS_EXT_MAX_FRAGMENT_LENGTH 1 /* RFC 4366 */ #define TLS_EXT_CLIENT_CERTIFICATE_URL 2 /* RFC 4366 */ #define TLS_EXT_TRUSTED_CA_KEYS 3 /* RFC 4366 */ #define TLS_EXT_TRUNCATED_HMAC 4 /* RFC 4366 */ #define TLS_EXT_STATUS_REQUEST 5 /* RFC 4366 */ #define TLS_EXT_SESSION_TICKET 35 /* RFC 4507 */ #define TLS_EXT_PAC_OPAQUE TLS_EXT_SESSION_TICKET /* EAP-FAST terminology */ typedef enum { TLS_KEY_X_NULL, TLS_KEY_X_RSA, TLS_KEY_X_RSA_EXPORT, TLS_KEY_X_DH_DSS_EXPORT, TLS_KEY_X_DH_DSS, TLS_KEY_X_DH_RSA_EXPORT, TLS_KEY_X_DH_RSA, TLS_KEY_X_DHE_DSS_EXPORT, TLS_KEY_X_DHE_DSS, TLS_KEY_X_DHE_RSA_EXPORT, TLS_KEY_X_DHE_RSA, TLS_KEY_X_DH_anon_EXPORT, TLS_KEY_X_DH_anon } tls_key_exchange; typedef enum { TLS_CIPHER_NULL, TLS_CIPHER_RC4_40, TLS_CIPHER_RC4_128, TLS_CIPHER_RC2_CBC_40, TLS_CIPHER_IDEA_CBC, TLS_CIPHER_DES40_CBC, TLS_CIPHER_DES_CBC, TLS_CIPHER_3DES_EDE_CBC, TLS_CIPHER_AES_128_CBC, TLS_CIPHER_AES_256_CBC } tls_cipher; typedef enum { TLS_HASH_NULL, TLS_HASH_MD5, TLS_HASH_SHA, TLS_HASH_SHA256 } tls_hash; struct tls_cipher_suite { u16 suite; tls_key_exchange key_exchange; tls_cipher cipher; tls_hash hash; }; typedef enum { TLS_CIPHER_STREAM, TLS_CIPHER_BLOCK } tls_cipher_type; struct tls_cipher_data { tls_cipher cipher; tls_cipher_type type; size_t key_material; size_t expanded_key_material; size_t block_size; /* also iv_size */ enum crypto_cipher_alg alg; }; struct tls_verify_hash { struct crypto_hash *md5_client; struct crypto_hash *sha1_client; struct crypto_hash *sha256_client; struct crypto_hash *md5_server; struct crypto_hash *sha1_server; struct crypto_hash *sha256_server; struct crypto_hash *md5_cert; struct crypto_hash *sha1_cert; struct crypto_hash *sha256_cert; }; const struct tls_cipher_suite * tls_get_cipher_suite(u16 suite); const struct tls_cipher_data * tls_get_cipher_data(tls_cipher cipher); int tls_server_key_exchange_allowed(tls_cipher cipher); int tls_parse_cert(const u8 *buf, size_t len, struct crypto_public_key **pk); int tls_verify_hash_init(struct tls_verify_hash *verify); void tls_verify_hash_add(struct tls_verify_hash *verify, const u8 *buf, size_t len); void tls_verify_hash_free(struct tls_verify_hash *verify); int tls_version_ok(u16 ver); const char * tls_version_str(u16 ver); int tls_prf(u16 ver, const u8 *secret, size_t secret_len, const char *label, const u8 *seed, size_t seed_len, u8 *out, size_t outlen); #endif /* TLSV1_COMMON_H */ wpa-2.1/src/tls/tlsv1_cred.c000066400000000000000000000301131227414666700157630ustar00rootroot00000000000000/* * TLSv1 credentials * Copyright (c) 2006-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 "base64.h" #include "crypto/crypto.h" #include "x509v3.h" #include "tlsv1_cred.h" struct tlsv1_credentials * tlsv1_cred_alloc(void) { struct tlsv1_credentials *cred; cred = os_zalloc(sizeof(*cred)); return cred; } void tlsv1_cred_free(struct tlsv1_credentials *cred) { if (cred == NULL) return; x509_certificate_chain_free(cred->trusted_certs); x509_certificate_chain_free(cred->cert); crypto_private_key_free(cred->key); os_free(cred->dh_p); os_free(cred->dh_g); os_free(cred); } static int tlsv1_add_cert_der(struct x509_certificate **chain, const u8 *buf, size_t len) { struct x509_certificate *cert, *p; char name[128]; cert = x509_certificate_parse(buf, len); if (cert == NULL) { wpa_printf(MSG_INFO, "TLSv1: %s - failed to parse certificate", __func__); return -1; } p = *chain; while (p && p->next) p = p->next; if (p && x509_name_compare(&cert->subject, &p->issuer) == 0) { /* * The new certificate is the issuer of the last certificate in * the chain - add the new certificate to the end. */ p->next = cert; } else { /* Add to the beginning of the chain */ cert->next = *chain; *chain = cert; } x509_name_string(&cert->subject, name, sizeof(name)); wpa_printf(MSG_DEBUG, "TLSv1: Added certificate: %s", name); return 0; } static const char *pem_cert_begin = "-----BEGIN CERTIFICATE-----"; static const char *pem_cert_end = "-----END CERTIFICATE-----"; static const char *pem_key_begin = "-----BEGIN RSA PRIVATE KEY-----"; static const char *pem_key_end = "-----END RSA PRIVATE KEY-----"; static const char *pem_key2_begin = "-----BEGIN PRIVATE KEY-----"; static const char *pem_key2_end = "-----END PRIVATE KEY-----"; static const char *pem_key_enc_begin = "-----BEGIN ENCRYPTED PRIVATE KEY-----"; static const char *pem_key_enc_end = "-----END ENCRYPTED PRIVATE KEY-----"; static const u8 * search_tag(const char *tag, const u8 *buf, size_t len) { size_t i, plen; plen = os_strlen(tag); if (len < plen) return NULL; for (i = 0; i < len - plen; i++) { if (os_memcmp(buf + i, tag, plen) == 0) return buf + i; } return NULL; } static int tlsv1_add_cert(struct x509_certificate **chain, const u8 *buf, size_t len) { const u8 *pos, *end; unsigned char *der; size_t der_len; pos = search_tag(pem_cert_begin, buf, len); if (!pos) { wpa_printf(MSG_DEBUG, "TLSv1: No PEM certificate tag found - " "assume DER format"); return tlsv1_add_cert_der(chain, buf, len); } wpa_printf(MSG_DEBUG, "TLSv1: Converting PEM format certificate into " "DER format"); while (pos) { pos += os_strlen(pem_cert_begin); end = search_tag(pem_cert_end, pos, buf + len - pos); if (end == NULL) { wpa_printf(MSG_INFO, "TLSv1: Could not find PEM " "certificate end tag (%s)", pem_cert_end); return -1; } der = base64_decode(pos, end - pos, &der_len); if (der == NULL) { wpa_printf(MSG_INFO, "TLSv1: Could not decode PEM " "certificate"); return -1; } if (tlsv1_add_cert_der(chain, der, der_len) < 0) { wpa_printf(MSG_INFO, "TLSv1: Failed to parse PEM " "certificate after DER conversion"); os_free(der); return -1; } os_free(der); end += os_strlen(pem_cert_end); pos = search_tag(pem_cert_begin, end, buf + len - end); } return 0; } static int tlsv1_set_cert_chain(struct x509_certificate **chain, const char *cert, const u8 *cert_blob, size_t cert_blob_len) { if (cert_blob) return tlsv1_add_cert(chain, cert_blob, cert_blob_len); if (cert) { u8 *buf; size_t len; int ret; buf = (u8 *) os_readfile(cert, &len); if (buf == NULL) { wpa_printf(MSG_INFO, "TLSv1: Failed to read '%s'", cert); return -1; } ret = tlsv1_add_cert(chain, buf, len); os_free(buf); return ret; } return 0; } /** * tlsv1_set_ca_cert - Set trusted CA certificate(s) * @cred: TLSv1 credentials from tlsv1_cred_alloc() * @cert: File or reference name for X.509 certificate in PEM or DER format * @cert_blob: cert as inlined data or %NULL if not used * @cert_blob_len: ca_cert_blob length * @path: Path to CA certificates (not yet supported) * Returns: 0 on success, -1 on failure */ int tlsv1_set_ca_cert(struct tlsv1_credentials *cred, const char *cert, const u8 *cert_blob, size_t cert_blob_len, const char *path) { if (tlsv1_set_cert_chain(&cred->trusted_certs, cert, cert_blob, cert_blob_len) < 0) return -1; if (path) { /* TODO: add support for reading number of certificate files */ wpa_printf(MSG_INFO, "TLSv1: Use of CA certificate directory " "not yet supported"); return -1; } return 0; } /** * tlsv1_set_cert - Set certificate * @cred: TLSv1 credentials from tlsv1_cred_alloc() * @cert: File or reference name for X.509 certificate in PEM or DER format * @cert_blob: cert as inlined data or %NULL if not used * @cert_blob_len: cert_blob length * Returns: 0 on success, -1 on failure */ int tlsv1_set_cert(struct tlsv1_credentials *cred, const char *cert, const u8 *cert_blob, size_t cert_blob_len) { return tlsv1_set_cert_chain(&cred->cert, cert, cert_blob, cert_blob_len); } static struct crypto_private_key * tlsv1_set_key_pem(const u8 *key, size_t len) { const u8 *pos, *end; unsigned char *der; size_t der_len; struct crypto_private_key *pkey; pos = search_tag(pem_key_begin, key, len); if (!pos) { pos = search_tag(pem_key2_begin, key, len); if (!pos) return NULL; pos += os_strlen(pem_key2_begin); end = search_tag(pem_key2_end, pos, key + len - pos); if (!end) return NULL; } else { const u8 *pos2; pos += os_strlen(pem_key_begin); end = search_tag(pem_key_end, pos, key + len - pos); if (!end) return NULL; pos2 = search_tag("Proc-Type: 4,ENCRYPTED", pos, end - pos); if (pos2) { wpa_printf(MSG_DEBUG, "TLSv1: Unsupported private key " "format (Proc-Type/DEK-Info)"); return NULL; } } der = base64_decode(pos, end - pos, &der_len); if (!der) return NULL; pkey = crypto_private_key_import(der, der_len, NULL); os_free(der); return pkey; } static struct crypto_private_key * tlsv1_set_key_enc_pem(const u8 *key, size_t len, const char *passwd) { const u8 *pos, *end; unsigned char *der; size_t der_len; struct crypto_private_key *pkey; if (passwd == NULL) return NULL; pos = search_tag(pem_key_enc_begin, key, len); if (!pos) return NULL; pos += os_strlen(pem_key_enc_begin); end = search_tag(pem_key_enc_end, pos, key + len - pos); if (!end) return NULL; der = base64_decode(pos, end - pos, &der_len); if (!der) return NULL; pkey = crypto_private_key_import(der, der_len, passwd); os_free(der); return pkey; } static int tlsv1_set_key(struct tlsv1_credentials *cred, const u8 *key, size_t len, const char *passwd) { cred->key = crypto_private_key_import(key, len, passwd); if (cred->key == NULL) cred->key = tlsv1_set_key_pem(key, len); if (cred->key == NULL) cred->key = tlsv1_set_key_enc_pem(key, len, passwd); if (cred->key == NULL) { wpa_printf(MSG_INFO, "TLSv1: Failed to parse private key"); return -1; } return 0; } /** * tlsv1_set_private_key - Set private key * @cred: TLSv1 credentials from tlsv1_cred_alloc() * @private_key: File or reference name for the key in PEM or DER format * @private_key_passwd: Passphrase for decrypted private key, %NULL if no * passphrase is used. * @private_key_blob: private_key as inlined data or %NULL if not used * @private_key_blob_len: private_key_blob length * Returns: 0 on success, -1 on failure */ int tlsv1_set_private_key(struct tlsv1_credentials *cred, const char *private_key, const char *private_key_passwd, const u8 *private_key_blob, size_t private_key_blob_len) { crypto_private_key_free(cred->key); cred->key = NULL; if (private_key_blob) return tlsv1_set_key(cred, private_key_blob, private_key_blob_len, private_key_passwd); if (private_key) { u8 *buf; size_t len; int ret; buf = (u8 *) os_readfile(private_key, &len); if (buf == NULL) { wpa_printf(MSG_INFO, "TLSv1: Failed to read '%s'", private_key); return -1; } ret = tlsv1_set_key(cred, buf, len, private_key_passwd); os_free(buf); return ret; } return 0; } static int tlsv1_set_dhparams_der(struct tlsv1_credentials *cred, const u8 *dh, size_t len) { struct asn1_hdr hdr; const u8 *pos, *end; pos = dh; end = dh + len; /* * DHParameter ::= SEQUENCE { * prime INTEGER, -- p * base INTEGER, -- g * privateValueLength INTEGER OPTIONAL } */ /* DHParamer ::= SEQUENCE */ if (asn1_get_next(pos, len, &hdr) < 0 || hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_SEQUENCE) { wpa_printf(MSG_DEBUG, "DH: DH parameters did not start with a " "valid SEQUENCE - found class %d tag 0x%x", hdr.class, hdr.tag); return -1; } pos = hdr.payload; /* prime INTEGER */ if (asn1_get_next(pos, end - pos, &hdr) < 0) return -1; if (hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_INTEGER) { wpa_printf(MSG_DEBUG, "DH: No INTEGER tag found for p; " "class=%d tag=0x%x", hdr.class, hdr.tag); return -1; } wpa_hexdump(MSG_MSGDUMP, "DH: prime (p)", hdr.payload, hdr.length); if (hdr.length == 0) return -1; os_free(cred->dh_p); cred->dh_p = os_malloc(hdr.length); if (cred->dh_p == NULL) return -1; os_memcpy(cred->dh_p, hdr.payload, hdr.length); cred->dh_p_len = hdr.length; pos = hdr.payload + hdr.length; /* base INTEGER */ if (asn1_get_next(pos, end - pos, &hdr) < 0) return -1; if (hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_INTEGER) { wpa_printf(MSG_DEBUG, "DH: No INTEGER tag found for g; " "class=%d tag=0x%x", hdr.class, hdr.tag); return -1; } wpa_hexdump(MSG_MSGDUMP, "DH: base (g)", hdr.payload, hdr.length); if (hdr.length == 0) return -1; os_free(cred->dh_g); cred->dh_g = os_malloc(hdr.length); if (cred->dh_g == NULL) return -1; os_memcpy(cred->dh_g, hdr.payload, hdr.length); cred->dh_g_len = hdr.length; return 0; } static const char *pem_dhparams_begin = "-----BEGIN DH PARAMETERS-----"; static const char *pem_dhparams_end = "-----END DH PARAMETERS-----"; static int tlsv1_set_dhparams_blob(struct tlsv1_credentials *cred, const u8 *buf, size_t len) { const u8 *pos, *end; unsigned char *der; size_t der_len; pos = search_tag(pem_dhparams_begin, buf, len); if (!pos) { wpa_printf(MSG_DEBUG, "TLSv1: No PEM dhparams tag found - " "assume DER format"); return tlsv1_set_dhparams_der(cred, buf, len); } wpa_printf(MSG_DEBUG, "TLSv1: Converting PEM format dhparams into DER " "format"); pos += os_strlen(pem_dhparams_begin); end = search_tag(pem_dhparams_end, pos, buf + len - pos); if (end == NULL) { wpa_printf(MSG_INFO, "TLSv1: Could not find PEM dhparams end " "tag (%s)", pem_dhparams_end); return -1; } der = base64_decode(pos, end - pos, &der_len); if (der == NULL) { wpa_printf(MSG_INFO, "TLSv1: Could not decode PEM dhparams"); return -1; } if (tlsv1_set_dhparams_der(cred, der, der_len) < 0) { wpa_printf(MSG_INFO, "TLSv1: Failed to parse PEM dhparams " "DER conversion"); os_free(der); return -1; } os_free(der); return 0; } /** * tlsv1_set_dhparams - Set Diffie-Hellman parameters * @cred: TLSv1 credentials from tlsv1_cred_alloc() * @dh_file: File or reference name for the DH params in PEM or DER format * @dh_blob: DH params as inlined data or %NULL if not used * @dh_blob_len: dh_blob length * Returns: 0 on success, -1 on failure */ int tlsv1_set_dhparams(struct tlsv1_credentials *cred, const char *dh_file, const u8 *dh_blob, size_t dh_blob_len) { if (dh_blob) return tlsv1_set_dhparams_blob(cred, dh_blob, dh_blob_len); if (dh_file) { u8 *buf; size_t len; int ret; buf = (u8 *) os_readfile(dh_file, &len); if (buf == NULL) { wpa_printf(MSG_INFO, "TLSv1: Failed to read '%s'", dh_file); return -1; } ret = tlsv1_set_dhparams_blob(cred, buf, len); os_free(buf); return ret; } return 0; } wpa-2.1/src/tls/tlsv1_cred.h000066400000000000000000000022671227414666700160010ustar00rootroot00000000000000/* * TLSv1 credentials * Copyright (c) 2006-2007, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef TLSV1_CRED_H #define TLSV1_CRED_H struct tlsv1_credentials { struct x509_certificate *trusted_certs; struct x509_certificate *cert; struct crypto_private_key *key; /* Diffie-Hellman parameters */ u8 *dh_p; /* prime */ size_t dh_p_len; u8 *dh_g; /* generator */ size_t dh_g_len; }; struct tlsv1_credentials * tlsv1_cred_alloc(void); void tlsv1_cred_free(struct tlsv1_credentials *cred); int tlsv1_set_ca_cert(struct tlsv1_credentials *cred, const char *cert, const u8 *cert_blob, size_t cert_blob_len, const char *path); int tlsv1_set_cert(struct tlsv1_credentials *cred, const char *cert, const u8 *cert_blob, size_t cert_blob_len); int tlsv1_set_private_key(struct tlsv1_credentials *cred, const char *private_key, const char *private_key_passwd, const u8 *private_key_blob, size_t private_key_blob_len); int tlsv1_set_dhparams(struct tlsv1_credentials *cred, const char *dh_file, const u8 *dh_blob, size_t dh_blob_len); #endif /* TLSV1_CRED_H */ wpa-2.1/src/tls/tlsv1_record.c000066400000000000000000000326401227414666700163330ustar00rootroot00000000000000/* * TLSv1 Record Protocol * Copyright (c) 2006-2011, 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/md5.h" #include "crypto/sha1.h" #include "crypto/sha256.h" #include "tlsv1_common.h" #include "tlsv1_record.h" /** * tlsv1_record_set_cipher_suite - TLS record layer: Set cipher suite * @rl: Pointer to TLS record layer data * @cipher_suite: New cipher suite * Returns: 0 on success, -1 on failure * * This function is used to prepare TLS record layer for cipher suite change. * tlsv1_record_change_write_cipher() and * tlsv1_record_change_read_cipher() functions can then be used to change the * currently used ciphers. */ int tlsv1_record_set_cipher_suite(struct tlsv1_record_layer *rl, u16 cipher_suite) { const struct tls_cipher_suite *suite; const struct tls_cipher_data *data; wpa_printf(MSG_DEBUG, "TLSv1: Selected cipher suite: 0x%04x", cipher_suite); rl->cipher_suite = cipher_suite; suite = tls_get_cipher_suite(cipher_suite); if (suite == NULL) return -1; if (suite->hash == TLS_HASH_MD5) { rl->hash_alg = CRYPTO_HASH_ALG_HMAC_MD5; rl->hash_size = MD5_MAC_LEN; } else if (suite->hash == TLS_HASH_SHA) { rl->hash_alg = CRYPTO_HASH_ALG_HMAC_SHA1; rl->hash_size = SHA1_MAC_LEN; } else if (suite->hash == TLS_HASH_SHA256) { rl->hash_alg = CRYPTO_HASH_ALG_HMAC_SHA256; rl->hash_size = SHA256_MAC_LEN; } data = tls_get_cipher_data(suite->cipher); if (data == NULL) return -1; rl->key_material_len = data->key_material; rl->iv_size = data->block_size; rl->cipher_alg = data->alg; return 0; } /** * tlsv1_record_change_write_cipher - TLS record layer: Change write cipher * @rl: Pointer to TLS record layer data * Returns: 0 on success (cipher changed), -1 on failure * * This function changes TLS record layer to use the new cipher suite * configured with tlsv1_record_set_cipher_suite() for writing. */ int tlsv1_record_change_write_cipher(struct tlsv1_record_layer *rl) { wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - New write cipher suite " "0x%04x", rl->cipher_suite); rl->write_cipher_suite = rl->cipher_suite; os_memset(rl->write_seq_num, 0, TLS_SEQ_NUM_LEN); if (rl->write_cbc) { crypto_cipher_deinit(rl->write_cbc); rl->write_cbc = NULL; } if (rl->cipher_alg != CRYPTO_CIPHER_NULL) { rl->write_cbc = crypto_cipher_init(rl->cipher_alg, rl->write_iv, rl->write_key, rl->key_material_len); if (rl->write_cbc == NULL) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to initialize " "cipher"); return -1; } } return 0; } /** * tlsv1_record_change_read_cipher - TLS record layer: Change read cipher * @rl: Pointer to TLS record layer data * Returns: 0 on success (cipher changed), -1 on failure * * This function changes TLS record layer to use the new cipher suite * configured with tlsv1_record_set_cipher_suite() for reading. */ int tlsv1_record_change_read_cipher(struct tlsv1_record_layer *rl) { wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - New read cipher suite " "0x%04x", rl->cipher_suite); rl->read_cipher_suite = rl->cipher_suite; os_memset(rl->read_seq_num, 0, TLS_SEQ_NUM_LEN); if (rl->read_cbc) { crypto_cipher_deinit(rl->read_cbc); rl->read_cbc = NULL; } if (rl->cipher_alg != CRYPTO_CIPHER_NULL) { rl->read_cbc = crypto_cipher_init(rl->cipher_alg, rl->read_iv, rl->read_key, rl->key_material_len); if (rl->read_cbc == NULL) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to initialize " "cipher"); return -1; } } return 0; } /** * tlsv1_record_send - TLS record layer: Send a message * @rl: Pointer to TLS record layer data * @content_type: Content type (TLS_CONTENT_TYPE_*) * @buf: Buffer for the generated TLS message (needs to have extra space for * header, IV (TLS v1.1), and HMAC) * @buf_size: Maximum buf size * @payload: Payload to be sent * @payload_len: Length of the payload * @out_len: Buffer for returning the used buf length * Returns: 0 on success, -1 on failure * * This function fills in the TLS record layer header, adds HMAC, and encrypts * the data using the current write cipher. */ int tlsv1_record_send(struct tlsv1_record_layer *rl, u8 content_type, u8 *buf, size_t buf_size, const u8 *payload, size_t payload_len, size_t *out_len) { u8 *pos, *ct_start, *length, *cpayload; struct crypto_hash *hmac; size_t clen; int explicit_iv; pos = buf; if (pos + TLS_RECORD_HEADER_LEN > buf + buf_size) return -1; /* ContentType type */ ct_start = pos; *pos++ = content_type; /* ProtocolVersion version */ WPA_PUT_BE16(pos, rl->tls_version); pos += 2; /* uint16 length */ length = pos; WPA_PUT_BE16(length, payload_len); pos += 2; cpayload = pos; explicit_iv = rl->write_cipher_suite != TLS_NULL_WITH_NULL_NULL && rl->iv_size && rl->tls_version >= TLS_VERSION_1_1; if (explicit_iv) { /* opaque IV[Cipherspec.block_length] */ if (pos + rl->iv_size > buf + buf_size) return -1; /* * Use random number R per the RFC 4346, 6.2.3.2 CBC Block * Cipher option 2a. */ if (os_get_random(pos, rl->iv_size)) return -1; pos += rl->iv_size; } /* * opaque fragment[TLSPlaintext.length] * (opaque content[TLSCompressed.length] in GenericBlockCipher) */ if (pos + payload_len > buf + buf_size) return -1; os_memmove(pos, payload, payload_len); pos += payload_len; if (rl->write_cipher_suite != TLS_NULL_WITH_NULL_NULL) { /* * MAC calculated over seq_num + TLSCompressed.type + * TLSCompressed.version + TLSCompressed.length + * TLSCompressed.fragment */ hmac = crypto_hash_init(rl->hash_alg, rl->write_mac_secret, rl->hash_size); if (hmac == NULL) { wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed " "to initialize HMAC"); return -1; } crypto_hash_update(hmac, rl->write_seq_num, TLS_SEQ_NUM_LEN); /* type + version + length + fragment */ crypto_hash_update(hmac, ct_start, TLS_RECORD_HEADER_LEN); crypto_hash_update(hmac, payload, payload_len); clen = buf + buf_size - pos; if (clen < rl->hash_size) { wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Not " "enough room for MAC"); crypto_hash_finish(hmac, NULL, NULL); return -1; } if (crypto_hash_finish(hmac, pos, &clen) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed " "to calculate HMAC"); return -1; } wpa_hexdump(MSG_MSGDUMP, "TLSv1: Record Layer - Write HMAC", pos, clen); pos += clen; if (rl->iv_size) { size_t len = pos - cpayload; size_t pad; pad = (len + 1) % rl->iv_size; if (pad) pad = rl->iv_size - pad; if (pos + pad + 1 > buf + buf_size) { wpa_printf(MSG_DEBUG, "TLSv1: No room for " "block cipher padding"); return -1; } os_memset(pos, pad, pad + 1); pos += pad + 1; } if (crypto_cipher_encrypt(rl->write_cbc, cpayload, cpayload, pos - cpayload) < 0) return -1; } WPA_PUT_BE16(length, pos - length - 2); inc_byte_array(rl->write_seq_num, TLS_SEQ_NUM_LEN); *out_len = pos - buf; return 0; } /** * tlsv1_record_receive - TLS record layer: Process a received message * @rl: Pointer to TLS record layer data * @in_data: Received data * @in_len: Length of the received data * @out_data: Buffer for output data (must be at least as long as in_data) * @out_len: Set to maximum out_data length by caller; used to return the * length of the used data * @alert: Buffer for returning an alert value on failure * Returns: Number of bytes used from in_data on success, 0 if record was not * complete (more data needed), or -1 on failure * * This function decrypts the received message, verifies HMAC and TLS record * layer header. */ int tlsv1_record_receive(struct tlsv1_record_layer *rl, const u8 *in_data, size_t in_len, u8 *out_data, size_t *out_len, u8 *alert) { size_t i, rlen, hlen; u8 padlen; struct crypto_hash *hmac; u8 len[2], hash[100]; int force_mac_error = 0; u8 ct; if (in_len < TLS_RECORD_HEADER_LEN) { wpa_printf(MSG_DEBUG, "TLSv1: Too short record (in_len=%lu) - " "need more data", (unsigned long) in_len); wpa_hexdump(MSG_MSGDUMP, "TLSv1: Record Layer - Received", in_data, in_len); return 0; } ct = in_data[0]; rlen = WPA_GET_BE16(in_data + 3); wpa_printf(MSG_DEBUG, "TLSv1: Received content type %d version %d.%d " "length %d", ct, in_data[1], in_data[2], (int) rlen); /* * TLS v1.0 and v1.1 RFCs were not exactly clear on the use of the * protocol version in record layer. As such, accept any {03,xx} value * to remain compatible with existing implementations. */ if (in_data[1] != 0x03) { wpa_printf(MSG_DEBUG, "TLSv1: Unexpected protocol version " "%u.%u", in_data[1], in_data[2]); *alert = TLS_ALERT_PROTOCOL_VERSION; return -1; } /* TLSCiphertext must not be more than 2^14+2048 bytes */ if (TLS_RECORD_HEADER_LEN + rlen > 18432) { wpa_printf(MSG_DEBUG, "TLSv1: Record overflow (len=%lu)", (unsigned long) (TLS_RECORD_HEADER_LEN + rlen)); *alert = TLS_ALERT_RECORD_OVERFLOW; return -1; } in_data += TLS_RECORD_HEADER_LEN; in_len -= TLS_RECORD_HEADER_LEN; if (rlen > in_len) { wpa_printf(MSG_DEBUG, "TLSv1: Not all record data included " "(rlen=%lu > in_len=%lu)", (unsigned long) rlen, (unsigned long) in_len); return 0; } wpa_hexdump(MSG_MSGDUMP, "TLSv1: Record Layer - Received", in_data, rlen); if (ct != TLS_CONTENT_TYPE_HANDSHAKE && ct != TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC && ct != TLS_CONTENT_TYPE_ALERT && ct != TLS_CONTENT_TYPE_APPLICATION_DATA) { wpa_printf(MSG_DEBUG, "TLSv1: Ignore record with unknown " "content type 0x%x", ct); *alert = TLS_ALERT_UNEXPECTED_MESSAGE; return -1; } in_len = rlen; if (*out_len < in_len) { wpa_printf(MSG_DEBUG, "TLSv1: Not enough output buffer for " "processing received record"); *alert = TLS_ALERT_INTERNAL_ERROR; return -1; } if (rl->read_cipher_suite != TLS_NULL_WITH_NULL_NULL) { size_t plen; if (crypto_cipher_decrypt(rl->read_cbc, in_data, out_data, in_len) < 0) { *alert = TLS_ALERT_DECRYPTION_FAILED; return -1; } plen = in_len; wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Record Layer - Decrypted " "data", out_data, plen); if (rl->iv_size) { /* * TLS v1.0 defines different alert values for various * failures. That may information to aid in attacks, so * use the same bad_record_mac alert regardless of the * issues. * * In addition, instead of returning immediately on * error, run through the MAC check to make timing * attacks more difficult. */ if (rl->tls_version >= TLS_VERSION_1_1) { /* Remove opaque IV[Cipherspec.block_length] */ if (plen < rl->iv_size) { wpa_printf(MSG_DEBUG, "TLSv1.1: Not " "enough room for IV"); force_mac_error = 1; goto check_mac; } os_memmove(out_data, out_data + rl->iv_size, plen - rl->iv_size); plen -= rl->iv_size; } /* Verify and remove padding */ if (plen == 0) { wpa_printf(MSG_DEBUG, "TLSv1: Too short record" " (no pad)"); force_mac_error = 1; goto check_mac; } padlen = out_data[plen - 1]; if (padlen >= plen) { wpa_printf(MSG_DEBUG, "TLSv1: Incorrect pad " "length (%u, plen=%lu) in " "received record", padlen, (unsigned long) plen); force_mac_error = 1; goto check_mac; } for (i = plen - padlen - 1; i < plen - 1; i++) { if (out_data[i] != padlen) { wpa_hexdump(MSG_DEBUG, "TLSv1: Invalid pad in " "received record", out_data + plen - padlen - 1, padlen + 1); force_mac_error = 1; goto check_mac; } } plen -= padlen + 1; wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Record Layer - " "Decrypted data with IV and padding " "removed", out_data, plen); } check_mac: if (plen < rl->hash_size) { wpa_printf(MSG_DEBUG, "TLSv1: Too short record; no " "hash value"); *alert = TLS_ALERT_BAD_RECORD_MAC; return -1; } plen -= rl->hash_size; hmac = crypto_hash_init(rl->hash_alg, rl->read_mac_secret, rl->hash_size); if (hmac == NULL) { wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed " "to initialize HMAC"); *alert = TLS_ALERT_INTERNAL_ERROR; return -1; } crypto_hash_update(hmac, rl->read_seq_num, TLS_SEQ_NUM_LEN); /* type + version + length + fragment */ crypto_hash_update(hmac, in_data - TLS_RECORD_HEADER_LEN, 3); WPA_PUT_BE16(len, plen); crypto_hash_update(hmac, len, 2); crypto_hash_update(hmac, out_data, plen); hlen = sizeof(hash); if (crypto_hash_finish(hmac, hash, &hlen) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed " "to calculate HMAC"); *alert = TLS_ALERT_INTERNAL_ERROR; return -1; } if (hlen != rl->hash_size || os_memcmp(hash, out_data + plen, hlen) != 0 || force_mac_error) { wpa_printf(MSG_DEBUG, "TLSv1: Invalid HMAC value in " "received message (force_mac_error=%d)", force_mac_error); *alert = TLS_ALERT_BAD_RECORD_MAC; return -1; } *out_len = plen; } else { os_memcpy(out_data, in_data, in_len); *out_len = in_len; } /* TLSCompressed must not be more than 2^14+1024 bytes */ if (TLS_RECORD_HEADER_LEN + *out_len > 17408) { wpa_printf(MSG_DEBUG, "TLSv1: Record overflow (len=%lu)", (unsigned long) (TLS_RECORD_HEADER_LEN + *out_len)); *alert = TLS_ALERT_RECORD_OVERFLOW; return -1; } inc_byte_array(rl->read_seq_num, TLS_SEQ_NUM_LEN); return TLS_RECORD_HEADER_LEN + rlen; } wpa-2.1/src/tls/tlsv1_record.h000066400000000000000000000036221227414666700163360ustar00rootroot00000000000000/* * TLSv1 Record Protocol * Copyright (c) 2006-2011, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef TLSV1_RECORD_H #define TLSV1_RECORD_H #include "crypto/crypto.h" #define TLS_MAX_WRITE_MAC_SECRET_LEN 32 #define TLS_MAX_WRITE_KEY_LEN 32 #define TLS_MAX_IV_LEN 16 #define TLS_MAX_KEY_BLOCK_LEN (2 * (TLS_MAX_WRITE_MAC_SECRET_LEN + \ TLS_MAX_WRITE_KEY_LEN + TLS_MAX_IV_LEN)) #define TLS_SEQ_NUM_LEN 8 #define TLS_RECORD_HEADER_LEN 5 /* ContentType */ enum { TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC = 20, TLS_CONTENT_TYPE_ALERT = 21, TLS_CONTENT_TYPE_HANDSHAKE = 22, TLS_CONTENT_TYPE_APPLICATION_DATA = 23 }; struct tlsv1_record_layer { u16 tls_version; u8 write_mac_secret[TLS_MAX_WRITE_MAC_SECRET_LEN]; u8 read_mac_secret[TLS_MAX_WRITE_MAC_SECRET_LEN]; u8 write_key[TLS_MAX_WRITE_KEY_LEN]; u8 read_key[TLS_MAX_WRITE_KEY_LEN]; u8 write_iv[TLS_MAX_IV_LEN]; u8 read_iv[TLS_MAX_IV_LEN]; size_t hash_size; size_t key_material_len; size_t iv_size; /* also block_size */ enum crypto_hash_alg hash_alg; enum crypto_cipher_alg cipher_alg; u8 write_seq_num[TLS_SEQ_NUM_LEN]; u8 read_seq_num[TLS_SEQ_NUM_LEN]; u16 cipher_suite; u16 write_cipher_suite; u16 read_cipher_suite; struct crypto_cipher *write_cbc; struct crypto_cipher *read_cbc; }; int tlsv1_record_set_cipher_suite(struct tlsv1_record_layer *rl, u16 cipher_suite); int tlsv1_record_change_write_cipher(struct tlsv1_record_layer *rl); int tlsv1_record_change_read_cipher(struct tlsv1_record_layer *rl); int tlsv1_record_send(struct tlsv1_record_layer *rl, u8 content_type, u8 *buf, size_t buf_size, const u8 *payload, size_t payload_len, size_t *out_len); int tlsv1_record_receive(struct tlsv1_record_layer *rl, const u8 *in_data, size_t in_len, u8 *out_data, size_t *out_len, u8 *alert); #endif /* TLSV1_RECORD_H */ wpa-2.1/src/tls/tlsv1_server.c000066400000000000000000000403061227414666700163610ustar00rootroot00000000000000/* * TLS v1.0/v1.1/v1.2 server (RFC 2246, RFC 4346, RFC 5246) * Copyright (c) 2006-2011, 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/sha1.h" #include "crypto/tls.h" #include "tlsv1_common.h" #include "tlsv1_record.h" #include "tlsv1_server.h" #include "tlsv1_server_i.h" /* TODO: * Support for a message fragmented across several records (RFC 2246, 6.2.1) */ void tlsv1_server_alert(struct tlsv1_server *conn, u8 level, u8 description) { conn->alert_level = level; conn->alert_description = description; } int tlsv1_server_derive_keys(struct tlsv1_server *conn, const u8 *pre_master_secret, size_t pre_master_secret_len) { u8 seed[2 * TLS_RANDOM_LEN]; u8 key_block[TLS_MAX_KEY_BLOCK_LEN]; u8 *pos; size_t key_block_len; if (pre_master_secret) { wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: pre_master_secret", pre_master_secret, pre_master_secret_len); os_memcpy(seed, conn->client_random, TLS_RANDOM_LEN); os_memcpy(seed + TLS_RANDOM_LEN, conn->server_random, TLS_RANDOM_LEN); if (tls_prf(conn->rl.tls_version, pre_master_secret, pre_master_secret_len, "master secret", seed, 2 * TLS_RANDOM_LEN, conn->master_secret, TLS_MASTER_SECRET_LEN)) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive " "master_secret"); return -1; } wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: master_secret", conn->master_secret, TLS_MASTER_SECRET_LEN); } os_memcpy(seed, conn->server_random, TLS_RANDOM_LEN); os_memcpy(seed + TLS_RANDOM_LEN, conn->client_random, TLS_RANDOM_LEN); key_block_len = 2 * (conn->rl.hash_size + conn->rl.key_material_len + conn->rl.iv_size); if (tls_prf(conn->rl.tls_version, conn->master_secret, TLS_MASTER_SECRET_LEN, "key expansion", seed, 2 * TLS_RANDOM_LEN, key_block, key_block_len)) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive key_block"); return -1; } wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: key_block", key_block, key_block_len); pos = key_block; /* client_write_MAC_secret */ os_memcpy(conn->rl.read_mac_secret, pos, conn->rl.hash_size); pos += conn->rl.hash_size; /* server_write_MAC_secret */ os_memcpy(conn->rl.write_mac_secret, pos, conn->rl.hash_size); pos += conn->rl.hash_size; /* client_write_key */ os_memcpy(conn->rl.read_key, pos, conn->rl.key_material_len); pos += conn->rl.key_material_len; /* server_write_key */ os_memcpy(conn->rl.write_key, pos, conn->rl.key_material_len); pos += conn->rl.key_material_len; /* client_write_IV */ os_memcpy(conn->rl.read_iv, pos, conn->rl.iv_size); pos += conn->rl.iv_size; /* server_write_IV */ os_memcpy(conn->rl.write_iv, pos, conn->rl.iv_size); pos += conn->rl.iv_size; return 0; } /** * tlsv1_server_handshake - Process TLS handshake * @conn: TLSv1 server connection data from tlsv1_server_init() * @in_data: Input data from TLS peer * @in_len: Input data length * @out_len: Length of the output buffer. * Returns: Pointer to output data, %NULL on failure */ u8 * tlsv1_server_handshake(struct tlsv1_server *conn, const u8 *in_data, size_t in_len, size_t *out_len) { const u8 *pos, *end; u8 *msg = NULL, *in_msg, *in_pos, *in_end, alert, ct; size_t in_msg_len; int used; if (in_data == NULL || in_len == 0) { wpa_printf(MSG_DEBUG, "TLSv1: No input data to server"); return NULL; } pos = in_data; end = in_data + in_len; in_msg = os_malloc(in_len); if (in_msg == NULL) return NULL; /* Each received packet may include multiple records */ while (pos < end) { in_msg_len = in_len; used = tlsv1_record_receive(&conn->rl, pos, end - pos, in_msg, &in_msg_len, &alert); if (used < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Processing received " "record failed"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert); goto failed; } if (used == 0) { /* need more data */ wpa_printf(MSG_DEBUG, "TLSv1: Partial processing not " "yet supported"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert); goto failed; } ct = pos[0]; in_pos = in_msg; in_end = in_msg + in_msg_len; /* Each received record may include multiple messages of the * same ContentType. */ while (in_pos < in_end) { in_msg_len = in_end - in_pos; if (tlsv1_server_process_handshake(conn, ct, in_pos, &in_msg_len) < 0) goto failed; in_pos += in_msg_len; } pos += used; } os_free(in_msg); in_msg = NULL; msg = tlsv1_server_handshake_write(conn, out_len); failed: os_free(in_msg); if (conn->alert_level) { if (conn->state == FAILED) { /* Avoid alert loops */ wpa_printf(MSG_DEBUG, "TLSv1: Drop alert loop"); os_free(msg); return NULL; } conn->state = FAILED; os_free(msg); msg = tlsv1_server_send_alert(conn, conn->alert_level, conn->alert_description, out_len); } return msg; } /** * tlsv1_server_encrypt - Encrypt data into TLS tunnel * @conn: TLSv1 server connection data from tlsv1_server_init() * @in_data: Pointer to plaintext data to be encrypted * @in_len: Input buffer length * @out_data: Pointer to output buffer (encrypted TLS data) * @out_len: Maximum out_data length * Returns: Number of bytes written to out_data, -1 on failure * * This function is used after TLS handshake has been completed successfully to * send data in the encrypted tunnel. */ int tlsv1_server_encrypt(struct tlsv1_server *conn, const u8 *in_data, size_t in_len, u8 *out_data, size_t out_len) { size_t rlen; wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Plaintext AppData", in_data, in_len); if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_APPLICATION_DATA, out_data, out_len, in_data, in_len, &rlen) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } return rlen; } /** * tlsv1_server_decrypt - Decrypt data from TLS tunnel * @conn: TLSv1 server connection data from tlsv1_server_init() * @in_data: Pointer to input buffer (encrypted TLS data) * @in_len: Input buffer length * @out_data: Pointer to output buffer (decrypted data from TLS tunnel) * @out_len: Maximum out_data length * Returns: Number of bytes written to out_data, -1 on failure * * This function is used after TLS handshake has been completed successfully to * receive data from the encrypted tunnel. */ int tlsv1_server_decrypt(struct tlsv1_server *conn, const u8 *in_data, size_t in_len, u8 *out_data, size_t out_len) { const u8 *in_end, *pos; int used; u8 alert, *out_end, *out_pos, ct; size_t olen; pos = in_data; in_end = in_data + in_len; out_pos = out_data; out_end = out_data + out_len; while (pos < in_end) { ct = pos[0]; olen = out_end - out_pos; used = tlsv1_record_receive(&conn->rl, pos, in_end - pos, out_pos, &olen, &alert); if (used < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Record layer processing " "failed"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert); return -1; } if (used == 0) { /* need more data */ wpa_printf(MSG_DEBUG, "TLSv1: Partial processing not " "yet supported"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert); return -1; } if (ct == TLS_CONTENT_TYPE_ALERT) { if (olen < 2) { wpa_printf(MSG_DEBUG, "TLSv1: Alert " "underflow"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; } wpa_printf(MSG_DEBUG, "TLSv1: Received alert %d:%d", out_pos[0], out_pos[1]); if (out_pos[0] == TLS_ALERT_LEVEL_WARNING) { /* Continue processing */ pos += used; continue; } tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, out_pos[1]); return -1; } if (ct != TLS_CONTENT_TYPE_APPLICATION_DATA) { wpa_printf(MSG_DEBUG, "TLSv1: Unexpected content type " "0x%x", pos[0]); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; } out_pos += olen; if (out_pos > out_end) { wpa_printf(MSG_DEBUG, "TLSv1: Buffer not large enough " "for processing the received record"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } pos += used; } return out_pos - out_data; } /** * tlsv1_server_global_init - Initialize TLSv1 server * Returns: 0 on success, -1 on failure * * This function must be called before using any other TLSv1 server functions. */ int tlsv1_server_global_init(void) { return crypto_global_init(); } /** * tlsv1_server_global_deinit - Deinitialize TLSv1 server * * This function can be used to deinitialize the TLSv1 server that was * initialized by calling tlsv1_server_global_init(). No TLSv1 server functions * can be called after this before calling tlsv1_server_global_init() again. */ void tlsv1_server_global_deinit(void) { crypto_global_deinit(); } /** * tlsv1_server_init - Initialize TLSv1 server connection * @cred: Pointer to server credentials from tlsv1_server_cred_alloc() * Returns: Pointer to TLSv1 server connection data or %NULL on failure */ struct tlsv1_server * tlsv1_server_init(struct tlsv1_credentials *cred) { struct tlsv1_server *conn; size_t count; u16 *suites; conn = os_zalloc(sizeof(*conn)); if (conn == NULL) return NULL; conn->cred = cred; conn->state = CLIENT_HELLO; if (tls_verify_hash_init(&conn->verify) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to initialize verify " "hash"); os_free(conn); return NULL; } count = 0; suites = conn->cipher_suites; suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA; suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA; suites[count++] = TLS_RSA_WITH_3DES_EDE_CBC_SHA; suites[count++] = TLS_RSA_WITH_RC4_128_SHA; suites[count++] = TLS_RSA_WITH_RC4_128_MD5; conn->num_cipher_suites = count; return conn; } static void tlsv1_server_clear_data(struct tlsv1_server *conn) { tlsv1_record_set_cipher_suite(&conn->rl, TLS_NULL_WITH_NULL_NULL); tlsv1_record_change_write_cipher(&conn->rl); tlsv1_record_change_read_cipher(&conn->rl); tls_verify_hash_free(&conn->verify); crypto_public_key_free(conn->client_rsa_key); conn->client_rsa_key = NULL; os_free(conn->session_ticket); conn->session_ticket = NULL; conn->session_ticket_len = 0; conn->use_session_ticket = 0; os_free(conn->dh_secret); conn->dh_secret = NULL; conn->dh_secret_len = 0; } /** * tlsv1_server_deinit - Deinitialize TLSv1 server connection * @conn: TLSv1 server connection data from tlsv1_server_init() */ void tlsv1_server_deinit(struct tlsv1_server *conn) { tlsv1_server_clear_data(conn); os_free(conn); } /** * tlsv1_server_established - Check whether connection has been established * @conn: TLSv1 server connection data from tlsv1_server_init() * Returns: 1 if connection is established, 0 if not */ int tlsv1_server_established(struct tlsv1_server *conn) { return conn->state == ESTABLISHED; } /** * tlsv1_server_prf - Use TLS-PRF to derive keying material * @conn: TLSv1 server connection data from tlsv1_server_init() * @label: Label (e.g., description of the key) for PRF * @server_random_first: seed is 0 = client_random|server_random, * 1 = server_random|client_random * @out: Buffer for output data from TLS-PRF * @out_len: Length of the output buffer * Returns: 0 on success, -1 on failure */ int tlsv1_server_prf(struct tlsv1_server *conn, const char *label, int server_random_first, u8 *out, size_t out_len) { u8 seed[2 * TLS_RANDOM_LEN]; if (conn->state != ESTABLISHED) return -1; if (server_random_first) { os_memcpy(seed, conn->server_random, TLS_RANDOM_LEN); os_memcpy(seed + TLS_RANDOM_LEN, conn->client_random, TLS_RANDOM_LEN); } else { os_memcpy(seed, conn->client_random, TLS_RANDOM_LEN); os_memcpy(seed + TLS_RANDOM_LEN, conn->server_random, TLS_RANDOM_LEN); } return tls_prf(conn->rl.tls_version, conn->master_secret, TLS_MASTER_SECRET_LEN, label, seed, 2 * TLS_RANDOM_LEN, out, out_len); } /** * tlsv1_server_get_cipher - Get current cipher name * @conn: TLSv1 server connection data from tlsv1_server_init() * @buf: Buffer for the cipher name * @buflen: buf size * Returns: 0 on success, -1 on failure * * Get the name of the currently used cipher. */ int tlsv1_server_get_cipher(struct tlsv1_server *conn, char *buf, size_t buflen) { char *cipher; switch (conn->rl.cipher_suite) { case TLS_RSA_WITH_RC4_128_MD5: cipher = "RC4-MD5"; break; case TLS_RSA_WITH_RC4_128_SHA: cipher = "RC4-SHA"; break; case TLS_RSA_WITH_DES_CBC_SHA: cipher = "DES-CBC-SHA"; break; case TLS_RSA_WITH_3DES_EDE_CBC_SHA: cipher = "DES-CBC3-SHA"; break; case TLS_DH_anon_WITH_AES_128_CBC_SHA: cipher = "ADH-AES-128-SHA"; break; case TLS_RSA_WITH_AES_256_CBC_SHA: cipher = "AES-256-SHA"; break; case TLS_RSA_WITH_AES_128_CBC_SHA: cipher = "AES-128-SHA"; break; default: return -1; } if (os_strlcpy(buf, cipher, buflen) >= buflen) return -1; return 0; } /** * tlsv1_server_shutdown - Shutdown TLS connection * @conn: TLSv1 server connection data from tlsv1_server_init() * Returns: 0 on success, -1 on failure */ int tlsv1_server_shutdown(struct tlsv1_server *conn) { conn->state = CLIENT_HELLO; if (tls_verify_hash_init(&conn->verify) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to re-initialize verify " "hash"); return -1; } tlsv1_server_clear_data(conn); return 0; } /** * tlsv1_server_resumed - Was session resumption used * @conn: TLSv1 server connection data from tlsv1_server_init() * Returns: 1 if current session used session resumption, 0 if not */ int tlsv1_server_resumed(struct tlsv1_server *conn) { return 0; } /** * tlsv1_server_get_keys - Get master key and random data from TLS connection * @conn: TLSv1 server connection data from tlsv1_server_init() * @keys: Structure of key/random data (filled on success) * Returns: 0 on success, -1 on failure */ int tlsv1_server_get_keys(struct tlsv1_server *conn, struct tls_keys *keys) { os_memset(keys, 0, sizeof(*keys)); if (conn->state == CLIENT_HELLO) return -1; keys->client_random = conn->client_random; keys->client_random_len = TLS_RANDOM_LEN; if (conn->state != SERVER_HELLO) { keys->server_random = conn->server_random; keys->server_random_len = TLS_RANDOM_LEN; keys->master_key = conn->master_secret; keys->master_key_len = TLS_MASTER_SECRET_LEN; } return 0; } /** * tlsv1_server_get_keyblock_size - Get TLS key_block size * @conn: TLSv1 server connection data from tlsv1_server_init() * Returns: Size of the key_block for the negotiated cipher suite or -1 on * failure */ int tlsv1_server_get_keyblock_size(struct tlsv1_server *conn) { if (conn->state == CLIENT_HELLO || conn->state == SERVER_HELLO) return -1; return 2 * (conn->rl.hash_size + conn->rl.key_material_len + conn->rl.iv_size); } /** * tlsv1_server_set_cipher_list - Configure acceptable cipher suites * @conn: TLSv1 server connection data from tlsv1_server_init() * @ciphers: Zero (TLS_CIPHER_NONE) terminated list of allowed ciphers * (TLS_CIPHER_*). * Returns: 0 on success, -1 on failure */ int tlsv1_server_set_cipher_list(struct tlsv1_server *conn, u8 *ciphers) { size_t count; u16 *suites; /* TODO: implement proper configuration of cipher suites */ if (ciphers[0] == TLS_CIPHER_ANON_DH_AES128_SHA) { count = 0; suites = conn->cipher_suites; suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA; suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA; suites[count++] = TLS_RSA_WITH_3DES_EDE_CBC_SHA; suites[count++] = TLS_RSA_WITH_RC4_128_SHA; suites[count++] = TLS_RSA_WITH_RC4_128_MD5; suites[count++] = TLS_DH_anon_WITH_AES_256_CBC_SHA; suites[count++] = TLS_DH_anon_WITH_AES_128_CBC_SHA; suites[count++] = TLS_DH_anon_WITH_3DES_EDE_CBC_SHA; suites[count++] = TLS_DH_anon_WITH_RC4_128_MD5; suites[count++] = TLS_DH_anon_WITH_DES_CBC_SHA; conn->num_cipher_suites = count; } return 0; } int tlsv1_server_set_verify(struct tlsv1_server *conn, int verify_peer) { conn->verify_peer = verify_peer; return 0; } void tlsv1_server_set_session_ticket_cb(struct tlsv1_server *conn, tlsv1_server_session_ticket_cb cb, void *ctx) { wpa_printf(MSG_DEBUG, "TLSv1: SessionTicket callback set %p (ctx %p)", cb, ctx); conn->session_ticket_cb = cb; conn->session_ticket_cb_ctx = ctx; } wpa-2.1/src/tls/tlsv1_server.h000066400000000000000000000035111227414666700163630ustar00rootroot00000000000000/* * TLS v1.0/v1.1/v1.2 server (RFC 2246, RFC 4346, RFC 5246) * Copyright (c) 2006-2011, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef TLSV1_SERVER_H #define TLSV1_SERVER_H #include "tlsv1_cred.h" struct tlsv1_server; int tlsv1_server_global_init(void); void tlsv1_server_global_deinit(void); struct tlsv1_server * tlsv1_server_init(struct tlsv1_credentials *cred); void tlsv1_server_deinit(struct tlsv1_server *conn); int tlsv1_server_established(struct tlsv1_server *conn); int tlsv1_server_prf(struct tlsv1_server *conn, const char *label, int server_random_first, u8 *out, size_t out_len); u8 * tlsv1_server_handshake(struct tlsv1_server *conn, const u8 *in_data, size_t in_len, size_t *out_len); int tlsv1_server_encrypt(struct tlsv1_server *conn, const u8 *in_data, size_t in_len, u8 *out_data, size_t out_len); int tlsv1_server_decrypt(struct tlsv1_server *conn, const u8 *in_data, size_t in_len, u8 *out_data, size_t out_len); int tlsv1_server_get_cipher(struct tlsv1_server *conn, char *buf, size_t buflen); int tlsv1_server_shutdown(struct tlsv1_server *conn); int tlsv1_server_resumed(struct tlsv1_server *conn); int tlsv1_server_get_keys(struct tlsv1_server *conn, struct tls_keys *keys); int tlsv1_server_get_keyblock_size(struct tlsv1_server *conn); int tlsv1_server_set_cipher_list(struct tlsv1_server *conn, u8 *ciphers); int tlsv1_server_set_verify(struct tlsv1_server *conn, int verify_peer); typedef int (*tlsv1_server_session_ticket_cb) (void *ctx, const u8 *ticket, size_t len, const u8 *client_random, const u8 *server_random, u8 *master_secret); void tlsv1_server_set_session_ticket_cb(struct tlsv1_server *conn, tlsv1_server_session_ticket_cb cb, void *ctx); #endif /* TLSV1_SERVER_H */ wpa-2.1/src/tls/tlsv1_server_i.h000066400000000000000000000034551227414666700167020ustar00rootroot00000000000000/* * TLSv1 server - internal structures * Copyright (c) 2006-2007, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef TLSV1_SERVER_I_H #define TLSV1_SERVER_I_H struct tlsv1_server { enum { CLIENT_HELLO, SERVER_HELLO, SERVER_CERTIFICATE, SERVER_KEY_EXCHANGE, SERVER_CERTIFICATE_REQUEST, SERVER_HELLO_DONE, CLIENT_CERTIFICATE, CLIENT_KEY_EXCHANGE, CERTIFICATE_VERIFY, CHANGE_CIPHER_SPEC, CLIENT_FINISHED, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, ESTABLISHED, FAILED } state; struct tlsv1_record_layer rl; u8 session_id[TLS_SESSION_ID_MAX_LEN]; size_t session_id_len; u8 client_random[TLS_RANDOM_LEN]; u8 server_random[TLS_RANDOM_LEN]; u8 master_secret[TLS_MASTER_SECRET_LEN]; u8 alert_level; u8 alert_description; struct crypto_public_key *client_rsa_key; struct tls_verify_hash verify; #define MAX_CIPHER_COUNT 30 u16 cipher_suites[MAX_CIPHER_COUNT]; size_t num_cipher_suites; u16 cipher_suite; struct tlsv1_credentials *cred; int verify_peer; u16 client_version; u8 *session_ticket; size_t session_ticket_len; tlsv1_server_session_ticket_cb session_ticket_cb; void *session_ticket_cb_ctx; int use_session_ticket; u8 *dh_secret; size_t dh_secret_len; }; void tlsv1_server_alert(struct tlsv1_server *conn, u8 level, u8 description); int tlsv1_server_derive_keys(struct tlsv1_server *conn, const u8 *pre_master_secret, size_t pre_master_secret_len); u8 * tlsv1_server_handshake_write(struct tlsv1_server *conn, size_t *out_len); u8 * tlsv1_server_send_alert(struct tlsv1_server *conn, u8 level, u8 description, size_t *out_len); int tlsv1_server_process_handshake(struct tlsv1_server *conn, u8 ct, const u8 *buf, size_t *len); #endif /* TLSV1_SERVER_I_H */ wpa-2.1/src/tls/tlsv1_server_read.c000066400000000000000000000775371227414666700173740ustar00rootroot00000000000000/* * TLSv1 server - read handshake message * Copyright (c) 2006-2011, 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/md5.h" #include "crypto/sha1.h" #include "crypto/sha256.h" #include "crypto/tls.h" #include "x509v3.h" #include "tlsv1_common.h" #include "tlsv1_record.h" #include "tlsv1_server.h" #include "tlsv1_server_i.h" static int tls_process_client_key_exchange(struct tlsv1_server *conn, u8 ct, const u8 *in_data, size_t *in_len); static int tls_process_change_cipher_spec(struct tlsv1_server *conn, u8 ct, const u8 *in_data, size_t *in_len); static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, const u8 *in_data, size_t *in_len) { const u8 *pos, *end, *c; size_t left, len, i, j; u16 cipher_suite; u16 num_suites; int compr_null_found; u16 ext_type, ext_len; if (ct != TLS_CONTENT_TYPE_HANDSHAKE) { wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; " "received content type 0x%x", ct); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; } pos = in_data; left = *in_len; if (left < 4) goto decode_error; /* HandshakeType msg_type */ if (*pos != TLS_HANDSHAKE_TYPE_CLIENT_HELLO) { wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake " "message %d (expected ClientHello)", *pos); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; } wpa_printf(MSG_DEBUG, "TLSv1: Received ClientHello"); pos++; /* uint24 length */ len = WPA_GET_BE24(pos); pos += 3; left -= 4; if (len > left) goto decode_error; /* body - ClientHello */ wpa_hexdump(MSG_MSGDUMP, "TLSv1: ClientHello", pos, len); end = pos + len; /* ProtocolVersion client_version */ if (end - pos < 2) goto decode_error; conn->client_version = WPA_GET_BE16(pos); wpa_printf(MSG_DEBUG, "TLSv1: Client version %d.%d", conn->client_version >> 8, conn->client_version & 0xff); if (conn->client_version < TLS_VERSION_1) { wpa_printf(MSG_DEBUG, "TLSv1: Unexpected protocol version in " "ClientHello %u.%u", conn->client_version >> 8, conn->client_version & 0xff); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_PROTOCOL_VERSION); return -1; } pos += 2; if (TLS_VERSION == TLS_VERSION_1) conn->rl.tls_version = TLS_VERSION_1; #ifdef CONFIG_TLSV12 else if (conn->client_version >= TLS_VERSION_1_2) conn->rl.tls_version = TLS_VERSION_1_2; #endif /* CONFIG_TLSV12 */ else if (conn->client_version > TLS_VERSION_1_1) conn->rl.tls_version = TLS_VERSION_1_1; else conn->rl.tls_version = conn->client_version; wpa_printf(MSG_DEBUG, "TLSv1: Using TLS v%s", tls_version_str(conn->rl.tls_version)); /* Random random */ if (end - pos < TLS_RANDOM_LEN) goto decode_error; os_memcpy(conn->client_random, pos, TLS_RANDOM_LEN); pos += TLS_RANDOM_LEN; wpa_hexdump(MSG_MSGDUMP, "TLSv1: client_random", conn->client_random, TLS_RANDOM_LEN); /* SessionID session_id */ if (end - pos < 1) goto decode_error; if (end - pos < 1 + *pos || *pos > TLS_SESSION_ID_MAX_LEN) goto decode_error; wpa_hexdump(MSG_MSGDUMP, "TLSv1: client session_id", pos + 1, *pos); pos += 1 + *pos; /* TODO: add support for session resumption */ /* CipherSuite cipher_suites<2..2^16-1> */ if (end - pos < 2) goto decode_error; num_suites = WPA_GET_BE16(pos); pos += 2; if (end - pos < num_suites) goto decode_error; wpa_hexdump(MSG_MSGDUMP, "TLSv1: client cipher suites", pos, num_suites); if (num_suites & 1) goto decode_error; num_suites /= 2; cipher_suite = 0; for (i = 0; !cipher_suite && i < conn->num_cipher_suites; i++) { c = pos; for (j = 0; j < num_suites; j++) { u16 tmp = WPA_GET_BE16(c); c += 2; if (!cipher_suite && tmp == conn->cipher_suites[i]) { cipher_suite = tmp; break; } } } pos += num_suites * 2; if (!cipher_suite) { wpa_printf(MSG_INFO, "TLSv1: No supported cipher suite " "available"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_ILLEGAL_PARAMETER); return -1; } if (tlsv1_record_set_cipher_suite(&conn->rl, cipher_suite) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to set CipherSuite for " "record layer"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } conn->cipher_suite = cipher_suite; /* CompressionMethod compression_methods<1..2^8-1> */ if (end - pos < 1) goto decode_error; num_suites = *pos++; if (end - pos < num_suites) goto decode_error; wpa_hexdump(MSG_MSGDUMP, "TLSv1: client compression_methods", pos, num_suites); compr_null_found = 0; for (i = 0; i < num_suites; i++) { if (*pos++ == TLS_COMPRESSION_NULL) compr_null_found = 1; } if (!compr_null_found) { wpa_printf(MSG_INFO, "TLSv1: Client does not accept NULL " "compression"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_ILLEGAL_PARAMETER); return -1; } if (end - pos == 1) { wpa_printf(MSG_DEBUG, "TLSv1: Unexpected extra octet in the " "end of ClientHello: 0x%02x", *pos); goto decode_error; } if (end - pos >= 2) { /* Extension client_hello_extension_list<0..2^16-1> */ ext_len = WPA_GET_BE16(pos); pos += 2; wpa_printf(MSG_DEBUG, "TLSv1: %u bytes of ClientHello " "extensions", ext_len); if (end - pos != ext_len) { wpa_printf(MSG_DEBUG, "TLSv1: Invalid ClientHello " "extension list length %u (expected %u)", ext_len, (unsigned int) (end - pos)); goto decode_error; } /* * struct { * ExtensionType extension_type (0..65535) * opaque extension_data<0..2^16-1> * } Extension; */ while (pos < end) { if (end - pos < 2) { wpa_printf(MSG_DEBUG, "TLSv1: Invalid " "extension_type field"); goto decode_error; } ext_type = WPA_GET_BE16(pos); pos += 2; if (end - pos < 2) { wpa_printf(MSG_DEBUG, "TLSv1: Invalid " "extension_data length field"); goto decode_error; } ext_len = WPA_GET_BE16(pos); pos += 2; if (end - pos < ext_len) { wpa_printf(MSG_DEBUG, "TLSv1: Invalid " "extension_data field"); goto decode_error; } wpa_printf(MSG_DEBUG, "TLSv1: ClientHello Extension " "type %u", ext_type); wpa_hexdump(MSG_MSGDUMP, "TLSv1: ClientHello " "Extension data", pos, ext_len); if (ext_type == TLS_EXT_SESSION_TICKET) { os_free(conn->session_ticket); conn->session_ticket = os_malloc(ext_len); if (conn->session_ticket) { os_memcpy(conn->session_ticket, pos, ext_len); conn->session_ticket_len = ext_len; } } pos += ext_len; } } *in_len = end - in_data; wpa_printf(MSG_DEBUG, "TLSv1: ClientHello OK - proceed to " "ServerHello"); conn->state = SERVER_HELLO; return 0; decode_error: wpa_printf(MSG_DEBUG, "TLSv1: Failed to decode ClientHello"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; } static int tls_process_certificate(struct tlsv1_server *conn, u8 ct, const u8 *in_data, size_t *in_len) { const u8 *pos, *end; size_t left, len, list_len, cert_len, idx; u8 type; struct x509_certificate *chain = NULL, *last = NULL, *cert; int reason; if (ct != TLS_CONTENT_TYPE_HANDSHAKE) { wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; " "received content type 0x%x", ct); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; } pos = in_data; left = *in_len; if (left < 4) { wpa_printf(MSG_DEBUG, "TLSv1: Too short Certificate message " "(len=%lu)", (unsigned long) left); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; } type = *pos++; len = WPA_GET_BE24(pos); pos += 3; left -= 4; if (len > left) { wpa_printf(MSG_DEBUG, "TLSv1: Unexpected Certificate message " "length (len=%lu != left=%lu)", (unsigned long) len, (unsigned long) left); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; } if (type == TLS_HANDSHAKE_TYPE_CLIENT_KEY_EXCHANGE) { if (conn->verify_peer) { wpa_printf(MSG_DEBUG, "TLSv1: Client did not include " "Certificate"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; } return tls_process_client_key_exchange(conn, ct, in_data, in_len); } if (type != TLS_HANDSHAKE_TYPE_CERTIFICATE) { wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake " "message %d (expected Certificate/" "ClientKeyExchange)", type); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; } wpa_printf(MSG_DEBUG, "TLSv1: Received Certificate (certificate_list len %lu)", (unsigned long) len); /* * opaque ASN.1Cert<2^24-1>; * * struct { * ASN.1Cert certificate_list<1..2^24-1>; * } Certificate; */ end = pos + len; if (end - pos < 3) { wpa_printf(MSG_DEBUG, "TLSv1: Too short Certificate " "(left=%lu)", (unsigned long) left); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; } list_len = WPA_GET_BE24(pos); pos += 3; if ((size_t) (end - pos) != list_len) { wpa_printf(MSG_DEBUG, "TLSv1: Unexpected certificate_list " "length (len=%lu left=%lu)", (unsigned long) list_len, (unsigned long) (end - pos)); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; } idx = 0; while (pos < end) { if (end - pos < 3) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse " "certificate_list"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); x509_certificate_chain_free(chain); return -1; } cert_len = WPA_GET_BE24(pos); pos += 3; if ((size_t) (end - pos) < cert_len) { wpa_printf(MSG_DEBUG, "TLSv1: Unexpected certificate " "length (len=%lu left=%lu)", (unsigned long) cert_len, (unsigned long) (end - pos)); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); x509_certificate_chain_free(chain); return -1; } wpa_printf(MSG_DEBUG, "TLSv1: Certificate %lu (len %lu)", (unsigned long) idx, (unsigned long) cert_len); if (idx == 0) { crypto_public_key_free(conn->client_rsa_key); if (tls_parse_cert(pos, cert_len, &conn->client_rsa_key)) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse " "the certificate"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_BAD_CERTIFICATE); x509_certificate_chain_free(chain); return -1; } } cert = x509_certificate_parse(pos, cert_len); if (cert == NULL) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse " "the certificate"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_BAD_CERTIFICATE); x509_certificate_chain_free(chain); return -1; } if (last == NULL) chain = cert; else last->next = cert; last = cert; idx++; pos += cert_len; } if (x509_certificate_chain_validate(conn->cred->trusted_certs, chain, &reason, 0) < 0) { int tls_reason; wpa_printf(MSG_DEBUG, "TLSv1: Server certificate chain " "validation failed (reason=%d)", reason); switch (reason) { case X509_VALIDATE_BAD_CERTIFICATE: tls_reason = TLS_ALERT_BAD_CERTIFICATE; break; case X509_VALIDATE_UNSUPPORTED_CERTIFICATE: tls_reason = TLS_ALERT_UNSUPPORTED_CERTIFICATE; break; case X509_VALIDATE_CERTIFICATE_REVOKED: tls_reason = TLS_ALERT_CERTIFICATE_REVOKED; break; case X509_VALIDATE_CERTIFICATE_EXPIRED: tls_reason = TLS_ALERT_CERTIFICATE_EXPIRED; break; case X509_VALIDATE_CERTIFICATE_UNKNOWN: tls_reason = TLS_ALERT_CERTIFICATE_UNKNOWN; break; case X509_VALIDATE_UNKNOWN_CA: tls_reason = TLS_ALERT_UNKNOWN_CA; break; default: tls_reason = TLS_ALERT_BAD_CERTIFICATE; break; } tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, tls_reason); x509_certificate_chain_free(chain); return -1; } x509_certificate_chain_free(chain); *in_len = end - in_data; conn->state = CLIENT_KEY_EXCHANGE; return 0; } static int tls_process_client_key_exchange_rsa( struct tlsv1_server *conn, const u8 *pos, const u8 *end) { u8 *out; size_t outlen, outbuflen; u16 encr_len; int res; int use_random = 0; if (end - pos < 2) { tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; } encr_len = WPA_GET_BE16(pos); pos += 2; if (pos + encr_len > end) { wpa_printf(MSG_DEBUG, "TLSv1: Invalid ClientKeyExchange " "format: encr_len=%u left=%u", encr_len, (unsigned int) (end - pos)); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; } outbuflen = outlen = end - pos; out = os_malloc(outlen >= TLS_PRE_MASTER_SECRET_LEN ? outlen : TLS_PRE_MASTER_SECRET_LEN); if (out == NULL) { tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } /* * struct { * ProtocolVersion client_version; * opaque random[46]; * } PreMasterSecret; * * struct { * public-key-encrypted PreMasterSecret pre_master_secret; * } EncryptedPreMasterSecret; */ /* * Note: To avoid Bleichenbacher attack, we do not report decryption or * parsing errors from EncryptedPreMasterSecret processing to the * client. Instead, a random pre-master secret is used to force the * handshake to fail. */ if (crypto_private_key_decrypt_pkcs1_v15(conn->cred->key, pos, encr_len, out, &outlen) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to decrypt " "PreMasterSecret (encr_len=%u outlen=%lu)", encr_len, (unsigned long) outlen); use_random = 1; } if (!use_random && outlen != TLS_PRE_MASTER_SECRET_LEN) { wpa_printf(MSG_DEBUG, "TLSv1: Unexpected PreMasterSecret " "length %lu", (unsigned long) outlen); use_random = 1; } if (!use_random && WPA_GET_BE16(out) != conn->client_version) { wpa_printf(MSG_DEBUG, "TLSv1: Client version in " "ClientKeyExchange does not match with version in " "ClientHello"); use_random = 1; } if (use_random) { wpa_printf(MSG_DEBUG, "TLSv1: Using random premaster secret " "to avoid revealing information about private key"); outlen = TLS_PRE_MASTER_SECRET_LEN; if (os_get_random(out, outlen)) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to get random " "data"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); os_free(out); return -1; } } res = tlsv1_server_derive_keys(conn, out, outlen); /* Clear the pre-master secret since it is not needed anymore */ os_memset(out, 0, outbuflen); os_free(out); if (res) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive keys"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } return 0; } static int tls_process_client_key_exchange_dh_anon( struct tlsv1_server *conn, const u8 *pos, const u8 *end) { const u8 *dh_yc; u16 dh_yc_len; u8 *shared; size_t shared_len; int res; /* * struct { * select (PublicValueEncoding) { * case implicit: struct { }; * case explicit: opaque dh_Yc<1..2^16-1>; * } dh_public; * } ClientDiffieHellmanPublic; */ wpa_hexdump(MSG_MSGDUMP, "TLSv1: ClientDiffieHellmanPublic", pos, end - pos); if (end == pos) { wpa_printf(MSG_DEBUG, "TLSv1: Implicit public value encoding " "not supported"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } if (end - pos < 3) { wpa_printf(MSG_DEBUG, "TLSv1: Invalid client public value " "length"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; } dh_yc_len = WPA_GET_BE16(pos); dh_yc = pos + 2; if (dh_yc + dh_yc_len > end) { wpa_printf(MSG_DEBUG, "TLSv1: Client public value overflow " "(length %d)", dh_yc_len); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; } wpa_hexdump(MSG_DEBUG, "TLSv1: DH Yc (client's public value)", dh_yc, dh_yc_len); if (conn->cred == NULL || conn->cred->dh_p == NULL || conn->dh_secret == NULL) { wpa_printf(MSG_DEBUG, "TLSv1: No DH parameters available"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } shared_len = conn->cred->dh_p_len; shared = os_malloc(shared_len); if (shared == NULL) { wpa_printf(MSG_DEBUG, "TLSv1: Could not allocate memory for " "DH"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } /* shared = Yc^secret mod p */ if (crypto_mod_exp(dh_yc, dh_yc_len, conn->dh_secret, conn->dh_secret_len, conn->cred->dh_p, conn->cred->dh_p_len, shared, &shared_len)) { os_free(shared); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } wpa_hexdump_key(MSG_DEBUG, "TLSv1: Shared secret from DH key exchange", shared, shared_len); os_memset(conn->dh_secret, 0, conn->dh_secret_len); os_free(conn->dh_secret); conn->dh_secret = NULL; res = tlsv1_server_derive_keys(conn, shared, shared_len); /* Clear the pre-master secret since it is not needed anymore */ os_memset(shared, 0, shared_len); os_free(shared); if (res) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive keys"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } return 0; } static int tls_process_client_key_exchange(struct tlsv1_server *conn, u8 ct, const u8 *in_data, size_t *in_len) { const u8 *pos, *end; size_t left, len; u8 type; tls_key_exchange keyx; const struct tls_cipher_suite *suite; if (ct != TLS_CONTENT_TYPE_HANDSHAKE) { wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; " "received content type 0x%x", ct); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; } pos = in_data; left = *in_len; if (left < 4) { wpa_printf(MSG_DEBUG, "TLSv1: Too short ClientKeyExchange " "(Left=%lu)", (unsigned long) left); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; } type = *pos++; len = WPA_GET_BE24(pos); pos += 3; left -= 4; if (len > left) { wpa_printf(MSG_DEBUG, "TLSv1: Mismatch in ClientKeyExchange " "length (len=%lu != left=%lu)", (unsigned long) len, (unsigned long) left); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; } end = pos + len; if (type != TLS_HANDSHAKE_TYPE_CLIENT_KEY_EXCHANGE) { wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake " "message %d (expected ClientKeyExchange)", type); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; } wpa_printf(MSG_DEBUG, "TLSv1: Received ClientKeyExchange"); wpa_hexdump(MSG_DEBUG, "TLSv1: ClientKeyExchange", pos, len); suite = tls_get_cipher_suite(conn->rl.cipher_suite); if (suite == NULL) keyx = TLS_KEY_X_NULL; else keyx = suite->key_exchange; if (keyx == TLS_KEY_X_DH_anon && tls_process_client_key_exchange_dh_anon(conn, pos, end) < 0) return -1; if (keyx != TLS_KEY_X_DH_anon && tls_process_client_key_exchange_rsa(conn, pos, end) < 0) return -1; *in_len = end - in_data; conn->state = CERTIFICATE_VERIFY; return 0; } static int tls_process_certificate_verify(struct tlsv1_server *conn, u8 ct, const u8 *in_data, size_t *in_len) { const u8 *pos, *end; size_t left, len; u8 type; size_t hlen, buflen; u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN], *hpos, *buf; enum { SIGN_ALG_RSA, SIGN_ALG_DSA } alg = SIGN_ALG_RSA; u16 slen; if (ct == TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC) { if (conn->verify_peer) { wpa_printf(MSG_DEBUG, "TLSv1: Client did not include " "CertificateVerify"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; } return tls_process_change_cipher_spec(conn, ct, in_data, in_len); } if (ct != TLS_CONTENT_TYPE_HANDSHAKE) { wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; " "received content type 0x%x", ct); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; } pos = in_data; left = *in_len; if (left < 4) { wpa_printf(MSG_DEBUG, "TLSv1: Too short CertificateVerify " "message (len=%lu)", (unsigned long) left); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; } type = *pos++; len = WPA_GET_BE24(pos); pos += 3; left -= 4; if (len > left) { wpa_printf(MSG_DEBUG, "TLSv1: Unexpected CertificateVerify " "message length (len=%lu != left=%lu)", (unsigned long) len, (unsigned long) left); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; } end = pos + len; if (type != TLS_HANDSHAKE_TYPE_CERTIFICATE_VERIFY) { wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake " "message %d (expected CertificateVerify)", type); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; } wpa_printf(MSG_DEBUG, "TLSv1: Received CertificateVerify"); /* * struct { * Signature signature; * } CertificateVerify; */ hpos = hash; #ifdef CONFIG_TLSV12 if (conn->rl.tls_version == TLS_VERSION_1_2) { /* * RFC 5246, 4.7: * TLS v1.2 adds explicit indication of the used signature and * hash algorithms. * * struct { * HashAlgorithm hash; * SignatureAlgorithm signature; * } SignatureAndHashAlgorithm; */ if (end - pos < 2) { tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; } if (pos[0] != TLS_HASH_ALG_SHA256 || pos[1] != TLS_SIGN_ALG_RSA) { wpa_printf(MSG_DEBUG, "TLSv1.2: Unsupported hash(%u)/" "signature(%u) algorithm", pos[0], pos[1]); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } pos += 2; hlen = SHA256_MAC_LEN; if (conn->verify.sha256_cert == NULL || crypto_hash_finish(conn->verify.sha256_cert, hpos, &hlen) < 0) { conn->verify.sha256_cert = NULL; tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } conn->verify.sha256_cert = NULL; } else { #endif /* CONFIG_TLSV12 */ if (alg == SIGN_ALG_RSA) { hlen = MD5_MAC_LEN; if (conn->verify.md5_cert == NULL || crypto_hash_finish(conn->verify.md5_cert, hpos, &hlen) < 0) { tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); conn->verify.md5_cert = NULL; crypto_hash_finish(conn->verify.sha1_cert, NULL, NULL); conn->verify.sha1_cert = NULL; return -1; } hpos += MD5_MAC_LEN; } else crypto_hash_finish(conn->verify.md5_cert, NULL, NULL); conn->verify.md5_cert = NULL; hlen = SHA1_MAC_LEN; if (conn->verify.sha1_cert == NULL || crypto_hash_finish(conn->verify.sha1_cert, hpos, &hlen) < 0) { conn->verify.sha1_cert = NULL; tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } conn->verify.sha1_cert = NULL; if (alg == SIGN_ALG_RSA) hlen += MD5_MAC_LEN; #ifdef CONFIG_TLSV12 } #endif /* CONFIG_TLSV12 */ wpa_hexdump(MSG_MSGDUMP, "TLSv1: CertificateVerify hash", hash, hlen); if (end - pos < 2) { tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; } slen = WPA_GET_BE16(pos); pos += 2; if (end - pos < slen) { tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; } wpa_hexdump(MSG_MSGDUMP, "TLSv1: Signature", pos, end - pos); if (conn->client_rsa_key == NULL) { wpa_printf(MSG_DEBUG, "TLSv1: No client public key to verify " "signature"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } buflen = end - pos; buf = os_malloc(end - pos); if (crypto_public_key_decrypt_pkcs1(conn->client_rsa_key, pos, end - pos, buf, &buflen) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to decrypt signature"); os_free(buf); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECRYPT_ERROR); return -1; } wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Decrypted Signature", buf, buflen); #ifdef CONFIG_TLSV12 if (conn->rl.tls_version >= TLS_VERSION_1_2) { /* * RFC 3447, A.2.4 RSASSA-PKCS1-v1_5 * * DigestInfo ::= SEQUENCE { * digestAlgorithm DigestAlgorithm, * digest OCTET STRING * } * * SHA-256 OID: sha256WithRSAEncryption ::= {pkcs-1 11} * * DER encoded DigestInfo for SHA256 per RFC 3447: * 30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00 04 20 || * H */ if (buflen >= 19 + 32 && os_memcmp(buf, "\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01" "\x65\x03\x04\x02\x01\x05\x00\x04\x20", 19) == 0) { wpa_printf(MSG_DEBUG, "TLSv1.2: DigestAlgorithn = " "SHA-256"); os_memmove(buf, buf + 19, buflen - 19); buflen -= 19; } else { wpa_printf(MSG_DEBUG, "TLSv1.2: Unrecognized " "DigestInfo"); os_free(buf); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECRYPT_ERROR); return -1; } } #endif /* CONFIG_TLSV12 */ if (buflen != hlen || os_memcmp(buf, hash, buflen) != 0) { wpa_printf(MSG_DEBUG, "TLSv1: Invalid Signature in " "CertificateVerify - did not match with calculated " "hash"); os_free(buf); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECRYPT_ERROR); return -1; } os_free(buf); *in_len = end - in_data; conn->state = CHANGE_CIPHER_SPEC; return 0; } static int tls_process_change_cipher_spec(struct tlsv1_server *conn, u8 ct, const u8 *in_data, size_t *in_len) { const u8 *pos; size_t left; if (ct != TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC) { wpa_printf(MSG_DEBUG, "TLSv1: Expected ChangeCipherSpec; " "received content type 0x%x", ct); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; } pos = in_data; left = *in_len; if (left < 1) { wpa_printf(MSG_DEBUG, "TLSv1: Too short ChangeCipherSpec"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; } if (*pos != TLS_CHANGE_CIPHER_SPEC) { wpa_printf(MSG_DEBUG, "TLSv1: Expected ChangeCipherSpec; " "received data 0x%x", *pos); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; } wpa_printf(MSG_DEBUG, "TLSv1: Received ChangeCipherSpec"); if (tlsv1_record_change_read_cipher(&conn->rl) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to change read cipher " "for record layer"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } *in_len = pos + 1 - in_data; conn->state = CLIENT_FINISHED; return 0; } static int tls_process_client_finished(struct tlsv1_server *conn, u8 ct, const u8 *in_data, size_t *in_len) { const u8 *pos, *end; size_t left, len, hlen; u8 verify_data[TLS_VERIFY_DATA_LEN]; u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN]; if (ct != TLS_CONTENT_TYPE_HANDSHAKE) { wpa_printf(MSG_DEBUG, "TLSv1: Expected Finished; " "received content type 0x%x", ct); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; } pos = in_data; left = *in_len; if (left < 4) { wpa_printf(MSG_DEBUG, "TLSv1: Too short record (left=%lu) for " "Finished", (unsigned long) left); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; } if (pos[0] != TLS_HANDSHAKE_TYPE_FINISHED) { wpa_printf(MSG_DEBUG, "TLSv1: Expected Finished; received " "type 0x%x", pos[0]); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; } len = WPA_GET_BE24(pos + 1); pos += 4; left -= 4; if (len > left) { wpa_printf(MSG_DEBUG, "TLSv1: Too short buffer for Finished " "(len=%lu > left=%lu)", (unsigned long) len, (unsigned long) left); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; } end = pos + len; if (len != TLS_VERIFY_DATA_LEN) { wpa_printf(MSG_DEBUG, "TLSv1: Unexpected verify_data length " "in Finished: %lu (expected %d)", (unsigned long) len, TLS_VERIFY_DATA_LEN); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; } wpa_hexdump(MSG_MSGDUMP, "TLSv1: verify_data in Finished", pos, TLS_VERIFY_DATA_LEN); #ifdef CONFIG_TLSV12 if (conn->rl.tls_version >= TLS_VERSION_1_2) { hlen = SHA256_MAC_LEN; if (conn->verify.sha256_client == NULL || crypto_hash_finish(conn->verify.sha256_client, hash, &hlen) < 0) { tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); conn->verify.sha256_client = NULL; return -1; } conn->verify.sha256_client = NULL; } else { #endif /* CONFIG_TLSV12 */ hlen = MD5_MAC_LEN; if (conn->verify.md5_client == NULL || crypto_hash_finish(conn->verify.md5_client, hash, &hlen) < 0) { tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); conn->verify.md5_client = NULL; crypto_hash_finish(conn->verify.sha1_client, NULL, NULL); conn->verify.sha1_client = NULL; return -1; } conn->verify.md5_client = NULL; hlen = SHA1_MAC_LEN; if (conn->verify.sha1_client == NULL || crypto_hash_finish(conn->verify.sha1_client, hash + MD5_MAC_LEN, &hlen) < 0) { conn->verify.sha1_client = NULL; tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } conn->verify.sha1_client = NULL; hlen = MD5_MAC_LEN + SHA1_MAC_LEN; #ifdef CONFIG_TLSV12 } #endif /* CONFIG_TLSV12 */ if (tls_prf(conn->rl.tls_version, conn->master_secret, TLS_MASTER_SECRET_LEN, "client finished", hash, hlen, verify_data, TLS_VERIFY_DATA_LEN)) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive verify_data"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECRYPT_ERROR); return -1; } wpa_hexdump_key(MSG_DEBUG, "TLSv1: verify_data (client)", verify_data, TLS_VERIFY_DATA_LEN); if (os_memcmp(pos, verify_data, TLS_VERIFY_DATA_LEN) != 0) { wpa_printf(MSG_INFO, "TLSv1: Mismatch in verify_data"); return -1; } wpa_printf(MSG_DEBUG, "TLSv1: Received Finished"); *in_len = end - in_data; if (conn->use_session_ticket) { /* Abbreviated handshake using session ticket; RFC 4507 */ wpa_printf(MSG_DEBUG, "TLSv1: Abbreviated handshake completed " "successfully"); conn->state = ESTABLISHED; } else { /* Full handshake */ conn->state = SERVER_CHANGE_CIPHER_SPEC; } return 0; } int tlsv1_server_process_handshake(struct tlsv1_server *conn, u8 ct, const u8 *buf, size_t *len) { if (ct == TLS_CONTENT_TYPE_ALERT) { if (*len < 2) { wpa_printf(MSG_DEBUG, "TLSv1: Alert underflow"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; } wpa_printf(MSG_DEBUG, "TLSv1: Received alert %d:%d", buf[0], buf[1]); *len = 2; conn->state = FAILED; return -1; } switch (conn->state) { case CLIENT_HELLO: if (tls_process_client_hello(conn, ct, buf, len)) return -1; break; case CLIENT_CERTIFICATE: if (tls_process_certificate(conn, ct, buf, len)) return -1; break; case CLIENT_KEY_EXCHANGE: if (tls_process_client_key_exchange(conn, ct, buf, len)) return -1; break; case CERTIFICATE_VERIFY: if (tls_process_certificate_verify(conn, ct, buf, len)) return -1; break; case CHANGE_CIPHER_SPEC: if (tls_process_change_cipher_spec(conn, ct, buf, len)) return -1; break; case CLIENT_FINISHED: if (tls_process_client_finished(conn, ct, buf, len)) return -1; break; default: wpa_printf(MSG_DEBUG, "TLSv1: Unexpected state %d " "while processing received message", conn->state); return -1; } if (ct == TLS_CONTENT_TYPE_HANDSHAKE) tls_verify_hash_add(&conn->verify, buf, *len); return 0; } wpa-2.1/src/tls/tlsv1_server_write.c000066400000000000000000000501371227414666700175760ustar00rootroot00000000000000/* * TLSv1 server - write handshake message * Copyright (c) 2006-2011, 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/md5.h" #include "crypto/sha1.h" #include "crypto/sha256.h" #include "crypto/tls.h" #include "crypto/random.h" #include "x509v3.h" #include "tlsv1_common.h" #include "tlsv1_record.h" #include "tlsv1_server.h" #include "tlsv1_server_i.h" static size_t tls_server_cert_chain_der_len(struct tlsv1_server *conn) { size_t len = 0; struct x509_certificate *cert; cert = conn->cred->cert; while (cert) { len += 3 + cert->cert_len; if (x509_certificate_self_signed(cert)) break; cert = x509_certificate_get_subject(conn->cred->trusted_certs, &cert->issuer); } return len; } static int tls_write_server_hello(struct tlsv1_server *conn, u8 **msgpos, u8 *end) { u8 *pos, *rhdr, *hs_start, *hs_length; struct os_time now; size_t rlen; pos = *msgpos; wpa_printf(MSG_DEBUG, "TLSv1: Send ServerHello"); rhdr = pos; pos += TLS_RECORD_HEADER_LEN; os_get_time(&now); WPA_PUT_BE32(conn->server_random, now.sec); if (random_get_bytes(conn->server_random + 4, TLS_RANDOM_LEN - 4)) { wpa_printf(MSG_ERROR, "TLSv1: Could not generate " "server_random"); return -1; } wpa_hexdump(MSG_MSGDUMP, "TLSv1: server_random", conn->server_random, TLS_RANDOM_LEN); conn->session_id_len = TLS_SESSION_ID_MAX_LEN; if (random_get_bytes(conn->session_id, conn->session_id_len)) { wpa_printf(MSG_ERROR, "TLSv1: Could not generate " "session_id"); return -1; } wpa_hexdump(MSG_MSGDUMP, "TLSv1: session_id", conn->session_id, conn->session_id_len); /* opaque fragment[TLSPlaintext.length] */ /* Handshake */ hs_start = pos; /* HandshakeType msg_type */ *pos++ = TLS_HANDSHAKE_TYPE_SERVER_HELLO; /* uint24 length (to be filled) */ hs_length = pos; pos += 3; /* body - ServerHello */ /* ProtocolVersion server_version */ WPA_PUT_BE16(pos, conn->rl.tls_version); pos += 2; /* Random random: uint32 gmt_unix_time, opaque random_bytes */ os_memcpy(pos, conn->server_random, TLS_RANDOM_LEN); pos += TLS_RANDOM_LEN; /* SessionID session_id */ *pos++ = conn->session_id_len; os_memcpy(pos, conn->session_id, conn->session_id_len); pos += conn->session_id_len; /* CipherSuite cipher_suite */ WPA_PUT_BE16(pos, conn->cipher_suite); pos += 2; /* CompressionMethod compression_method */ *pos++ = TLS_COMPRESSION_NULL; if (conn->session_ticket && conn->session_ticket_cb) { int res = conn->session_ticket_cb( conn->session_ticket_cb_ctx, conn->session_ticket, conn->session_ticket_len, conn->client_random, conn->server_random, conn->master_secret); if (res < 0) { wpa_printf(MSG_DEBUG, "TLSv1: SessionTicket callback " "indicated failure"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_HANDSHAKE_FAILURE); return -1; } conn->use_session_ticket = res; if (conn->use_session_ticket) { if (tlsv1_server_derive_keys(conn, NULL, 0) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to " "derive keys"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } } /* * RFC 4507 specifies that server would include an empty * SessionTicket extension in ServerHello and a * NewSessionTicket message after the ServerHello. However, * EAP-FAST (RFC 4851), i.e., the only user of SessionTicket * extension at the moment, does not use such extensions. * * TODO: Add support for configuring RFC 4507 behavior and make * EAP-FAST disable it. */ } WPA_PUT_BE24(hs_length, pos - hs_length - 3); tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start); if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, rhdr, end - rhdr, hs_start, pos - hs_start, &rlen) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to create TLS record"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } pos = rhdr + rlen; *msgpos = pos; return 0; } static int tls_write_server_certificate(struct tlsv1_server *conn, u8 **msgpos, u8 *end) { u8 *pos, *rhdr, *hs_start, *hs_length, *cert_start; size_t rlen; struct x509_certificate *cert; const struct tls_cipher_suite *suite; suite = tls_get_cipher_suite(conn->rl.cipher_suite); if (suite && suite->key_exchange == TLS_KEY_X_DH_anon) { wpa_printf(MSG_DEBUG, "TLSv1: Do not send Certificate when " "using anonymous DH"); return 0; } pos = *msgpos; wpa_printf(MSG_DEBUG, "TLSv1: Send Certificate"); rhdr = pos; pos += TLS_RECORD_HEADER_LEN; /* opaque fragment[TLSPlaintext.length] */ /* Handshake */ hs_start = pos; /* HandshakeType msg_type */ *pos++ = TLS_HANDSHAKE_TYPE_CERTIFICATE; /* uint24 length (to be filled) */ hs_length = pos; pos += 3; /* body - Certificate */ /* uint24 length (to be filled) */ cert_start = pos; pos += 3; cert = conn->cred->cert; while (cert) { if (pos + 3 + cert->cert_len > end) { wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space " "for Certificate (cert_len=%lu left=%lu)", (unsigned long) cert->cert_len, (unsigned long) (end - pos)); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } WPA_PUT_BE24(pos, cert->cert_len); pos += 3; os_memcpy(pos, cert->cert_start, cert->cert_len); pos += cert->cert_len; if (x509_certificate_self_signed(cert)) break; cert = x509_certificate_get_subject(conn->cred->trusted_certs, &cert->issuer); } if (cert == conn->cred->cert || cert == NULL) { /* * Server was not configured with all the needed certificates * to form a full certificate chain. The client may fail to * validate the chain unless it is configured with all the * missing CA certificates. */ wpa_printf(MSG_DEBUG, "TLSv1: Full server certificate chain " "not configured - validation may fail"); } WPA_PUT_BE24(cert_start, pos - cert_start - 3); WPA_PUT_BE24(hs_length, pos - hs_length - 3); if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, rhdr, end - rhdr, hs_start, pos - hs_start, &rlen) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } pos = rhdr + rlen; tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start); *msgpos = pos; return 0; } static int tls_write_server_key_exchange(struct tlsv1_server *conn, u8 **msgpos, u8 *end) { tls_key_exchange keyx; const struct tls_cipher_suite *suite; u8 *pos, *rhdr, *hs_start, *hs_length; size_t rlen; u8 *dh_ys; size_t dh_ys_len; suite = tls_get_cipher_suite(conn->rl.cipher_suite); if (suite == NULL) keyx = TLS_KEY_X_NULL; else keyx = suite->key_exchange; if (!tls_server_key_exchange_allowed(conn->rl.cipher_suite)) { wpa_printf(MSG_DEBUG, "TLSv1: No ServerKeyExchange needed"); return 0; } if (keyx != TLS_KEY_X_DH_anon) { /* TODO? */ wpa_printf(MSG_DEBUG, "TLSv1: ServerKeyExchange not yet " "supported with key exchange type %d", keyx); return -1; } if (conn->cred == NULL || conn->cred->dh_p == NULL || conn->cred->dh_g == NULL) { wpa_printf(MSG_DEBUG, "TLSv1: No DH parameters available for " "ServerKeyExhcange"); return -1; } os_free(conn->dh_secret); conn->dh_secret_len = conn->cred->dh_p_len; conn->dh_secret = os_malloc(conn->dh_secret_len); if (conn->dh_secret == NULL) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate " "memory for secret (Diffie-Hellman)"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } if (random_get_bytes(conn->dh_secret, conn->dh_secret_len)) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to get random " "data for Diffie-Hellman"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); os_free(conn->dh_secret); conn->dh_secret = NULL; return -1; } if (os_memcmp(conn->dh_secret, conn->cred->dh_p, conn->dh_secret_len) > 0) conn->dh_secret[0] = 0; /* make sure secret < p */ pos = conn->dh_secret; while (pos + 1 < conn->dh_secret + conn->dh_secret_len && *pos == 0) pos++; if (pos != conn->dh_secret) { os_memmove(conn->dh_secret, pos, conn->dh_secret_len - (pos - conn->dh_secret)); conn->dh_secret_len -= pos - conn->dh_secret; } wpa_hexdump_key(MSG_DEBUG, "TLSv1: DH server's secret value", conn->dh_secret, conn->dh_secret_len); /* Ys = g^secret mod p */ dh_ys_len = conn->cred->dh_p_len; dh_ys = os_malloc(dh_ys_len); if (dh_ys == NULL) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate memory for " "Diffie-Hellman"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } if (crypto_mod_exp(conn->cred->dh_g, conn->cred->dh_g_len, conn->dh_secret, conn->dh_secret_len, conn->cred->dh_p, conn->cred->dh_p_len, dh_ys, &dh_ys_len)) { tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); os_free(dh_ys); return -1; } wpa_hexdump(MSG_DEBUG, "TLSv1: DH Ys (server's public value)", dh_ys, dh_ys_len); /* * struct { * select (KeyExchangeAlgorithm) { * case diffie_hellman: * ServerDHParams params; * Signature signed_params; * case rsa: * ServerRSAParams params; * Signature signed_params; * }; * } ServerKeyExchange; * * struct { * opaque dh_p<1..2^16-1>; * opaque dh_g<1..2^16-1>; * opaque dh_Ys<1..2^16-1>; * } ServerDHParams; */ pos = *msgpos; wpa_printf(MSG_DEBUG, "TLSv1: Send ServerKeyExchange"); rhdr = pos; pos += TLS_RECORD_HEADER_LEN; /* opaque fragment[TLSPlaintext.length] */ /* Handshake */ hs_start = pos; /* HandshakeType msg_type */ *pos++ = TLS_HANDSHAKE_TYPE_SERVER_KEY_EXCHANGE; /* uint24 length (to be filled) */ hs_length = pos; pos += 3; /* body - ServerDHParams */ /* dh_p */ if (pos + 2 + conn->cred->dh_p_len > end) { wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space for " "dh_p"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); os_free(dh_ys); return -1; } WPA_PUT_BE16(pos, conn->cred->dh_p_len); pos += 2; os_memcpy(pos, conn->cred->dh_p, conn->cred->dh_p_len); pos += conn->cred->dh_p_len; /* dh_g */ if (pos + 2 + conn->cred->dh_g_len > end) { wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space for " "dh_g"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); os_free(dh_ys); return -1; } WPA_PUT_BE16(pos, conn->cred->dh_g_len); pos += 2; os_memcpy(pos, conn->cred->dh_g, conn->cred->dh_g_len); pos += conn->cred->dh_g_len; /* dh_Ys */ if (pos + 2 + dh_ys_len > end) { wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space for " "dh_Ys"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); os_free(dh_ys); return -1; } WPA_PUT_BE16(pos, dh_ys_len); pos += 2; os_memcpy(pos, dh_ys, dh_ys_len); pos += dh_ys_len; os_free(dh_ys); WPA_PUT_BE24(hs_length, pos - hs_length - 3); if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, rhdr, end - rhdr, hs_start, pos - hs_start, &rlen) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } pos = rhdr + rlen; tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start); *msgpos = pos; return 0; } static int tls_write_server_certificate_request(struct tlsv1_server *conn, u8 **msgpos, u8 *end) { u8 *pos, *rhdr, *hs_start, *hs_length; size_t rlen; if (!conn->verify_peer) { wpa_printf(MSG_DEBUG, "TLSv1: No CertificateRequest needed"); return 0; } pos = *msgpos; wpa_printf(MSG_DEBUG, "TLSv1: Send CertificateRequest"); rhdr = pos; pos += TLS_RECORD_HEADER_LEN; /* opaque fragment[TLSPlaintext.length] */ /* Handshake */ hs_start = pos; /* HandshakeType msg_type */ *pos++ = TLS_HANDSHAKE_TYPE_CERTIFICATE_REQUEST; /* uint24 length (to be filled) */ hs_length = pos; pos += 3; /* body - CertificateRequest */ /* * enum { * rsa_sign(1), dss_sign(2), rsa_fixed_dh(3), dss_fixed_dh(4), * (255) * } ClientCertificateType; * ClientCertificateType certificate_types<1..2^8-1> */ *pos++ = 1; *pos++ = 1; /* rsa_sign */ /* * opaque DistinguishedName<1..2^16-1> * DistinguishedName certificate_authorities<3..2^16-1> */ /* TODO: add support for listing DNs for trusted CAs */ WPA_PUT_BE16(pos, 0); pos += 2; WPA_PUT_BE24(hs_length, pos - hs_length - 3); if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, rhdr, end - rhdr, hs_start, pos - hs_start, &rlen) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } pos = rhdr + rlen; tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start); *msgpos = pos; return 0; } static int tls_write_server_hello_done(struct tlsv1_server *conn, u8 **msgpos, u8 *end) { u8 *pos; size_t rlen; u8 payload[4]; wpa_printf(MSG_DEBUG, "TLSv1: Send ServerHelloDone"); /* opaque fragment[TLSPlaintext.length] */ /* Handshake */ pos = payload; /* HandshakeType msg_type */ *pos++ = TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE; /* uint24 length */ WPA_PUT_BE24(pos, 0); pos += 3; /* body - ServerHelloDone (empty) */ if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, *msgpos, end - *msgpos, payload, pos - payload, &rlen) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } tls_verify_hash_add(&conn->verify, payload, pos - payload); *msgpos += rlen; return 0; } static int tls_write_server_change_cipher_spec(struct tlsv1_server *conn, u8 **msgpos, u8 *end) { size_t rlen; u8 payload[1]; wpa_printf(MSG_DEBUG, "TLSv1: Send ChangeCipherSpec"); payload[0] = TLS_CHANGE_CIPHER_SPEC; if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC, *msgpos, end - *msgpos, payload, sizeof(payload), &rlen) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } if (tlsv1_record_change_write_cipher(&conn->rl) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to set write cipher for " "record layer"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } *msgpos += rlen; return 0; } static int tls_write_server_finished(struct tlsv1_server *conn, u8 **msgpos, u8 *end) { u8 *pos, *hs_start; size_t rlen, hlen; u8 verify_data[1 + 3 + TLS_VERIFY_DATA_LEN]; u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN]; pos = *msgpos; wpa_printf(MSG_DEBUG, "TLSv1: Send Finished"); /* Encrypted Handshake Message: Finished */ #ifdef CONFIG_TLSV12 if (conn->rl.tls_version >= TLS_VERSION_1_2) { hlen = SHA256_MAC_LEN; if (conn->verify.sha256_server == NULL || crypto_hash_finish(conn->verify.sha256_server, hash, &hlen) < 0) { conn->verify.sha256_server = NULL; tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } conn->verify.sha256_server = NULL; } else { #endif /* CONFIG_TLSV12 */ hlen = MD5_MAC_LEN; if (conn->verify.md5_server == NULL || crypto_hash_finish(conn->verify.md5_server, hash, &hlen) < 0) { tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); conn->verify.md5_server = NULL; crypto_hash_finish(conn->verify.sha1_server, NULL, NULL); conn->verify.sha1_server = NULL; return -1; } conn->verify.md5_server = NULL; hlen = SHA1_MAC_LEN; if (conn->verify.sha1_server == NULL || crypto_hash_finish(conn->verify.sha1_server, hash + MD5_MAC_LEN, &hlen) < 0) { conn->verify.sha1_server = NULL; tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } conn->verify.sha1_server = NULL; hlen = MD5_MAC_LEN + SHA1_MAC_LEN; #ifdef CONFIG_TLSV12 } #endif /* CONFIG_TLSV12 */ if (tls_prf(conn->rl.tls_version, conn->master_secret, TLS_MASTER_SECRET_LEN, "server finished", hash, hlen, verify_data + 1 + 3, TLS_VERIFY_DATA_LEN)) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate verify_data"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } wpa_hexdump_key(MSG_DEBUG, "TLSv1: verify_data (server)", verify_data + 1 + 3, TLS_VERIFY_DATA_LEN); /* Handshake */ pos = hs_start = verify_data; /* HandshakeType msg_type */ *pos++ = TLS_HANDSHAKE_TYPE_FINISHED; /* uint24 length */ WPA_PUT_BE24(pos, TLS_VERIFY_DATA_LEN); pos += 3; pos += TLS_VERIFY_DATA_LEN; tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start); if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, *msgpos, end - *msgpos, hs_start, pos - hs_start, &rlen) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } *msgpos += rlen; return 0; } static u8 * tls_send_server_hello(struct tlsv1_server *conn, size_t *out_len) { u8 *msg, *end, *pos; size_t msglen; *out_len = 0; msglen = 1000 + tls_server_cert_chain_der_len(conn); msg = os_malloc(msglen); if (msg == NULL) return NULL; pos = msg; end = msg + msglen; if (tls_write_server_hello(conn, &pos, end) < 0) { os_free(msg); return NULL; } if (conn->use_session_ticket) { /* Abbreviated handshake using session ticket; RFC 4507 */ if (tls_write_server_change_cipher_spec(conn, &pos, end) < 0 || tls_write_server_finished(conn, &pos, end) < 0) { os_free(msg); return NULL; } *out_len = pos - msg; conn->state = CHANGE_CIPHER_SPEC; return msg; } /* Full handshake */ if (tls_write_server_certificate(conn, &pos, end) < 0 || tls_write_server_key_exchange(conn, &pos, end) < 0 || tls_write_server_certificate_request(conn, &pos, end) < 0 || tls_write_server_hello_done(conn, &pos, end) < 0) { os_free(msg); return NULL; } *out_len = pos - msg; conn->state = CLIENT_CERTIFICATE; return msg; } static u8 * tls_send_change_cipher_spec(struct tlsv1_server *conn, size_t *out_len) { u8 *msg, *end, *pos; *out_len = 0; msg = os_malloc(1000); if (msg == NULL) return NULL; pos = msg; end = msg + 1000; if (tls_write_server_change_cipher_spec(conn, &pos, end) < 0 || tls_write_server_finished(conn, &pos, end) < 0) { os_free(msg); return NULL; } *out_len = pos - msg; wpa_printf(MSG_DEBUG, "TLSv1: Handshake completed successfully"); conn->state = ESTABLISHED; return msg; } u8 * tlsv1_server_handshake_write(struct tlsv1_server *conn, size_t *out_len) { switch (conn->state) { case SERVER_HELLO: return tls_send_server_hello(conn, out_len); case SERVER_CHANGE_CIPHER_SPEC: return tls_send_change_cipher_spec(conn, out_len); default: if (conn->state == ESTABLISHED && conn->use_session_ticket) { /* Abbreviated handshake was already completed. */ return NULL; } wpa_printf(MSG_DEBUG, "TLSv1: Unexpected state %d while " "generating reply", conn->state); return NULL; } } u8 * tlsv1_server_send_alert(struct tlsv1_server *conn, u8 level, u8 description, size_t *out_len) { u8 *alert, *pos, *length; wpa_printf(MSG_DEBUG, "TLSv1: Send Alert(%d:%d)", level, description); *out_len = 0; alert = os_malloc(10); if (alert == NULL) return NULL; pos = alert; /* TLSPlaintext */ /* ContentType type */ *pos++ = TLS_CONTENT_TYPE_ALERT; /* ProtocolVersion version */ WPA_PUT_BE16(pos, conn->rl.tls_version ? conn->rl.tls_version : TLS_VERSION); pos += 2; /* uint16 length (to be filled) */ length = pos; pos += 2; /* opaque fragment[TLSPlaintext.length] */ /* Alert */ /* AlertLevel level */ *pos++ = level; /* AlertDescription description */ *pos++ = description; WPA_PUT_BE16(length, pos - length - 2); *out_len = pos - alert; return alert; } wpa-2.1/src/tls/x509v3.c000066400000000000000000001405041227414666700147010ustar00rootroot00000000000000/* * X.509v3 certificate parsing and processing (RFC 3280 profile) * Copyright (c) 2006-2011, 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/crypto.h" #include "asn1.h" #include "x509v3.h" static void x509_free_name(struct x509_name *name) { size_t i; for (i = 0; i < name->num_attr; i++) { os_free(name->attr[i].value); name->attr[i].value = NULL; name->attr[i].type = X509_NAME_ATTR_NOT_USED; } name->num_attr = 0; os_free(name->email); name->email = NULL; os_free(name->alt_email); os_free(name->dns); os_free(name->uri); os_free(name->ip); name->alt_email = name->dns = name->uri = NULL; name->ip = NULL; name->ip_len = 0; os_memset(&name->rid, 0, sizeof(name->rid)); } /** * x509_certificate_free - Free an X.509 certificate * @cert: Certificate to be freed */ void x509_certificate_free(struct x509_certificate *cert) { if (cert == NULL) return; if (cert->next) { wpa_printf(MSG_DEBUG, "X509: x509_certificate_free: cer=%p " "was still on a list (next=%p)\n", cert, cert->next); } x509_free_name(&cert->issuer); x509_free_name(&cert->subject); os_free(cert->public_key); os_free(cert->sign_value); os_free(cert); } /** * x509_certificate_free - Free an X.509 certificate chain * @cert: Pointer to the first certificate in the chain */ void x509_certificate_chain_free(struct x509_certificate *cert) { struct x509_certificate *next; while (cert) { next = cert->next; cert->next = NULL; x509_certificate_free(cert); cert = next; } } static int x509_whitespace(char c) { return c == ' ' || c == '\t'; } static void x509_str_strip_whitespace(char *a) { char *ipos, *opos; int remove_whitespace = 1; ipos = opos = a; while (*ipos) { if (remove_whitespace && x509_whitespace(*ipos)) ipos++; else { remove_whitespace = x509_whitespace(*ipos); *opos++ = *ipos++; } } *opos-- = '\0'; if (opos > a && x509_whitespace(*opos)) *opos = '\0'; } static int x509_str_compare(const char *a, const char *b) { char *aa, *bb; int ret; if (!a && b) return -1; if (a && !b) return 1; if (!a && !b) return 0; aa = os_strdup(a); bb = os_strdup(b); if (aa == NULL || bb == NULL) { os_free(aa); os_free(bb); return os_strcasecmp(a, b); } x509_str_strip_whitespace(aa); x509_str_strip_whitespace(bb); ret = os_strcasecmp(aa, bb); os_free(aa); os_free(bb); return ret; } /** * x509_name_compare - Compare X.509 certificate names * @a: Certificate name * @b: Certificate name * Returns: <0, 0, or >0 based on whether a is less than, equal to, or * greater than b */ int x509_name_compare(struct x509_name *a, struct x509_name *b) { int res; size_t i; if (!a && b) return -1; if (a && !b) return 1; if (!a && !b) return 0; if (a->num_attr < b->num_attr) return -1; if (a->num_attr > b->num_attr) return 1; for (i = 0; i < a->num_attr; i++) { if (a->attr[i].type < b->attr[i].type) return -1; if (a->attr[i].type > b->attr[i].type) return -1; res = x509_str_compare(a->attr[i].value, b->attr[i].value); if (res) return res; } res = x509_str_compare(a->email, b->email); if (res) return res; return 0; } static int x509_parse_algorithm_identifier( const u8 *buf, size_t len, struct x509_algorithm_identifier *id, const u8 **next) { struct asn1_hdr hdr; const u8 *pos, *end; /* * AlgorithmIdentifier ::= SEQUENCE { * algorithm OBJECT IDENTIFIER, * parameters ANY DEFINED BY algorithm OPTIONAL * } */ if (asn1_get_next(buf, len, &hdr) < 0 || hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_SEQUENCE) { wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE " "(AlgorithmIdentifier) - found class %d tag 0x%x", hdr.class, hdr.tag); return -1; } pos = hdr.payload; end = pos + hdr.length; if (end > buf + len) return -1; *next = end; if (asn1_get_oid(pos, end - pos, &id->oid, &pos)) return -1; /* TODO: optional parameters */ return 0; } static int x509_parse_public_key(const u8 *buf, size_t len, struct x509_certificate *cert, const u8 **next) { struct asn1_hdr hdr; const u8 *pos, *end; /* * SubjectPublicKeyInfo ::= SEQUENCE { * algorithm AlgorithmIdentifier, * subjectPublicKey BIT STRING * } */ pos = buf; end = buf + len; if (asn1_get_next(pos, end - pos, &hdr) < 0 || hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_SEQUENCE) { wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE " "(SubjectPublicKeyInfo) - found class %d tag 0x%x", hdr.class, hdr.tag); return -1; } pos = hdr.payload; if (pos + hdr.length > end) return -1; end = pos + hdr.length; *next = end; if (x509_parse_algorithm_identifier(pos, end - pos, &cert->public_key_alg, &pos)) return -1; if (asn1_get_next(pos, end - pos, &hdr) < 0 || hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_BITSTRING) { wpa_printf(MSG_DEBUG, "X509: Expected BITSTRING " "(subjectPublicKey) - found class %d tag 0x%x", hdr.class, hdr.tag); return -1; } if (hdr.length < 1) return -1; pos = hdr.payload; if (*pos) { wpa_printf(MSG_DEBUG, "X509: BITSTRING - %d unused bits", *pos); /* * TODO: should this be rejected? X.509 certificates are * unlikely to use such a construction. Now we would end up * including the extra bits in the buffer which may also be * ok. */ } os_free(cert->public_key); cert->public_key = os_malloc(hdr.length - 1); if (cert->public_key == NULL) { wpa_printf(MSG_DEBUG, "X509: Failed to allocate memory for " "public key"); return -1; } os_memcpy(cert->public_key, pos + 1, hdr.length - 1); cert->public_key_len = hdr.length - 1; wpa_hexdump(MSG_MSGDUMP, "X509: subjectPublicKey", cert->public_key, cert->public_key_len); return 0; } static int x509_parse_name(const u8 *buf, size_t len, struct x509_name *name, const u8 **next) { struct asn1_hdr hdr; const u8 *pos, *end, *set_pos, *set_end, *seq_pos, *seq_end; struct asn1_oid oid; char *val; /* * Name ::= CHOICE { RDNSequence } * RDNSequence ::= SEQUENCE OF RelativeDistinguishedName * RelativeDistinguishedName ::= SET OF AttributeTypeAndValue * AttributeTypeAndValue ::= SEQUENCE { * type AttributeType, * value AttributeValue * } * AttributeType ::= OBJECT IDENTIFIER * AttributeValue ::= ANY DEFINED BY AttributeType */ if (asn1_get_next(buf, len, &hdr) < 0 || hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_SEQUENCE) { wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE " "(Name / RDNSequencer) - found class %d tag 0x%x", hdr.class, hdr.tag); return -1; } pos = hdr.payload; if (pos + hdr.length > buf + len) return -1; end = *next = pos + hdr.length; while (pos < end) { enum x509_name_attr_type type; if (asn1_get_next(pos, end - pos, &hdr) < 0 || hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_SET) { wpa_printf(MSG_DEBUG, "X509: Expected SET " "(RelativeDistinguishedName) - found class " "%d tag 0x%x", hdr.class, hdr.tag); x509_free_name(name); return -1; } set_pos = hdr.payload; pos = set_end = hdr.payload + hdr.length; if (asn1_get_next(set_pos, set_end - set_pos, &hdr) < 0 || hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_SEQUENCE) { wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE " "(AttributeTypeAndValue) - found class %d " "tag 0x%x", hdr.class, hdr.tag); x509_free_name(name); return -1; } seq_pos = hdr.payload; seq_end = hdr.payload + hdr.length; if (asn1_get_oid(seq_pos, seq_end - seq_pos, &oid, &seq_pos)) { x509_free_name(name); return -1; } if (asn1_get_next(seq_pos, seq_end - seq_pos, &hdr) < 0 || hdr.class != ASN1_CLASS_UNIVERSAL) { wpa_printf(MSG_DEBUG, "X509: Failed to parse " "AttributeValue"); x509_free_name(name); return -1; } /* RFC 3280: * MUST: country, organization, organizational-unit, * distinguished name qualifier, state or province name, * common name, serial number. * SHOULD: locality, title, surname, given name, initials, * pseudonym, generation qualifier. * MUST: domainComponent (RFC 2247). */ type = X509_NAME_ATTR_NOT_USED; if (oid.len == 4 && oid.oid[0] == 2 && oid.oid[1] == 5 && oid.oid[2] == 4) { /* id-at ::= 2.5.4 */ switch (oid.oid[3]) { case 3: /* commonName */ type = X509_NAME_ATTR_CN; break; case 6: /* countryName */ type = X509_NAME_ATTR_C; break; case 7: /* localityName */ type = X509_NAME_ATTR_L; break; case 8: /* stateOrProvinceName */ type = X509_NAME_ATTR_ST; break; case 10: /* organizationName */ type = X509_NAME_ATTR_O; break; case 11: /* organizationalUnitName */ type = X509_NAME_ATTR_OU; break; } } else if (oid.len == 7 && oid.oid[0] == 1 && oid.oid[1] == 2 && oid.oid[2] == 840 && oid.oid[3] == 113549 && oid.oid[4] == 1 && oid.oid[5] == 9 && oid.oid[6] == 1) { /* 1.2.840.113549.1.9.1 - e-mailAddress */ os_free(name->email); name->email = os_malloc(hdr.length + 1); if (name->email == NULL) { x509_free_name(name); return -1; } os_memcpy(name->email, hdr.payload, hdr.length); name->email[hdr.length] = '\0'; continue; } else if (oid.len == 7 && oid.oid[0] == 0 && oid.oid[1] == 9 && oid.oid[2] == 2342 && oid.oid[3] == 19200300 && oid.oid[4] == 100 && oid.oid[5] == 1 && oid.oid[6] == 25) { /* 0.9.2342.19200300.100.1.25 - domainComponent */ type = X509_NAME_ATTR_DC; } if (type == X509_NAME_ATTR_NOT_USED) { wpa_hexdump(MSG_DEBUG, "X509: Unrecognized OID", (u8 *) oid.oid, oid.len * sizeof(oid.oid[0])); wpa_hexdump_ascii(MSG_MSGDUMP, "X509: Attribute Data", hdr.payload, hdr.length); continue; } if (name->num_attr == X509_MAX_NAME_ATTRIBUTES) { wpa_printf(MSG_INFO, "X509: Too many Name attributes"); x509_free_name(name); return -1; } val = dup_binstr(hdr.payload, hdr.length); if (val == NULL) { x509_free_name(name); return -1; } if (os_strlen(val) != hdr.length) { wpa_printf(MSG_INFO, "X509: Reject certificate with " "embedded NUL byte in a string (%s[NUL])", val); os_free(val); x509_free_name(name); return -1; } name->attr[name->num_attr].type = type; name->attr[name->num_attr].value = val; name->num_attr++; } return 0; } static char * x509_name_attr_str(enum x509_name_attr_type type) { switch (type) { case X509_NAME_ATTR_NOT_USED: return "[N/A]"; case X509_NAME_ATTR_DC: return "DC"; case X509_NAME_ATTR_CN: return "CN"; case X509_NAME_ATTR_C: return "C"; case X509_NAME_ATTR_L: return "L"; case X509_NAME_ATTR_ST: return "ST"; case X509_NAME_ATTR_O: return "O"; case X509_NAME_ATTR_OU: return "OU"; } return "?"; } /** * x509_name_string - Convert an X.509 certificate name into a string * @name: Name to convert * @buf: Buffer for the string * @len: Maximum buffer length */ void x509_name_string(struct x509_name *name, char *buf, size_t len) { char *pos, *end; int ret; size_t i; if (len == 0) return; pos = buf; end = buf + len; for (i = 0; i < name->num_attr; i++) { ret = os_snprintf(pos, end - pos, "%s=%s, ", x509_name_attr_str(name->attr[i].type), name->attr[i].value); if (ret < 0 || ret >= end - pos) goto done; pos += ret; } if (pos > buf + 1 && pos[-1] == ' ' && pos[-2] == ',') { pos--; *pos = '\0'; pos--; *pos = '\0'; } if (name->email) { ret = os_snprintf(pos, end - pos, "/emailAddress=%s", name->email); if (ret < 0 || ret >= end - pos) goto done; pos += ret; } done: end[-1] = '\0'; } static int x509_parse_time(const u8 *buf, size_t len, u8 asn1_tag, os_time_t *val) { const char *pos; int year, month, day, hour, min, sec; /* * Time ::= CHOICE { * utcTime UTCTime, * generalTime GeneralizedTime * } * * UTCTime: YYMMDDHHMMSSZ * GeneralizedTime: YYYYMMDDHHMMSSZ */ pos = (const char *) buf; switch (asn1_tag) { case ASN1_TAG_UTCTIME: if (len != 13 || buf[12] != 'Z') { wpa_hexdump_ascii(MSG_DEBUG, "X509: Unrecognized " "UTCTime format", buf, len); return -1; } if (sscanf(pos, "%02d", &year) != 1) { wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse " "UTCTime year", buf, len); return -1; } if (year < 50) year += 2000; else year += 1900; pos += 2; break; case ASN1_TAG_GENERALIZEDTIME: if (len != 15 || buf[14] != 'Z') { wpa_hexdump_ascii(MSG_DEBUG, "X509: Unrecognized " "GeneralizedTime format", buf, len); return -1; } if (sscanf(pos, "%04d", &year) != 1) { wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse " "GeneralizedTime year", buf, len); return -1; } pos += 4; break; default: wpa_printf(MSG_DEBUG, "X509: Expected UTCTime or " "GeneralizedTime - found tag 0x%x", asn1_tag); return -1; } if (sscanf(pos, "%02d", &month) != 1) { wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time " "(month)", buf, len); return -1; } pos += 2; if (sscanf(pos, "%02d", &day) != 1) { wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time " "(day)", buf, len); return -1; } pos += 2; if (sscanf(pos, "%02d", &hour) != 1) { wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time " "(hour)", buf, len); return -1; } pos += 2; if (sscanf(pos, "%02d", &min) != 1) { wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time " "(min)", buf, len); return -1; } pos += 2; if (sscanf(pos, "%02d", &sec) != 1) { wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time " "(sec)", buf, len); return -1; } if (os_mktime(year, month, day, hour, min, sec, val) < 0) { wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to convert Time", buf, len); if (year < 1970) { /* * At least some test certificates have been configured * to use dates prior to 1970. Set the date to * beginning of 1970 to handle these case. */ wpa_printf(MSG_DEBUG, "X509: Year=%d before epoch - " "assume epoch as the time", year); *val = 0; return 0; } return -1; } return 0; } static int x509_parse_validity(const u8 *buf, size_t len, struct x509_certificate *cert, const u8 **next) { struct asn1_hdr hdr; const u8 *pos; size_t plen; /* * Validity ::= SEQUENCE { * notBefore Time, * notAfter Time * } * * RFC 3280, 4.1.2.5: * CAs conforming to this profile MUST always encode certificate * validity dates through the year 2049 as UTCTime; certificate * validity dates in 2050 or later MUST be encoded as GeneralizedTime. */ if (asn1_get_next(buf, len, &hdr) < 0 || hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_SEQUENCE) { wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE " "(Validity) - found class %d tag 0x%x", hdr.class, hdr.tag); return -1; } pos = hdr.payload; plen = hdr.length; if (pos + plen > buf + len) return -1; *next = pos + plen; if (asn1_get_next(pos, plen, &hdr) < 0 || hdr.class != ASN1_CLASS_UNIVERSAL || x509_parse_time(hdr.payload, hdr.length, hdr.tag, &cert->not_before) < 0) { wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse notBefore " "Time", hdr.payload, hdr.length); return -1; } pos = hdr.payload + hdr.length; plen = *next - pos; if (asn1_get_next(pos, plen, &hdr) < 0 || hdr.class != ASN1_CLASS_UNIVERSAL || x509_parse_time(hdr.payload, hdr.length, hdr.tag, &cert->not_after) < 0) { wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse notAfter " "Time", hdr.payload, hdr.length); return -1; } wpa_printf(MSG_MSGDUMP, "X509: Validity: notBefore: %lu notAfter: %lu", (unsigned long) cert->not_before, (unsigned long) cert->not_after); return 0; } static int x509_id_ce_oid(struct asn1_oid *oid) { /* id-ce arc from X.509 for standard X.509v3 extensions */ return oid->len >= 4 && oid->oid[0] == 2 /* joint-iso-ccitt */ && oid->oid[1] == 5 /* ds */ && oid->oid[2] == 29 /* id-ce */; } static int x509_parse_ext_key_usage(struct x509_certificate *cert, const u8 *pos, size_t len) { struct asn1_hdr hdr; /* * KeyUsage ::= BIT STRING { * digitalSignature (0), * nonRepudiation (1), * keyEncipherment (2), * dataEncipherment (3), * keyAgreement (4), * keyCertSign (5), * cRLSign (6), * encipherOnly (7), * decipherOnly (8) } */ if (asn1_get_next(pos, len, &hdr) < 0 || hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_BITSTRING || hdr.length < 1) { wpa_printf(MSG_DEBUG, "X509: Expected BIT STRING in " "KeyUsage; found %d tag 0x%x len %d", hdr.class, hdr.tag, hdr.length); return -1; } cert->extensions_present |= X509_EXT_KEY_USAGE; cert->key_usage = asn1_bit_string_to_long(hdr.payload, hdr.length); wpa_printf(MSG_DEBUG, "X509: KeyUsage 0x%lx", cert->key_usage); return 0; } static int x509_parse_ext_basic_constraints(struct x509_certificate *cert, const u8 *pos, size_t len) { struct asn1_hdr hdr; unsigned long value; size_t left; /* * BasicConstraints ::= SEQUENCE { * cA BOOLEAN DEFAULT FALSE, * pathLenConstraint INTEGER (0..MAX) OPTIONAL } */ if (asn1_get_next(pos, len, &hdr) < 0 || hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_SEQUENCE) { wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE in " "BasicConstraints; found %d tag 0x%x", hdr.class, hdr.tag); return -1; } cert->extensions_present |= X509_EXT_BASIC_CONSTRAINTS; if (hdr.length == 0) return 0; if (asn1_get_next(hdr.payload, hdr.length, &hdr) < 0 || hdr.class != ASN1_CLASS_UNIVERSAL) { wpa_printf(MSG_DEBUG, "X509: Failed to parse " "BasicConstraints"); return -1; } if (hdr.tag == ASN1_TAG_BOOLEAN) { if (hdr.length != 1) { wpa_printf(MSG_DEBUG, "X509: Unexpected " "Boolean length (%u) in BasicConstraints", hdr.length); return -1; } cert->ca = hdr.payload[0]; if (hdr.payload + hdr.length == pos + len) { wpa_printf(MSG_DEBUG, "X509: BasicConstraints - cA=%d", cert->ca); return 0; } if (asn1_get_next(hdr.payload + hdr.length, len - hdr.length, &hdr) < 0 || hdr.class != ASN1_CLASS_UNIVERSAL) { wpa_printf(MSG_DEBUG, "X509: Failed to parse " "BasicConstraints"); return -1; } } if (hdr.tag != ASN1_TAG_INTEGER) { wpa_printf(MSG_DEBUG, "X509: Expected INTEGER in " "BasicConstraints; found class %d tag 0x%x", hdr.class, hdr.tag); return -1; } pos = hdr.payload; left = hdr.length; value = 0; while (left) { value <<= 8; value |= *pos++; left--; } cert->path_len_constraint = value; cert->extensions_present |= X509_EXT_PATH_LEN_CONSTRAINT; wpa_printf(MSG_DEBUG, "X509: BasicConstraints - cA=%d " "pathLenConstraint=%lu", cert->ca, cert->path_len_constraint); return 0; } static int x509_parse_alt_name_rfc8222(struct x509_name *name, const u8 *pos, size_t len) { /* rfc822Name IA5String */ wpa_hexdump_ascii(MSG_MSGDUMP, "X509: altName - rfc822Name", pos, len); os_free(name->alt_email); name->alt_email = os_zalloc(len + 1); if (name->alt_email == NULL) return -1; os_memcpy(name->alt_email, pos, len); if (os_strlen(name->alt_email) != len) { wpa_printf(MSG_INFO, "X509: Reject certificate with " "embedded NUL byte in rfc822Name (%s[NUL])", name->alt_email); os_free(name->alt_email); name->alt_email = NULL; return -1; } return 0; } static int x509_parse_alt_name_dns(struct x509_name *name, const u8 *pos, size_t len) { /* dNSName IA5String */ wpa_hexdump_ascii(MSG_MSGDUMP, "X509: altName - dNSName", pos, len); os_free(name->dns); name->dns = os_zalloc(len + 1); if (name->dns == NULL) return -1; os_memcpy(name->dns, pos, len); if (os_strlen(name->dns) != len) { wpa_printf(MSG_INFO, "X509: Reject certificate with " "embedded NUL byte in dNSName (%s[NUL])", name->dns); os_free(name->dns); name->dns = NULL; return -1; } return 0; } static int x509_parse_alt_name_uri(struct x509_name *name, const u8 *pos, size_t len) { /* uniformResourceIdentifier IA5String */ wpa_hexdump_ascii(MSG_MSGDUMP, "X509: altName - uniformResourceIdentifier", pos, len); os_free(name->uri); name->uri = os_zalloc(len + 1); if (name->uri == NULL) return -1; os_memcpy(name->uri, pos, len); if (os_strlen(name->uri) != len) { wpa_printf(MSG_INFO, "X509: Reject certificate with " "embedded NUL byte in uniformResourceIdentifier " "(%s[NUL])", name->uri); os_free(name->uri); name->uri = NULL; return -1; } return 0; } static int x509_parse_alt_name_ip(struct x509_name *name, const u8 *pos, size_t len) { /* iPAddress OCTET STRING */ wpa_hexdump(MSG_MSGDUMP, "X509: altName - iPAddress", pos, len); os_free(name->ip); name->ip = os_malloc(len); if (name->ip == NULL) return -1; os_memcpy(name->ip, pos, len); name->ip_len = len; return 0; } static int x509_parse_alt_name_rid(struct x509_name *name, const u8 *pos, size_t len) { char buf[80]; /* registeredID OBJECT IDENTIFIER */ if (asn1_parse_oid(pos, len, &name->rid) < 0) return -1; asn1_oid_to_str(&name->rid, buf, sizeof(buf)); wpa_printf(MSG_MSGDUMP, "X509: altName - registeredID: %s", buf); return 0; } static int x509_parse_ext_alt_name(struct x509_name *name, const u8 *pos, size_t len) { struct asn1_hdr hdr; const u8 *p, *end; /* * GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName * * GeneralName ::= CHOICE { * otherName [0] OtherName, * rfc822Name [1] IA5String, * dNSName [2] IA5String, * x400Address [3] ORAddress, * directoryName [4] Name, * ediPartyName [5] EDIPartyName, * uniformResourceIdentifier [6] IA5String, * iPAddress [7] OCTET STRING, * registeredID [8] OBJECT IDENTIFIER } * * OtherName ::= SEQUENCE { * type-id OBJECT IDENTIFIER, * value [0] EXPLICIT ANY DEFINED BY type-id } * * EDIPartyName ::= SEQUENCE { * nameAssigner [0] DirectoryString OPTIONAL, * partyName [1] DirectoryString } */ for (p = pos, end = pos + len; p < end; p = hdr.payload + hdr.length) { int res; if (asn1_get_next(p, end - p, &hdr) < 0) { wpa_printf(MSG_DEBUG, "X509: Failed to parse " "SubjectAltName item"); return -1; } if (hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC) continue; switch (hdr.tag) { case 1: res = x509_parse_alt_name_rfc8222(name, hdr.payload, hdr.length); break; case 2: res = x509_parse_alt_name_dns(name, hdr.payload, hdr.length); break; case 6: res = x509_parse_alt_name_uri(name, hdr.payload, hdr.length); break; case 7: res = x509_parse_alt_name_ip(name, hdr.payload, hdr.length); break; case 8: res = x509_parse_alt_name_rid(name, hdr.payload, hdr.length); break; case 0: /* TODO: otherName */ case 3: /* TODO: x500Address */ case 4: /* TODO: directoryName */ case 5: /* TODO: ediPartyName */ default: res = 0; break; } if (res < 0) return res; } return 0; } static int x509_parse_ext_subject_alt_name(struct x509_certificate *cert, const u8 *pos, size_t len) { struct asn1_hdr hdr; /* SubjectAltName ::= GeneralNames */ if (asn1_get_next(pos, len, &hdr) < 0 || hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_SEQUENCE) { wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE in " "SubjectAltName; found %d tag 0x%x", hdr.class, hdr.tag); return -1; } wpa_printf(MSG_DEBUG, "X509: SubjectAltName"); cert->extensions_present |= X509_EXT_SUBJECT_ALT_NAME; if (hdr.length == 0) return 0; return x509_parse_ext_alt_name(&cert->subject, hdr.payload, hdr.length); } static int x509_parse_ext_issuer_alt_name(struct x509_certificate *cert, const u8 *pos, size_t len) { struct asn1_hdr hdr; /* IssuerAltName ::= GeneralNames */ if (asn1_get_next(pos, len, &hdr) < 0 || hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_SEQUENCE) { wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE in " "IssuerAltName; found %d tag 0x%x", hdr.class, hdr.tag); return -1; } wpa_printf(MSG_DEBUG, "X509: IssuerAltName"); cert->extensions_present |= X509_EXT_ISSUER_ALT_NAME; if (hdr.length == 0) return 0; return x509_parse_ext_alt_name(&cert->issuer, hdr.payload, hdr.length); } static int x509_parse_extension_data(struct x509_certificate *cert, struct asn1_oid *oid, const u8 *pos, size_t len) { if (!x509_id_ce_oid(oid)) return 1; /* TODO: add other extensions required by RFC 3280, Ch 4.2: * certificate policies (section 4.2.1.5) * name constraints (section 4.2.1.11) * policy constraints (section 4.2.1.12) * extended key usage (section 4.2.1.13) * inhibit any-policy (section 4.2.1.15) */ switch (oid->oid[3]) { case 15: /* id-ce-keyUsage */ return x509_parse_ext_key_usage(cert, pos, len); case 17: /* id-ce-subjectAltName */ return x509_parse_ext_subject_alt_name(cert, pos, len); case 18: /* id-ce-issuerAltName */ return x509_parse_ext_issuer_alt_name(cert, pos, len); case 19: /* id-ce-basicConstraints */ return x509_parse_ext_basic_constraints(cert, pos, len); default: return 1; } } static int x509_parse_extension(struct x509_certificate *cert, const u8 *pos, size_t len, const u8 **next) { const u8 *end; struct asn1_hdr hdr; struct asn1_oid oid; int critical_ext = 0, res; char buf[80]; /* * Extension ::= SEQUENCE { * extnID OBJECT IDENTIFIER, * critical BOOLEAN DEFAULT FALSE, * extnValue OCTET STRING * } */ if (asn1_get_next(pos, len, &hdr) < 0 || hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_SEQUENCE) { wpa_printf(MSG_DEBUG, "X509: Unexpected ASN.1 header in " "Extensions: class %d tag 0x%x; expected SEQUENCE", hdr.class, hdr.tag); return -1; } pos = hdr.payload; *next = end = pos + hdr.length; if (asn1_get_oid(pos, end - pos, &oid, &pos) < 0) { wpa_printf(MSG_DEBUG, "X509: Unexpected ASN.1 data for " "Extension (expected OID)"); return -1; } if (asn1_get_next(pos, end - pos, &hdr) < 0 || hdr.class != ASN1_CLASS_UNIVERSAL || (hdr.tag != ASN1_TAG_BOOLEAN && hdr.tag != ASN1_TAG_OCTETSTRING)) { wpa_printf(MSG_DEBUG, "X509: Unexpected ASN.1 header in " "Extensions: class %d tag 0x%x; expected BOOLEAN " "or OCTET STRING", hdr.class, hdr.tag); return -1; } if (hdr.tag == ASN1_TAG_BOOLEAN) { if (hdr.length != 1) { wpa_printf(MSG_DEBUG, "X509: Unexpected " "Boolean length (%u)", hdr.length); return -1; } critical_ext = hdr.payload[0]; pos = hdr.payload; if (asn1_get_next(pos, end - pos, &hdr) < 0 || (hdr.class != ASN1_CLASS_UNIVERSAL && hdr.class != ASN1_CLASS_PRIVATE) || hdr.tag != ASN1_TAG_OCTETSTRING) { wpa_printf(MSG_DEBUG, "X509: Unexpected ASN.1 header " "in Extensions: class %d tag 0x%x; " "expected OCTET STRING", hdr.class, hdr.tag); return -1; } } asn1_oid_to_str(&oid, buf, sizeof(buf)); wpa_printf(MSG_DEBUG, "X509: Extension: extnID=%s critical=%d", buf, critical_ext); wpa_hexdump(MSG_MSGDUMP, "X509: extnValue", hdr.payload, hdr.length); res = x509_parse_extension_data(cert, &oid, hdr.payload, hdr.length); if (res < 0) return res; if (res == 1 && critical_ext) { wpa_printf(MSG_INFO, "X509: Unknown critical extension %s", buf); return -1; } return 0; } static int x509_parse_extensions(struct x509_certificate *cert, const u8 *pos, size_t len) { const u8 *end; struct asn1_hdr hdr; /* Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension */ if (asn1_get_next(pos, len, &hdr) < 0 || hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_SEQUENCE) { wpa_printf(MSG_DEBUG, "X509: Unexpected ASN.1 data " "for Extensions: class %d tag 0x%x; " "expected SEQUENCE", hdr.class, hdr.tag); return -1; } pos = hdr.payload; end = pos + hdr.length; while (pos < end) { if (x509_parse_extension(cert, pos, end - pos, &pos) < 0) return -1; } return 0; } static int x509_parse_tbs_certificate(const u8 *buf, size_t len, struct x509_certificate *cert, const u8 **next) { struct asn1_hdr hdr; const u8 *pos, *end; size_t left; char sbuf[128]; unsigned long value; /* tbsCertificate TBSCertificate ::= SEQUENCE */ if (asn1_get_next(buf, len, &hdr) < 0 || hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_SEQUENCE) { wpa_printf(MSG_DEBUG, "X509: tbsCertificate did not start " "with a valid SEQUENCE - found class %d tag 0x%x", hdr.class, hdr.tag); return -1; } pos = hdr.payload; end = *next = pos + hdr.length; /* * version [0] EXPLICIT Version DEFAULT v1 * Version ::= INTEGER { v1(0), v2(1), v3(2) } */ if (asn1_get_next(pos, end - pos, &hdr) < 0) return -1; pos = hdr.payload; if (hdr.class == ASN1_CLASS_CONTEXT_SPECIFIC) { if (asn1_get_next(pos, end - pos, &hdr) < 0) return -1; if (hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_INTEGER) { wpa_printf(MSG_DEBUG, "X509: No INTEGER tag found for " "version field - found class %d tag 0x%x", hdr.class, hdr.tag); return -1; } if (hdr.length != 1) { wpa_printf(MSG_DEBUG, "X509: Unexpected version field " "length %u (expected 1)", hdr.length); return -1; } pos = hdr.payload; left = hdr.length; value = 0; while (left) { value <<= 8; value |= *pos++; left--; } cert->version = value; if (cert->version != X509_CERT_V1 && cert->version != X509_CERT_V2 && cert->version != X509_CERT_V3) { wpa_printf(MSG_DEBUG, "X509: Unsupported version %d", cert->version + 1); return -1; } if (asn1_get_next(pos, end - pos, &hdr) < 0) return -1; } else cert->version = X509_CERT_V1; wpa_printf(MSG_MSGDUMP, "X509: Version X.509v%d", cert->version + 1); /* serialNumber CertificateSerialNumber ::= INTEGER */ if (hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_INTEGER) { wpa_printf(MSG_DEBUG, "X509: No INTEGER tag found for " "serialNumber; class=%d tag=0x%x", hdr.class, hdr.tag); return -1; } pos = hdr.payload; left = hdr.length; while (left) { cert->serial_number <<= 8; cert->serial_number |= *pos++; left--; } wpa_printf(MSG_MSGDUMP, "X509: serialNumber %lu", cert->serial_number); /* signature AlgorithmIdentifier */ if (x509_parse_algorithm_identifier(pos, end - pos, &cert->signature, &pos)) return -1; /* issuer Name */ if (x509_parse_name(pos, end - pos, &cert->issuer, &pos)) return -1; x509_name_string(&cert->issuer, sbuf, sizeof(sbuf)); wpa_printf(MSG_MSGDUMP, "X509: issuer %s", sbuf); /* validity Validity */ if (x509_parse_validity(pos, end - pos, cert, &pos)) return -1; /* subject Name */ if (x509_parse_name(pos, end - pos, &cert->subject, &pos)) return -1; x509_name_string(&cert->subject, sbuf, sizeof(sbuf)); wpa_printf(MSG_MSGDUMP, "X509: subject %s", sbuf); /* subjectPublicKeyInfo SubjectPublicKeyInfo */ if (x509_parse_public_key(pos, end - pos, cert, &pos)) return -1; if (pos == end) return 0; if (cert->version == X509_CERT_V1) return 0; if (asn1_get_next(pos, end - pos, &hdr) < 0 || hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC) { wpa_printf(MSG_DEBUG, "X509: Expected Context-Specific" " tag to parse optional tbsCertificate " "field(s); parsed class %d tag 0x%x", hdr.class, hdr.tag); return -1; } if (hdr.tag == 1) { /* issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL */ wpa_printf(MSG_DEBUG, "X509: issuerUniqueID"); /* TODO: parse UniqueIdentifier ::= BIT STRING */ if (hdr.payload + hdr.length == end) return 0; if (asn1_get_next(pos, end - pos, &hdr) < 0 || hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC) { wpa_printf(MSG_DEBUG, "X509: Expected Context-Specific" " tag to parse optional tbsCertificate " "field(s); parsed class %d tag 0x%x", hdr.class, hdr.tag); return -1; } } if (hdr.tag == 2) { /* subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL */ wpa_printf(MSG_DEBUG, "X509: subjectUniqueID"); /* TODO: parse UniqueIdentifier ::= BIT STRING */ if (hdr.payload + hdr.length == end) return 0; if (asn1_get_next(pos, end - pos, &hdr) < 0 || hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC) { wpa_printf(MSG_DEBUG, "X509: Expected Context-Specific" " tag to parse optional tbsCertificate " "field(s); parsed class %d tag 0x%x", hdr.class, hdr.tag); return -1; } } if (hdr.tag != 3) { wpa_printf(MSG_DEBUG, "X509: Ignored unexpected " "Context-Specific tag %d in optional " "tbsCertificate fields", hdr.tag); return 0; } /* extensions [3] EXPLICIT Extensions OPTIONAL */ if (cert->version != X509_CERT_V3) { wpa_printf(MSG_DEBUG, "X509: X.509%d certificate and " "Extensions data which are only allowed for " "version 3", cert->version + 1); return -1; } if (x509_parse_extensions(cert, hdr.payload, hdr.length) < 0) return -1; pos = hdr.payload + hdr.length; if (pos < end) { wpa_hexdump(MSG_DEBUG, "X509: Ignored extra tbsCertificate data", pos, end - pos); } return 0; } static int x509_rsadsi_oid(struct asn1_oid *oid) { return oid->len >= 4 && oid->oid[0] == 1 /* iso */ && oid->oid[1] == 2 /* member-body */ && oid->oid[2] == 840 /* us */ && oid->oid[3] == 113549 /* rsadsi */; } static int x509_pkcs_oid(struct asn1_oid *oid) { return oid->len >= 5 && x509_rsadsi_oid(oid) && oid->oid[4] == 1 /* pkcs */; } static int x509_digest_oid(struct asn1_oid *oid) { return oid->len >= 5 && x509_rsadsi_oid(oid) && oid->oid[4] == 2 /* digestAlgorithm */; } static int x509_sha1_oid(struct asn1_oid *oid) { return oid->len == 6 && oid->oid[0] == 1 /* iso */ && oid->oid[1] == 3 /* identified-organization */ && oid->oid[2] == 14 /* oiw */ && oid->oid[3] == 3 /* secsig */ && oid->oid[4] == 2 /* algorithms */ && oid->oid[5] == 26 /* id-sha1 */; } static int x509_sha256_oid(struct asn1_oid *oid) { return oid->len == 9 && oid->oid[0] == 2 /* joint-iso-itu-t */ && oid->oid[1] == 16 /* country */ && oid->oid[2] == 840 /* us */ && oid->oid[3] == 1 /* organization */ && oid->oid[4] == 101 /* gov */ && oid->oid[5] == 3 /* csor */ && oid->oid[6] == 4 /* nistAlgorithm */ && oid->oid[7] == 2 /* hashAlgs */ && oid->oid[8] == 1 /* sha256 */; } /** * x509_certificate_parse - Parse a X.509 certificate in DER format * @buf: Pointer to the X.509 certificate in DER format * @len: Buffer length * Returns: Pointer to the parsed certificate or %NULL on failure * * Caller is responsible for freeing the returned certificate by calling * x509_certificate_free(). */ struct x509_certificate * x509_certificate_parse(const u8 *buf, size_t len) { struct asn1_hdr hdr; const u8 *pos, *end, *hash_start; struct x509_certificate *cert; cert = os_zalloc(sizeof(*cert) + len); if (cert == NULL) return NULL; os_memcpy(cert + 1, buf, len); cert->cert_start = (u8 *) (cert + 1); cert->cert_len = len; pos = buf; end = buf + len; /* RFC 3280 - X.509 v3 certificate / ASN.1 DER */ /* Certificate ::= SEQUENCE */ if (asn1_get_next(pos, len, &hdr) < 0 || hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_SEQUENCE) { wpa_printf(MSG_DEBUG, "X509: Certificate did not start with " "a valid SEQUENCE - found class %d tag 0x%x", hdr.class, hdr.tag); x509_certificate_free(cert); return NULL; } pos = hdr.payload; if (pos + hdr.length > end) { x509_certificate_free(cert); return NULL; } if (pos + hdr.length < end) { wpa_hexdump(MSG_MSGDUMP, "X509: Ignoring extra data after DER " "encoded certificate", pos + hdr.length, end - pos + hdr.length); end = pos + hdr.length; } hash_start = pos; cert->tbs_cert_start = cert->cert_start + (hash_start - buf); if (x509_parse_tbs_certificate(pos, end - pos, cert, &pos)) { x509_certificate_free(cert); return NULL; } cert->tbs_cert_len = pos - hash_start; /* signatureAlgorithm AlgorithmIdentifier */ if (x509_parse_algorithm_identifier(pos, end - pos, &cert->signature_alg, &pos)) { x509_certificate_free(cert); return NULL; } /* signatureValue BIT STRING */ if (asn1_get_next(pos, end - pos, &hdr) < 0 || hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_BITSTRING) { wpa_printf(MSG_DEBUG, "X509: Expected BITSTRING " "(signatureValue) - found class %d tag 0x%x", hdr.class, hdr.tag); x509_certificate_free(cert); return NULL; } if (hdr.length < 1) { x509_certificate_free(cert); return NULL; } pos = hdr.payload; if (*pos) { wpa_printf(MSG_DEBUG, "X509: BITSTRING - %d unused bits", *pos); /* PKCS #1 v1.5 10.2.1: * It is an error if the length in bits of the signature S is * not a multiple of eight. */ x509_certificate_free(cert); return NULL; } os_free(cert->sign_value); cert->sign_value = os_malloc(hdr.length - 1); if (cert->sign_value == NULL) { wpa_printf(MSG_DEBUG, "X509: Failed to allocate memory for " "signatureValue"); x509_certificate_free(cert); return NULL; } os_memcpy(cert->sign_value, pos + 1, hdr.length - 1); cert->sign_value_len = hdr.length - 1; wpa_hexdump(MSG_MSGDUMP, "X509: signature", cert->sign_value, cert->sign_value_len); return cert; } /** * x509_certificate_check_signature - Verify certificate signature * @issuer: Issuer certificate * @cert: Certificate to be verified * Returns: 0 if cert has a valid signature that was signed by the issuer, * -1 if not */ int x509_certificate_check_signature(struct x509_certificate *issuer, struct x509_certificate *cert) { struct crypto_public_key *pk; u8 *data; const u8 *pos, *end, *next, *da_end; size_t data_len; struct asn1_hdr hdr; struct asn1_oid oid; u8 hash[32]; size_t hash_len; if (!x509_pkcs_oid(&cert->signature.oid) || cert->signature.oid.len != 7 || cert->signature.oid.oid[5] != 1 /* pkcs-1 */) { wpa_printf(MSG_DEBUG, "X509: Unrecognized signature " "algorithm"); return -1; } pk = crypto_public_key_import(issuer->public_key, issuer->public_key_len); if (pk == NULL) return -1; data_len = cert->sign_value_len; data = os_malloc(data_len); if (data == NULL) { crypto_public_key_free(pk); return -1; } if (crypto_public_key_decrypt_pkcs1(pk, cert->sign_value, cert->sign_value_len, data, &data_len) < 0) { wpa_printf(MSG_DEBUG, "X509: Failed to decrypt signature"); crypto_public_key_free(pk); os_free(data); return -1; } crypto_public_key_free(pk); wpa_hexdump(MSG_MSGDUMP, "X509: Signature data D", data, data_len); /* * PKCS #1 v1.5, 10.1.2: * * DigestInfo ::= SEQUENCE { * digestAlgorithm DigestAlgorithmIdentifier, * digest Digest * } * * DigestAlgorithmIdentifier ::= AlgorithmIdentifier * * Digest ::= OCTET STRING * */ if (asn1_get_next(data, data_len, &hdr) < 0 || hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_SEQUENCE) { wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE " "(DigestInfo) - found class %d tag 0x%x", hdr.class, hdr.tag); os_free(data); return -1; } pos = hdr.payload; end = pos + hdr.length; /* * X.509: * AlgorithmIdentifier ::= SEQUENCE { * algorithm OBJECT IDENTIFIER, * parameters ANY DEFINED BY algorithm OPTIONAL * } */ if (asn1_get_next(pos, end - pos, &hdr) < 0 || hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_SEQUENCE) { wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE " "(AlgorithmIdentifier) - found class %d tag 0x%x", hdr.class, hdr.tag); os_free(data); return -1; } da_end = hdr.payload + hdr.length; if (asn1_get_oid(hdr.payload, hdr.length, &oid, &next)) { wpa_printf(MSG_DEBUG, "X509: Failed to parse digestAlgorithm"); os_free(data); return -1; } if (x509_sha1_oid(&oid)) { if (cert->signature.oid.oid[6] != 5 /* sha-1WithRSAEncryption */) { wpa_printf(MSG_DEBUG, "X509: digestAlgorithm SHA1 " "does not match with certificate " "signatureAlgorithm (%lu)", cert->signature.oid.oid[6]); os_free(data); return -1; } goto skip_digest_oid; } if (x509_sha256_oid(&oid)) { if (cert->signature.oid.oid[6] != 11 /* sha2561WithRSAEncryption */) { wpa_printf(MSG_DEBUG, "X509: digestAlgorithm SHA256 " "does not match with certificate " "signatureAlgorithm (%lu)", cert->signature.oid.oid[6]); os_free(data); return -1; } goto skip_digest_oid; } if (!x509_digest_oid(&oid)) { wpa_printf(MSG_DEBUG, "X509: Unrecognized digestAlgorithm"); os_free(data); return -1; } switch (oid.oid[5]) { case 5: /* md5 */ if (cert->signature.oid.oid[6] != 4 /* md5WithRSAEncryption */) { wpa_printf(MSG_DEBUG, "X509: digestAlgorithm MD5 does " "not match with certificate " "signatureAlgorithm (%lu)", cert->signature.oid.oid[6]); os_free(data); return -1; } break; case 2: /* md2 */ case 4: /* md4 */ default: wpa_printf(MSG_DEBUG, "X509: Unsupported digestAlgorithm " "(%lu)", oid.oid[5]); os_free(data); return -1; } skip_digest_oid: /* Digest ::= OCTET STRING */ pos = da_end; end = data + data_len; if (asn1_get_next(pos, end - pos, &hdr) < 0 || hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_OCTETSTRING) { wpa_printf(MSG_DEBUG, "X509: Expected OCTETSTRING " "(Digest) - found class %d tag 0x%x", hdr.class, hdr.tag); os_free(data); return -1; } wpa_hexdump(MSG_MSGDUMP, "X509: Decrypted Digest", hdr.payload, hdr.length); switch (cert->signature.oid.oid[6]) { case 4: /* md5WithRSAEncryption */ md5_vector(1, &cert->tbs_cert_start, &cert->tbs_cert_len, hash); hash_len = 16; wpa_hexdump(MSG_MSGDUMP, "X509: Certificate hash (MD5)", hash, hash_len); break; case 5: /* sha-1WithRSAEncryption */ sha1_vector(1, &cert->tbs_cert_start, &cert->tbs_cert_len, hash); hash_len = 20; wpa_hexdump(MSG_MSGDUMP, "X509: Certificate hash (SHA1)", hash, hash_len); break; case 11: /* sha256WithRSAEncryption */ sha256_vector(1, &cert->tbs_cert_start, &cert->tbs_cert_len, hash); hash_len = 32; wpa_hexdump(MSG_MSGDUMP, "X509: Certificate hash (SHA256)", hash, hash_len); break; case 2: /* md2WithRSAEncryption */ case 12: /* sha384WithRSAEncryption */ case 13: /* sha512WithRSAEncryption */ default: wpa_printf(MSG_INFO, "X509: Unsupported certificate signature " "algorithm (%lu)", cert->signature.oid.oid[6]); os_free(data); return -1; } if (hdr.length != hash_len || os_memcmp(hdr.payload, hash, hdr.length) != 0) { wpa_printf(MSG_INFO, "X509: Certificate Digest does not match " "with calculated tbsCertificate hash"); os_free(data); return -1; } os_free(data); wpa_printf(MSG_DEBUG, "X509: Certificate Digest matches with " "calculated tbsCertificate hash"); return 0; } static int x509_valid_issuer(const struct x509_certificate *cert) { if ((cert->extensions_present & X509_EXT_BASIC_CONSTRAINTS) && !cert->ca) { wpa_printf(MSG_DEBUG, "X509: Non-CA certificate used as an " "issuer"); return -1; } if (cert->version == X509_CERT_V3 && !(cert->extensions_present & X509_EXT_BASIC_CONSTRAINTS)) { wpa_printf(MSG_DEBUG, "X509: v3 CA certificate did not " "include BasicConstraints extension"); return -1; } if ((cert->extensions_present & X509_EXT_KEY_USAGE) && !(cert->key_usage & X509_KEY_USAGE_KEY_CERT_SIGN)) { wpa_printf(MSG_DEBUG, "X509: Issuer certificate did not have " "keyCertSign bit in Key Usage"); return -1; } return 0; } /** * x509_certificate_chain_validate - Validate X.509 certificate chain * @trusted: List of trusted certificates * @chain: Certificate chain to be validated (first chain must be issued by * signed by the second certificate in the chain and so on) * @reason: Buffer for returning failure reason (X509_VALIDATE_*) * Returns: 0 if chain is valid, -1 if not */ int x509_certificate_chain_validate(struct x509_certificate *trusted, struct x509_certificate *chain, int *reason, int disable_time_checks) { long unsigned idx; int chain_trusted = 0; struct x509_certificate *cert, *trust; char buf[128]; struct os_time now; *reason = X509_VALIDATE_OK; wpa_printf(MSG_DEBUG, "X509: Validate certificate chain"); os_get_time(&now); for (cert = chain, idx = 0; cert; cert = cert->next, idx++) { x509_name_string(&cert->subject, buf, sizeof(buf)); wpa_printf(MSG_DEBUG, "X509: %lu: %s", idx, buf); if (chain_trusted) continue; if (!disable_time_checks && ((unsigned long) now.sec < (unsigned long) cert->not_before || (unsigned long) now.sec > (unsigned long) cert->not_after)) { wpa_printf(MSG_INFO, "X509: Certificate not valid " "(now=%lu not_before=%lu not_after=%lu)", now.sec, cert->not_before, cert->not_after); *reason = X509_VALIDATE_CERTIFICATE_EXPIRED; return -1; } if (cert->next) { if (x509_name_compare(&cert->issuer, &cert->next->subject) != 0) { wpa_printf(MSG_DEBUG, "X509: Certificate " "chain issuer name mismatch"); x509_name_string(&cert->issuer, buf, sizeof(buf)); wpa_printf(MSG_DEBUG, "X509: cert issuer: %s", buf); x509_name_string(&cert->next->subject, buf, sizeof(buf)); wpa_printf(MSG_DEBUG, "X509: next cert " "subject: %s", buf); *reason = X509_VALIDATE_CERTIFICATE_UNKNOWN; return -1; } if (x509_valid_issuer(cert->next) < 0) { *reason = X509_VALIDATE_BAD_CERTIFICATE; return -1; } if ((cert->next->extensions_present & X509_EXT_PATH_LEN_CONSTRAINT) && idx > cert->next->path_len_constraint) { wpa_printf(MSG_DEBUG, "X509: pathLenConstraint" " not met (idx=%lu issuer " "pathLenConstraint=%lu)", idx, cert->next->path_len_constraint); *reason = X509_VALIDATE_BAD_CERTIFICATE; return -1; } if (x509_certificate_check_signature(cert->next, cert) < 0) { wpa_printf(MSG_DEBUG, "X509: Invalid " "certificate signature within " "chain"); *reason = X509_VALIDATE_BAD_CERTIFICATE; return -1; } } for (trust = trusted; trust; trust = trust->next) { if (x509_name_compare(&cert->issuer, &trust->subject) == 0) break; } if (trust) { wpa_printf(MSG_DEBUG, "X509: Found issuer from the " "list of trusted certificates"); if (x509_valid_issuer(trust) < 0) { *reason = X509_VALIDATE_BAD_CERTIFICATE; return -1; } if (x509_certificate_check_signature(trust, cert) < 0) { wpa_printf(MSG_DEBUG, "X509: Invalid " "certificate signature"); *reason = X509_VALIDATE_BAD_CERTIFICATE; return -1; } wpa_printf(MSG_DEBUG, "X509: Trusted certificate " "found to complete the chain"); chain_trusted = 1; } } if (!chain_trusted) { wpa_printf(MSG_DEBUG, "X509: Did not find any of the issuers " "from the list of trusted certificates"); if (trusted) { *reason = X509_VALIDATE_UNKNOWN_CA; return -1; } wpa_printf(MSG_DEBUG, "X509: Certificate chain validation " "disabled - ignore unknown CA issue"); } wpa_printf(MSG_DEBUG, "X509: Certificate chain valid"); return 0; } /** * x509_certificate_get_subject - Get a certificate based on Subject name * @chain: Certificate chain to search through * @name: Subject name to search for * Returns: Pointer to the certificate with the given Subject name or * %NULL on failure */ struct x509_certificate * x509_certificate_get_subject(struct x509_certificate *chain, struct x509_name *name) { struct x509_certificate *cert; for (cert = chain; cert; cert = cert->next) { if (x509_name_compare(&cert->subject, name) == 0) return cert; } return NULL; } /** * x509_certificate_self_signed - Is the certificate self-signed? * @cert: Certificate * Returns: 1 if certificate is self-signed, 0 if not */ int x509_certificate_self_signed(struct x509_certificate *cert) { return x509_name_compare(&cert->issuer, &cert->subject) == 0; } wpa-2.1/src/tls/x509v3.h000066400000000000000000000067751227414666700147210ustar00rootroot00000000000000/* * X.509v3 certificate parsing and processing * Copyright (c) 2006-2011, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef X509V3_H #define X509V3_H #include "asn1.h" struct x509_algorithm_identifier { struct asn1_oid oid; }; struct x509_name_attr { enum x509_name_attr_type { X509_NAME_ATTR_NOT_USED, X509_NAME_ATTR_DC, X509_NAME_ATTR_CN, X509_NAME_ATTR_C, X509_NAME_ATTR_L, X509_NAME_ATTR_ST, X509_NAME_ATTR_O, X509_NAME_ATTR_OU } type; char *value; }; #define X509_MAX_NAME_ATTRIBUTES 20 struct x509_name { struct x509_name_attr attr[X509_MAX_NAME_ATTRIBUTES]; size_t num_attr; char *email; /* emailAddress */ /* from alternative name extension */ char *alt_email; /* rfc822Name */ char *dns; /* dNSName */ char *uri; /* uniformResourceIdentifier */ u8 *ip; /* iPAddress */ size_t ip_len; /* IPv4: 4, IPv6: 16 */ struct asn1_oid rid; /* registeredID */ }; struct x509_certificate { struct x509_certificate *next; enum { X509_CERT_V1 = 0, X509_CERT_V2 = 1, X509_CERT_V3 = 2 } version; unsigned long serial_number; struct x509_algorithm_identifier signature; struct x509_name issuer; struct x509_name subject; os_time_t not_before; os_time_t not_after; struct x509_algorithm_identifier public_key_alg; u8 *public_key; size_t public_key_len; struct x509_algorithm_identifier signature_alg; u8 *sign_value; size_t sign_value_len; /* Extensions */ unsigned int extensions_present; #define X509_EXT_BASIC_CONSTRAINTS (1 << 0) #define X509_EXT_PATH_LEN_CONSTRAINT (1 << 1) #define X509_EXT_KEY_USAGE (1 << 2) #define X509_EXT_SUBJECT_ALT_NAME (1 << 3) #define X509_EXT_ISSUER_ALT_NAME (1 << 4) /* BasicConstraints */ int ca; /* cA */ unsigned long path_len_constraint; /* pathLenConstraint */ /* KeyUsage */ unsigned long key_usage; #define X509_KEY_USAGE_DIGITAL_SIGNATURE (1 << 0) #define X509_KEY_USAGE_NON_REPUDIATION (1 << 1) #define X509_KEY_USAGE_KEY_ENCIPHERMENT (1 << 2) #define X509_KEY_USAGE_DATA_ENCIPHERMENT (1 << 3) #define X509_KEY_USAGE_KEY_AGREEMENT (1 << 4) #define X509_KEY_USAGE_KEY_CERT_SIGN (1 << 5) #define X509_KEY_USAGE_CRL_SIGN (1 << 6) #define X509_KEY_USAGE_ENCIPHER_ONLY (1 << 7) #define X509_KEY_USAGE_DECIPHER_ONLY (1 << 8) /* * The DER format certificate follows struct x509_certificate. These * pointers point to that buffer. */ const u8 *cert_start; size_t cert_len; const u8 *tbs_cert_start; size_t tbs_cert_len; }; enum { X509_VALIDATE_OK, X509_VALIDATE_BAD_CERTIFICATE, X509_VALIDATE_UNSUPPORTED_CERTIFICATE, X509_VALIDATE_CERTIFICATE_REVOKED, X509_VALIDATE_CERTIFICATE_EXPIRED, X509_VALIDATE_CERTIFICATE_UNKNOWN, X509_VALIDATE_UNKNOWN_CA }; void x509_certificate_free(struct x509_certificate *cert); struct x509_certificate * x509_certificate_parse(const u8 *buf, size_t len); void x509_name_string(struct x509_name *name, char *buf, size_t len); int x509_name_compare(struct x509_name *a, struct x509_name *b); void x509_certificate_chain_free(struct x509_certificate *cert); int x509_certificate_check_signature(struct x509_certificate *issuer, struct x509_certificate *cert); int x509_certificate_chain_validate(struct x509_certificate *trusted, struct x509_certificate *chain, int *reason, int disable_time_checks); struct x509_certificate * x509_certificate_get_subject(struct x509_certificate *chain, struct x509_name *name); int x509_certificate_self_signed(struct x509_certificate *cert); #endif /* X509V3_H */ wpa-2.1/src/utils/000077500000000000000000000000001227414666700141115ustar00rootroot00000000000000wpa-2.1/src/utils/.gitignore000066400000000000000000000000131227414666700160730ustar00rootroot00000000000000libutils.a wpa-2.1/src/utils/Makefile000066400000000000000000000011441227414666700155510ustar00rootroot00000000000000all: libutils.a clean: rm -f *~ *.o *.d *.gcno *.gcda *.gcov libutils.a install: @echo Nothing to be made. include ../lib.rules #CFLAGS += -DWPA_TRACE CFLAGS += -DCONFIG_IPV6 CFLAGS += -DCONFIG_DEBUG_FILE LIB_OBJS= \ base64.o \ bitfield.o \ common.o \ ip_addr.o \ radiotap.o \ trace.o \ uuid.o \ wpa_debug.o \ wpabuf.o # Pick correct OS wrapper implementation LIB_OBJS += os_unix.o # Pick correct event loop implementation LIB_OBJS += eloop.o # Pick correct edit implementation LIB_OBJS += edit.o #LIB_OBJS += pcsc_funcs.o libutils.a: $(LIB_OBJS) $(AR) crT $@ $? -include $(OBJS:%.o=%.d) wpa-2.1/src/utils/base64.c000066400000000000000000000066121227414666700153460ustar00rootroot00000000000000/* * Base64 encoding/decoding (RFC1341) * Copyright (c) 2005-2011, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "includes.h" #include "os.h" #include "base64.h" static const unsigned char base64_table[65] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; /** * base64_encode - Base64 encode * @src: Data to be encoded * @len: Length of the data to be encoded * @out_len: Pointer to output length variable, or %NULL if not used * Returns: Allocated buffer of out_len bytes of encoded data, * or %NULL on failure * * Caller is responsible for freeing the returned buffer. Returned buffer is * nul terminated to make it easier to use as a C string. The nul terminator is * not included in out_len. */ unsigned char * base64_encode(const unsigned char *src, size_t len, size_t *out_len) { unsigned char *out, *pos; const unsigned char *end, *in; size_t olen; int line_len; olen = len * 4 / 3 + 4; /* 3-byte blocks to 4-byte */ olen += olen / 72; /* line feeds */ olen++; /* nul termination */ if (olen < len) return NULL; /* integer overflow */ out = os_malloc(olen); if (out == NULL) return NULL; end = src + len; in = src; pos = out; line_len = 0; while (end - in >= 3) { *pos++ = base64_table[in[0] >> 2]; *pos++ = base64_table[((in[0] & 0x03) << 4) | (in[1] >> 4)]; *pos++ = base64_table[((in[1] & 0x0f) << 2) | (in[2] >> 6)]; *pos++ = base64_table[in[2] & 0x3f]; in += 3; line_len += 4; if (line_len >= 72) { *pos++ = '\n'; line_len = 0; } } if (end - in) { *pos++ = base64_table[in[0] >> 2]; if (end - in == 1) { *pos++ = base64_table[(in[0] & 0x03) << 4]; *pos++ = '='; } else { *pos++ = base64_table[((in[0] & 0x03) << 4) | (in[1] >> 4)]; *pos++ = base64_table[(in[1] & 0x0f) << 2]; } *pos++ = '='; line_len += 4; } if (line_len) *pos++ = '\n'; *pos = '\0'; if (out_len) *out_len = pos - out; return out; } /** * base64_decode - Base64 decode * @src: Data to be decoded * @len: Length of the data to be decoded * @out_len: Pointer to output length variable * Returns: Allocated buffer of out_len bytes of decoded data, * or %NULL on failure * * Caller is responsible for freeing the returned buffer. */ unsigned char * base64_decode(const unsigned char *src, size_t len, size_t *out_len) { unsigned char dtable[256], *out, *pos, block[4], tmp; size_t i, count, olen; int pad = 0; os_memset(dtable, 0x80, 256); for (i = 0; i < sizeof(base64_table) - 1; i++) dtable[base64_table[i]] = (unsigned char) i; dtable['='] = 0; count = 0; for (i = 0; i < len; i++) { if (dtable[src[i]] != 0x80) count++; } if (count == 0 || count % 4) return NULL; olen = count / 4 * 3; pos = out = os_malloc(olen); if (out == NULL) return NULL; count = 0; for (i = 0; i < len; i++) { tmp = dtable[src[i]]; if (tmp == 0x80) continue; if (src[i] == '=') pad++; block[count] = tmp; count++; if (count == 4) { *pos++ = (block[0] << 2) | (block[1] >> 4); *pos++ = (block[1] << 4) | (block[2] >> 2); *pos++ = (block[2] << 6) | block[3]; count = 0; if (pad) { if (pad == 1) pos--; else if (pad == 2) pos -= 2; else { /* Invalid padding */ os_free(out); return NULL; } break; } } } *out_len = pos - out; return out; } wpa-2.1/src/utils/base64.h000066400000000000000000000007001227414666700153430ustar00rootroot00000000000000/* * Base64 encoding/decoding (RFC1341) * Copyright (c) 2005, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef BASE64_H #define BASE64_H unsigned char * base64_encode(const unsigned char *src, size_t len, size_t *out_len); unsigned char * base64_decode(const unsigned char *src, size_t len, size_t *out_len); #endif /* BASE64_H */ wpa-2.1/src/utils/bitfield.c000066400000000000000000000025741227414666700160470ustar00rootroot00000000000000/* * Bitfield * Copyright (c) 2013, 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 "bitfield.h" struct bitfield { u8 *bits; size_t max_bits; }; struct bitfield * bitfield_alloc(size_t max_bits) { struct bitfield *bf; bf = os_zalloc(sizeof(*bf) + (max_bits + 7) / 8); if (bf == NULL) return NULL; bf->bits = (u8 *) (bf + 1); bf->max_bits = max_bits; return bf; } void bitfield_free(struct bitfield *bf) { os_free(bf); } void bitfield_set(struct bitfield *bf, size_t bit) { if (bit >= bf->max_bits) return; bf->bits[bit / 8] |= BIT(bit % 8); } void bitfield_clear(struct bitfield *bf, size_t bit) { if (bit >= bf->max_bits) return; bf->bits[bit / 8] &= ~BIT(bit % 8); } int bitfield_is_set(struct bitfield *bf, size_t bit) { if (bit >= bf->max_bits) return 0; return !!(bf->bits[bit / 8] & BIT(bit % 8)); } static int first_zero(u8 val) { int i; for (i = 0; i < 8; i++) { if (!(val & 0x01)) return i; val >>= 1; } return -1; } int bitfield_get_first_zero(struct bitfield *bf) { size_t i; for (i = 0; i <= (bf->max_bits + 7) / 8; i++) { if (bf->bits[i] != 0xff) break; } if (i > (bf->max_bits + 7) / 8) return -1; i = i * 8 + first_zero(bf->bits[i]); if (i >= bf->max_bits) return -1; return i; } wpa-2.1/src/utils/bitfield.h000066400000000000000000000010561227414666700160460ustar00rootroot00000000000000/* * Bitfield * Copyright (c) 2013, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef BITFIELD_H #define BITFIELD_H struct bitfield; struct bitfield * bitfield_alloc(size_t max_bits); void bitfield_free(struct bitfield *bf); void bitfield_set(struct bitfield *bf, size_t bit); void bitfield_clear(struct bitfield *bf, size_t bit); int bitfield_is_set(struct bitfield *bf, size_t bit); int bitfield_get_first_zero(struct bitfield *bf); #endif /* BITFIELD_H */ wpa-2.1/src/utils/build_config.h000066400000000000000000000026001227414666700167040ustar00rootroot00000000000000/* * wpa_supplicant/hostapd - Build time configuration defines * Copyright (c) 2005-2006, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. * * This header file can be used to define configuration defines that were * originally defined in Makefile. This is mainly meant for IDE use or for * systems that do not have suitable 'make' tool. In these cases, it may be * easier to have a single place for defining all the needed C pre-processor * defines. */ #ifndef BUILD_CONFIG_H #define BUILD_CONFIG_H /* Insert configuration defines, e.g., #define EAP_MD5, here, if needed. */ #ifdef CONFIG_WIN32_DEFAULTS #define CONFIG_NATIVE_WINDOWS #define CONFIG_ANSI_C_EXTRA #define CONFIG_WINPCAP #define IEEE8021X_EAPOL #define PKCS12_FUNCS #define PCSC_FUNCS #define CONFIG_CTRL_IFACE #define CONFIG_CTRL_IFACE_NAMED_PIPE #define CONFIG_DRIVER_NDIS #define CONFIG_NDIS_EVENTS_INTEGRATED #define CONFIG_DEBUG_FILE #define EAP_MD5 #define EAP_TLS #define EAP_MSCHAPv2 #define EAP_PEAP #define EAP_TTLS #define EAP_GTC #define EAP_OTP #define EAP_LEAP #define EAP_TNC #define _CRT_SECURE_NO_DEPRECATE #ifdef USE_INTERNAL_CRYPTO #define CONFIG_TLS_INTERNAL_CLIENT #define CONFIG_INTERNAL_LIBTOMMATH #define CONFIG_CRYPTO_INTERNAL #endif /* USE_INTERNAL_CRYPTO */ #endif /* CONFIG_WIN32_DEFAULTS */ #endif /* BUILD_CONFIG_H */ wpa-2.1/src/utils/common.c000066400000000000000000000357671227414666700155670ustar00rootroot00000000000000/* * wpa_supplicant/hostapd / common helper functions, etc. * Copyright (c) 2002-2007, 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" static int hex2num(char c) { if (c >= '0' && c <= '9') return c - '0'; if (c >= 'a' && c <= 'f') return c - 'a' + 10; if (c >= 'A' && c <= 'F') return c - 'A' + 10; return -1; } int hex2byte(const char *hex) { int a, b; a = hex2num(*hex++); if (a < 0) return -1; b = hex2num(*hex++); if (b < 0) return -1; return (a << 4) | b; } /** * hwaddr_aton - Convert ASCII string to MAC address (colon-delimited format) * @txt: MAC address as a string (e.g., "00:11:22:33:44:55") * @addr: Buffer for the MAC address (ETH_ALEN = 6 bytes) * Returns: 0 on success, -1 on failure (e.g., string not a MAC address) */ int hwaddr_aton(const char *txt, u8 *addr) { int i; for (i = 0; i < 6; i++) { int a, b; a = hex2num(*txt++); if (a < 0) return -1; b = hex2num(*txt++); if (b < 0) return -1; *addr++ = (a << 4) | b; if (i < 5 && *txt++ != ':') return -1; } return 0; } /** * hwaddr_compact_aton - Convert ASCII string to MAC address (no colon delimitors format) * @txt: MAC address as a string (e.g., "001122334455") * @addr: Buffer for the MAC address (ETH_ALEN = 6 bytes) * Returns: 0 on success, -1 on failure (e.g., string not a MAC address) */ int hwaddr_compact_aton(const char *txt, u8 *addr) { int i; for (i = 0; i < 6; i++) { int a, b; a = hex2num(*txt++); if (a < 0) return -1; b = hex2num(*txt++); if (b < 0) return -1; *addr++ = (a << 4) | b; } return 0; } /** * hwaddr_aton2 - Convert ASCII string to MAC address (in any known format) * @txt: MAC address as a string (e.g., 00:11:22:33:44:55 or 0011.2233.4455) * @addr: Buffer for the MAC address (ETH_ALEN = 6 bytes) * Returns: Characters used (> 0) on success, -1 on failure */ int hwaddr_aton2(const char *txt, u8 *addr) { int i; const char *pos = txt; for (i = 0; i < 6; i++) { int a, b; while (*pos == ':' || *pos == '.' || *pos == '-') pos++; a = hex2num(*pos++); if (a < 0) return -1; b = hex2num(*pos++); if (b < 0) return -1; *addr++ = (a << 4) | b; } return pos - txt; } /** * hexstr2bin - Convert ASCII hex string into binary data * @hex: ASCII hex string (e.g., "01ab") * @buf: Buffer for the binary data * @len: Length of the text to convert in bytes (of buf); hex will be double * this size * Returns: 0 on success, -1 on failure (invalid hex string) */ int hexstr2bin(const char *hex, u8 *buf, size_t len) { size_t i; int a; const char *ipos = hex; u8 *opos = buf; for (i = 0; i < len; i++) { a = hex2byte(ipos); if (a < 0) return -1; *opos++ = a; ipos += 2; } return 0; } /** * inc_byte_array - Increment arbitrary length byte array by one * @counter: Pointer to byte array * @len: Length of the counter in bytes * * This function increments the last byte of the counter by one and continues * rolling over to more significant bytes if the byte was incremented from * 0xff to 0x00. */ void inc_byte_array(u8 *counter, size_t len) { int pos = len - 1; while (pos >= 0) { counter[pos]++; if (counter[pos] != 0) break; pos--; } } void wpa_get_ntp_timestamp(u8 *buf) { struct os_time now; u32 sec, usec; be32 tmp; /* 64-bit NTP timestamp (time from 1900-01-01 00:00:00) */ os_get_time(&now); sec = now.sec + 2208988800U; /* Epoch to 1900 */ /* Estimate 2^32/10^6 = 4295 - 1/32 - 1/512 */ usec = now.usec; usec = 4295 * usec - (usec >> 5) - (usec >> 9); tmp = host_to_be32(sec); os_memcpy(buf, (u8 *) &tmp, 4); tmp = host_to_be32(usec); os_memcpy(buf + 4, (u8 *) &tmp, 4); } static inline int _wpa_snprintf_hex(char *buf, size_t buf_size, const u8 *data, size_t len, int uppercase) { size_t i; char *pos = buf, *end = buf + buf_size; int ret; if (buf_size == 0) return 0; for (i = 0; i < len; i++) { ret = os_snprintf(pos, end - pos, uppercase ? "%02X" : "%02x", data[i]); if (ret < 0 || ret >= end - pos) { end[-1] = '\0'; return pos - buf; } pos += ret; } end[-1] = '\0'; return pos - buf; } /** * wpa_snprintf_hex - Print data as a hex string into a buffer * @buf: Memory area to use as the output buffer * @buf_size: Maximum buffer size in bytes (should be at least 2 * len + 1) * @data: Data to be printed * @len: Length of data in bytes * Returns: Number of bytes written */ int wpa_snprintf_hex(char *buf, size_t buf_size, const u8 *data, size_t len) { return _wpa_snprintf_hex(buf, buf_size, data, len, 0); } /** * wpa_snprintf_hex_uppercase - Print data as a upper case hex string into buf * @buf: Memory area to use as the output buffer * @buf_size: Maximum buffer size in bytes (should be at least 2 * len + 1) * @data: Data to be printed * @len: Length of data in bytes * Returns: Number of bytes written */ int wpa_snprintf_hex_uppercase(char *buf, size_t buf_size, const u8 *data, size_t len) { return _wpa_snprintf_hex(buf, buf_size, data, len, 1); } #ifdef CONFIG_ANSI_C_EXTRA #ifdef _WIN32_WCE void perror(const char *s) { wpa_printf(MSG_ERROR, "%s: GetLastError: %d", s, (int) GetLastError()); } #endif /* _WIN32_WCE */ int optind = 1; int optopt; char *optarg; int getopt(int argc, char *const argv[], const char *optstring) { static int optchr = 1; char *cp; if (optchr == 1) { if (optind >= argc) { /* all arguments processed */ return EOF; } if (argv[optind][0] != '-' || argv[optind][1] == '\0') { /* no option characters */ return EOF; } } if (os_strcmp(argv[optind], "--") == 0) { /* no more options */ optind++; return EOF; } optopt = argv[optind][optchr]; cp = os_strchr(optstring, optopt); if (cp == NULL || optopt == ':') { if (argv[optind][++optchr] == '\0') { optchr = 1; optind++; } return '?'; } if (cp[1] == ':') { /* Argument required */ optchr = 1; if (argv[optind][optchr + 1]) { /* No space between option and argument */ optarg = &argv[optind++][optchr + 1]; } else if (++optind >= argc) { /* option requires an argument */ return '?'; } else { /* Argument in the next argv */ optarg = argv[optind++]; } } else { /* No argument */ if (argv[optind][++optchr] == '\0') { optchr = 1; optind++; } optarg = NULL; } return *cp; } #endif /* CONFIG_ANSI_C_EXTRA */ #ifdef CONFIG_NATIVE_WINDOWS /** * wpa_unicode2ascii_inplace - Convert unicode string into ASCII * @str: Pointer to string to convert * * This function converts a unicode string to ASCII using the same * buffer for output. If UNICODE is not set, the buffer is not * modified. */ void wpa_unicode2ascii_inplace(TCHAR *str) { #ifdef UNICODE char *dst = (char *) str; while (*str) *dst++ = (char) *str++; *dst = '\0'; #endif /* UNICODE */ } TCHAR * wpa_strdup_tchar(const char *str) { #ifdef UNICODE TCHAR *buf; buf = os_malloc((strlen(str) + 1) * sizeof(TCHAR)); if (buf == NULL) return NULL; wsprintf(buf, L"%S", str); return buf; #else /* UNICODE */ return os_strdup(str); #endif /* UNICODE */ } #endif /* CONFIG_NATIVE_WINDOWS */ void printf_encode(char *txt, size_t maxlen, const u8 *data, size_t len) { char *end = txt + maxlen; size_t i; for (i = 0; i < len; i++) { if (txt + 4 > end) break; switch (data[i]) { case '\"': *txt++ = '\\'; *txt++ = '\"'; break; case '\\': *txt++ = '\\'; *txt++ = '\\'; break; case '\e': *txt++ = '\\'; *txt++ = 'e'; break; case '\n': *txt++ = '\\'; *txt++ = 'n'; break; case '\r': *txt++ = '\\'; *txt++ = 'r'; break; case '\t': *txt++ = '\\'; *txt++ = 't'; break; default: if (data[i] >= 32 && data[i] <= 127) { *txt++ = data[i]; } else { txt += os_snprintf(txt, end - txt, "\\x%02x", data[i]); } break; } } *txt = '\0'; } size_t printf_decode(u8 *buf, size_t maxlen, const char *str) { const char *pos = str; size_t len = 0; int val; while (*pos) { if (len + 1 >= maxlen) break; switch (*pos) { case '\\': pos++; switch (*pos) { case '\\': buf[len++] = '\\'; pos++; break; case '"': buf[len++] = '"'; pos++; break; case 'n': buf[len++] = '\n'; pos++; break; case 'r': buf[len++] = '\r'; pos++; break; case 't': buf[len++] = '\t'; pos++; break; case 'e': buf[len++] = '\e'; pos++; break; case 'x': pos++; val = hex2byte(pos); if (val < 0) { val = hex2num(*pos); if (val < 0) break; buf[len++] = val; pos++; } else { buf[len++] = val; pos += 2; } break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': val = *pos++ - '0'; if (*pos >= '0' && *pos <= '7') val = val * 8 + (*pos++ - '0'); if (*pos >= '0' && *pos <= '7') val = val * 8 + (*pos++ - '0'); buf[len++] = val; break; default: break; } break; default: buf[len++] = *pos++; break; } } if (maxlen > len) buf[len] = '\0'; return len; } /** * wpa_ssid_txt - Convert SSID to a printable string * @ssid: SSID (32-octet string) * @ssid_len: Length of ssid in octets * Returns: Pointer to a printable string * * This function can be used to convert SSIDs into printable form. In most * cases, SSIDs do not use unprintable characters, but IEEE 802.11 standard * does not limit the used character set, so anything could be used in an SSID. * * This function uses a static buffer, so only one call can be used at the * time, i.e., this is not re-entrant and the returned buffer must be used * before calling this again. */ const char * wpa_ssid_txt(const u8 *ssid, size_t ssid_len) { static char ssid_txt[32 * 4 + 1]; if (ssid == NULL) { ssid_txt[0] = '\0'; return ssid_txt; } printf_encode(ssid_txt, sizeof(ssid_txt), ssid, ssid_len); return ssid_txt; } void * __hide_aliasing_typecast(void *foo) { return foo; } char * wpa_config_parse_string(const char *value, size_t *len) { if (*value == '"') { const char *pos; char *str; value++; pos = os_strrchr(value, '"'); if (pos == NULL || pos[1] != '\0') return NULL; *len = pos - value; str = dup_binstr(value, *len); if (str == NULL) return NULL; return str; } else if (*value == 'P' && value[1] == '"') { const char *pos; char *tstr, *str; size_t tlen; value += 2; pos = os_strrchr(value, '"'); if (pos == NULL || pos[1] != '\0') return NULL; tlen = pos - value; tstr = dup_binstr(value, tlen); if (tstr == NULL) return NULL; str = os_malloc(tlen + 1); if (str == NULL) { os_free(tstr); return NULL; } *len = printf_decode((u8 *) str, tlen + 1, tstr); os_free(tstr); return str; } else { u8 *str; size_t tlen, hlen = os_strlen(value); if (hlen & 1) return NULL; tlen = hlen / 2; str = os_malloc(tlen + 1); if (str == NULL) return NULL; if (hexstr2bin(value, str, tlen)) { os_free(str); return NULL; } str[tlen] = '\0'; *len = tlen; return (char *) str; } } int is_hex(const u8 *data, size_t len) { size_t i; for (i = 0; i < len; i++) { if (data[i] < 32 || data[i] >= 127) return 1; } return 0; } int find_first_bit(u32 value) { int pos = 0; while (value) { if (value & 0x1) return pos; value >>= 1; pos++; } return -1; } size_t merge_byte_arrays(u8 *res, size_t res_len, const u8 *src1, size_t src1_len, const u8 *src2, size_t src2_len) { size_t len = 0; os_memset(res, 0, res_len); if (src1) { if (src1_len >= res_len) { os_memcpy(res, src1, res_len); return res_len; } os_memcpy(res, src1, src1_len); len += src1_len; } if (src2) { if (len + src2_len >= res_len) { os_memcpy(res + len, src2, res_len - len); return res_len; } os_memcpy(res + len, src2, src2_len); len += src2_len; } return len; } char * dup_binstr(const void *src, size_t len) { char *res; if (src == NULL) return NULL; res = os_malloc(len + 1); if (res == NULL) return NULL; os_memcpy(res, src, len); res[len] = '\0'; return res; } int freq_range_list_parse(struct wpa_freq_range_list *res, const char *value) { struct wpa_freq_range *freq = NULL, *n; unsigned int count = 0; const char *pos, *pos2, *pos3; /* * Comma separated list of frequency ranges. * For example: 2412-2432,2462,5000-6000 */ pos = value; while (pos && pos[0]) { n = os_realloc_array(freq, count + 1, sizeof(struct wpa_freq_range)); if (n == NULL) { os_free(freq); return -1; } freq = n; freq[count].min = atoi(pos); pos2 = os_strchr(pos, '-'); pos3 = os_strchr(pos, ','); if (pos2 && (!pos3 || pos2 < pos3)) { pos2++; freq[count].max = atoi(pos2); } else freq[count].max = freq[count].min; pos = pos3; if (pos) pos++; count++; } os_free(res->range); res->range = freq; res->num = count; return 0; } int freq_range_list_includes(const struct wpa_freq_range_list *list, unsigned int freq) { unsigned int i; if (list == NULL) return 0; for (i = 0; i < list->num; i++) { if (freq >= list->range[i].min && freq <= list->range[i].max) return 1; } return 0; } char * freq_range_list_str(const struct wpa_freq_range_list *list) { char *buf, *pos, *end; size_t maxlen; unsigned int i; int res; if (list->num == 0) return NULL; maxlen = list->num * 30; buf = os_malloc(maxlen); if (buf == NULL) return NULL; pos = buf; end = buf + maxlen; for (i = 0; i < list->num; i++) { struct wpa_freq_range *range = &list->range[i]; if (range->min == range->max) res = os_snprintf(pos, end - pos, "%s%u", i == 0 ? "" : ",", range->min); else res = os_snprintf(pos, end - pos, "%s%u-%u", i == 0 ? "" : ",", range->min, range->max); if (res < 0 || res > end - pos) { os_free(buf); return NULL; } pos += res; } return buf; } int int_array_len(const int *a) { int i; for (i = 0; a && a[i]; i++) ; return i; } void int_array_concat(int **res, const int *a) { int reslen, alen, i; int *n; reslen = int_array_len(*res); alen = int_array_len(a); n = os_realloc_array(*res, reslen + alen + 1, sizeof(int)); if (n == NULL) { os_free(*res); *res = NULL; return; } for (i = 0; i <= alen; i++) n[reslen + i] = a[i]; *res = n; } static int freq_cmp(const void *a, const void *b) { int _a = *(int *) a; int _b = *(int *) b; if (_a == 0) return 1; if (_b == 0) return -1; return _a - _b; } void int_array_sort_unique(int *a) { int alen; int i, j; if (a == NULL) return; alen = int_array_len(a); qsort(a, alen, sizeof(int), freq_cmp); i = 0; j = 1; while (a[i] && a[j]) { if (a[i] == a[j]) { j++; continue; } a[++i] = a[j++]; } if (a[i]) i++; a[i] = 0; } void int_array_add_unique(int **res, int a) { int reslen; int *n; for (reslen = 0; *res && (*res)[reslen]; reslen++) { if ((*res)[reslen] == a) return; /* already in the list */ } n = os_realloc_array(*res, reslen + 2, sizeof(int)); if (n == NULL) { os_free(*res); *res = NULL; return; } n[reslen] = a; n[reslen + 1] = 0; *res = n; } wpa-2.1/src/utils/common.h000066400000000000000000000332561227414666700155630ustar00rootroot00000000000000/* * wpa_supplicant/hostapd / common helper functions, etc. * Copyright (c) 2002-2007, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef COMMON_H #define COMMON_H #include "os.h" #if defined(__linux__) || defined(__GLIBC__) #include #include #endif /* __linux__ */ #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__DragonFly__) || \ defined(__OpenBSD__) #include #include #define __BYTE_ORDER _BYTE_ORDER #define __LITTLE_ENDIAN _LITTLE_ENDIAN #define __BIG_ENDIAN _BIG_ENDIAN #ifdef __OpenBSD__ #define bswap_16 swap16 #define bswap_32 swap32 #define bswap_64 swap64 #else /* __OpenBSD__ */ #define bswap_16 bswap16 #define bswap_32 bswap32 #define bswap_64 bswap64 #endif /* __OpenBSD__ */ #endif /* defined(__FreeBSD__) || defined(__NetBSD__) || * defined(__DragonFly__) || defined(__OpenBSD__) */ #ifdef __APPLE__ #include #include #define __BYTE_ORDER _BYTE_ORDER #define __LITTLE_ENDIAN _LITTLE_ENDIAN #define __BIG_ENDIAN _BIG_ENDIAN static inline unsigned short bswap_16(unsigned short v) { return ((v & 0xff) << 8) | (v >> 8); } static inline unsigned int bswap_32(unsigned int v) { return ((v & 0xff) << 24) | ((v & 0xff00) << 8) | ((v & 0xff0000) >> 8) | (v >> 24); } #endif /* __APPLE__ */ #ifdef CONFIG_TI_COMPILER #define __BIG_ENDIAN 4321 #define __LITTLE_ENDIAN 1234 #ifdef __big_endian__ #define __BYTE_ORDER __BIG_ENDIAN #else #define __BYTE_ORDER __LITTLE_ENDIAN #endif #endif /* CONFIG_TI_COMPILER */ #ifdef CONFIG_NATIVE_WINDOWS #include typedef int socklen_t; #ifndef MSG_DONTWAIT #define MSG_DONTWAIT 0 /* not supported */ #endif #endif /* CONFIG_NATIVE_WINDOWS */ #ifdef _MSC_VER #define inline __inline #undef vsnprintf #define vsnprintf _vsnprintf #undef close #define close closesocket #endif /* _MSC_VER */ /* Define platform specific integer types */ #ifdef _MSC_VER typedef UINT64 u64; typedef UINT32 u32; typedef UINT16 u16; typedef UINT8 u8; typedef INT64 s64; typedef INT32 s32; typedef INT16 s16; typedef INT8 s8; #define WPA_TYPES_DEFINED #endif /* _MSC_VER */ #ifdef __vxworks typedef unsigned long long u64; typedef UINT32 u32; typedef UINT16 u16; typedef UINT8 u8; typedef long long s64; typedef INT32 s32; typedef INT16 s16; typedef INT8 s8; #define WPA_TYPES_DEFINED #endif /* __vxworks */ #ifdef CONFIG_TI_COMPILER #ifdef _LLONG_AVAILABLE typedef unsigned long long u64; #else /* * TODO: 64-bit variable not available. Using long as a workaround to test the * build, but this will likely not work for all operations. */ typedef unsigned long u64; #endif typedef unsigned int u32; typedef unsigned short u16; typedef unsigned char u8; #define WPA_TYPES_DEFINED #endif /* CONFIG_TI_COMPILER */ #ifndef WPA_TYPES_DEFINED #ifdef CONFIG_USE_INTTYPES_H #include #else #include #endif typedef uint64_t u64; typedef uint32_t u32; typedef uint16_t u16; typedef uint8_t u8; typedef int64_t s64; typedef int32_t s32; typedef int16_t s16; typedef int8_t s8; #define WPA_TYPES_DEFINED #endif /* !WPA_TYPES_DEFINED */ /* Define platform specific byte swapping macros */ #if defined(__CYGWIN__) || defined(CONFIG_NATIVE_WINDOWS) static inline unsigned short wpa_swap_16(unsigned short v) { return ((v & 0xff) << 8) | (v >> 8); } static inline unsigned int wpa_swap_32(unsigned int v) { return ((v & 0xff) << 24) | ((v & 0xff00) << 8) | ((v & 0xff0000) >> 8) | (v >> 24); } #define le_to_host16(n) (n) #define host_to_le16(n) (n) #define be_to_host16(n) wpa_swap_16(n) #define host_to_be16(n) wpa_swap_16(n) #define le_to_host32(n) (n) #define be_to_host32(n) wpa_swap_32(n) #define host_to_be32(n) wpa_swap_32(n) #define WPA_BYTE_SWAP_DEFINED #endif /* __CYGWIN__ || CONFIG_NATIVE_WINDOWS */ #ifndef WPA_BYTE_SWAP_DEFINED #ifndef __BYTE_ORDER #ifndef __LITTLE_ENDIAN #ifndef __BIG_ENDIAN #define __LITTLE_ENDIAN 1234 #define __BIG_ENDIAN 4321 #if defined(sparc) #define __BYTE_ORDER __BIG_ENDIAN #endif #endif /* __BIG_ENDIAN */ #endif /* __LITTLE_ENDIAN */ #endif /* __BYTE_ORDER */ #if __BYTE_ORDER == __LITTLE_ENDIAN #define le_to_host16(n) ((__force u16) (le16) (n)) #define host_to_le16(n) ((__force le16) (u16) (n)) #define be_to_host16(n) bswap_16((__force u16) (be16) (n)) #define host_to_be16(n) ((__force be16) bswap_16((n))) #define le_to_host32(n) ((__force u32) (le32) (n)) #define host_to_le32(n) ((__force le32) (u32) (n)) #define be_to_host32(n) bswap_32((__force u32) (be32) (n)) #define host_to_be32(n) ((__force be32) bswap_32((n))) #define le_to_host64(n) ((__force u64) (le64) (n)) #define host_to_le64(n) ((__force le64) (u64) (n)) #define be_to_host64(n) bswap_64((__force u64) (be64) (n)) #define host_to_be64(n) ((__force be64) bswap_64((n))) #elif __BYTE_ORDER == __BIG_ENDIAN #define le_to_host16(n) bswap_16(n) #define host_to_le16(n) bswap_16(n) #define be_to_host16(n) (n) #define host_to_be16(n) (n) #define le_to_host32(n) bswap_32(n) #define host_to_le32(n) bswap_32(n) #define be_to_host32(n) (n) #define host_to_be32(n) (n) #define le_to_host64(n) bswap_64(n) #define host_to_le64(n) bswap_64(n) #define be_to_host64(n) (n) #define host_to_be64(n) (n) #ifndef WORDS_BIGENDIAN #define WORDS_BIGENDIAN #endif #else #error Could not determine CPU byte order #endif #define WPA_BYTE_SWAP_DEFINED #endif /* !WPA_BYTE_SWAP_DEFINED */ /* Macros for handling unaligned memory accesses */ static inline u16 WPA_GET_BE16(const u8 *a) { return (a[0] << 8) | a[1]; } static inline void WPA_PUT_BE16(u8 *a, u16 val) { a[0] = val >> 8; a[1] = val & 0xff; } static inline u16 WPA_GET_LE16(const u8 *a) { return (a[1] << 8) | a[0]; } static inline void WPA_PUT_LE16(u8 *a, u16 val) { a[1] = val >> 8; a[0] = val & 0xff; } static inline u32 WPA_GET_BE24(const u8 *a) { return (a[0] << 16) | (a[1] << 8) | a[2]; } static inline void WPA_PUT_BE24(u8 *a, u32 val) { a[0] = (val >> 16) & 0xff; a[1] = (val >> 8) & 0xff; a[2] = val & 0xff; } static inline u32 WPA_GET_BE32(const u8 *a) { return (a[0] << 24) | (a[1] << 16) | (a[2] << 8) | a[3]; } static inline void WPA_PUT_BE32(u8 *a, u32 val) { a[0] = (val >> 24) & 0xff; a[1] = (val >> 16) & 0xff; a[2] = (val >> 8) & 0xff; a[3] = val & 0xff; } static inline u32 WPA_GET_LE32(const u8 *a) { return (a[3] << 24) | (a[2] << 16) | (a[1] << 8) | a[0]; } static inline void WPA_PUT_LE32(u8 *a, u32 val) { a[3] = (val >> 24) & 0xff; a[2] = (val >> 16) & 0xff; a[1] = (val >> 8) & 0xff; a[0] = val & 0xff; } static inline u64 WPA_GET_BE64(const u8 *a) { return (((u64) a[0]) << 56) | (((u64) a[1]) << 48) | (((u64) a[2]) << 40) | (((u64) a[3]) << 32) | (((u64) a[4]) << 24) | (((u64) a[5]) << 16) | (((u64) a[6]) << 8) | ((u64) a[7]); } static inline void WPA_PUT_BE64(u8 *a, u64 val) { a[0] = val >> 56; a[1] = val >> 48; a[2] = val >> 40; a[3] = val >> 32; a[4] = val >> 24; a[5] = val >> 16; a[6] = val >> 8; a[7] = val & 0xff; } static inline u64 WPA_GET_LE64(const u8 *a) { return (((u64) a[7]) << 56) | (((u64) a[6]) << 48) | (((u64) a[5]) << 40) | (((u64) a[4]) << 32) | (((u64) a[3]) << 24) | (((u64) a[2]) << 16) | (((u64) a[1]) << 8) | ((u64) a[0]); } static inline void WPA_PUT_LE64(u8 *a, u64 val) { a[7] = val >> 56; a[6] = val >> 48; a[5] = val >> 40; a[4] = val >> 32; a[3] = val >> 24; a[2] = val >> 16; a[1] = val >> 8; a[0] = val & 0xff; } #ifndef ETH_ALEN #define ETH_ALEN 6 #endif #ifndef IFNAMSIZ #define IFNAMSIZ 16 #endif #ifndef ETH_P_ALL #define ETH_P_ALL 0x0003 #endif #ifndef ETH_P_80211_ENCAP #define ETH_P_80211_ENCAP 0x890d /* TDLS comes under this category */ #endif #ifndef ETH_P_PAE #define ETH_P_PAE 0x888E /* Port Access Entity (IEEE 802.1X) */ #endif /* ETH_P_PAE */ #ifndef ETH_P_EAPOL #define ETH_P_EAPOL ETH_P_PAE #endif /* ETH_P_EAPOL */ #ifndef ETH_P_RSN_PREAUTH #define ETH_P_RSN_PREAUTH 0x88c7 #endif /* ETH_P_RSN_PREAUTH */ #ifndef ETH_P_RRB #define ETH_P_RRB 0x890D #endif /* ETH_P_RRB */ #ifdef __GNUC__ #define PRINTF_FORMAT(a,b) __attribute__ ((format (printf, (a), (b)))) #define STRUCT_PACKED __attribute__ ((packed)) #else #define PRINTF_FORMAT(a,b) #define STRUCT_PACKED #endif #ifdef CONFIG_ANSI_C_EXTRA #if !defined(_MSC_VER) || _MSC_VER < 1400 /* snprintf - used in number of places; sprintf() is _not_ a good replacement * due to possible buffer overflow; see, e.g., * http://www.ijs.si/software/snprintf/ for portable implementation of * snprintf. */ int snprintf(char *str, size_t size, const char *format, ...); /* vsnprintf - only used for wpa_msg() in wpa_supplicant.c */ int vsnprintf(char *str, size_t size, const char *format, va_list ap); #endif /* !defined(_MSC_VER) || _MSC_VER < 1400 */ /* getopt - only used in main.c */ int getopt(int argc, char *const argv[], const char *optstring); extern char *optarg; extern int optind; #ifndef CONFIG_NO_SOCKLEN_T_TYPEDEF #ifndef __socklen_t_defined typedef int socklen_t; #endif #endif /* inline - define as __inline or just define it to be empty, if needed */ #ifdef CONFIG_NO_INLINE #define inline #else #define inline __inline #endif #ifndef __func__ #define __func__ "__func__ not defined" #endif #ifndef bswap_16 #define bswap_16(a) ((((u16) (a) << 8) & 0xff00) | (((u16) (a) >> 8) & 0xff)) #endif #ifndef bswap_32 #define bswap_32(a) ((((u32) (a) << 24) & 0xff000000) | \ (((u32) (a) << 8) & 0xff0000) | \ (((u32) (a) >> 8) & 0xff00) | \ (((u32) (a) >> 24) & 0xff)) #endif #ifndef MSG_DONTWAIT #define MSG_DONTWAIT 0 #endif #ifdef _WIN32_WCE void perror(const char *s); #endif /* _WIN32_WCE */ #endif /* CONFIG_ANSI_C_EXTRA */ #ifndef MAC2STR #define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5] #define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x" /* * Compact form for string representation of MAC address * To be used, e.g., for constructing dbus paths for P2P Devices */ #define COMPACT_MACSTR "%02x%02x%02x%02x%02x%02x" #endif #ifndef BIT #define BIT(x) (1 << (x)) #endif /* * Definitions for sparse validation * (http://kernel.org/pub/linux/kernel/people/josh/sparse/) */ #ifdef __CHECKER__ #define __force __attribute__((force)) #define __bitwise __attribute__((bitwise)) #else #define __force #define __bitwise #endif typedef u16 __bitwise be16; typedef u16 __bitwise le16; typedef u32 __bitwise be32; typedef u32 __bitwise le32; typedef u64 __bitwise be64; typedef u64 __bitwise le64; #ifndef __must_check #if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) #define __must_check __attribute__((__warn_unused_result__)) #else #define __must_check #endif /* __GNUC__ */ #endif /* __must_check */ #ifndef __maybe_unused #if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) #define __maybe_unused __attribute__((unused)) #else #define __maybe_unused #endif /* __GNUC__ */ #endif /* __must_check */ int hwaddr_aton(const char *txt, u8 *addr); int hwaddr_compact_aton(const char *txt, u8 *addr); int hwaddr_aton2(const char *txt, u8 *addr); int hex2byte(const char *hex); int hexstr2bin(const char *hex, u8 *buf, size_t len); void inc_byte_array(u8 *counter, size_t len); void wpa_get_ntp_timestamp(u8 *buf); int wpa_snprintf_hex(char *buf, size_t buf_size, const u8 *data, size_t len); int wpa_snprintf_hex_uppercase(char *buf, size_t buf_size, const u8 *data, size_t len); #ifdef CONFIG_NATIVE_WINDOWS void wpa_unicode2ascii_inplace(TCHAR *str); TCHAR * wpa_strdup_tchar(const char *str); #else /* CONFIG_NATIVE_WINDOWS */ #define wpa_unicode2ascii_inplace(s) do { } while (0) #define wpa_strdup_tchar(s) strdup((s)) #endif /* CONFIG_NATIVE_WINDOWS */ void printf_encode(char *txt, size_t maxlen, const u8 *data, size_t len); size_t printf_decode(u8 *buf, size_t maxlen, const char *str); const char * wpa_ssid_txt(const u8 *ssid, size_t ssid_len); char * wpa_config_parse_string(const char *value, size_t *len); int is_hex(const u8 *data, size_t len); int find_first_bit(u32 value); size_t merge_byte_arrays(u8 *res, size_t res_len, const u8 *src1, size_t src1_len, const u8 *src2, size_t src2_len); char * dup_binstr(const void *src, size_t len); static inline int is_zero_ether_addr(const u8 *a) { return !(a[0] | a[1] | a[2] | a[3] | a[4] | a[5]); } static inline int is_broadcast_ether_addr(const u8 *a) { return (a[0] & a[1] & a[2] & a[3] & a[4] & a[5]) == 0xff; } #define broadcast_ether_addr (const u8 *) "\xff\xff\xff\xff\xff\xff" #include "wpa_debug.h" struct wpa_freq_range_list { struct wpa_freq_range { unsigned int min; unsigned int max; } *range; unsigned int num; }; int freq_range_list_parse(struct wpa_freq_range_list *res, const char *value); int freq_range_list_includes(const struct wpa_freq_range_list *list, unsigned int freq); char * freq_range_list_str(const struct wpa_freq_range_list *list); int int_array_len(const int *a); void int_array_concat(int **res, const int *a); void int_array_sort_unique(int *a); void int_array_add_unique(int **res, int a); #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) /* * gcc 4.4 ends up generating strict-aliasing warnings about some very common * networking socket uses that do not really result in a real problem and * cannot be easily avoided with union-based type-punning due to struct * definitions including another struct in system header files. To avoid having * to fully disable strict-aliasing warnings, provide a mechanism to hide the * typecast from aliasing for now. A cleaner solution will hopefully be found * in the future to handle these cases. */ void * __hide_aliasing_typecast(void *foo); #define aliasing_hide_typecast(a,t) (t *) __hide_aliasing_typecast((a)) #ifdef CONFIG_VALGRIND #include #define WPA_MEM_DEFINED(ptr, len) VALGRIND_MAKE_MEM_DEFINED((ptr), (len)) #else /* CONFIG_VALGRIND */ #define WPA_MEM_DEFINED(ptr, len) do { } while (0) #endif /* CONFIG_VALGRIND */ #endif /* COMMON_H */ wpa-2.1/src/utils/edit.c000066400000000000000000000503241227414666700152060ustar00rootroot00000000000000/* * Command line editing and history * Copyright (c) 2010-2011, 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.h" #include "eloop.h" #include "list.h" #include "edit.h" #define CMD_BUF_LEN 256 static char cmdbuf[CMD_BUF_LEN]; static int cmdbuf_pos = 0; static int cmdbuf_len = 0; static char currbuf[CMD_BUF_LEN]; static int currbuf_valid = 0; static const char *ps2 = NULL; #define HISTORY_MAX 100 struct edit_history { struct dl_list list; char str[1]; }; static struct dl_list history_list; static struct edit_history *history_curr; static void *edit_cb_ctx; static void (*edit_cmd_cb)(void *ctx, char *cmd); static void (*edit_eof_cb)(void *ctx); static char ** (*edit_completion_cb)(void *ctx, const char *cmd, int pos) = NULL; static struct termios prevt, newt; #define CLEAR_END_LINE "\e[K" void edit_clear_line(void) { int i; putchar('\r'); for (i = 0; i < cmdbuf_len + 2 + (ps2 ? (int) os_strlen(ps2) : 0); i++) putchar(' '); } static void move_start(void) { cmdbuf_pos = 0; edit_redraw(); } static void move_end(void) { cmdbuf_pos = cmdbuf_len; edit_redraw(); } static void move_left(void) { if (cmdbuf_pos > 0) { cmdbuf_pos--; edit_redraw(); } } static void move_right(void) { if (cmdbuf_pos < cmdbuf_len) { cmdbuf_pos++; edit_redraw(); } } static void move_word_left(void) { while (cmdbuf_pos > 0 && cmdbuf[cmdbuf_pos - 1] == ' ') cmdbuf_pos--; while (cmdbuf_pos > 0 && cmdbuf[cmdbuf_pos - 1] != ' ') cmdbuf_pos--; edit_redraw(); } static void move_word_right(void) { while (cmdbuf_pos < cmdbuf_len && cmdbuf[cmdbuf_pos] == ' ') cmdbuf_pos++; while (cmdbuf_pos < cmdbuf_len && cmdbuf[cmdbuf_pos] != ' ') cmdbuf_pos++; edit_redraw(); } static void delete_left(void) { if (cmdbuf_pos == 0) return; edit_clear_line(); os_memmove(cmdbuf + cmdbuf_pos - 1, cmdbuf + cmdbuf_pos, cmdbuf_len - cmdbuf_pos); cmdbuf_pos--; cmdbuf_len--; edit_redraw(); } static void delete_current(void) { if (cmdbuf_pos == cmdbuf_len) return; edit_clear_line(); os_memmove(cmdbuf + cmdbuf_pos, cmdbuf + cmdbuf_pos + 1, cmdbuf_len - cmdbuf_pos); cmdbuf_len--; edit_redraw(); } static void delete_word(void) { int pos; edit_clear_line(); pos = cmdbuf_pos; while (pos > 0 && cmdbuf[pos - 1] == ' ') pos--; while (pos > 0 && cmdbuf[pos - 1] != ' ') pos--; os_memmove(cmdbuf + pos, cmdbuf + cmdbuf_pos, cmdbuf_len - cmdbuf_pos); cmdbuf_len -= cmdbuf_pos - pos; cmdbuf_pos = pos; edit_redraw(); } static void clear_left(void) { if (cmdbuf_pos == 0) return; edit_clear_line(); os_memmove(cmdbuf, cmdbuf + cmdbuf_pos, cmdbuf_len - cmdbuf_pos); cmdbuf_len -= cmdbuf_pos; cmdbuf_pos = 0; edit_redraw(); } static void clear_right(void) { if (cmdbuf_pos == cmdbuf_len) return; edit_clear_line(); cmdbuf_len = cmdbuf_pos; edit_redraw(); } static void history_add(const char *str) { struct edit_history *h, *match = NULL, *last = NULL; size_t len, count = 0; if (str[0] == '\0') return; dl_list_for_each(h, &history_list, struct edit_history, list) { if (os_strcmp(str, h->str) == 0) { match = h; break; } last = h; count++; } if (match) { dl_list_del(&h->list); dl_list_add(&history_list, &h->list); history_curr = h; return; } if (count >= HISTORY_MAX && last) { dl_list_del(&last->list); os_free(last); } len = os_strlen(str); h = os_zalloc(sizeof(*h) + len); if (h == NULL) return; dl_list_add(&history_list, &h->list); os_strlcpy(h->str, str, len + 1); history_curr = h; } static void history_use(void) { edit_clear_line(); cmdbuf_len = cmdbuf_pos = os_strlen(history_curr->str); os_memcpy(cmdbuf, history_curr->str, cmdbuf_len); edit_redraw(); } static void history_prev(void) { if (history_curr == NULL) return; if (history_curr == dl_list_first(&history_list, struct edit_history, list)) { if (!currbuf_valid) { cmdbuf[cmdbuf_len] = '\0'; os_memcpy(currbuf, cmdbuf, cmdbuf_len + 1); currbuf_valid = 1; history_use(); return; } } if (history_curr == dl_list_last(&history_list, struct edit_history, list)) return; history_curr = dl_list_entry(history_curr->list.next, struct edit_history, list); history_use(); } static void history_next(void) { if (history_curr == NULL || history_curr == dl_list_first(&history_list, struct edit_history, list)) { if (currbuf_valid) { currbuf_valid = 0; edit_clear_line(); cmdbuf_len = cmdbuf_pos = os_strlen(currbuf); os_memcpy(cmdbuf, currbuf, cmdbuf_len); edit_redraw(); } return; } history_curr = dl_list_entry(history_curr->list.prev, struct edit_history, list); history_use(); } static void history_read(const char *fname) { FILE *f; char buf[CMD_BUF_LEN], *pos; f = fopen(fname, "r"); if (f == NULL) return; while (fgets(buf, CMD_BUF_LEN, f)) { for (pos = buf; *pos; pos++) { if (*pos == '\r' || *pos == '\n') { *pos = '\0'; break; } } history_add(buf); } fclose(f); } static void history_write(const char *fname, int (*filter_cb)(void *ctx, const char *cmd)) { FILE *f; struct edit_history *h; f = fopen(fname, "w"); if (f == NULL) return; dl_list_for_each_reverse(h, &history_list, struct edit_history, list) { if (filter_cb && filter_cb(edit_cb_ctx, h->str)) continue; fprintf(f, "%s\n", h->str); } fclose(f); } static void history_debug_dump(void) { struct edit_history *h; edit_clear_line(); printf("\r"); dl_list_for_each_reverse(h, &history_list, struct edit_history, list) printf("%s%s\n", h == history_curr ? "[C]" : "", h->str); if (currbuf_valid) printf("{%s}\n", currbuf); edit_redraw(); } static void insert_char(int c) { if (cmdbuf_len >= (int) sizeof(cmdbuf) - 1) return; if (cmdbuf_len == cmdbuf_pos) { cmdbuf[cmdbuf_pos++] = c; cmdbuf_len++; putchar(c); fflush(stdout); } else { os_memmove(cmdbuf + cmdbuf_pos + 1, cmdbuf + cmdbuf_pos, cmdbuf_len - cmdbuf_pos); cmdbuf[cmdbuf_pos++] = c; cmdbuf_len++; edit_redraw(); } } static void process_cmd(void) { currbuf_valid = 0; if (cmdbuf_len == 0) { printf("\n%s> ", ps2 ? ps2 : ""); fflush(stdout); return; } printf("\n"); cmdbuf[cmdbuf_len] = '\0'; history_add(cmdbuf); cmdbuf_pos = 0; cmdbuf_len = 0; edit_cmd_cb(edit_cb_ctx, cmdbuf); printf("%s> ", ps2 ? ps2 : ""); fflush(stdout); } static void free_completions(char **c) { int i; if (c == NULL) return; for (i = 0; c[i]; i++) os_free(c[i]); os_free(c); } static int filter_strings(char **c, char *str, size_t len) { int i, j; for (i = 0, j = 0; c[j]; j++) { if (os_strncasecmp(c[j], str, len) == 0) { if (i != j) { c[i] = c[j]; c[j] = NULL; } i++; } else { os_free(c[j]); c[j] = NULL; } } c[i] = NULL; return i; } static int common_len(const char *a, const char *b) { int len = 0; while (a[len] && a[len] == b[len]) len++; return len; } static int max_common_length(char **c) { int len, i; len = os_strlen(c[0]); for (i = 1; c[i]; i++) { int same = common_len(c[0], c[i]); if (same < len) len = same; } return len; } static int cmp_str(const void *a, const void *b) { return os_strcmp(* (const char **) a, * (const char **) b); } static void complete(int list) { char **c; int i, len, count; int start, end; int room, plen, add_space; if (edit_completion_cb == NULL) return; cmdbuf[cmdbuf_len] = '\0'; c = edit_completion_cb(edit_cb_ctx, cmdbuf, cmdbuf_pos); if (c == NULL) return; end = cmdbuf_pos; start = end; while (start > 0 && cmdbuf[start - 1] != ' ') start--; plen = end - start; count = filter_strings(c, &cmdbuf[start], plen); if (count == 0) { free_completions(c); return; } len = max_common_length(c); if (len <= plen && count > 1) { if (list) { qsort(c, count, sizeof(char *), cmp_str); edit_clear_line(); printf("\r"); for (i = 0; c[i]; i++) printf("%s%s", i > 0 ? " " : "", c[i]); printf("\n"); edit_redraw(); } free_completions(c); return; } len -= plen; room = sizeof(cmdbuf) - 1 - cmdbuf_len; if (room < len) len = room; add_space = count == 1 && len < room; os_memmove(cmdbuf + cmdbuf_pos + len + add_space, cmdbuf + cmdbuf_pos, cmdbuf_len - cmdbuf_pos); os_memcpy(&cmdbuf[cmdbuf_pos - plen], c[0], plen + len); if (add_space) cmdbuf[cmdbuf_pos + len] = ' '; cmdbuf_pos += len + add_space; cmdbuf_len += len + add_space; edit_redraw(); free_completions(c); } enum edit_key_code { EDIT_KEY_NONE = 256, EDIT_KEY_TAB, EDIT_KEY_UP, EDIT_KEY_DOWN, EDIT_KEY_RIGHT, EDIT_KEY_LEFT, EDIT_KEY_ENTER, EDIT_KEY_BACKSPACE, EDIT_KEY_INSERT, EDIT_KEY_DELETE, EDIT_KEY_HOME, EDIT_KEY_END, EDIT_KEY_PAGE_UP, EDIT_KEY_PAGE_DOWN, EDIT_KEY_F1, EDIT_KEY_F2, EDIT_KEY_F3, EDIT_KEY_F4, EDIT_KEY_F5, EDIT_KEY_F6, EDIT_KEY_F7, EDIT_KEY_F8, EDIT_KEY_F9, EDIT_KEY_F10, EDIT_KEY_F11, EDIT_KEY_F12, EDIT_KEY_CTRL_UP, EDIT_KEY_CTRL_DOWN, EDIT_KEY_CTRL_RIGHT, EDIT_KEY_CTRL_LEFT, EDIT_KEY_CTRL_A, EDIT_KEY_CTRL_B, EDIT_KEY_CTRL_D, EDIT_KEY_CTRL_E, EDIT_KEY_CTRL_F, EDIT_KEY_CTRL_G, EDIT_KEY_CTRL_H, EDIT_KEY_CTRL_J, EDIT_KEY_CTRL_K, EDIT_KEY_CTRL_L, EDIT_KEY_CTRL_N, EDIT_KEY_CTRL_O, EDIT_KEY_CTRL_P, EDIT_KEY_CTRL_R, EDIT_KEY_CTRL_T, EDIT_KEY_CTRL_U, EDIT_KEY_CTRL_V, EDIT_KEY_CTRL_W, EDIT_KEY_ALT_UP, EDIT_KEY_ALT_DOWN, EDIT_KEY_ALT_RIGHT, EDIT_KEY_ALT_LEFT, EDIT_KEY_SHIFT_UP, EDIT_KEY_SHIFT_DOWN, EDIT_KEY_SHIFT_RIGHT, EDIT_KEY_SHIFT_LEFT, EDIT_KEY_ALT_SHIFT_UP, EDIT_KEY_ALT_SHIFT_DOWN, EDIT_KEY_ALT_SHIFT_RIGHT, EDIT_KEY_ALT_SHIFT_LEFT, EDIT_KEY_EOF }; static void show_esc_buf(const char *esc_buf, char c, int i) { edit_clear_line(); printf("\rESC buffer '%s' c='%c' [%d]\n", esc_buf, c, i); edit_redraw(); } static enum edit_key_code esc_seq_to_key1_no(char last) { switch (last) { case 'A': return EDIT_KEY_UP; case 'B': return EDIT_KEY_DOWN; case 'C': return EDIT_KEY_RIGHT; case 'D': return EDIT_KEY_LEFT; default: return EDIT_KEY_NONE; } } static enum edit_key_code esc_seq_to_key1_shift(char last) { switch (last) { case 'A': return EDIT_KEY_SHIFT_UP; case 'B': return EDIT_KEY_SHIFT_DOWN; case 'C': return EDIT_KEY_SHIFT_RIGHT; case 'D': return EDIT_KEY_SHIFT_LEFT; default: return EDIT_KEY_NONE; } } static enum edit_key_code esc_seq_to_key1_alt(char last) { switch (last) { case 'A': return EDIT_KEY_ALT_UP; case 'B': return EDIT_KEY_ALT_DOWN; case 'C': return EDIT_KEY_ALT_RIGHT; case 'D': return EDIT_KEY_ALT_LEFT; default: return EDIT_KEY_NONE; } } static enum edit_key_code esc_seq_to_key1_alt_shift(char last) { switch (last) { case 'A': return EDIT_KEY_ALT_SHIFT_UP; case 'B': return EDIT_KEY_ALT_SHIFT_DOWN; case 'C': return EDIT_KEY_ALT_SHIFT_RIGHT; case 'D': return EDIT_KEY_ALT_SHIFT_LEFT; default: return EDIT_KEY_NONE; } } static enum edit_key_code esc_seq_to_key1_ctrl(char last) { switch (last) { case 'A': return EDIT_KEY_CTRL_UP; case 'B': return EDIT_KEY_CTRL_DOWN; case 'C': return EDIT_KEY_CTRL_RIGHT; case 'D': return EDIT_KEY_CTRL_LEFT; default: return EDIT_KEY_NONE; } } static enum edit_key_code esc_seq_to_key1(int param1, int param2, char last) { /* ESC-[; */ if (param1 < 0 && param2 < 0) return esc_seq_to_key1_no(last); if (param1 == 1 && param2 == 2) return esc_seq_to_key1_shift(last); if (param1 == 1 && param2 == 3) return esc_seq_to_key1_alt(last); if (param1 == 1 && param2 == 4) return esc_seq_to_key1_alt_shift(last); if (param1 == 1 && param2 == 5) return esc_seq_to_key1_ctrl(last); if (param2 < 0) { if (last != '~') return EDIT_KEY_NONE; switch (param1) { case 2: return EDIT_KEY_INSERT; case 3: return EDIT_KEY_DELETE; case 5: return EDIT_KEY_PAGE_UP; case 6: return EDIT_KEY_PAGE_DOWN; case 15: return EDIT_KEY_F5; case 17: return EDIT_KEY_F6; case 18: return EDIT_KEY_F7; case 19: return EDIT_KEY_F8; case 20: return EDIT_KEY_F9; case 21: return EDIT_KEY_F10; case 23: return EDIT_KEY_F11; case 24: return EDIT_KEY_F12; } } return EDIT_KEY_NONE; } static enum edit_key_code esc_seq_to_key2(int param1, int param2, char last) { /* ESC-O; */ if (param1 >= 0 || param2 >= 0) return EDIT_KEY_NONE; switch (last) { case 'F': return EDIT_KEY_END; case 'H': return EDIT_KEY_HOME; case 'P': return EDIT_KEY_F1; case 'Q': return EDIT_KEY_F2; case 'R': return EDIT_KEY_F3; case 'S': return EDIT_KEY_F4; default: return EDIT_KEY_NONE; } } static enum edit_key_code esc_seq_to_key(char *seq) { char last, *pos; int param1 = -1, param2 = -1; enum edit_key_code ret = EDIT_KEY_NONE; last = '\0'; for (pos = seq; *pos; pos++) last = *pos; if (seq[1] >= '0' && seq[1] <= '9') { param1 = atoi(&seq[1]); pos = os_strchr(seq, ';'); if (pos) param2 = atoi(pos + 1); } if (seq[0] == '[') ret = esc_seq_to_key1(param1, param2, last); else if (seq[0] == 'O') ret = esc_seq_to_key2(param1, param2, last); if (ret != EDIT_KEY_NONE) return ret; edit_clear_line(); printf("\rUnknown escape sequence '%s'\n", seq); edit_redraw(); return EDIT_KEY_NONE; } static enum edit_key_code edit_read_key(int sock) { int c; unsigned char buf[1]; int res; static int esc = -1; static char esc_buf[7]; res = read(sock, buf, 1); if (res < 0) perror("read"); if (res <= 0) return EDIT_KEY_EOF; c = buf[0]; if (esc >= 0) { if (c == 27 /* ESC */) { esc = 0; return EDIT_KEY_NONE; } if (esc == 6) { show_esc_buf(esc_buf, c, 0); esc = -1; } else { esc_buf[esc++] = c; esc_buf[esc] = '\0'; } } if (esc == 1) { if (esc_buf[0] != '[' && esc_buf[0] != 'O') { show_esc_buf(esc_buf, c, 1); esc = -1; return EDIT_KEY_NONE; } else return EDIT_KEY_NONE; /* Escape sequence continues */ } if (esc > 1) { if ((c >= '0' && c <= '9') || c == ';') return EDIT_KEY_NONE; /* Escape sequence continues */ if (c == '~' || (c >= 'A' && c <= 'Z')) { esc = -1; return esc_seq_to_key(esc_buf); } show_esc_buf(esc_buf, c, 2); esc = -1; return EDIT_KEY_NONE; } switch (c) { case 1: return EDIT_KEY_CTRL_A; case 2: return EDIT_KEY_CTRL_B; case 4: return EDIT_KEY_CTRL_D; case 5: return EDIT_KEY_CTRL_E; case 6: return EDIT_KEY_CTRL_F; case 7: return EDIT_KEY_CTRL_G; case 8: return EDIT_KEY_CTRL_H; case 9: return EDIT_KEY_TAB; case 10: return EDIT_KEY_CTRL_J; case 13: /* CR */ return EDIT_KEY_ENTER; case 11: return EDIT_KEY_CTRL_K; case 12: return EDIT_KEY_CTRL_L; case 14: return EDIT_KEY_CTRL_N; case 15: return EDIT_KEY_CTRL_O; case 16: return EDIT_KEY_CTRL_P; case 18: return EDIT_KEY_CTRL_R; case 20: return EDIT_KEY_CTRL_T; case 21: return EDIT_KEY_CTRL_U; case 22: return EDIT_KEY_CTRL_V; case 23: return EDIT_KEY_CTRL_W; case 27: /* ESC */ esc = 0; return EDIT_KEY_NONE; case 127: return EDIT_KEY_BACKSPACE; default: return c; } } static char search_buf[21]; static int search_skip; static char * search_find(void) { struct edit_history *h; size_t len = os_strlen(search_buf); int skip = search_skip; if (len == 0) return NULL; dl_list_for_each(h, &history_list, struct edit_history, list) { if (os_strstr(h->str, search_buf)) { if (skip == 0) return h->str; skip--; } } search_skip = 0; return NULL; } static void search_redraw(void) { char *match = search_find(); printf("\rsearch '%s': %s" CLEAR_END_LINE, search_buf, match ? match : ""); printf("\rsearch '%s", search_buf); fflush(stdout); } static void search_start(void) { edit_clear_line(); search_buf[0] = '\0'; search_skip = 0; search_redraw(); } static void search_clear(void) { search_redraw(); printf("\r" CLEAR_END_LINE); } static void search_stop(void) { char *match = search_find(); search_buf[0] = '\0'; search_clear(); if (match) { os_strlcpy(cmdbuf, match, CMD_BUF_LEN); cmdbuf_len = os_strlen(cmdbuf); cmdbuf_pos = cmdbuf_len; } edit_redraw(); } static void search_cancel(void) { search_buf[0] = '\0'; search_clear(); edit_redraw(); } static void search_backspace(void) { size_t len; len = os_strlen(search_buf); if (len == 0) return; search_buf[len - 1] = '\0'; search_skip = 0; search_redraw(); } static void search_next(void) { search_skip++; search_find(); search_redraw(); } static void search_char(char c) { size_t len; len = os_strlen(search_buf); if (len == sizeof(search_buf) - 1) return; search_buf[len] = c; search_buf[len + 1] = '\0'; search_skip = 0; search_redraw(); } static enum edit_key_code search_key(enum edit_key_code c) { switch (c) { case EDIT_KEY_ENTER: case EDIT_KEY_CTRL_J: case EDIT_KEY_LEFT: case EDIT_KEY_RIGHT: case EDIT_KEY_HOME: case EDIT_KEY_END: case EDIT_KEY_CTRL_A: case EDIT_KEY_CTRL_E: search_stop(); return c; case EDIT_KEY_DOWN: case EDIT_KEY_UP: search_cancel(); return EDIT_KEY_EOF; case EDIT_KEY_CTRL_H: case EDIT_KEY_BACKSPACE: search_backspace(); break; case EDIT_KEY_CTRL_R: search_next(); break; default: if (c >= 32 && c <= 255) search_char(c); break; } return EDIT_KEY_NONE; } static void edit_read_char(int sock, void *eloop_ctx, void *sock_ctx) { static int last_tab = 0; static int search = 0; enum edit_key_code c; c = edit_read_key(sock); if (search) { c = search_key(c); if (c == EDIT_KEY_NONE) return; search = 0; if (c == EDIT_KEY_EOF) return; } if (c != EDIT_KEY_TAB && c != EDIT_KEY_NONE) last_tab = 0; switch (c) { case EDIT_KEY_NONE: break; case EDIT_KEY_EOF: edit_eof_cb(edit_cb_ctx); break; case EDIT_KEY_TAB: complete(last_tab); last_tab = 1; break; case EDIT_KEY_UP: case EDIT_KEY_CTRL_P: history_prev(); break; case EDIT_KEY_DOWN: case EDIT_KEY_CTRL_N: history_next(); break; case EDIT_KEY_RIGHT: case EDIT_KEY_CTRL_F: move_right(); break; case EDIT_KEY_LEFT: case EDIT_KEY_CTRL_B: move_left(); break; case EDIT_KEY_CTRL_RIGHT: move_word_right(); break; case EDIT_KEY_CTRL_LEFT: move_word_left(); break; case EDIT_KEY_DELETE: delete_current(); break; case EDIT_KEY_END: move_end(); break; case EDIT_KEY_HOME: case EDIT_KEY_CTRL_A: move_start(); break; case EDIT_KEY_F2: history_debug_dump(); break; case EDIT_KEY_CTRL_D: if (cmdbuf_len > 0) { delete_current(); return; } printf("\n"); edit_eof_cb(edit_cb_ctx); break; case EDIT_KEY_CTRL_E: move_end(); break; case EDIT_KEY_CTRL_H: case EDIT_KEY_BACKSPACE: delete_left(); break; case EDIT_KEY_ENTER: case EDIT_KEY_CTRL_J: process_cmd(); break; case EDIT_KEY_CTRL_K: clear_right(); break; case EDIT_KEY_CTRL_L: edit_clear_line(); edit_redraw(); break; case EDIT_KEY_CTRL_R: search = 1; search_start(); break; case EDIT_KEY_CTRL_U: clear_left(); break; case EDIT_KEY_CTRL_W: delete_word(); break; default: if (c >= 32 && c <= 255) insert_char(c); break; } } int edit_init(void (*cmd_cb)(void *ctx, char *cmd), void (*eof_cb)(void *ctx), char ** (*completion_cb)(void *ctx, const char *cmd, int pos), void *ctx, const char *history_file, const char *ps) { currbuf[0] = '\0'; dl_list_init(&history_list); history_curr = NULL; if (history_file) history_read(history_file); edit_cb_ctx = ctx; edit_cmd_cb = cmd_cb; edit_eof_cb = eof_cb; edit_completion_cb = completion_cb; tcgetattr(STDIN_FILENO, &prevt); newt = prevt; newt.c_lflag &= ~(ICANON | ECHO); tcsetattr(STDIN_FILENO, TCSANOW, &newt); eloop_register_read_sock(STDIN_FILENO, edit_read_char, NULL, NULL); ps2 = ps; printf("%s> ", ps2 ? ps2 : ""); fflush(stdout); return 0; } void edit_deinit(const char *history_file, int (*filter_cb)(void *ctx, const char *cmd)) { struct edit_history *h; if (history_file) history_write(history_file, filter_cb); while ((h = dl_list_first(&history_list, struct edit_history, list))) { dl_list_del(&h->list); os_free(h); } edit_clear_line(); putchar('\r'); fflush(stdout); eloop_unregister_read_sock(STDIN_FILENO); tcsetattr(STDIN_FILENO, TCSANOW, &prevt); } void edit_redraw(void) { char tmp; cmdbuf[cmdbuf_len] = '\0'; printf("\r%s> %s", ps2 ? ps2 : "", cmdbuf); if (cmdbuf_pos != cmdbuf_len) { tmp = cmdbuf[cmdbuf_pos]; cmdbuf[cmdbuf_pos] = '\0'; printf("\r%s> %s", ps2 ? ps2 : "", cmdbuf); cmdbuf[cmdbuf_pos] = tmp; } fflush(stdout); } wpa-2.1/src/utils/edit.h000066400000000000000000000011441227414666700152070ustar00rootroot00000000000000/* * Command line editing and history * Copyright (c) 2010, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef EDIT_H #define EDIT_H int edit_init(void (*cmd_cb)(void *ctx, char *cmd), void (*eof_cb)(void *ctx), char ** (*completion_cb)(void *ctx, const char *cmd, int pos), void *ctx, const char *history_file, const char *ps); void edit_deinit(const char *history_file, int (*filter_cb)(void *ctx, const char *cmd)); void edit_clear_line(void); void edit_redraw(void); #endif /* EDIT_H */ wpa-2.1/src/utils/edit_readline.c000066400000000000000000000074371227414666700170600ustar00rootroot00000000000000/* * Command line editing and history wrapper for readline * Copyright (c) 2010, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "includes.h" #include #include #include "common.h" #include "eloop.h" #include "edit.h" static void *edit_cb_ctx; static void (*edit_cmd_cb)(void *ctx, char *cmd); static void (*edit_eof_cb)(void *ctx); static char ** (*edit_completion_cb)(void *ctx, const char *cmd, int pos) = NULL; static char **pending_completions = NULL; static void readline_free_completions(void) { int i; if (pending_completions == NULL) return; for (i = 0; pending_completions[i]; i++) os_free(pending_completions[i]); os_free(pending_completions); pending_completions = NULL; } static char * readline_completion_func(const char *text, int state) { static int pos = 0; static size_t len = 0; if (pending_completions == NULL) { rl_attempted_completion_over = 1; return NULL; } if (state == 0) { pos = 0; len = os_strlen(text); } for (; pending_completions[pos]; pos++) { if (strncmp(pending_completions[pos], text, len) == 0) return strdup(pending_completions[pos++]); } rl_attempted_completion_over = 1; return NULL; } static char ** readline_completion(const char *text, int start, int end) { readline_free_completions(); if (edit_completion_cb) pending_completions = edit_completion_cb(edit_cb_ctx, rl_line_buffer, end); return rl_completion_matches(text, readline_completion_func); } static void edit_read_char(int sock, void *eloop_ctx, void *sock_ctx) { rl_callback_read_char(); } static void trunc_nl(char *str) { char *pos = str; while (*pos != '\0') { if (*pos == '\n') { *pos = '\0'; break; } pos++; } } static void readline_cmd_handler(char *cmd) { if (cmd && *cmd) { HIST_ENTRY *h; while (next_history()) ; h = previous_history(); if (h == NULL || os_strcmp(cmd, h->line) != 0) add_history(cmd); next_history(); } if (cmd == NULL) { edit_eof_cb(edit_cb_ctx); return; } trunc_nl(cmd); edit_cmd_cb(edit_cb_ctx, cmd); } int edit_init(void (*cmd_cb)(void *ctx, char *cmd), void (*eof_cb)(void *ctx), char ** (*completion_cb)(void *ctx, const char *cmd, int pos), void *ctx, const char *history_file, const char *ps) { edit_cb_ctx = ctx; edit_cmd_cb = cmd_cb; edit_eof_cb = eof_cb; edit_completion_cb = completion_cb; rl_attempted_completion_function = readline_completion; if (history_file) { read_history(history_file); stifle_history(100); } eloop_register_read_sock(STDIN_FILENO, edit_read_char, NULL, NULL); if (ps) { size_t blen = os_strlen(ps) + 3; char *ps2 = os_malloc(blen); if (ps2) { os_snprintf(ps2, blen, "%s> ", ps); rl_callback_handler_install(ps2, readline_cmd_handler); os_free(ps2); return 0; } } rl_callback_handler_install("> ", readline_cmd_handler); return 0; } void edit_deinit(const char *history_file, int (*filter_cb)(void *ctx, const char *cmd)) { rl_set_prompt(""); rl_replace_line("", 0); rl_redisplay(); rl_callback_handler_remove(); readline_free_completions(); eloop_unregister_read_sock(STDIN_FILENO); if (history_file) { /* Save command history, excluding lines that may contain * passwords. */ HIST_ENTRY *h; history_set_pos(0); while ((h = current_history())) { char *p = h->line; while (*p == ' ' || *p == '\t') p++; if (filter_cb && filter_cb(edit_cb_ctx, p)) { h = remove_history(where_history()); if (h) { free(h->line); free(h->data); free(h); } else next_history(); } else next_history(); } write_history(history_file); } } void edit_clear_line(void) { } void edit_redraw(void) { rl_on_new_line(); rl_redisplay(); } wpa-2.1/src/utils/edit_simple.c000066400000000000000000000033101227414666700165500ustar00rootroot00000000000000/* * Minimal command line editing * Copyright (c) 2010, 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 "eloop.h" #include "edit.h" #define CMD_BUF_LEN 256 static char cmdbuf[CMD_BUF_LEN]; static int cmdbuf_pos = 0; static const char *ps2 = NULL; static void *edit_cb_ctx; static void (*edit_cmd_cb)(void *ctx, char *cmd); static void (*edit_eof_cb)(void *ctx); static void edit_read_char(int sock, void *eloop_ctx, void *sock_ctx) { int c; unsigned char buf[1]; int res; res = read(sock, buf, 1); if (res < 0) perror("read"); if (res <= 0) { edit_eof_cb(edit_cb_ctx); return; } c = buf[0]; if (c == '\r' || c == '\n') { cmdbuf[cmdbuf_pos] = '\0'; cmdbuf_pos = 0; edit_cmd_cb(edit_cb_ctx, cmdbuf); printf("%s> ", ps2 ? ps2 : ""); fflush(stdout); return; } if (c >= 32 && c <= 255) { if (cmdbuf_pos < (int) sizeof(cmdbuf) - 1) { cmdbuf[cmdbuf_pos++] = c; } } } int edit_init(void (*cmd_cb)(void *ctx, char *cmd), void (*eof_cb)(void *ctx), char ** (*completion_cb)(void *ctx, const char *cmd, int pos), void *ctx, const char *history_file, const char *ps) { edit_cb_ctx = ctx; edit_cmd_cb = cmd_cb; edit_eof_cb = eof_cb; eloop_register_read_sock(STDIN_FILENO, edit_read_char, NULL, NULL); ps2 = ps; printf("%s> ", ps2 ? ps2 : ""); fflush(stdout); return 0; } void edit_deinit(const char *history_file, int (*filter_cb)(void *ctx, const char *cmd)) { eloop_unregister_read_sock(STDIN_FILENO); } void edit_clear_line(void) { } void edit_redraw(void) { cmdbuf[cmdbuf_pos] = '\0'; printf("\r> %s", cmdbuf); } wpa-2.1/src/utils/eloop.c000066400000000000000000000533111227414666700153760ustar00rootroot00000000000000/* * Event loop based on select() loop * Copyright (c) 2002-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 "trace.h" #include "list.h" #include "eloop.h" #ifdef CONFIG_ELOOP_POLL #include #include #endif /* CONFIG_ELOOP_POLL */ struct eloop_sock { int sock; void *eloop_data; void *user_data; eloop_sock_handler handler; WPA_TRACE_REF(eloop); WPA_TRACE_REF(user); WPA_TRACE_INFO }; struct eloop_timeout { struct dl_list list; struct os_reltime time; void *eloop_data; void *user_data; eloop_timeout_handler handler; WPA_TRACE_REF(eloop); WPA_TRACE_REF(user); WPA_TRACE_INFO }; struct eloop_signal { int sig; void *user_data; eloop_signal_handler handler; int signaled; }; struct eloop_sock_table { int count; struct eloop_sock *table; int changed; }; struct eloop_data { int max_sock; int count; /* sum of all table counts */ #ifdef CONFIG_ELOOP_POLL int max_pollfd_map; /* number of pollfds_map currently allocated */ int max_poll_fds; /* number of pollfds currently allocated */ struct pollfd *pollfds; struct pollfd **pollfds_map; #endif /* CONFIG_ELOOP_POLL */ struct eloop_sock_table readers; struct eloop_sock_table writers; struct eloop_sock_table exceptions; struct dl_list timeout; int signal_count; struct eloop_signal *signals; int signaled; int pending_terminate; int terminate; int reader_table_changed; }; static struct eloop_data eloop; #ifdef WPA_TRACE static void eloop_sigsegv_handler(int sig) { wpa_trace_show("eloop SIGSEGV"); abort(); } static void eloop_trace_sock_add_ref(struct eloop_sock_table *table) { int i; if (table == NULL || table->table == NULL) return; for (i = 0; i < table->count; i++) { wpa_trace_add_ref(&table->table[i], eloop, table->table[i].eloop_data); wpa_trace_add_ref(&table->table[i], user, table->table[i].user_data); } } static void eloop_trace_sock_remove_ref(struct eloop_sock_table *table) { int i; if (table == NULL || table->table == NULL) return; for (i = 0; i < table->count; i++) { wpa_trace_remove_ref(&table->table[i], eloop, table->table[i].eloop_data); wpa_trace_remove_ref(&table->table[i], user, table->table[i].user_data); } } #else /* WPA_TRACE */ #define eloop_trace_sock_add_ref(table) do { } while (0) #define eloop_trace_sock_remove_ref(table) do { } while (0) #endif /* WPA_TRACE */ int eloop_init(void) { os_memset(&eloop, 0, sizeof(eloop)); dl_list_init(&eloop.timeout); #ifdef WPA_TRACE signal(SIGSEGV, eloop_sigsegv_handler); #endif /* WPA_TRACE */ return 0; } static int eloop_sock_table_add_sock(struct eloop_sock_table *table, int sock, eloop_sock_handler handler, void *eloop_data, void *user_data) { struct eloop_sock *tmp; int new_max_sock; if (sock > eloop.max_sock) new_max_sock = sock; else new_max_sock = eloop.max_sock; if (table == NULL) return -1; #ifdef CONFIG_ELOOP_POLL if (new_max_sock >= eloop.max_pollfd_map) { struct pollfd **nmap; nmap = os_realloc_array(eloop.pollfds_map, new_max_sock + 50, sizeof(struct pollfd *)); if (nmap == NULL) return -1; eloop.max_pollfd_map = new_max_sock + 50; eloop.pollfds_map = nmap; } if (eloop.count + 1 > eloop.max_poll_fds) { struct pollfd *n; int nmax = eloop.count + 1 + 50; n = os_realloc_array(eloop.pollfds, nmax, sizeof(struct pollfd)); if (n == NULL) return -1; eloop.max_poll_fds = nmax; eloop.pollfds = n; } #endif /* CONFIG_ELOOP_POLL */ eloop_trace_sock_remove_ref(table); tmp = os_realloc_array(table->table, table->count + 1, sizeof(struct eloop_sock)); if (tmp == NULL) return -1; tmp[table->count].sock = sock; tmp[table->count].eloop_data = eloop_data; tmp[table->count].user_data = user_data; tmp[table->count].handler = handler; wpa_trace_record(&tmp[table->count]); table->count++; table->table = tmp; eloop.max_sock = new_max_sock; eloop.count++; table->changed = 1; eloop_trace_sock_add_ref(table); return 0; } static void eloop_sock_table_remove_sock(struct eloop_sock_table *table, int sock) { int i; if (table == NULL || table->table == NULL || table->count == 0) return; for (i = 0; i < table->count; i++) { if (table->table[i].sock == sock) break; } if (i == table->count) return; eloop_trace_sock_remove_ref(table); if (i != table->count - 1) { os_memmove(&table->table[i], &table->table[i + 1], (table->count - i - 1) * sizeof(struct eloop_sock)); } table->count--; eloop.count--; table->changed = 1; eloop_trace_sock_add_ref(table); } #ifdef CONFIG_ELOOP_POLL static struct pollfd * find_pollfd(struct pollfd **pollfds_map, int fd, int mx) { if (fd < mx && fd >= 0) return pollfds_map[fd]; return NULL; } static int eloop_sock_table_set_fds(struct eloop_sock_table *readers, struct eloop_sock_table *writers, struct eloop_sock_table *exceptions, struct pollfd *pollfds, struct pollfd **pollfds_map, int max_pollfd_map) { int i; int nxt = 0; int fd; struct pollfd *pfd; /* Clear pollfd lookup map. It will be re-populated below. */ os_memset(pollfds_map, 0, sizeof(struct pollfd *) * max_pollfd_map); if (readers && readers->table) { for (i = 0; i < readers->count; i++) { fd = readers->table[i].sock; assert(fd >= 0 && fd < max_pollfd_map); pollfds[nxt].fd = fd; pollfds[nxt].events = POLLIN; pollfds[nxt].revents = 0; pollfds_map[fd] = &(pollfds[nxt]); nxt++; } } if (writers && writers->table) { for (i = 0; i < writers->count; i++) { /* * See if we already added this descriptor, update it * if so. */ fd = writers->table[i].sock; assert(fd >= 0 && fd < max_pollfd_map); pfd = pollfds_map[fd]; if (!pfd) { pfd = &(pollfds[nxt]); pfd->events = 0; pfd->fd = fd; pollfds[i].revents = 0; pollfds_map[fd] = pfd; nxt++; } pfd->events |= POLLOUT; } } /* * Exceptions are always checked when using poll, but I suppose it's * possible that someone registered a socket *only* for exception * handling. Set the POLLIN bit in this case. */ if (exceptions && exceptions->table) { for (i = 0; i < exceptions->count; i++) { /* * See if we already added this descriptor, just use it * if so. */ fd = exceptions->table[i].sock; assert(fd >= 0 && fd < max_pollfd_map); pfd = pollfds_map[fd]; if (!pfd) { pfd = &(pollfds[nxt]); pfd->events = POLLIN; pfd->fd = fd; pollfds[i].revents = 0; pollfds_map[fd] = pfd; nxt++; } } } return nxt; } static int eloop_sock_table_dispatch_table(struct eloop_sock_table *table, struct pollfd **pollfds_map, int max_pollfd_map, short int revents) { int i; struct pollfd *pfd; if (!table || !table->table) return 0; table->changed = 0; for (i = 0; i < table->count; i++) { pfd = find_pollfd(pollfds_map, table->table[i].sock, max_pollfd_map); if (!pfd) continue; if (!(pfd->revents & revents)) continue; table->table[i].handler(table->table[i].sock, table->table[i].eloop_data, table->table[i].user_data); if (table->changed) return 1; } return 0; } static void eloop_sock_table_dispatch(struct eloop_sock_table *readers, struct eloop_sock_table *writers, struct eloop_sock_table *exceptions, struct pollfd **pollfds_map, int max_pollfd_map) { if (eloop_sock_table_dispatch_table(readers, pollfds_map, max_pollfd_map, POLLIN | POLLERR | POLLHUP)) return; /* pollfds may be invalid at this point */ if (eloop_sock_table_dispatch_table(writers, pollfds_map, max_pollfd_map, POLLOUT)) return; /* pollfds may be invalid at this point */ eloop_sock_table_dispatch_table(exceptions, pollfds_map, max_pollfd_map, POLLERR | POLLHUP); } #else /* CONFIG_ELOOP_POLL */ static void eloop_sock_table_set_fds(struct eloop_sock_table *table, fd_set *fds) { int i; FD_ZERO(fds); if (table->table == NULL) return; for (i = 0; i < table->count; i++) FD_SET(table->table[i].sock, fds); } static void eloop_sock_table_dispatch(struct eloop_sock_table *table, fd_set *fds) { int i; if (table == NULL || table->table == NULL) return; table->changed = 0; for (i = 0; i < table->count; i++) { if (FD_ISSET(table->table[i].sock, fds)) { table->table[i].handler(table->table[i].sock, table->table[i].eloop_data, table->table[i].user_data); if (table->changed) break; } } } #endif /* CONFIG_ELOOP_POLL */ static void eloop_sock_table_destroy(struct eloop_sock_table *table) { if (table) { int i; for (i = 0; i < table->count && table->table; i++) { wpa_printf(MSG_INFO, "ELOOP: remaining socket: " "sock=%d eloop_data=%p user_data=%p " "handler=%p", table->table[i].sock, table->table[i].eloop_data, table->table[i].user_data, table->table[i].handler); wpa_trace_dump_funcname("eloop unregistered socket " "handler", table->table[i].handler); wpa_trace_dump("eloop sock", &table->table[i]); } os_free(table->table); } } int eloop_register_read_sock(int sock, eloop_sock_handler handler, void *eloop_data, void *user_data) { return eloop_register_sock(sock, EVENT_TYPE_READ, handler, eloop_data, user_data); } void eloop_unregister_read_sock(int sock) { eloop_unregister_sock(sock, EVENT_TYPE_READ); } static struct eloop_sock_table *eloop_get_sock_table(eloop_event_type type) { switch (type) { case EVENT_TYPE_READ: return &eloop.readers; case EVENT_TYPE_WRITE: return &eloop.writers; case EVENT_TYPE_EXCEPTION: return &eloop.exceptions; } return NULL; } int eloop_register_sock(int sock, eloop_event_type type, eloop_sock_handler handler, void *eloop_data, void *user_data) { struct eloop_sock_table *table; table = eloop_get_sock_table(type); return eloop_sock_table_add_sock(table, sock, handler, eloop_data, user_data); } void eloop_unregister_sock(int sock, eloop_event_type type) { struct eloop_sock_table *table; table = eloop_get_sock_table(type); eloop_sock_table_remove_sock(table, sock); } int eloop_register_timeout(unsigned int secs, unsigned int usecs, eloop_timeout_handler handler, void *eloop_data, void *user_data) { struct eloop_timeout *timeout, *tmp; os_time_t now_sec; timeout = os_zalloc(sizeof(*timeout)); if (timeout == NULL) return -1; if (os_get_reltime(&timeout->time) < 0) { os_free(timeout); return -1; } now_sec = timeout->time.sec; timeout->time.sec += secs; if (timeout->time.sec < now_sec) { /* * Integer overflow - assume long enough timeout to be assumed * to be infinite, i.e., the timeout would never happen. */ wpa_printf(MSG_DEBUG, "ELOOP: Too long timeout (secs=%u) to " "ever happen - ignore it", secs); os_free(timeout); return 0; } timeout->time.usec += usecs; while (timeout->time.usec >= 1000000) { timeout->time.sec++; timeout->time.usec -= 1000000; } timeout->eloop_data = eloop_data; timeout->user_data = user_data; timeout->handler = handler; wpa_trace_add_ref(timeout, eloop, eloop_data); wpa_trace_add_ref(timeout, user, user_data); wpa_trace_record(timeout); /* Maintain timeouts in order of increasing time */ dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) { if (os_reltime_before(&timeout->time, &tmp->time)) { dl_list_add(tmp->list.prev, &timeout->list); return 0; } } dl_list_add_tail(&eloop.timeout, &timeout->list); return 0; } static void eloop_remove_timeout(struct eloop_timeout *timeout) { dl_list_del(&timeout->list); wpa_trace_remove_ref(timeout, eloop, timeout->eloop_data); wpa_trace_remove_ref(timeout, user, timeout->user_data); os_free(timeout); } int eloop_cancel_timeout(eloop_timeout_handler handler, void *eloop_data, void *user_data) { struct eloop_timeout *timeout, *prev; int removed = 0; dl_list_for_each_safe(timeout, prev, &eloop.timeout, struct eloop_timeout, list) { if (timeout->handler == handler && (timeout->eloop_data == eloop_data || eloop_data == ELOOP_ALL_CTX) && (timeout->user_data == user_data || user_data == ELOOP_ALL_CTX)) { eloop_remove_timeout(timeout); removed++; } } return removed; } int eloop_cancel_timeout_one(eloop_timeout_handler handler, void *eloop_data, void *user_data, struct os_reltime *remaining) { struct eloop_timeout *timeout, *prev; int removed = 0; struct os_reltime now; os_get_reltime(&now); remaining->sec = remaining->usec = 0; dl_list_for_each_safe(timeout, prev, &eloop.timeout, struct eloop_timeout, list) { if (timeout->handler == handler && (timeout->eloop_data == eloop_data) && (timeout->user_data == user_data)) { removed = 1; if (os_reltime_before(&now, &timeout->time)) os_reltime_sub(&timeout->time, &now, remaining); eloop_remove_timeout(timeout); break; } } return removed; } int eloop_is_timeout_registered(eloop_timeout_handler handler, void *eloop_data, void *user_data) { struct eloop_timeout *tmp; dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) { if (tmp->handler == handler && tmp->eloop_data == eloop_data && tmp->user_data == user_data) return 1; } return 0; } int eloop_deplete_timeout(unsigned int req_secs, unsigned int req_usecs, eloop_timeout_handler handler, void *eloop_data, void *user_data) { struct os_reltime now, requested, remaining; struct eloop_timeout *tmp; dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) { if (tmp->handler == handler && tmp->eloop_data == eloop_data && tmp->user_data == user_data) { requested.sec = req_secs; requested.usec = req_usecs; os_get_reltime(&now); os_reltime_sub(&tmp->time, &now, &remaining); if (os_reltime_before(&requested, &remaining)) { eloop_cancel_timeout(handler, eloop_data, user_data); eloop_register_timeout(requested.sec, requested.usec, handler, eloop_data, user_data); return 1; } return 0; } } return -1; } int eloop_replenish_timeout(unsigned int req_secs, unsigned int req_usecs, eloop_timeout_handler handler, void *eloop_data, void *user_data) { struct os_reltime now, requested, remaining; struct eloop_timeout *tmp; dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) { if (tmp->handler == handler && tmp->eloop_data == eloop_data && tmp->user_data == user_data) { requested.sec = req_secs; requested.usec = req_usecs; os_get_reltime(&now); os_reltime_sub(&tmp->time, &now, &remaining); if (os_reltime_before(&remaining, &requested)) { eloop_cancel_timeout(handler, eloop_data, user_data); eloop_register_timeout(requested.sec, requested.usec, handler, eloop_data, user_data); return 1; } return 0; } } return -1; } #ifndef CONFIG_NATIVE_WINDOWS static void eloop_handle_alarm(int sig) { wpa_printf(MSG_ERROR, "eloop: could not process SIGINT or SIGTERM in " "two seconds. Looks like there\n" "is a bug that ends up in a busy loop that " "prevents clean shutdown.\n" "Killing program forcefully.\n"); exit(1); } #endif /* CONFIG_NATIVE_WINDOWS */ static void eloop_handle_signal(int sig) { int i; #ifndef CONFIG_NATIVE_WINDOWS if ((sig == SIGINT || sig == SIGTERM) && !eloop.pending_terminate) { /* Use SIGALRM to break out from potential busy loops that * would not allow the program to be killed. */ eloop.pending_terminate = 1; signal(SIGALRM, eloop_handle_alarm); alarm(2); } #endif /* CONFIG_NATIVE_WINDOWS */ eloop.signaled++; for (i = 0; i < eloop.signal_count; i++) { if (eloop.signals[i].sig == sig) { eloop.signals[i].signaled++; break; } } } static void eloop_process_pending_signals(void) { int i; if (eloop.signaled == 0) return; eloop.signaled = 0; if (eloop.pending_terminate) { #ifndef CONFIG_NATIVE_WINDOWS alarm(0); #endif /* CONFIG_NATIVE_WINDOWS */ eloop.pending_terminate = 0; } for (i = 0; i < eloop.signal_count; i++) { if (eloop.signals[i].signaled) { eloop.signals[i].signaled = 0; eloop.signals[i].handler(eloop.signals[i].sig, eloop.signals[i].user_data); } } } int eloop_register_signal(int sig, eloop_signal_handler handler, void *user_data) { struct eloop_signal *tmp; tmp = os_realloc_array(eloop.signals, eloop.signal_count + 1, sizeof(struct eloop_signal)); if (tmp == NULL) return -1; tmp[eloop.signal_count].sig = sig; tmp[eloop.signal_count].user_data = user_data; tmp[eloop.signal_count].handler = handler; tmp[eloop.signal_count].signaled = 0; eloop.signal_count++; eloop.signals = tmp; signal(sig, eloop_handle_signal); return 0; } int eloop_register_signal_terminate(eloop_signal_handler handler, void *user_data) { int ret = eloop_register_signal(SIGINT, handler, user_data); if (ret == 0) ret = eloop_register_signal(SIGTERM, handler, user_data); return ret; } int eloop_register_signal_reconfig(eloop_signal_handler handler, void *user_data) { #ifdef CONFIG_NATIVE_WINDOWS return 0; #else /* CONFIG_NATIVE_WINDOWS */ return eloop_register_signal(SIGHUP, handler, user_data); #endif /* CONFIG_NATIVE_WINDOWS */ } void eloop_run(void) { #ifdef CONFIG_ELOOP_POLL int num_poll_fds; int timeout_ms = 0; #else /* CONFIG_ELOOP_POLL */ fd_set *rfds, *wfds, *efds; struct timeval _tv; #endif /* CONFIG_ELOOP_POLL */ int res; struct os_reltime tv, now; #ifndef CONFIG_ELOOP_POLL rfds = os_malloc(sizeof(*rfds)); wfds = os_malloc(sizeof(*wfds)); efds = os_malloc(sizeof(*efds)); if (rfds == NULL || wfds == NULL || efds == NULL) goto out; #endif /* CONFIG_ELOOP_POLL */ while (!eloop.terminate && (!dl_list_empty(&eloop.timeout) || eloop.readers.count > 0 || eloop.writers.count > 0 || eloop.exceptions.count > 0)) { struct eloop_timeout *timeout; timeout = dl_list_first(&eloop.timeout, struct eloop_timeout, list); if (timeout) { os_get_reltime(&now); if (os_reltime_before(&now, &timeout->time)) os_reltime_sub(&timeout->time, &now, &tv); else tv.sec = tv.usec = 0; #ifdef CONFIG_ELOOP_POLL timeout_ms = tv.sec * 1000 + tv.usec / 1000; #else /* CONFIG_ELOOP_POLL */ _tv.tv_sec = tv.sec; _tv.tv_usec = tv.usec; #endif /* CONFIG_ELOOP_POLL */ } #ifdef CONFIG_ELOOP_POLL num_poll_fds = eloop_sock_table_set_fds( &eloop.readers, &eloop.writers, &eloop.exceptions, eloop.pollfds, eloop.pollfds_map, eloop.max_pollfd_map); res = poll(eloop.pollfds, num_poll_fds, timeout ? timeout_ms : -1); if (res < 0 && errno != EINTR && errno != 0) { wpa_printf(MSG_INFO, "eloop: poll: %s", strerror(errno)); goto out; } #else /* CONFIG_ELOOP_POLL */ eloop_sock_table_set_fds(&eloop.readers, rfds); eloop_sock_table_set_fds(&eloop.writers, wfds); eloop_sock_table_set_fds(&eloop.exceptions, efds); res = select(eloop.max_sock + 1, rfds, wfds, efds, timeout ? &_tv : NULL); if (res < 0 && errno != EINTR && errno != 0) { wpa_printf(MSG_INFO, "eloop: select: %s", strerror(errno)); goto out; } #endif /* CONFIG_ELOOP_POLL */ eloop_process_pending_signals(); /* check if some registered timeouts have occurred */ timeout = dl_list_first(&eloop.timeout, struct eloop_timeout, list); if (timeout) { os_get_reltime(&now); if (!os_reltime_before(&now, &timeout->time)) { void *eloop_data = timeout->eloop_data; void *user_data = timeout->user_data; eloop_timeout_handler handler = timeout->handler; eloop_remove_timeout(timeout); handler(eloop_data, user_data); } } if (res <= 0) continue; #ifdef CONFIG_ELOOP_POLL eloop_sock_table_dispatch(&eloop.readers, &eloop.writers, &eloop.exceptions, eloop.pollfds_map, eloop.max_pollfd_map); #else /* CONFIG_ELOOP_POLL */ eloop_sock_table_dispatch(&eloop.readers, rfds); eloop_sock_table_dispatch(&eloop.writers, wfds); eloop_sock_table_dispatch(&eloop.exceptions, efds); #endif /* CONFIG_ELOOP_POLL */ } eloop.terminate = 0; out: #ifndef CONFIG_ELOOP_POLL os_free(rfds); os_free(wfds); os_free(efds); #endif /* CONFIG_ELOOP_POLL */ return; } void eloop_terminate(void) { eloop.terminate = 1; } void eloop_destroy(void) { struct eloop_timeout *timeout, *prev; struct os_reltime now; os_get_reltime(&now); dl_list_for_each_safe(timeout, prev, &eloop.timeout, struct eloop_timeout, list) { int sec, usec; sec = timeout->time.sec - now.sec; usec = timeout->time.usec - now.usec; if (timeout->time.usec < now.usec) { sec--; usec += 1000000; } wpa_printf(MSG_INFO, "ELOOP: remaining timeout: %d.%06d " "eloop_data=%p user_data=%p handler=%p", sec, usec, timeout->eloop_data, timeout->user_data, timeout->handler); wpa_trace_dump_funcname("eloop unregistered timeout handler", timeout->handler); wpa_trace_dump("eloop timeout", timeout); eloop_remove_timeout(timeout); } eloop_sock_table_destroy(&eloop.readers); eloop_sock_table_destroy(&eloop.writers); eloop_sock_table_destroy(&eloop.exceptions); os_free(eloop.signals); #ifdef CONFIG_ELOOP_POLL os_free(eloop.pollfds); os_free(eloop.pollfds_map); #endif /* CONFIG_ELOOP_POLL */ } int eloop_terminated(void) { return eloop.terminate; } void eloop_wait_for_read_sock(int sock) { #ifdef CONFIG_ELOOP_POLL struct pollfd pfd; if (sock < 0) return; os_memset(&pfd, 0, sizeof(pfd)); pfd.fd = sock; pfd.events = POLLIN; poll(&pfd, 1, -1); #else /* CONFIG_ELOOP_POLL */ fd_set rfds; if (sock < 0) return; FD_ZERO(&rfds); FD_SET(sock, &rfds); select(sock + 1, &rfds, NULL, NULL, NULL); #endif /* CONFIG_ELOOP_POLL */ } wpa-2.1/src/utils/eloop.h000066400000000000000000000331361227414666700154060ustar00rootroot00000000000000/* * Event loop * Copyright (c) 2002-2006, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. * * This file defines an event loop interface that supports processing events * from registered timeouts (i.e., do something after N seconds), sockets * (e.g., a new packet available for reading), and signals. eloop.c is an * implementation of this interface using select() and sockets. This is * suitable for most UNIX/POSIX systems. When porting to other operating * systems, it may be necessary to replace that implementation with OS specific * mechanisms. */ #ifndef ELOOP_H #define ELOOP_H /** * ELOOP_ALL_CTX - eloop_cancel_timeout() magic number to match all timeouts */ #define ELOOP_ALL_CTX (void *) -1 /** * eloop_event_type - eloop socket event type for eloop_register_sock() * @EVENT_TYPE_READ: Socket has data available for reading * @EVENT_TYPE_WRITE: Socket has room for new data to be written * @EVENT_TYPE_EXCEPTION: An exception has been reported */ typedef enum { EVENT_TYPE_READ = 0, EVENT_TYPE_WRITE, EVENT_TYPE_EXCEPTION } eloop_event_type; /** * eloop_sock_handler - eloop socket event callback type * @sock: File descriptor number for the socket * @eloop_ctx: Registered callback context data (eloop_data) * @sock_ctx: Registered callback context data (user_data) */ typedef void (*eloop_sock_handler)(int sock, void *eloop_ctx, void *sock_ctx); /** * eloop_event_handler - eloop generic event callback type * @eloop_ctx: Registered callback context data (eloop_data) * @sock_ctx: Registered callback context data (user_data) */ typedef void (*eloop_event_handler)(void *eloop_data, void *user_ctx); /** * eloop_timeout_handler - eloop timeout event callback type * @eloop_ctx: Registered callback context data (eloop_data) * @sock_ctx: Registered callback context data (user_data) */ typedef void (*eloop_timeout_handler)(void *eloop_data, void *user_ctx); /** * eloop_signal_handler - eloop signal event callback type * @sig: Signal number * @signal_ctx: Registered callback context data (user_data from * eloop_register_signal(), eloop_register_signal_terminate(), or * eloop_register_signal_reconfig() call) */ typedef void (*eloop_signal_handler)(int sig, void *signal_ctx); /** * eloop_init() - Initialize global event loop data * Returns: 0 on success, -1 on failure * * This function must be called before any other eloop_* function. */ int eloop_init(void); /** * eloop_register_read_sock - Register handler for read events * @sock: File descriptor number for the socket * @handler: Callback function to be called when data is available for reading * @eloop_data: Callback context data (eloop_ctx) * @user_data: Callback context data (sock_ctx) * Returns: 0 on success, -1 on failure * * Register a read socket notifier for the given file descriptor. The handler * function will be called whenever data is available for reading from the * socket. The handler function is responsible for clearing the event after * having processed it in order to avoid eloop from calling the handler again * for the same event. */ int eloop_register_read_sock(int sock, eloop_sock_handler handler, void *eloop_data, void *user_data); /** * eloop_unregister_read_sock - Unregister handler for read events * @sock: File descriptor number for the socket * * Unregister a read socket notifier that was previously registered with * eloop_register_read_sock(). */ void eloop_unregister_read_sock(int sock); /** * eloop_register_sock - Register handler for socket events * @sock: File descriptor number for the socket * @type: Type of event to wait for * @handler: Callback function to be called when the event is triggered * @eloop_data: Callback context data (eloop_ctx) * @user_data: Callback context data (sock_ctx) * Returns: 0 on success, -1 on failure * * Register an event notifier for the given socket's file descriptor. The * handler function will be called whenever the that event is triggered for the * socket. The handler function is responsible for clearing the event after * having processed it in order to avoid eloop from calling the handler again * for the same event. */ int eloop_register_sock(int sock, eloop_event_type type, eloop_sock_handler handler, void *eloop_data, void *user_data); /** * eloop_unregister_sock - Unregister handler for socket events * @sock: File descriptor number for the socket * @type: Type of event for which sock was registered * * Unregister a socket event notifier that was previously registered with * eloop_register_sock(). */ void eloop_unregister_sock(int sock, eloop_event_type type); /** * eloop_register_event - Register handler for generic events * @event: Event to wait (eloop implementation specific) * @event_size: Size of event data * @handler: Callback function to be called when event is triggered * @eloop_data: Callback context data (eloop_data) * @user_data: Callback context data (user_data) * Returns: 0 on success, -1 on failure * * Register an event handler for the given event. This function is used to * register eloop implementation specific events which are mainly targeted for * operating system specific code (driver interface and l2_packet) since the * portable code will not be able to use such an OS-specific call. The handler * function will be called whenever the event is triggered. The handler * function is responsible for clearing the event after having processed it in * order to avoid eloop from calling the handler again for the same event. * * In case of Windows implementation (eloop_win.c), event pointer is of HANDLE * type, i.e., void*. The callers are likely to have 'HANDLE h' type variable, * and they would call this function with eloop_register_event(h, sizeof(h), * ...). */ int eloop_register_event(void *event, size_t event_size, eloop_event_handler handler, void *eloop_data, void *user_data); /** * eloop_unregister_event - Unregister handler for a generic event * @event: Event to cancel (eloop implementation specific) * @event_size: Size of event data * * Unregister a generic event notifier that was previously registered with * eloop_register_event(). */ void eloop_unregister_event(void *event, size_t event_size); /** * eloop_register_timeout - Register timeout * @secs: Number of seconds to the timeout * @usecs: Number of microseconds to the timeout * @handler: Callback function to be called when timeout occurs * @eloop_data: Callback context data (eloop_ctx) * @user_data: Callback context data (sock_ctx) * Returns: 0 on success, -1 on failure * * Register a timeout that will cause the handler function to be called after * given time. */ int eloop_register_timeout(unsigned int secs, unsigned int usecs, eloop_timeout_handler handler, void *eloop_data, void *user_data); /** * eloop_cancel_timeout - Cancel timeouts * @handler: Matching callback function * @eloop_data: Matching eloop_data or %ELOOP_ALL_CTX to match all * @user_data: Matching user_data or %ELOOP_ALL_CTX to match all * Returns: Number of cancelled timeouts * * Cancel matching timeouts registered with * eloop_register_timeout(). ELOOP_ALL_CTX can be used as a wildcard for * cancelling all timeouts regardless of eloop_data/user_data. */ int eloop_cancel_timeout(eloop_timeout_handler handler, void *eloop_data, void *user_data); /** * eloop_cancel_timeout_one - Cancel a single timeout * @handler: Matching callback function * @eloop_data: Matching eloop_data * @user_data: Matching user_data * @remaining: Time left on the cancelled timer * Returns: Number of cancelled timeouts * * Cancel matching timeout registered with * eloop_register_timeout() and return the remaining time left. */ int eloop_cancel_timeout_one(eloop_timeout_handler handler, void *eloop_data, void *user_data, struct os_reltime *remaining); /** * eloop_is_timeout_registered - Check if a timeout is already registered * @handler: Matching callback function * @eloop_data: Matching eloop_data * @user_data: Matching user_data * Returns: 1 if the timeout is registered, 0 if the timeout is not registered * * Determine if a matching timeout is registered * with eloop_register_timeout(). */ int eloop_is_timeout_registered(eloop_timeout_handler handler, void *eloop_data, void *user_data); /** * eloop_deplete_timeout - Deplete a timeout that is already registered * @req_secs: Requested number of seconds to the timeout * @req_usecs: Requested number of microseconds to the timeout * @handler: Matching callback function * @eloop_data: Matching eloop_data * @user_data: Matching user_data * Returns: 1 if the timeout is depleted, 0 if no change is made, -1 if no * timeout matched * * Find a registered matching timeout. If found, * deplete the timeout if remaining time is more than the requested time. */ int eloop_deplete_timeout(unsigned int req_secs, unsigned int req_usecs, eloop_timeout_handler handler, void *eloop_data, void *user_data); /** * eloop_replenish_timeout - Replenish a timeout that is already registered * @req_secs: Requested number of seconds to the timeout * @req_usecs: Requested number of microseconds to the timeout * @handler: Matching callback function * @eloop_data: Matching eloop_data * @user_data: Matching user_data * Returns: 1 if the timeout is replenished, 0 if no change is made, -1 if no * timeout matched * * Find a registered matching timeout. If found, * replenish the timeout if remaining time is less than the requested time. */ int eloop_replenish_timeout(unsigned int req_secs, unsigned int req_usecs, eloop_timeout_handler handler, void *eloop_data, void *user_data); /** * eloop_register_signal - Register handler for signals * @sig: Signal number (e.g., SIGHUP) * @handler: Callback function to be called when the signal is received * @user_data: Callback context data (signal_ctx) * Returns: 0 on success, -1 on failure * * Register a callback function that will be called when a signal is received. * The callback function is actually called only after the system signal * handler has returned. This means that the normal limits for sighandlers * (i.e., only "safe functions" allowed) do not apply for the registered * callback. */ int eloop_register_signal(int sig, eloop_signal_handler handler, void *user_data); /** * eloop_register_signal_terminate - Register handler for terminate signals * @handler: Callback function to be called when the signal is received * @user_data: Callback context data (signal_ctx) * Returns: 0 on success, -1 on failure * * Register a callback function that will be called when a process termination * signal is received. The callback function is actually called only after the * system signal handler has returned. This means that the normal limits for * sighandlers (i.e., only "safe functions" allowed) do not apply for the * registered callback. * * This function is a more portable version of eloop_register_signal() since * the knowledge of exact details of the signals is hidden in eloop * implementation. In case of operating systems using signal(), this function * registers handlers for SIGINT and SIGTERM. */ int eloop_register_signal_terminate(eloop_signal_handler handler, void *user_data); /** * eloop_register_signal_reconfig - Register handler for reconfig signals * @handler: Callback function to be called when the signal is received * @user_data: Callback context data (signal_ctx) * Returns: 0 on success, -1 on failure * * Register a callback function that will be called when a reconfiguration / * hangup signal is received. The callback function is actually called only * after the system signal handler has returned. This means that the normal * limits for sighandlers (i.e., only "safe functions" allowed) do not apply * for the registered callback. * * This function is a more portable version of eloop_register_signal() since * the knowledge of exact details of the signals is hidden in eloop * implementation. In case of operating systems using signal(), this function * registers a handler for SIGHUP. */ int eloop_register_signal_reconfig(eloop_signal_handler handler, void *user_data); /** * eloop_run - Start the event loop * * Start the event loop and continue running as long as there are any * registered event handlers. This function is run after event loop has been * initialized with event_init() and one or more events have been registered. */ void eloop_run(void); /** * eloop_terminate - Terminate event loop * * Terminate event loop even if there are registered events. This can be used * to request the program to be terminated cleanly. */ void eloop_terminate(void); /** * eloop_destroy - Free any resources allocated for the event loop * * After calling eloop_destroy(), other eloop_* functions must not be called * before re-running eloop_init(). */ void eloop_destroy(void); /** * eloop_terminated - Check whether event loop has been terminated * Returns: 1 = event loop terminate, 0 = event loop still running * * This function can be used to check whether eloop_terminate() has been called * to request termination of the event loop. This is normally used to abort * operations that may still be queued to be run when eloop_terminate() was * called. */ int eloop_terminated(void); /** * eloop_wait_for_read_sock - Wait for a single reader * @sock: File descriptor number for the socket * * Do a blocking wait for a single read socket. */ void eloop_wait_for_read_sock(int sock); #endif /* ELOOP_H */ wpa-2.1/src/utils/eloop_win.c000066400000000000000000000361771227414666700162660ustar00rootroot00000000000000/* * Event loop based on Windows events and WaitForMultipleObjects * Copyright (c) 2002-2009, 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.h" #include "list.h" #include "eloop.h" struct eloop_sock { int sock; void *eloop_data; void *user_data; eloop_sock_handler handler; WSAEVENT event; }; struct eloop_event { void *eloop_data; void *user_data; eloop_event_handler handler; HANDLE event; }; struct eloop_timeout { struct dl_list list; struct os_reltime time; void *eloop_data; void *user_data; eloop_timeout_handler handler; }; struct eloop_signal { int sig; void *user_data; eloop_signal_handler handler; int signaled; }; struct eloop_data { int max_sock; size_t reader_count; struct eloop_sock *readers; size_t event_count; struct eloop_event *events; struct dl_list timeout; int signal_count; struct eloop_signal *signals; int signaled; int pending_terminate; int terminate; int reader_table_changed; struct eloop_signal term_signal; HANDLE term_event; HANDLE *handles; size_t num_handles; }; static struct eloop_data eloop; int eloop_init(void) { os_memset(&eloop, 0, sizeof(eloop)); dl_list_init(&eloop.timeout); eloop.num_handles = 1; eloop.handles = os_malloc(eloop.num_handles * sizeof(eloop.handles[0])); if (eloop.handles == NULL) return -1; eloop.term_event = CreateEvent(NULL, FALSE, FALSE, NULL); if (eloop.term_event == NULL) { printf("CreateEvent() failed: %d\n", (int) GetLastError()); os_free(eloop.handles); return -1; } return 0; } static int eloop_prepare_handles(void) { HANDLE *n; if (eloop.num_handles > eloop.reader_count + eloop.event_count + 8) return 0; n = os_realloc_array(eloop.handles, eloop.num_handles * 2, sizeof(eloop.handles[0])); if (n == NULL) return -1; eloop.handles = n; eloop.num_handles *= 2; return 0; } int eloop_register_read_sock(int sock, eloop_sock_handler handler, void *eloop_data, void *user_data) { WSAEVENT event; struct eloop_sock *tmp; if (eloop_prepare_handles()) return -1; event = WSACreateEvent(); if (event == WSA_INVALID_EVENT) { printf("WSACreateEvent() failed: %d\n", WSAGetLastError()); return -1; } if (WSAEventSelect(sock, event, FD_READ)) { printf("WSAEventSelect() failed: %d\n", WSAGetLastError()); WSACloseEvent(event); return -1; } tmp = os_realloc_array(eloop.readers, eloop.reader_count + 1, sizeof(struct eloop_sock)); if (tmp == NULL) { WSAEventSelect(sock, event, 0); WSACloseEvent(event); return -1; } tmp[eloop.reader_count].sock = sock; tmp[eloop.reader_count].eloop_data = eloop_data; tmp[eloop.reader_count].user_data = user_data; tmp[eloop.reader_count].handler = handler; tmp[eloop.reader_count].event = event; eloop.reader_count++; eloop.readers = tmp; if (sock > eloop.max_sock) eloop.max_sock = sock; eloop.reader_table_changed = 1; return 0; } void eloop_unregister_read_sock(int sock) { size_t i; if (eloop.readers == NULL || eloop.reader_count == 0) return; for (i = 0; i < eloop.reader_count; i++) { if (eloop.readers[i].sock == sock) break; } if (i == eloop.reader_count) return; WSAEventSelect(eloop.readers[i].sock, eloop.readers[i].event, 0); WSACloseEvent(eloop.readers[i].event); if (i != eloop.reader_count - 1) { os_memmove(&eloop.readers[i], &eloop.readers[i + 1], (eloop.reader_count - i - 1) * sizeof(struct eloop_sock)); } eloop.reader_count--; eloop.reader_table_changed = 1; } int eloop_register_event(void *event, size_t event_size, eloop_event_handler handler, void *eloop_data, void *user_data) { struct eloop_event *tmp; HANDLE h = event; if (event_size != sizeof(HANDLE) || h == INVALID_HANDLE_VALUE) return -1; if (eloop_prepare_handles()) return -1; tmp = os_realloc_array(eloop.events, eloop.event_count + 1, sizeof(struct eloop_event)); if (tmp == NULL) return -1; tmp[eloop.event_count].eloop_data = eloop_data; tmp[eloop.event_count].user_data = user_data; tmp[eloop.event_count].handler = handler; tmp[eloop.event_count].event = h; eloop.event_count++; eloop.events = tmp; return 0; } void eloop_unregister_event(void *event, size_t event_size) { size_t i; HANDLE h = event; if (eloop.events == NULL || eloop.event_count == 0 || event_size != sizeof(HANDLE)) return; for (i = 0; i < eloop.event_count; i++) { if (eloop.events[i].event == h) break; } if (i == eloop.event_count) return; if (i != eloop.event_count - 1) { os_memmove(&eloop.events[i], &eloop.events[i + 1], (eloop.event_count - i - 1) * sizeof(struct eloop_event)); } eloop.event_count--; } int eloop_register_timeout(unsigned int secs, unsigned int usecs, eloop_timeout_handler handler, void *eloop_data, void *user_data) { struct eloop_timeout *timeout, *tmp; os_time_t now_sec; timeout = os_zalloc(sizeof(*timeout)); if (timeout == NULL) return -1; if (os_get_reltime(&timeout->time) < 0) { os_free(timeout); return -1; } now_sec = timeout->time.sec; timeout->time.sec += secs; if (timeout->time.sec < now_sec) { /* * Integer overflow - assume long enough timeout to be assumed * to be infinite, i.e., the timeout would never happen. */ wpa_printf(MSG_DEBUG, "ELOOP: Too long timeout (secs=%u) to " "ever happen - ignore it", secs); os_free(timeout); return 0; } timeout->time.usec += usecs; while (timeout->time.usec >= 1000000) { timeout->time.sec++; timeout->time.usec -= 1000000; } timeout->eloop_data = eloop_data; timeout->user_data = user_data; timeout->handler = handler; /* Maintain timeouts in order of increasing time */ dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) { if (os_reltime_before(&timeout->time, &tmp->time)) { dl_list_add(tmp->list.prev, &timeout->list); return 0; } } dl_list_add_tail(&eloop.timeout, &timeout->list); return 0; } static void eloop_remove_timeout(struct eloop_timeout *timeout) { dl_list_del(&timeout->list); os_free(timeout); } int eloop_cancel_timeout(eloop_timeout_handler handler, void *eloop_data, void *user_data) { struct eloop_timeout *timeout, *prev; int removed = 0; dl_list_for_each_safe(timeout, prev, &eloop.timeout, struct eloop_timeout, list) { if (timeout->handler == handler && (timeout->eloop_data == eloop_data || eloop_data == ELOOP_ALL_CTX) && (timeout->user_data == user_data || user_data == ELOOP_ALL_CTX)) { eloop_remove_timeout(timeout); removed++; } } return removed; } int eloop_cancel_timeout_one(eloop_timeout_handler handler, void *eloop_data, void *user_data, struct os_reltime *remaining) { struct eloop_timeout *timeout, *prev; int removed = 0; struct os_reltime now; os_get_reltime(&now); remaining->sec = remaining->usec = 0; dl_list_for_each_safe(timeout, prev, &eloop.timeout, struct eloop_timeout, list) { if (timeout->handler == handler && (timeout->eloop_data == eloop_data) && (timeout->user_data == user_data)) { removed = 1; if (os_reltime_before(&now, &timeout->time)) os_reltime_sub(&timeout->time, &now, remaining); eloop_remove_timeout(timeout); break; } } return removed; } int eloop_is_timeout_registered(eloop_timeout_handler handler, void *eloop_data, void *user_data) { struct eloop_timeout *tmp; dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) { if (tmp->handler == handler && tmp->eloop_data == eloop_data && tmp->user_data == user_data) return 1; } return 0; } int eloop_deplete_timeout(unsigned int req_secs, unsigned int req_usecs, eloop_timeout_handler handler, void *eloop_data, void *user_data) { struct os_reltime now, requested, remaining; struct eloop_timeout *tmp; dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) { if (tmp->handler == handler && tmp->eloop_data == eloop_data && tmp->user_data == user_data) { requested.sec = req_secs; requested.usec = req_usecs; os_get_reltime(&now); os_reltime_sub(&tmp->time, &now, &remaining); if (os_reltime_before(&requested, &remaining)) { eloop_cancel_timeout(handler, eloop_data, user_data); eloop_register_timeout(requested.sec, requested.usec, handler, eloop_data, user_data); return 1; } return 0; } } return -1; } int eloop_replenish_timeout(unsigned int req_secs, unsigned int req_usecs, eloop_timeout_handler handler, void *eloop_data, void *user_data) { struct os_reltime now, requested, remaining; struct eloop_timeout *tmp; dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) { if (tmp->handler == handler && tmp->eloop_data == eloop_data && tmp->user_data == user_data) { requested.sec = req_secs; requested.usec = req_usecs; os_get_reltime(&now); os_reltime_sub(&tmp->time, &now, &remaining); if (os_reltime_before(&remaining, &requested)) { eloop_cancel_timeout(handler, eloop_data, user_data); eloop_register_timeout(requested.sec, requested.usec, handler, eloop_data, user_data); return 1; } return 0; } } return -1; } /* TODO: replace with suitable signal handler */ #if 0 static void eloop_handle_signal(int sig) { int i; eloop.signaled++; for (i = 0; i < eloop.signal_count; i++) { if (eloop.signals[i].sig == sig) { eloop.signals[i].signaled++; break; } } } #endif static void eloop_process_pending_signals(void) { int i; if (eloop.signaled == 0) return; eloop.signaled = 0; if (eloop.pending_terminate) { eloop.pending_terminate = 0; } for (i = 0; i < eloop.signal_count; i++) { if (eloop.signals[i].signaled) { eloop.signals[i].signaled = 0; eloop.signals[i].handler(eloop.signals[i].sig, eloop.signals[i].user_data); } } if (eloop.term_signal.signaled) { eloop.term_signal.signaled = 0; eloop.term_signal.handler(eloop.term_signal.sig, eloop.term_signal.user_data); } } int eloop_register_signal(int sig, eloop_signal_handler handler, void *user_data) { struct eloop_signal *tmp; tmp = os_realloc_array(eloop.signals, eloop.signal_count + 1, sizeof(struct eloop_signal)); if (tmp == NULL) return -1; tmp[eloop.signal_count].sig = sig; tmp[eloop.signal_count].user_data = user_data; tmp[eloop.signal_count].handler = handler; tmp[eloop.signal_count].signaled = 0; eloop.signal_count++; eloop.signals = tmp; /* TODO: register signal handler */ return 0; } #ifndef _WIN32_WCE static BOOL eloop_handle_console_ctrl(DWORD type) { switch (type) { case CTRL_C_EVENT: case CTRL_BREAK_EVENT: eloop.signaled++; eloop.term_signal.signaled++; SetEvent(eloop.term_event); return TRUE; default: return FALSE; } } #endif /* _WIN32_WCE */ int eloop_register_signal_terminate(eloop_signal_handler handler, void *user_data) { #ifndef _WIN32_WCE if (SetConsoleCtrlHandler((PHANDLER_ROUTINE) eloop_handle_console_ctrl, TRUE) == 0) { printf("SetConsoleCtrlHandler() failed: %d\n", (int) GetLastError()); return -1; } #endif /* _WIN32_WCE */ eloop.term_signal.handler = handler; eloop.term_signal.user_data = user_data; return 0; } int eloop_register_signal_reconfig(eloop_signal_handler handler, void *user_data) { /* TODO */ return 0; } void eloop_run(void) { struct os_reltime tv, now; DWORD count, ret, timeout_val, err; size_t i; while (!eloop.terminate && (!dl_list_empty(&eloop.timeout) || eloop.reader_count > 0 || eloop.event_count > 0)) { struct eloop_timeout *timeout; tv.sec = tv.usec = 0; timeout = dl_list_first(&eloop.timeout, struct eloop_timeout, list); if (timeout) { os_get_reltime(&now); if (os_reltime_before(&now, &timeout->time)) os_reltime_sub(&timeout->time, &now, &tv); } count = 0; for (i = 0; i < eloop.event_count; i++) eloop.handles[count++] = eloop.events[i].event; for (i = 0; i < eloop.reader_count; i++) eloop.handles[count++] = eloop.readers[i].event; if (eloop.term_event) eloop.handles[count++] = eloop.term_event; if (timeout) timeout_val = tv.sec * 1000 + tv.usec / 1000; else timeout_val = INFINITE; if (count > MAXIMUM_WAIT_OBJECTS) { printf("WaitForMultipleObjects: Too many events: " "%d > %d (ignoring extra events)\n", (int) count, MAXIMUM_WAIT_OBJECTS); count = MAXIMUM_WAIT_OBJECTS; } #ifdef _WIN32_WCE ret = WaitForMultipleObjects(count, eloop.handles, FALSE, timeout_val); #else /* _WIN32_WCE */ ret = WaitForMultipleObjectsEx(count, eloop.handles, FALSE, timeout_val, TRUE); #endif /* _WIN32_WCE */ err = GetLastError(); eloop_process_pending_signals(); /* check if some registered timeouts have occurred */ timeout = dl_list_first(&eloop.timeout, struct eloop_timeout, list); if (timeout) { os_get_reltime(&now); if (!os_reltime_before(&now, &timeout->time)) { void *eloop_data = timeout->eloop_data; void *user_data = timeout->user_data; eloop_timeout_handler handler = timeout->handler; eloop_remove_timeout(timeout); handler(eloop_data, user_data); } } if (ret == WAIT_FAILED) { printf("WaitForMultipleObjects(count=%d) failed: %d\n", (int) count, (int) err); os_sleep(1, 0); continue; } #ifndef _WIN32_WCE if (ret == WAIT_IO_COMPLETION) continue; #endif /* _WIN32_WCE */ if (ret == WAIT_TIMEOUT) continue; while (ret >= WAIT_OBJECT_0 && ret < WAIT_OBJECT_0 + eloop.event_count) { eloop.events[ret].handler( eloop.events[ret].eloop_data, eloop.events[ret].user_data); ret = WaitForMultipleObjects(eloop.event_count, eloop.handles, FALSE, 0); } eloop.reader_table_changed = 0; for (i = 0; i < eloop.reader_count; i++) { WSANETWORKEVENTS events; if (WSAEnumNetworkEvents(eloop.readers[i].sock, eloop.readers[i].event, &events) == 0 && (events.lNetworkEvents & FD_READ)) { eloop.readers[i].handler( eloop.readers[i].sock, eloop.readers[i].eloop_data, eloop.readers[i].user_data); if (eloop.reader_table_changed) break; } } } } void eloop_terminate(void) { eloop.terminate = 1; SetEvent(eloop.term_event); } void eloop_destroy(void) { struct eloop_timeout *timeout, *prev; dl_list_for_each_safe(timeout, prev, &eloop.timeout, struct eloop_timeout, list) { eloop_remove_timeout(timeout); } os_free(eloop.readers); os_free(eloop.signals); if (eloop.term_event) CloseHandle(eloop.term_event); os_free(eloop.handles); eloop.handles = NULL; os_free(eloop.events); eloop.events = NULL; } int eloop_terminated(void) { return eloop.terminate; } void eloop_wait_for_read_sock(int sock) { WSAEVENT event; event = WSACreateEvent(); if (event == WSA_INVALID_EVENT) { printf("WSACreateEvent() failed: %d\n", WSAGetLastError()); return; } if (WSAEventSelect(sock, event, FD_READ)) { printf("WSAEventSelect() failed: %d\n", WSAGetLastError()); WSACloseEvent(event); return ; } WaitForSingleObject(event, INFINITE); WSAEventSelect(sock, event, 0); WSACloseEvent(event); } wpa-2.1/src/utils/ext_password.c000066400000000000000000000042221227414666700167770ustar00rootroot00000000000000/* * External password backend * Copyright (c) 2012, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "includes.h" #ifdef __linux__ #include #endif /* __linux__ */ #include "common.h" #include "ext_password_i.h" #ifdef CONFIG_EXT_PASSWORD_TEST extern struct ext_password_backend ext_password_test; #endif /* CONFIG_EXT_PASSWORD_TEST */ static const struct ext_password_backend *backends[] = { #ifdef CONFIG_EXT_PASSWORD_TEST &ext_password_test, #endif /* CONFIG_EXT_PASSWORD_TEST */ NULL }; struct ext_password_data { const struct ext_password_backend *backend; void *priv; }; struct ext_password_data * ext_password_init(const char *backend, const char *params) { struct ext_password_data *data; int i; data = os_zalloc(sizeof(*data)); if (data == NULL) return NULL; for (i = 0; backends[i]; i++) { if (os_strcmp(backends[i]->name, backend) == 0) { data->backend = backends[i]; break; } } if (!data->backend) { os_free(data); return NULL; } data->priv = data->backend->init(params); if (data->priv == NULL) { os_free(data); return NULL; } return data; } void ext_password_deinit(struct ext_password_data *data) { if (data && data->backend && data->priv) data->backend->deinit(data->priv); os_free(data); } struct wpabuf * ext_password_get(struct ext_password_data *data, const char *name) { if (data == NULL) return NULL; return data->backend->get(data->priv, name); } struct wpabuf * ext_password_alloc(size_t len) { struct wpabuf *buf; buf = wpabuf_alloc(len); if (buf == NULL) return NULL; #ifdef __linux__ if (mlock(wpabuf_head(buf), wpabuf_len(buf)) < 0) { wpa_printf(MSG_ERROR, "EXT PW: mlock failed: %s", strerror(errno)); } #endif /* __linux__ */ return buf; } void ext_password_free(struct wpabuf *pw) { if (pw == NULL) return; os_memset(wpabuf_mhead(pw), 0, wpabuf_len(pw)); #ifdef __linux__ if (munlock(wpabuf_head(pw), wpabuf_len(pw)) < 0) { wpa_printf(MSG_ERROR, "EXT PW: munlock failed: %s", strerror(errno)); } #endif /* __linux__ */ wpabuf_free(pw); } wpa-2.1/src/utils/ext_password.h000066400000000000000000000015271227414666700170110ustar00rootroot00000000000000/* * External password backend * Copyright (c) 2012, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef EXT_PASSWORD_H #define EXT_PASSWORD_H struct ext_password_data; #ifdef CONFIG_EXT_PASSWORD struct ext_password_data * ext_password_init(const char *backend, const char *params); void ext_password_deinit(struct ext_password_data *data); struct wpabuf * ext_password_get(struct ext_password_data *data, const char *name); void ext_password_free(struct wpabuf *pw); #else /* CONFIG_EXT_PASSWORD */ #define ext_password_init(b, p) ((void *) 1) #define ext_password_deinit(d) do { } while (0) #define ext_password_get(d, n) (NULL) #define ext_password_free(p) do { } while (0) #endif /* CONFIG_EXT_PASSWORD */ #endif /* EXT_PASSWORD_H */ wpa-2.1/src/utils/ext_password_i.h000066400000000000000000000010371227414666700173150ustar00rootroot00000000000000/* * External password backend - internal definitions * Copyright (c) 2012, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef EXT_PASSWORD_I_H #define EXT_PASSWORD_I_H #include "ext_password.h" struct ext_password_backend { const char *name; void * (*init)(const char *params); void (*deinit)(void *ctx); struct wpabuf * (*get)(void *ctx, const char *name); }; struct wpabuf * ext_password_alloc(size_t len); #endif /* EXT_PASSWORD_I_H */ wpa-2.1/src/utils/ext_password_test.c000066400000000000000000000033271227414666700200430ustar00rootroot00000000000000/* * External password backend * Copyright (c) 2012, 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 "ext_password_i.h" struct ext_password_test_data { char *params; }; static void * ext_password_test_init(const char *params) { struct ext_password_test_data *data; data = os_zalloc(sizeof(*data)); if (data == NULL) return NULL; if (params) data->params = os_strdup(params); return data; } static void ext_password_test_deinit(void *ctx) { struct ext_password_test_data *data = ctx; os_free(data->params); os_free(data); } static struct wpabuf * ext_password_test_get(void *ctx, const char *name) { struct ext_password_test_data *data = ctx; char *pos, *pos2; size_t nlen; wpa_printf(MSG_DEBUG, "EXT PW TEST: get(%s)", name); pos = data->params; if (pos == NULL) return NULL; nlen = os_strlen(name); while (pos && *pos) { if (os_strncmp(pos, name, nlen) == 0 && pos[nlen] == '=') { struct wpabuf *buf; pos += nlen + 1; pos2 = pos; while (*pos2 != '|' && *pos2 != '\0') pos2++; buf = ext_password_alloc(pos2 - pos); if (buf == NULL) return NULL; wpabuf_put_data(buf, pos, pos2 - pos); wpa_hexdump_ascii_key(MSG_DEBUG, "EXT PW TEST: value", wpabuf_head(buf), wpabuf_len(buf)); return buf; } pos = os_strchr(pos + 1, '|'); if (pos) pos++; } wpa_printf(MSG_DEBUG, "EXT PW TEST: get(%s) - not found", name); return NULL; } const struct ext_password_backend ext_password_test = { .name = "test", .init = ext_password_test_init, .deinit = ext_password_test_deinit, .get = ext_password_test_get, }; wpa-2.1/src/utils/includes.h000066400000000000000000000023711227414666700160730ustar00rootroot00000000000000/* * wpa_supplicant/hostapd - Default include files * Copyright (c) 2005-2006, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. * * This header file is included into all C files so that commonly used header * files can be selected with OS specific ifdef blocks in one place instead of * having to have OS/C library specific selection in many files. */ #ifndef INCLUDES_H #define INCLUDES_H /* Include possible build time configuration before including anything else */ #include "build_config.h" #include #include #include #include #ifndef _WIN32_WCE #ifndef CONFIG_TI_COMPILER #include #include #endif /* CONFIG_TI_COMPILER */ #include #endif /* _WIN32_WCE */ #include #ifndef CONFIG_TI_COMPILER #ifndef _MSC_VER #include #endif /* _MSC_VER */ #endif /* CONFIG_TI_COMPILER */ #ifndef CONFIG_NATIVE_WINDOWS #ifndef CONFIG_TI_COMPILER #include #include #include #ifndef __vxworks #include #include #endif /* __vxworks */ #endif /* CONFIG_TI_COMPILER */ #endif /* CONFIG_NATIVE_WINDOWS */ #endif /* INCLUDES_H */ wpa-2.1/src/utils/ip_addr.c000066400000000000000000000026661227414666700156710ustar00rootroot00000000000000/* * IP address processing * Copyright (c) 2003-2006, 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 "ip_addr.h" const char * hostapd_ip_txt(const struct hostapd_ip_addr *addr, char *buf, size_t buflen) { if (buflen == 0 || addr == NULL) return NULL; if (addr->af == AF_INET) { os_strlcpy(buf, inet_ntoa(addr->u.v4), buflen); } else { buf[0] = '\0'; } #ifdef CONFIG_IPV6 if (addr->af == AF_INET6) { if (inet_ntop(AF_INET6, &addr->u.v6, buf, buflen) == NULL) buf[0] = '\0'; } #endif /* CONFIG_IPV6 */ return buf; } int hostapd_ip_diff(struct hostapd_ip_addr *a, struct hostapd_ip_addr *b) { if (a == NULL && b == NULL) return 0; if (a == NULL || b == NULL) return 1; switch (a->af) { case AF_INET: if (a->u.v4.s_addr != b->u.v4.s_addr) return 1; break; #ifdef CONFIG_IPV6 case AF_INET6: if (os_memcmp(&a->u.v6, &b->u.v6, sizeof(a->u.v6)) != 0) return 1; break; #endif /* CONFIG_IPV6 */ } return 0; } int hostapd_parse_ip_addr(const char *txt, struct hostapd_ip_addr *addr) { #ifndef CONFIG_NATIVE_WINDOWS if (inet_aton(txt, &addr->u.v4)) { addr->af = AF_INET; return 0; } #ifdef CONFIG_IPV6 if (inet_pton(AF_INET6, txt, &addr->u.v6) > 0) { addr->af = AF_INET6; return 0; } #endif /* CONFIG_IPV6 */ #endif /* CONFIG_NATIVE_WINDOWS */ return -1; } wpa-2.1/src/utils/ip_addr.h000066400000000000000000000012531227414666700156650ustar00rootroot00000000000000/* * IP address processing * Copyright (c) 2003-2006, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef IP_ADDR_H #define IP_ADDR_H struct hostapd_ip_addr { int af; /* AF_INET / AF_INET6 */ union { struct in_addr v4; #ifdef CONFIG_IPV6 struct in6_addr v6; #endif /* CONFIG_IPV6 */ u8 max_len[16]; } u; }; const char * hostapd_ip_txt(const struct hostapd_ip_addr *addr, char *buf, size_t buflen); int hostapd_ip_diff(struct hostapd_ip_addr *a, struct hostapd_ip_addr *b); int hostapd_parse_ip_addr(const char *txt, struct hostapd_ip_addr *addr); #endif /* IP_ADDR_H */ wpa-2.1/src/utils/list.h000066400000000000000000000045201227414666700152360ustar00rootroot00000000000000/* * Doubly-linked list * Copyright (c) 2009, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef LIST_H #define LIST_H /** * struct dl_list - Doubly-linked list */ struct dl_list { struct dl_list *next; struct dl_list *prev; }; static inline void dl_list_init(struct dl_list *list) { list->next = list; list->prev = list; } static inline void dl_list_add(struct dl_list *list, struct dl_list *item) { item->next = list->next; item->prev = list; list->next->prev = item; list->next = item; } static inline void dl_list_add_tail(struct dl_list *list, struct dl_list *item) { dl_list_add(list->prev, item); } static inline void dl_list_del(struct dl_list *item) { item->next->prev = item->prev; item->prev->next = item->next; item->next = NULL; item->prev = NULL; } static inline int dl_list_empty(struct dl_list *list) { return list->next == list; } static inline unsigned int dl_list_len(struct dl_list *list) { struct dl_list *item; int count = 0; for (item = list->next; item != list; item = item->next) count++; return count; } #ifndef offsetof #define offsetof(type, member) ((long) &((type *) 0)->member) #endif #define dl_list_entry(item, type, member) \ ((type *) ((char *) item - offsetof(type, member))) #define dl_list_first(list, type, member) \ (dl_list_empty((list)) ? NULL : \ dl_list_entry((list)->next, type, member)) #define dl_list_last(list, type, member) \ (dl_list_empty((list)) ? NULL : \ dl_list_entry((list)->prev, type, member)) #define dl_list_for_each(item, list, type, member) \ for (item = dl_list_entry((list)->next, type, member); \ &item->member != (list); \ item = dl_list_entry(item->member.next, type, member)) #define dl_list_for_each_safe(item, n, list, type, member) \ for (item = dl_list_entry((list)->next, type, member), \ n = dl_list_entry(item->member.next, type, member); \ &item->member != (list); \ item = n, n = dl_list_entry(n->member.next, type, member)) #define dl_list_for_each_reverse(item, list, type, member) \ for (item = dl_list_entry((list)->prev, type, member); \ &item->member != (list); \ item = dl_list_entry(item->member.prev, type, member)) #define DEFINE_DL_LIST(name) \ struct dl_list name = { &(name), &(name) } #endif /* LIST_H */ wpa-2.1/src/utils/os.h000066400000000000000000000414711227414666700147120ustar00rootroot00000000000000/* * OS specific functions * Copyright (c) 2005-2009, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef OS_H #define OS_H typedef long os_time_t; /** * os_sleep - Sleep (sec, usec) * @sec: Number of seconds to sleep * @usec: Number of microseconds to sleep */ void os_sleep(os_time_t sec, os_time_t usec); struct os_time { os_time_t sec; os_time_t usec; }; struct os_reltime { os_time_t sec; os_time_t usec; }; /** * os_get_time - Get current time (sec, usec) * @t: Pointer to buffer for the time * Returns: 0 on success, -1 on failure */ int os_get_time(struct os_time *t); /** * os_get_reltime - Get relative time (sec, usec) * @t: Pointer to buffer for the time * Returns: 0 on success, -1 on failure */ int os_get_reltime(struct os_reltime *t); /* Helpers for handling struct os_time */ static inline int os_time_before(struct os_time *a, struct os_time *b) { return (a->sec < b->sec) || (a->sec == b->sec && a->usec < b->usec); } static inline void os_time_sub(struct os_time *a, struct os_time *b, struct os_time *res) { res->sec = a->sec - b->sec; res->usec = a->usec - b->usec; if (res->usec < 0) { res->sec--; res->usec += 1000000; } } /* Helpers for handling struct os_reltime */ static inline int os_reltime_before(struct os_reltime *a, struct os_reltime *b) { return (a->sec < b->sec) || (a->sec == b->sec && a->usec < b->usec); } static inline void os_reltime_sub(struct os_reltime *a, struct os_reltime *b, struct os_reltime *res) { res->sec = a->sec - b->sec; res->usec = a->usec - b->usec; if (res->usec < 0) { res->sec--; res->usec += 1000000; } } static inline void os_reltime_age(struct os_reltime *start, struct os_reltime *age) { struct os_reltime now; os_get_reltime(&now); os_reltime_sub(&now, start, age); } static inline int os_reltime_expired(struct os_reltime *now, struct os_reltime *ts, os_time_t timeout_secs) { struct os_reltime age; os_reltime_sub(now, ts, &age); return (age.sec > timeout_secs) || (age.sec == timeout_secs && age.usec > 0); } static inline int os_reltime_initialized(struct os_reltime *t) { return t->sec != 0 || t->usec != 0; } /** * os_mktime - Convert broken-down time into seconds since 1970-01-01 * @year: Four digit year * @month: Month (1 .. 12) * @day: Day of month (1 .. 31) * @hour: Hour (0 .. 23) * @min: Minute (0 .. 59) * @sec: Second (0 .. 60) * @t: Buffer for returning calendar time representation (seconds since * 1970-01-01 00:00:00) * Returns: 0 on success, -1 on failure * * Note: The result is in seconds from Epoch, i.e., in UTC, not in local time * which is used by POSIX mktime(). */ int os_mktime(int year, int month, int day, int hour, int min, int sec, os_time_t *t); struct os_tm { int sec; /* 0..59 or 60 for leap seconds */ int min; /* 0..59 */ int hour; /* 0..23 */ int day; /* 1..31 */ int month; /* 1..12 */ int year; /* Four digit year */ }; int os_gmtime(os_time_t t, struct os_tm *tm); /** * os_daemonize - Run in the background (detach from the controlling terminal) * @pid_file: File name to write the process ID to or %NULL to skip this * Returns: 0 on success, -1 on failure */ int os_daemonize(const char *pid_file); /** * os_daemonize_terminate - Stop running in the background (remove pid file) * @pid_file: File name to write the process ID to or %NULL to skip this */ void os_daemonize_terminate(const char *pid_file); /** * os_get_random - Get cryptographically strong pseudo random data * @buf: Buffer for pseudo random data * @len: Length of the buffer * Returns: 0 on success, -1 on failure */ int os_get_random(unsigned char *buf, size_t len); /** * os_random - Get pseudo random value (not necessarily very strong) * Returns: Pseudo random value */ unsigned long os_random(void); /** * os_rel2abs_path - Get an absolute path for a file * @rel_path: Relative path to a file * Returns: Absolute path for the file or %NULL on failure * * This function tries to convert a relative path of a file to an absolute path * in order for the file to be found even if current working directory has * changed. The returned value is allocated and caller is responsible for * freeing it. It is acceptable to just return the same path in an allocated * buffer, e.g., return strdup(rel_path). This function is only used to find * configuration files when os_daemonize() may have changed the current working * directory and relative path would be pointing to a different location. */ char * os_rel2abs_path(const char *rel_path); /** * os_program_init - Program initialization (called at start) * Returns: 0 on success, -1 on failure * * This function is called when a programs starts. If there are any OS specific * processing that is needed, it can be placed here. It is also acceptable to * just return 0 if not special processing is needed. */ int os_program_init(void); /** * os_program_deinit - Program deinitialization (called just before exit) * * This function is called just before a program exists. If there are any OS * specific processing, e.g., freeing resourced allocated in os_program_init(), * it should be done here. It is also acceptable for this function to do * nothing. */ void os_program_deinit(void); /** * os_setenv - Set environment variable * @name: Name of the variable * @value: Value to set to the variable * @overwrite: Whether existing variable should be overwritten * Returns: 0 on success, -1 on error * * This function is only used for wpa_cli action scripts. OS wrapper does not * need to implement this if such functionality is not needed. */ int os_setenv(const char *name, const char *value, int overwrite); /** * os_unsetenv - Delete environent variable * @name: Name of the variable * Returns: 0 on success, -1 on error * * This function is only used for wpa_cli action scripts. OS wrapper does not * need to implement this if such functionality is not needed. */ int os_unsetenv(const char *name); /** * os_readfile - Read a file to an allocated memory buffer * @name: Name of the file to read * @len: For returning the length of the allocated buffer * Returns: Pointer to the allocated buffer or %NULL on failure * * This function allocates memory and reads the given file to this buffer. Both * binary and text files can be read with this function. The caller is * responsible for freeing the returned buffer with os_free(). */ char * os_readfile(const char *name, size_t *len); /** * os_zalloc - Allocate and zero memory * @size: Number of bytes to allocate * Returns: Pointer to allocated and zeroed memory or %NULL on failure * * Caller is responsible for freeing the returned buffer with os_free(). */ void * os_zalloc(size_t size); /** * os_calloc - Allocate and zero memory for an array * @nmemb: Number of members in the array * @size: Number of bytes in each member * Returns: Pointer to allocated and zeroed memory or %NULL on failure * * This function can be used as a wrapper for os_zalloc(nmemb * size) when an * allocation is used for an array. The main benefit over os_zalloc() is in * having an extra check to catch integer overflows in multiplication. * * Caller is responsible for freeing the returned buffer with os_free(). */ static inline void * os_calloc(size_t nmemb, size_t size) { if (size && nmemb > (~(size_t) 0) / size) return NULL; return os_zalloc(nmemb * size); } /* * The following functions are wrapper for standard ANSI C or POSIX functions. * By default, they are just defined to use the standard function name and no * os_*.c implementation is needed for them. This avoids extra function calls * by allowing the C pre-processor take care of the function name mapping. * * If the target system uses a C library that does not provide these functions, * build_config.h can be used to define the wrappers to use a different * function name. This can be done on function-by-function basis since the * defines here are only used if build_config.h does not define the os_* name. * If needed, os_*.c file can be used to implement the functions that are not * included in the C library on the target system. Alternatively, * OS_NO_C_LIB_DEFINES can be defined to skip all defines here in which case * these functions need to be implemented in os_*.c file for the target system. */ #ifdef OS_NO_C_LIB_DEFINES /** * os_malloc - Allocate dynamic memory * @size: Size of the buffer to allocate * Returns: Allocated buffer or %NULL on failure * * Caller is responsible for freeing the returned buffer with os_free(). */ void * os_malloc(size_t size); /** * os_realloc - Re-allocate dynamic memory * @ptr: Old buffer from os_malloc() or os_realloc() * @size: Size of the new buffer * Returns: Allocated buffer or %NULL on failure * * Caller is responsible for freeing the returned buffer with os_free(). * If re-allocation fails, %NULL is returned and the original buffer (ptr) is * not freed and caller is still responsible for freeing it. */ void * os_realloc(void *ptr, size_t size); /** * os_free - Free dynamic memory * @ptr: Old buffer from os_malloc() or os_realloc(); can be %NULL */ void os_free(void *ptr); /** * os_memcpy - Copy memory area * @dest: Destination * @src: Source * @n: Number of bytes to copy * Returns: dest * * The memory areas src and dst must not overlap. os_memmove() can be used with * overlapping memory. */ void * os_memcpy(void *dest, const void *src, size_t n); /** * os_memmove - Copy memory area * @dest: Destination * @src: Source * @n: Number of bytes to copy * Returns: dest * * The memory areas src and dst may overlap. */ void * os_memmove(void *dest, const void *src, size_t n); /** * os_memset - Fill memory with a constant byte * @s: Memory area to be filled * @c: Constant byte * @n: Number of bytes started from s to fill with c * Returns: s */ void * os_memset(void *s, int c, size_t n); /** * os_memcmp - Compare memory areas * @s1: First buffer * @s2: Second buffer * @n: Maximum numbers of octets to compare * Returns: An integer less than, equal to, or greater than zero if s1 is * found to be less than, to match, or be greater than s2. Only first n * characters will be compared. */ int os_memcmp(const void *s1, const void *s2, size_t n); /** * os_strdup - Duplicate a string * @s: Source string * Returns: Allocated buffer with the string copied into it or %NULL on failure * * Caller is responsible for freeing the returned buffer with os_free(). */ char * os_strdup(const char *s); /** * os_strlen - Calculate the length of a string * @s: '\0' terminated string * Returns: Number of characters in s (not counting the '\0' terminator) */ size_t os_strlen(const char *s); /** * os_strcasecmp - Compare two strings ignoring case * @s1: First string * @s2: Second string * Returns: An integer less than, equal to, or greater than zero if s1 is * found to be less than, to match, or be greatred than s2 */ int os_strcasecmp(const char *s1, const char *s2); /** * os_strncasecmp - Compare two strings ignoring case * @s1: First string * @s2: Second string * @n: Maximum numbers of characters to compare * Returns: An integer less than, equal to, or greater than zero if s1 is * found to be less than, to match, or be greater than s2. Only first n * characters will be compared. */ int os_strncasecmp(const char *s1, const char *s2, size_t n); /** * os_strchr - Locate the first occurrence of a character in string * @s: String * @c: Character to search for * Returns: Pointer to the matched character or %NULL if not found */ char * os_strchr(const char *s, int c); /** * os_strrchr - Locate the last occurrence of a character in string * @s: String * @c: Character to search for * Returns: Pointer to the matched character or %NULL if not found */ char * os_strrchr(const char *s, int c); /** * os_strcmp - Compare two strings * @s1: First string * @s2: Second string * Returns: An integer less than, equal to, or greater than zero if s1 is * found to be less than, to match, or be greatred than s2 */ int os_strcmp(const char *s1, const char *s2); /** * os_strncmp - Compare two strings * @s1: First string * @s2: Second string * @n: Maximum numbers of characters to compare * Returns: An integer less than, equal to, or greater than zero if s1 is * found to be less than, to match, or be greater than s2. Only first n * characters will be compared. */ int os_strncmp(const char *s1, const char *s2, size_t n); /** * os_strstr - Locate a substring * @haystack: String (haystack) to search from * @needle: Needle to search from haystack * Returns: Pointer to the beginning of the substring or %NULL if not found */ char * os_strstr(const char *haystack, const char *needle); /** * os_snprintf - Print to a memory buffer * @str: Memory buffer to print into * @size: Maximum length of the str buffer * @format: printf format * Returns: Number of characters printed (not including trailing '\0'). * * If the output buffer is truncated, number of characters which would have * been written is returned. Since some C libraries return -1 in such a case, * the caller must be prepared on that value, too, to indicate truncation. * * Note: Some C library implementations of snprintf() may not guarantee null * termination in case the output is truncated. The OS wrapper function of * os_snprintf() should provide this guarantee, i.e., to null terminate the * output buffer if a C library version of the function is used and if that * function does not guarantee null termination. * * If the target system does not include snprintf(), see, e.g., * http://www.ijs.si/software/snprintf/ for an example of a portable * implementation of snprintf. */ int os_snprintf(char *str, size_t size, const char *format, ...); #else /* OS_NO_C_LIB_DEFINES */ #ifdef WPA_TRACE void * os_malloc(size_t size); void * os_realloc(void *ptr, size_t size); void os_free(void *ptr); char * os_strdup(const char *s); #else /* WPA_TRACE */ #ifndef os_malloc #define os_malloc(s) malloc((s)) #endif #ifndef os_realloc #define os_realloc(p, s) realloc((p), (s)) #endif #ifndef os_free #define os_free(p) free((p)) #endif #ifndef os_strdup #ifdef _MSC_VER #define os_strdup(s) _strdup(s) #else #define os_strdup(s) strdup(s) #endif #endif #endif /* WPA_TRACE */ #ifndef os_memcpy #define os_memcpy(d, s, n) memcpy((d), (s), (n)) #endif #ifndef os_memmove #define os_memmove(d, s, n) memmove((d), (s), (n)) #endif #ifndef os_memset #define os_memset(s, c, n) memset(s, c, n) #endif #ifndef os_memcmp #define os_memcmp(s1, s2, n) memcmp((s1), (s2), (n)) #endif #ifndef os_strlen #define os_strlen(s) strlen(s) #endif #ifndef os_strcasecmp #ifdef _MSC_VER #define os_strcasecmp(s1, s2) _stricmp((s1), (s2)) #else #define os_strcasecmp(s1, s2) strcasecmp((s1), (s2)) #endif #endif #ifndef os_strncasecmp #ifdef _MSC_VER #define os_strncasecmp(s1, s2, n) _strnicmp((s1), (s2), (n)) #else #define os_strncasecmp(s1, s2, n) strncasecmp((s1), (s2), (n)) #endif #endif #ifndef os_strchr #define os_strchr(s, c) strchr((s), (c)) #endif #ifndef os_strcmp #define os_strcmp(s1, s2) strcmp((s1), (s2)) #endif #ifndef os_strncmp #define os_strncmp(s1, s2, n) strncmp((s1), (s2), (n)) #endif #ifndef os_strrchr #define os_strrchr(s, c) strrchr((s), (c)) #endif #ifndef os_strstr #define os_strstr(h, n) strstr((h), (n)) #endif #ifndef os_snprintf #ifdef _MSC_VER #define os_snprintf _snprintf #else #define os_snprintf snprintf #endif #endif #endif /* OS_NO_C_LIB_DEFINES */ static inline void * os_realloc_array(void *ptr, size_t nmemb, size_t size) { if (size && nmemb > (~(size_t) 0) / size) return NULL; return os_realloc(ptr, nmemb * size); } /** * os_strlcpy - Copy a string with size bound and NUL-termination * @dest: Destination * @src: Source * @siz: Size of the target buffer * Returns: Total length of the target string (length of src) (not including * NUL-termination) * * This function matches in behavior with the strlcpy(3) function in OpenBSD. */ size_t os_strlcpy(char *dest, const char *src, size_t siz); #ifdef OS_REJECT_C_LIB_FUNCTIONS #define malloc OS_DO_NOT_USE_malloc #define realloc OS_DO_NOT_USE_realloc #define free OS_DO_NOT_USE_free #define memcpy OS_DO_NOT_USE_memcpy #define memmove OS_DO_NOT_USE_memmove #define memset OS_DO_NOT_USE_memset #define memcmp OS_DO_NOT_USE_memcmp #undef strdup #define strdup OS_DO_NOT_USE_strdup #define strlen OS_DO_NOT_USE_strlen #define strcasecmp OS_DO_NOT_USE_strcasecmp #define strncasecmp OS_DO_NOT_USE_strncasecmp #undef strchr #define strchr OS_DO_NOT_USE_strchr #undef strcmp #define strcmp OS_DO_NOT_USE_strcmp #undef strncmp #define strncmp OS_DO_NOT_USE_strncmp #undef strncpy #define strncpy OS_DO_NOT_USE_strncpy #define strrchr OS_DO_NOT_USE_strrchr #define strstr OS_DO_NOT_USE_strstr #undef snprintf #define snprintf OS_DO_NOT_USE_snprintf #define strcpy OS_DO_NOT_USE_strcpy #endif /* OS_REJECT_C_LIB_FUNCTIONS */ #endif /* OS_H */ wpa-2.1/src/utils/os_internal.c000066400000000000000000000170731227414666700166020ustar00rootroot00000000000000/* * wpa_supplicant/hostapd / Internal implementation of OS specific functions * Copyright (c) 2005-2006, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. * * This file is an example of operating system specific wrapper functions. * This version implements many of the functions internally, so it can be used * to fill in missing functions from the target system C libraries. * * Some of the functions are using standard C library calls in order to keep * this file in working condition to allow the functions to be tested on a * Linux target. Please note that OS_NO_C_LIB_DEFINES needs to be defined for * this file to work correctly. Note that these implementations are only * examples and are not optimized for speed. */ #include "includes.h" #undef OS_REJECT_C_LIB_FUNCTIONS #include "os.h" void os_sleep(os_time_t sec, os_time_t usec) { if (sec) sleep(sec); if (usec) usleep(usec); } int os_get_time(struct os_time *t) { int res; struct timeval tv; res = gettimeofday(&tv, NULL); t->sec = tv.tv_sec; t->usec = tv.tv_usec; return res; } int os_get_reltime(struct os_reltime *t) { int res; struct timeval tv; res = gettimeofday(&tv, NULL); t->sec = tv.tv_sec; t->usec = tv.tv_usec; return res; } int os_mktime(int year, int month, int day, int hour, int min, int sec, os_time_t *t) { struct tm tm; if (year < 1970 || month < 1 || month > 12 || day < 1 || day > 31 || hour < 0 || hour > 23 || min < 0 || min > 59 || sec < 0 || sec > 60) return -1; os_memset(&tm, 0, sizeof(tm)); tm.tm_year = year - 1900; tm.tm_mon = month - 1; tm.tm_mday = day; tm.tm_hour = hour; tm.tm_min = min; tm.tm_sec = sec; *t = (os_time_t) mktime(&tm); return 0; } int os_gmtime(os_time_t t, struct os_tm *tm) { struct tm *tm2; time_t t2 = t; tm2 = gmtime(&t2); if (tm2 == NULL) return -1; tm->sec = tm2->tm_sec; tm->min = tm2->tm_min; tm->hour = tm2->tm_hour; tm->day = tm2->tm_mday; tm->month = tm2->tm_mon + 1; tm->year = tm2->tm_year + 1900; return 0; } int os_daemonize(const char *pid_file) { if (daemon(0, 0)) { perror("daemon"); return -1; } if (pid_file) { FILE *f = fopen(pid_file, "w"); if (f) { fprintf(f, "%u\n", getpid()); fclose(f); } } return -0; } void os_daemonize_terminate(const char *pid_file) { if (pid_file) unlink(pid_file); } int os_get_random(unsigned char *buf, size_t len) { FILE *f; size_t rc; f = fopen("/dev/urandom", "rb"); if (f == NULL) { printf("Could not open /dev/urandom.\n"); return -1; } rc = fread(buf, 1, len, f); fclose(f); return rc != len ? -1 : 0; } unsigned long os_random(void) { return random(); } char * os_rel2abs_path(const char *rel_path) { char *buf = NULL, *cwd, *ret; size_t len = 128, cwd_len, rel_len, ret_len; if (rel_path[0] == '/') return os_strdup(rel_path); for (;;) { buf = os_malloc(len); if (buf == NULL) return NULL; cwd = getcwd(buf, len); if (cwd == NULL) { os_free(buf); if (errno != ERANGE) { return NULL; } len *= 2; } else { break; } } cwd_len = strlen(cwd); rel_len = strlen(rel_path); ret_len = cwd_len + 1 + rel_len + 1; ret = os_malloc(ret_len); if (ret) { os_memcpy(ret, cwd, cwd_len); ret[cwd_len] = '/'; os_memcpy(ret + cwd_len + 1, rel_path, rel_len); ret[ret_len - 1] = '\0'; } os_free(buf); return ret; } int os_program_init(void) { return 0; } void os_program_deinit(void) { } int os_setenv(const char *name, const char *value, int overwrite) { return setenv(name, value, overwrite); } int os_unsetenv(const char *name) { #if defined(__FreeBSD__) || defined(__NetBSD__) unsetenv(name); return 0; #else return unsetenv(name); #endif } char * os_readfile(const char *name, size_t *len) { FILE *f; char *buf; f = fopen(name, "rb"); if (f == NULL) return NULL; fseek(f, 0, SEEK_END); *len = ftell(f); fseek(f, 0, SEEK_SET); buf = os_malloc(*len); if (buf == NULL) { fclose(f); return NULL; } if (fread(buf, 1, *len, f) != *len) { fclose(f); os_free(buf); return NULL; } fclose(f); return buf; } void * os_zalloc(size_t size) { void *n = os_malloc(size); if (n) os_memset(n, 0, size); return n; } void * os_malloc(size_t size) { return malloc(size); } void * os_realloc(void *ptr, size_t size) { return realloc(ptr, size); } void os_free(void *ptr) { free(ptr); } void * os_memcpy(void *dest, const void *src, size_t n) { char *d = dest; const char *s = src; while (n--) *d++ = *s++; return dest; } void * os_memmove(void *dest, const void *src, size_t n) { if (dest < src) os_memcpy(dest, src, n); else { /* overlapping areas */ char *d = (char *) dest + n; const char *s = (const char *) src + n; while (n--) *--d = *--s; } return dest; } void * os_memset(void *s, int c, size_t n) { char *p = s; while (n--) *p++ = c; return s; } int os_memcmp(const void *s1, const void *s2, size_t n) { const unsigned char *p1 = s1, *p2 = s2; if (n == 0) return 0; while (*p1 == *p2) { p1++; p2++; n--; if (n == 0) return 0; } return *p1 - *p2; } char * os_strdup(const char *s) { char *res; size_t len; if (s == NULL) return NULL; len = os_strlen(s); res = os_malloc(len + 1); if (res) os_memcpy(res, s, len + 1); return res; } size_t os_strlen(const char *s) { const char *p = s; while (*p) p++; return p - s; } int os_strcasecmp(const char *s1, const char *s2) { /* * Ignoring case is not required for main functionality, so just use * the case sensitive version of the function. */ return os_strcmp(s1, s2); } int os_strncasecmp(const char *s1, const char *s2, size_t n) { /* * Ignoring case is not required for main functionality, so just use * the case sensitive version of the function. */ return os_strncmp(s1, s2, n); } char * os_strchr(const char *s, int c) { while (*s) { if (*s == c) return (char *) s; s++; } return NULL; } char * os_strrchr(const char *s, int c) { const char *p = s; while (*p) p++; p--; while (p >= s) { if (*p == c) return (char *) p; p--; } return NULL; } int os_strcmp(const char *s1, const char *s2) { while (*s1 == *s2) { if (*s1 == '\0') break; s1++; s2++; } return *s1 - *s2; } int os_strncmp(const char *s1, const char *s2, size_t n) { if (n == 0) return 0; while (*s1 == *s2) { if (*s1 == '\0') break; s1++; s2++; n--; if (n == 0) return 0; } return *s1 - *s2; } char * os_strncpy(char *dest, const char *src, size_t n) { char *d = dest; while (n--) { *d = *src; if (*src == '\0') break; d++; src++; } return dest; } size_t os_strlcpy(char *dest, const char *src, size_t siz) { const char *s = src; size_t left = siz; if (left) { /* Copy string up to the maximum size of the dest buffer */ while (--left != 0) { if ((*dest++ = *s++) == '\0') break; } } if (left == 0) { /* Not enough room for the string; force NUL-termination */ if (siz != 0) *dest = '\0'; while (*s++) ; /* determine total src string length */ } return s - src - 1; } char * os_strstr(const char *haystack, const char *needle) { size_t len = os_strlen(needle); while (*haystack) { if (os_strncmp(haystack, needle, len) == 0) return (char *) haystack; haystack++; } return NULL; } int os_snprintf(char *str, size_t size, const char *format, ...) { va_list ap; int ret; /* See http://www.ijs.si/software/snprintf/ for portable * implementation of snprintf. */ va_start(ap, format); ret = vsnprintf(str, size, format, ap); va_end(ap); if (size > 0) str[size - 1] = '\0'; return ret; } wpa-2.1/src/utils/os_none.c000066400000000000000000000061471227414666700157250ustar00rootroot00000000000000/* * wpa_supplicant/hostapd / Empty OS specific functions * Copyright (c) 2005-2006, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. * * This file can be used as a starting point when adding a new OS target. The * functions here do not really work as-is since they are just empty or only * return an error value. os_internal.c can be used as another starting point * or reference since it has example implementation of many of these functions. */ #include "includes.h" #include "os.h" void os_sleep(os_time_t sec, os_time_t usec) { } int os_get_time(struct os_time *t) { return -1; } int os_get_reltime(struct os_reltime *t) { return -1; } int os_mktime(int year, int month, int day, int hour, int min, int sec, os_time_t *t) { return -1; } int os_gmtime(os_time_t t, struct os_tm *tm) { return -1; } int os_daemonize(const char *pid_file) { return -1; } void os_daemonize_terminate(const char *pid_file) { } int os_get_random(unsigned char *buf, size_t len) { return -1; } unsigned long os_random(void) { return 0; } char * os_rel2abs_path(const char *rel_path) { return NULL; /* strdup(rel_path) can be used here */ } int os_program_init(void) { return 0; } void os_program_deinit(void) { } int os_setenv(const char *name, const char *value, int overwrite) { return -1; } int os_unsetenv(const char *name) { return -1; } char * os_readfile(const char *name, size_t *len) { return NULL; } void * os_zalloc(size_t size) { return NULL; } #ifdef OS_NO_C_LIB_DEFINES void * os_malloc(size_t size) { return NULL; } void * os_realloc(void *ptr, size_t size) { return NULL; } void os_free(void *ptr) { } void * os_memcpy(void *dest, const void *src, size_t n) { return dest; } void * os_memmove(void *dest, const void *src, size_t n) { return dest; } void * os_memset(void *s, int c, size_t n) { return s; } int os_memcmp(const void *s1, const void *s2, size_t n) { return 0; } char * os_strdup(const char *s) { return NULL; } size_t os_strlen(const char *s) { return 0; } int os_strcasecmp(const char *s1, const char *s2) { /* * Ignoring case is not required for main functionality, so just use * the case sensitive version of the function. */ return os_strcmp(s1, s2); } int os_strncasecmp(const char *s1, const char *s2, size_t n) { /* * Ignoring case is not required for main functionality, so just use * the case sensitive version of the function. */ return os_strncmp(s1, s2, n); } char * os_strchr(const char *s, int c) { return NULL; } char * os_strrchr(const char *s, int c) { return NULL; } int os_strcmp(const char *s1, const char *s2) { return 0; } int os_strncmp(const char *s1, const char *s2, size_t n) { return 0; } char * os_strncpy(char *dest, const char *src, size_t n) { return dest; } size_t os_strlcpy(char *dest, const char *src, size_t size) { return 0; } char * os_strstr(const char *haystack, const char *needle) { return NULL; } int os_snprintf(char *str, size_t size, const char *format, ...) { return 0; } #endif /* OS_NO_C_LIB_DEFINES */ wpa-2.1/src/utils/os_unix.c000066400000000000000000000222361227414666700157460ustar00rootroot00000000000000/* * OS specific functions for UNIX/POSIX systems * Copyright (c) 2005-2009, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "includes.h" #include #ifdef ANDROID #include #include #include #endif /* ANDROID */ #include "os.h" #include "common.h" #ifdef WPA_TRACE #include "wpa_debug.h" #include "trace.h" #include "list.h" static struct dl_list alloc_list; #define ALLOC_MAGIC 0xa84ef1b2 #define FREED_MAGIC 0x67fd487a struct os_alloc_trace { unsigned int magic; struct dl_list list; size_t len; WPA_TRACE_INFO }; #endif /* WPA_TRACE */ void os_sleep(os_time_t sec, os_time_t usec) { if (sec) sleep(sec); if (usec) usleep(usec); } int os_get_time(struct os_time *t) { int res; struct timeval tv; res = gettimeofday(&tv, NULL); t->sec = tv.tv_sec; t->usec = tv.tv_usec; return res; } int os_get_reltime(struct os_reltime *t) { #if defined(CLOCK_BOOTTIME) static clockid_t clock_id = CLOCK_BOOTTIME; #elif defined(CLOCK_MONOTONIC) static clockid_t clock_id = CLOCK_MONOTONIC; #else static clockid_t clock_id = CLOCK_REALTIME; #endif struct timespec ts; int res; while (1) { res = clock_gettime(clock_id, &ts); if (res == 0) { t->sec = ts.tv_sec; t->usec = ts.tv_nsec / 1000; return 0; } switch (clock_id) { #ifdef CLOCK_BOOTTIME case CLOCK_BOOTTIME: clock_id = CLOCK_MONOTONIC; break; #endif #ifdef CLOCK_MONOTONIC case CLOCK_MONOTONIC: clock_id = CLOCK_REALTIME; break; #endif case CLOCK_REALTIME: return -1; } } } int os_mktime(int year, int month, int day, int hour, int min, int sec, os_time_t *t) { struct tm tm, *tm1; time_t t_local, t1, t2; os_time_t tz_offset; if (year < 1970 || month < 1 || month > 12 || day < 1 || day > 31 || hour < 0 || hour > 23 || min < 0 || min > 59 || sec < 0 || sec > 60) return -1; memset(&tm, 0, sizeof(tm)); tm.tm_year = year - 1900; tm.tm_mon = month - 1; tm.tm_mday = day; tm.tm_hour = hour; tm.tm_min = min; tm.tm_sec = sec; t_local = mktime(&tm); /* figure out offset to UTC */ tm1 = localtime(&t_local); if (tm1) { t1 = mktime(tm1); tm1 = gmtime(&t_local); if (tm1) { t2 = mktime(tm1); tz_offset = t2 - t1; } else tz_offset = 0; } else tz_offset = 0; *t = (os_time_t) t_local - tz_offset; return 0; } int os_gmtime(os_time_t t, struct os_tm *tm) { struct tm *tm2; time_t t2 = t; tm2 = gmtime(&t2); if (tm2 == NULL) return -1; tm->sec = tm2->tm_sec; tm->min = tm2->tm_min; tm->hour = tm2->tm_hour; tm->day = tm2->tm_mday; tm->month = tm2->tm_mon + 1; tm->year = tm2->tm_year + 1900; return 0; } #ifdef __APPLE__ #include static int os_daemon(int nochdir, int noclose) { int devnull; if (chdir("/") < 0) return -1; devnull = open("/dev/null", O_RDWR); if (devnull < 0) return -1; if (dup2(devnull, STDIN_FILENO) < 0) { close(devnull); return -1; } if (dup2(devnull, STDOUT_FILENO) < 0) { close(devnull); return -1; } if (dup2(devnull, STDERR_FILENO) < 0) { close(devnull); return -1; } return 0; } #else /* __APPLE__ */ #define os_daemon daemon #endif /* __APPLE__ */ int os_daemonize(const char *pid_file) { #if defined(__uClinux__) || defined(__sun__) return -1; #else /* defined(__uClinux__) || defined(__sun__) */ if (os_daemon(0, 0)) { perror("daemon"); return -1; } if (pid_file) { FILE *f = fopen(pid_file, "w"); if (f) { fprintf(f, "%u\n", getpid()); fclose(f); } } return -0; #endif /* defined(__uClinux__) || defined(__sun__) */ } void os_daemonize_terminate(const char *pid_file) { if (pid_file) unlink(pid_file); } int os_get_random(unsigned char *buf, size_t len) { FILE *f; size_t rc; f = fopen("/dev/urandom", "rb"); if (f == NULL) { printf("Could not open /dev/urandom.\n"); return -1; } rc = fread(buf, 1, len, f); fclose(f); return rc != len ? -1 : 0; } unsigned long os_random(void) { return random(); } char * os_rel2abs_path(const char *rel_path) { char *buf = NULL, *cwd, *ret; size_t len = 128, cwd_len, rel_len, ret_len; int last_errno; if (!rel_path) return NULL; if (rel_path[0] == '/') return os_strdup(rel_path); for (;;) { buf = os_malloc(len); if (buf == NULL) return NULL; cwd = getcwd(buf, len); if (cwd == NULL) { last_errno = errno; os_free(buf); if (last_errno != ERANGE) return NULL; len *= 2; if (len > 2000) return NULL; } else { buf[len - 1] = '\0'; break; } } cwd_len = os_strlen(cwd); rel_len = os_strlen(rel_path); ret_len = cwd_len + 1 + rel_len + 1; ret = os_malloc(ret_len); if (ret) { os_memcpy(ret, cwd, cwd_len); ret[cwd_len] = '/'; os_memcpy(ret + cwd_len + 1, rel_path, rel_len); ret[ret_len - 1] = '\0'; } os_free(buf); return ret; } int os_program_init(void) { #ifdef ANDROID /* * We ignore errors here since errors are normal if we * are already running as non-root. */ #ifdef ANDROID_SETGROUPS_OVERRIDE gid_t groups[] = { ANDROID_SETGROUPS_OVERRIDE }; #else /* ANDROID_SETGROUPS_OVERRIDE */ gid_t groups[] = { AID_INET, AID_WIFI, AID_KEYSTORE }; #endif /* ANDROID_SETGROUPS_OVERRIDE */ struct __user_cap_header_struct header; struct __user_cap_data_struct cap; setgroups(ARRAY_SIZE(groups), groups); prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0); setgid(AID_WIFI); setuid(AID_WIFI); header.version = _LINUX_CAPABILITY_VERSION; header.pid = 0; cap.effective = cap.permitted = (1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW); cap.inheritable = 0; capset(&header, &cap); #endif /* ANDROID */ #ifdef WPA_TRACE dl_list_init(&alloc_list); #endif /* WPA_TRACE */ return 0; } void os_program_deinit(void) { #ifdef WPA_TRACE struct os_alloc_trace *a; unsigned long total = 0; dl_list_for_each(a, &alloc_list, struct os_alloc_trace, list) { total += a->len; if (a->magic != ALLOC_MAGIC) { wpa_printf(MSG_INFO, "MEMLEAK[%p]: invalid magic 0x%x " "len %lu", a, a->magic, (unsigned long) a->len); continue; } wpa_printf(MSG_INFO, "MEMLEAK[%p]: len %lu", a, (unsigned long) a->len); wpa_trace_dump("memleak", a); } if (total) wpa_printf(MSG_INFO, "MEMLEAK: total %lu bytes", (unsigned long) total); #endif /* WPA_TRACE */ } int os_setenv(const char *name, const char *value, int overwrite) { return setenv(name, value, overwrite); } int os_unsetenv(const char *name) { #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__APPLE__) || \ defined(__OpenBSD__) unsetenv(name); return 0; #else return unsetenv(name); #endif } char * os_readfile(const char *name, size_t *len) { FILE *f; char *buf; long pos; f = fopen(name, "rb"); if (f == NULL) return NULL; if (fseek(f, 0, SEEK_END) < 0 || (pos = ftell(f)) < 0) { fclose(f); return NULL; } *len = pos; if (fseek(f, 0, SEEK_SET) < 0) { fclose(f); return NULL; } buf = os_malloc(*len); if (buf == NULL) { fclose(f); return NULL; } if (fread(buf, 1, *len, f) != *len) { fclose(f); os_free(buf); return NULL; } fclose(f); return buf; } #ifndef WPA_TRACE void * os_zalloc(size_t size) { return calloc(1, size); } #endif /* WPA_TRACE */ size_t os_strlcpy(char *dest, const char *src, size_t siz) { const char *s = src; size_t left = siz; if (left) { /* Copy string up to the maximum size of the dest buffer */ while (--left != 0) { if ((*dest++ = *s++) == '\0') break; } } if (left == 0) { /* Not enough room for the string; force NUL-termination */ if (siz != 0) *dest = '\0'; while (*s++) ; /* determine total src string length */ } return s - src - 1; } #ifdef WPA_TRACE void * os_malloc(size_t size) { struct os_alloc_trace *a; a = malloc(sizeof(*a) + size); if (a == NULL) return NULL; a->magic = ALLOC_MAGIC; dl_list_add(&alloc_list, &a->list); a->len = size; wpa_trace_record(a); return a + 1; } void * os_realloc(void *ptr, size_t size) { struct os_alloc_trace *a; size_t copy_len; void *n; if (ptr == NULL) return os_malloc(size); a = (struct os_alloc_trace *) ptr - 1; if (a->magic != ALLOC_MAGIC) { wpa_printf(MSG_INFO, "REALLOC[%p]: invalid magic 0x%x%s", a, a->magic, a->magic == FREED_MAGIC ? " (already freed)" : ""); wpa_trace_show("Invalid os_realloc() call"); abort(); } n = os_malloc(size); if (n == NULL) return NULL; copy_len = a->len; if (copy_len > size) copy_len = size; os_memcpy(n, a + 1, copy_len); os_free(ptr); return n; } void os_free(void *ptr) { struct os_alloc_trace *a; if (ptr == NULL) return; a = (struct os_alloc_trace *) ptr - 1; if (a->magic != ALLOC_MAGIC) { wpa_printf(MSG_INFO, "FREE[%p]: invalid magic 0x%x%s", a, a->magic, a->magic == FREED_MAGIC ? " (already freed)" : ""); wpa_trace_show("Invalid os_free() call"); abort(); } dl_list_del(&a->list); a->magic = FREED_MAGIC; wpa_trace_check_ref(ptr); free(a); } void * os_zalloc(size_t size) { void *ptr = os_malloc(size); if (ptr) os_memset(ptr, 0, size); return ptr; } char * os_strdup(const char *s) { size_t len; char *d; len = os_strlen(s); d = os_malloc(len + 1); if (d == NULL) return NULL; os_memcpy(d, s, len); d[len] = '\0'; return d; } #endif /* WPA_TRACE */ wpa-2.1/src/utils/os_win32.c000066400000000000000000000100341227414666700157160ustar00rootroot00000000000000/* * wpa_supplicant/hostapd / OS specific functions for Win32 systems * Copyright (c) 2005-2006, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "includes.h" #include #include #include #include "os.h" void os_sleep(os_time_t sec, os_time_t usec) { if (sec) Sleep(sec * 1000); if (usec) Sleep(usec / 1000); } int os_get_time(struct os_time *t) { #define EPOCHFILETIME (116444736000000000ULL) FILETIME ft; LARGE_INTEGER li; ULONGLONG tt; #ifdef _WIN32_WCE SYSTEMTIME st; GetSystemTime(&st); SystemTimeToFileTime(&st, &ft); #else /* _WIN32_WCE */ GetSystemTimeAsFileTime(&ft); #endif /* _WIN32_WCE */ li.LowPart = ft.dwLowDateTime; li.HighPart = ft.dwHighDateTime; tt = (li.QuadPart - EPOCHFILETIME) / 10; t->sec = (os_time_t) (tt / 1000000); t->usec = (os_time_t) (tt % 1000000); return 0; } int os_get_reltime(struct os_reltime *t) { /* consider using performance counters or so instead */ struct os_time now; int res = os_get_time(&now); t->sec = now.sec; t->usec = now.usec; return res; } int os_mktime(int year, int month, int day, int hour, int min, int sec, os_time_t *t) { struct tm tm, *tm1; time_t t_local, t1, t2; os_time_t tz_offset; if (year < 1970 || month < 1 || month > 12 || day < 1 || day > 31 || hour < 0 || hour > 23 || min < 0 || min > 59 || sec < 0 || sec > 60) return -1; memset(&tm, 0, sizeof(tm)); tm.tm_year = year - 1900; tm.tm_mon = month - 1; tm.tm_mday = day; tm.tm_hour = hour; tm.tm_min = min; tm.tm_sec = sec; t_local = mktime(&tm); /* figure out offset to UTC */ tm1 = localtime(&t_local); if (tm1) { t1 = mktime(tm1); tm1 = gmtime(&t_local); if (tm1) { t2 = mktime(tm1); tz_offset = t2 - t1; } else tz_offset = 0; } else tz_offset = 0; *t = (os_time_t) t_local - tz_offset; return 0; } int os_gmtime(os_time_t t, struct os_tm *tm) { struct tm *tm2; time_t t2 = t; tm2 = gmtime(&t2); if (tm2 == NULL) return -1; tm->sec = tm2->tm_sec; tm->min = tm2->tm_min; tm->hour = tm2->tm_hour; tm->day = tm2->tm_mday; tm->month = tm2->tm_mon + 1; tm->year = tm2->tm_year + 1900; return 0; } int os_daemonize(const char *pid_file) { /* TODO */ return -1; } void os_daemonize_terminate(const char *pid_file) { } int os_get_random(unsigned char *buf, size_t len) { HCRYPTPROV prov; BOOL ret; if (!CryptAcquireContext(&prov, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) return -1; ret = CryptGenRandom(prov, len, buf); CryptReleaseContext(prov, 0); return ret ? 0 : -1; } unsigned long os_random(void) { return rand(); } char * os_rel2abs_path(const char *rel_path) { return _strdup(rel_path); } int os_program_init(void) { #ifdef CONFIG_NATIVE_WINDOWS WSADATA wsaData; if (WSAStartup(MAKEWORD(2, 0), &wsaData)) { printf("Could not find a usable WinSock.dll\n"); return -1; } #endif /* CONFIG_NATIVE_WINDOWS */ return 0; } void os_program_deinit(void) { #ifdef CONFIG_NATIVE_WINDOWS WSACleanup(); #endif /* CONFIG_NATIVE_WINDOWS */ } int os_setenv(const char *name, const char *value, int overwrite) { return -1; } int os_unsetenv(const char *name) { return -1; } char * os_readfile(const char *name, size_t *len) { FILE *f; char *buf; f = fopen(name, "rb"); if (f == NULL) return NULL; fseek(f, 0, SEEK_END); *len = ftell(f); fseek(f, 0, SEEK_SET); buf = malloc(*len); if (buf == NULL) { fclose(f); return NULL; } fread(buf, 1, *len, f); fclose(f); return buf; } void * os_zalloc(size_t size) { return calloc(1, size); } size_t os_strlcpy(char *dest, const char *src, size_t siz) { const char *s = src; size_t left = siz; if (left) { /* Copy string up to the maximum size of the dest buffer */ while (--left != 0) { if ((*dest++ = *s++) == '\0') break; } } if (left == 0) { /* Not enough room for the string; force NUL-termination */ if (siz != 0) *dest = '\0'; while (*s++) ; /* determine total src string length */ } return s - src - 1; } wpa-2.1/src/utils/pcsc_funcs.c000066400000000000000000001124151227414666700164070ustar00rootroot00000000000000/* * WPA Supplicant / PC/SC smartcard interface for USIM, GSM SIM * Copyright (c) 2004-2007, 2012, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. * * This file implements wrapper functions for accessing GSM SIM and 3GPP USIM * cards through PC/SC smartcard library. These functions are used to implement * authentication routines for EAP-SIM and EAP-AKA. */ #include "includes.h" #include #include "common.h" #include "pcsc_funcs.h" /* See ETSI GSM 11.11 and ETSI TS 102 221 for details. * SIM commands: * Command APDU: CLA INS P1 P2 P3 Data * CLA (class of instruction): A0 for GSM, 00 for USIM * INS (instruction) * P1 P2 P3 (parameters, P3 = length of Data) * Response APDU: Data SW1 SW2 * SW1 SW2 (Status words) * Commands (INS P1 P2 P3): * SELECT: A4 00 00 02 * GET RESPONSE: C0 00 00 * RUN GSM ALG: 88 00 00 00 * RUN UMTS ALG: 88 00 81 data: 0x10 | RAND | 0x10 | AUTN * P1 = ID of alg in card * P2 = ID of secret key * READ BINARY: B0 * READ RECORD: B2 * P2 (mode) = '02' (next record), '03' (previous record), * '04' (absolute mode) * VERIFY CHV: 20 00 08 * CHANGE CHV: 24 00 10 * DISABLE CHV: 26 00 01 08 * ENABLE CHV: 28 00 01 08 * UNBLOCK CHV: 2C 00 <00=CHV1, 02=CHV2> 10 * SLEEP: FA 00 00 00 */ /* GSM SIM commands */ #define SIM_CMD_SELECT 0xa0, 0xa4, 0x00, 0x00, 0x02 #define SIM_CMD_RUN_GSM_ALG 0xa0, 0x88, 0x00, 0x00, 0x10 #define SIM_CMD_GET_RESPONSE 0xa0, 0xc0, 0x00, 0x00 #define SIM_CMD_READ_BIN 0xa0, 0xb0, 0x00, 0x00 #define SIM_CMD_READ_RECORD 0xa0, 0xb2, 0x00, 0x00 #define SIM_CMD_VERIFY_CHV1 0xa0, 0x20, 0x00, 0x01, 0x08 /* USIM commands */ #define USIM_CLA 0x00 #define USIM_CMD_RUN_UMTS_ALG 0x00, 0x88, 0x00, 0x81, 0x22 #define USIM_CMD_GET_RESPONSE 0x00, 0xc0, 0x00, 0x00 #define SIM_RECORD_MODE_ABSOLUTE 0x04 #define USIM_FSP_TEMPL_TAG 0x62 #define USIM_TLV_FILE_DESC 0x82 #define USIM_TLV_FILE_ID 0x83 #define USIM_TLV_DF_NAME 0x84 #define USIM_TLV_PROPR_INFO 0xA5 #define USIM_TLV_LIFE_CYCLE_STATUS 0x8A #define USIM_TLV_FILE_SIZE 0x80 #define USIM_TLV_TOTAL_FILE_SIZE 0x81 #define USIM_TLV_PIN_STATUS_TEMPLATE 0xC6 #define USIM_TLV_SHORT_FILE_ID 0x88 #define USIM_TLV_SECURITY_ATTR_8B 0x8B #define USIM_TLV_SECURITY_ATTR_8C 0x8C #define USIM_TLV_SECURITY_ATTR_AB 0xAB #define USIM_PS_DO_TAG 0x90 #define AKA_RAND_LEN 16 #define AKA_AUTN_LEN 16 #define AKA_AUTS_LEN 14 #define RES_MAX_LEN 16 #define IK_LEN 16 #define CK_LEN 16 /* GSM files * File type in first octet: * 3F = Master File * 7F = Dedicated File * 2F = Elementary File under the Master File * 6F = Elementary File under a Dedicated File */ #define SCARD_FILE_MF 0x3F00 #define SCARD_FILE_GSM_DF 0x7F20 #define SCARD_FILE_UMTS_DF 0x7F50 #define SCARD_FILE_GSM_EF_IMSI 0x6F07 #define SCARD_FILE_GSM_EF_AD 0x6FAD #define SCARD_FILE_EF_DIR 0x2F00 #define SCARD_FILE_EF_ICCID 0x2FE2 #define SCARD_FILE_EF_CK 0x6FE1 #define SCARD_FILE_EF_IK 0x6FE2 #define SCARD_CHV1_OFFSET 13 #define SCARD_CHV1_FLAG 0x80 typedef enum { SCARD_GSM_SIM, SCARD_USIM } sim_types; struct scard_data { SCARDCONTEXT ctx; SCARDHANDLE card; DWORD protocol; sim_types sim_type; int pin1_required; }; #ifdef __MINGW32_VERSION /* MinGW does not yet support WinScard, so load the needed functions * dynamically from winscard.dll for now. */ static HINSTANCE dll = NULL; /* winscard.dll */ static const SCARD_IO_REQUEST *dll_g_rgSCardT0Pci, *dll_g_rgSCardT1Pci; #undef SCARD_PCI_T0 #define SCARD_PCI_T0 (dll_g_rgSCardT0Pci) #undef SCARD_PCI_T1 #define SCARD_PCI_T1 (dll_g_rgSCardT1Pci) static WINSCARDAPI LONG WINAPI (*dll_SCardEstablishContext)(IN DWORD dwScope, IN LPCVOID pvReserved1, IN LPCVOID pvReserved2, OUT LPSCARDCONTEXT phContext); #define SCardEstablishContext dll_SCardEstablishContext static long (*dll_SCardReleaseContext)(long hContext); #define SCardReleaseContext dll_SCardReleaseContext static WINSCARDAPI LONG WINAPI (*dll_SCardListReadersA)(IN SCARDCONTEXT hContext, IN LPCSTR mszGroups, OUT LPSTR mszReaders, IN OUT LPDWORD pcchReaders); #undef SCardListReaders #define SCardListReaders dll_SCardListReadersA static WINSCARDAPI LONG WINAPI (*dll_SCardConnectA)(IN SCARDCONTEXT hContext, IN LPCSTR szReader, IN DWORD dwShareMode, IN DWORD dwPreferredProtocols, OUT LPSCARDHANDLE phCard, OUT LPDWORD pdwActiveProtocol); #undef SCardConnect #define SCardConnect dll_SCardConnectA static WINSCARDAPI LONG WINAPI (*dll_SCardDisconnect)(IN SCARDHANDLE hCard, IN DWORD dwDisposition); #define SCardDisconnect dll_SCardDisconnect static WINSCARDAPI LONG WINAPI (*dll_SCardTransmit)(IN SCARDHANDLE hCard, IN LPCSCARD_IO_REQUEST pioSendPci, IN LPCBYTE pbSendBuffer, IN DWORD cbSendLength, IN OUT LPSCARD_IO_REQUEST pioRecvPci, OUT LPBYTE pbRecvBuffer, IN OUT LPDWORD pcbRecvLength); #define SCardTransmit dll_SCardTransmit static WINSCARDAPI LONG WINAPI (*dll_SCardBeginTransaction)(IN SCARDHANDLE hCard); #define SCardBeginTransaction dll_SCardBeginTransaction static WINSCARDAPI LONG WINAPI (*dll_SCardEndTransaction)(IN SCARDHANDLE hCard, IN DWORD dwDisposition); #define SCardEndTransaction dll_SCardEndTransaction static int mingw_load_symbols(void) { char *sym; if (dll) return 0; dll = LoadLibrary("winscard"); if (dll == NULL) { wpa_printf(MSG_DEBUG, "WinSCard: Could not load winscard.dll " "library"); return -1; } #define LOADSYM(s) \ sym = #s; \ dll_ ## s = (void *) GetProcAddress(dll, sym); \ if (dll_ ## s == NULL) \ goto fail; LOADSYM(SCardEstablishContext); LOADSYM(SCardReleaseContext); LOADSYM(SCardListReadersA); LOADSYM(SCardConnectA); LOADSYM(SCardDisconnect); LOADSYM(SCardTransmit); LOADSYM(SCardBeginTransaction); LOADSYM(SCardEndTransaction); LOADSYM(g_rgSCardT0Pci); LOADSYM(g_rgSCardT1Pci); #undef LOADSYM return 0; fail: wpa_printf(MSG_DEBUG, "WinSCard: Could not get address for %s from " "winscard.dll", sym); FreeLibrary(dll); dll = NULL; return -1; } static void mingw_unload_symbols(void) { if (dll == NULL) return; FreeLibrary(dll); dll = NULL; } #else /* __MINGW32_VERSION */ #define mingw_load_symbols() 0 #define mingw_unload_symbols() do { } while (0) #endif /* __MINGW32_VERSION */ static int _scard_select_file(struct scard_data *scard, unsigned short file_id, unsigned char *buf, size_t *buf_len, sim_types sim_type, unsigned char *aid, size_t aidlen); static int scard_select_file(struct scard_data *scard, unsigned short file_id, unsigned char *buf, size_t *buf_len); static int scard_verify_pin(struct scard_data *scard, const char *pin); static int scard_get_record_len(struct scard_data *scard, unsigned char recnum, unsigned char mode); static int scard_read_record(struct scard_data *scard, unsigned char *data, size_t len, unsigned char recnum, unsigned char mode); static int scard_parse_fsp_templ(unsigned char *buf, size_t buf_len, int *ps_do, int *file_len) { unsigned char *pos, *end; if (ps_do) *ps_do = -1; if (file_len) *file_len = -1; pos = buf; end = pos + buf_len; if (*pos != USIM_FSP_TEMPL_TAG) { wpa_printf(MSG_DEBUG, "SCARD: file header did not " "start with FSP template tag"); return -1; } pos++; if (pos >= end) return -1; if ((pos + pos[0]) < end) end = pos + 1 + pos[0]; pos++; wpa_hexdump(MSG_DEBUG, "SCARD: file header FSP template", pos, end - pos); while (pos + 1 < end) { wpa_printf(MSG_MSGDUMP, "SCARD: file header TLV 0x%02x len=%d", pos[0], pos[1]); if (pos + 2 + pos[1] > end) break; switch (pos[0]) { case USIM_TLV_FILE_DESC: wpa_hexdump(MSG_MSGDUMP, "SCARD: File Descriptor TLV", pos + 2, pos[1]); break; case USIM_TLV_FILE_ID: wpa_hexdump(MSG_MSGDUMP, "SCARD: File Identifier TLV", pos + 2, pos[1]); break; case USIM_TLV_DF_NAME: wpa_hexdump(MSG_MSGDUMP, "SCARD: DF name (AID) TLV", pos + 2, pos[1]); break; case USIM_TLV_PROPR_INFO: wpa_hexdump(MSG_MSGDUMP, "SCARD: Proprietary " "information TLV", pos + 2, pos[1]); break; case USIM_TLV_LIFE_CYCLE_STATUS: wpa_hexdump(MSG_MSGDUMP, "SCARD: Life Cycle Status " "Integer TLV", pos + 2, pos[1]); break; case USIM_TLV_FILE_SIZE: wpa_hexdump(MSG_MSGDUMP, "SCARD: File size TLV", pos + 2, pos[1]); if ((pos[1] == 1 || pos[1] == 2) && file_len) { if (pos[1] == 1) *file_len = (int) pos[2]; else *file_len = ((int) pos[2] << 8) | (int) pos[3]; wpa_printf(MSG_DEBUG, "SCARD: file_size=%d", *file_len); } break; case USIM_TLV_TOTAL_FILE_SIZE: wpa_hexdump(MSG_MSGDUMP, "SCARD: Total file size TLV", pos + 2, pos[1]); break; case USIM_TLV_PIN_STATUS_TEMPLATE: wpa_hexdump(MSG_MSGDUMP, "SCARD: PIN Status Template " "DO TLV", pos + 2, pos[1]); if (pos[1] >= 2 && pos[2] == USIM_PS_DO_TAG && pos[3] >= 1 && ps_do) { wpa_printf(MSG_DEBUG, "SCARD: PS_DO=0x%02x", pos[4]); *ps_do = (int) pos[4]; } break; case USIM_TLV_SHORT_FILE_ID: wpa_hexdump(MSG_MSGDUMP, "SCARD: Short File " "Identifier (SFI) TLV", pos + 2, pos[1]); break; case USIM_TLV_SECURITY_ATTR_8B: case USIM_TLV_SECURITY_ATTR_8C: case USIM_TLV_SECURITY_ATTR_AB: wpa_hexdump(MSG_MSGDUMP, "SCARD: Security attribute " "TLV", pos + 2, pos[1]); break; default: wpa_hexdump(MSG_MSGDUMP, "SCARD: Unrecognized TLV", pos, 2 + pos[1]); break; } pos += 2 + pos[1]; if (pos == end) return 0; } return -1; } static int scard_pin_needed(struct scard_data *scard, unsigned char *hdr, size_t hlen) { if (scard->sim_type == SCARD_GSM_SIM) { if (hlen > SCARD_CHV1_OFFSET && !(hdr[SCARD_CHV1_OFFSET] & SCARD_CHV1_FLAG)) return 1; return 0; } if (scard->sim_type == SCARD_USIM) { int ps_do; if (scard_parse_fsp_templ(hdr, hlen, &ps_do, NULL)) return -1; /* TODO: there could be more than one PS_DO entry because of * multiple PINs in key reference.. */ if (ps_do > 0 && (ps_do & 0x80)) return 1; return 0; } return -1; } static int scard_get_aid(struct scard_data *scard, unsigned char *aid, size_t maxlen) { int rlen, rec; struct efdir { unsigned char appl_template_tag; /* 0x61 */ unsigned char appl_template_len; unsigned char appl_id_tag; /* 0x4f */ unsigned char aid_len; unsigned char rid[5]; unsigned char appl_code[2]; /* 0x1002 for 3G USIM */ } *efdir; unsigned char buf[127]; size_t blen; efdir = (struct efdir *) buf; blen = sizeof(buf); if (scard_select_file(scard, SCARD_FILE_EF_DIR, buf, &blen)) { wpa_printf(MSG_DEBUG, "SCARD: Failed to read EF_DIR"); return -1; } wpa_hexdump(MSG_DEBUG, "SCARD: EF_DIR select", buf, blen); for (rec = 1; rec < 10; rec++) { rlen = scard_get_record_len(scard, rec, SIM_RECORD_MODE_ABSOLUTE); if (rlen < 0) { wpa_printf(MSG_DEBUG, "SCARD: Failed to get EF_DIR " "record length"); return -1; } blen = sizeof(buf); if (rlen > (int) blen) { wpa_printf(MSG_DEBUG, "SCARD: Too long EF_DIR record"); return -1; } if (scard_read_record(scard, buf, rlen, rec, SIM_RECORD_MODE_ABSOLUTE) < 0) { wpa_printf(MSG_DEBUG, "SCARD: Failed to read " "EF_DIR record %d", rec); return -1; } wpa_hexdump(MSG_DEBUG, "SCARD: EF_DIR record", buf, rlen); if (efdir->appl_template_tag != 0x61) { wpa_printf(MSG_DEBUG, "SCARD: Unexpected application " "template tag 0x%x", efdir->appl_template_tag); continue; } if (efdir->appl_template_len > rlen - 2) { wpa_printf(MSG_DEBUG, "SCARD: Too long application " "template (len=%d rlen=%d)", efdir->appl_template_len, rlen); continue; } if (efdir->appl_id_tag != 0x4f) { wpa_printf(MSG_DEBUG, "SCARD: Unexpected application " "identifier tag 0x%x", efdir->appl_id_tag); continue; } if (efdir->aid_len < 1 || efdir->aid_len > 16) { wpa_printf(MSG_DEBUG, "SCARD: Invalid AID length %d", efdir->aid_len); continue; } wpa_hexdump(MSG_DEBUG, "SCARD: AID from EF_DIR record", efdir->rid, efdir->aid_len); if (efdir->appl_code[0] == 0x10 && efdir->appl_code[1] == 0x02) { wpa_printf(MSG_DEBUG, "SCARD: 3G USIM app found from " "EF_DIR record %d", rec); break; } } if (rec >= 10) { wpa_printf(MSG_DEBUG, "SCARD: 3G USIM app not found " "from EF_DIR records"); return -1; } if (efdir->aid_len > maxlen) { wpa_printf(MSG_DEBUG, "SCARD: Too long AID"); return -1; } os_memcpy(aid, efdir->rid, efdir->aid_len); return efdir->aid_len; } /** * scard_init - Initialize SIM/USIM connection using PC/SC * @reader: Reader name prefix to search for * Returns: Pointer to private data structure, or %NULL on failure * * This function is used to initialize SIM/USIM connection. PC/SC is used to * open connection to the SIM/USIM card. In addition, local flag is set if a * PIN is needed to access some of the card functions. Once the connection is * not needed anymore, scard_deinit() can be used to close it. */ struct scard_data * scard_init(const char *reader) { long ret; unsigned long len, pos; struct scard_data *scard; #ifdef CONFIG_NATIVE_WINDOWS TCHAR *readers = NULL; #else /* CONFIG_NATIVE_WINDOWS */ char *readers = NULL; #endif /* CONFIG_NATIVE_WINDOWS */ unsigned char buf[100]; size_t blen; int transaction = 0; int pin_needed; wpa_printf(MSG_DEBUG, "SCARD: initializing smart card interface"); if (mingw_load_symbols()) return NULL; scard = os_zalloc(sizeof(*scard)); if (scard == NULL) return NULL; ret = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &scard->ctx); if (ret != SCARD_S_SUCCESS) { wpa_printf(MSG_DEBUG, "SCARD: Could not establish smart card " "context (err=%ld)", ret); goto failed; } ret = SCardListReaders(scard->ctx, NULL, NULL, &len); if (ret != SCARD_S_SUCCESS) { wpa_printf(MSG_DEBUG, "SCARD: SCardListReaders failed " "(err=%ld)", ret); goto failed; } #ifdef UNICODE len *= 2; #endif /* UNICODE */ readers = os_malloc(len); if (readers == NULL) { wpa_printf(MSG_INFO, "SCARD: malloc failed\n"); goto failed; } ret = SCardListReaders(scard->ctx, NULL, readers, &len); if (ret != SCARD_S_SUCCESS) { wpa_printf(MSG_DEBUG, "SCARD: SCardListReaders failed(2) " "(err=%ld)", ret); goto failed; } if (len < 3) { wpa_printf(MSG_WARNING, "SCARD: No smart card readers " "available."); goto failed; } wpa_hexdump_ascii(MSG_DEBUG, "SCARD: Readers", (u8 *) readers, len); /* * readers is a list of available readers. The last entry is terminated * with double null. */ pos = 0; #ifdef UNICODE /* TODO */ #else /* UNICODE */ while (pos < len) { if (reader == NULL || os_strncmp(&readers[pos], reader, os_strlen(reader)) == 0) break; while (pos < len && readers[pos]) pos++; pos++; /* skip separating null */ if (pos < len && readers[pos] == '\0') pos = len; /* double null terminates list */ } #endif /* UNICODE */ if (pos >= len) { wpa_printf(MSG_WARNING, "SCARD: No reader with prefix '%s' " "found", reader); goto failed; } #ifdef UNICODE wpa_printf(MSG_DEBUG, "SCARD: Selected reader='%S'", &readers[pos]); #else /* UNICODE */ wpa_printf(MSG_DEBUG, "SCARD: Selected reader='%s'", &readers[pos]); #endif /* UNICODE */ ret = SCardConnect(scard->ctx, &readers[pos], SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, &scard->card, &scard->protocol); if (ret != SCARD_S_SUCCESS) { if (ret == (long) SCARD_E_NO_SMARTCARD) wpa_printf(MSG_INFO, "No smart card inserted."); else wpa_printf(MSG_WARNING, "SCardConnect err=%lx", ret); goto failed; } os_free(readers); readers = NULL; wpa_printf(MSG_DEBUG, "SCARD: card=0x%x active_protocol=%lu (%s)", (unsigned int) scard->card, scard->protocol, scard->protocol == SCARD_PROTOCOL_T0 ? "T0" : "T1"); ret = SCardBeginTransaction(scard->card); if (ret != SCARD_S_SUCCESS) { wpa_printf(MSG_DEBUG, "SCARD: Could not begin transaction: " "0x%x", (unsigned int) ret); goto failed; } transaction = 1; blen = sizeof(buf); wpa_printf(MSG_DEBUG, "SCARD: verifying USIM support"); if (_scard_select_file(scard, SCARD_FILE_MF, buf, &blen, SCARD_USIM, NULL, 0)) { wpa_printf(MSG_DEBUG, "SCARD: USIM is not supported. Trying to use GSM SIM"); scard->sim_type = SCARD_GSM_SIM; } else { wpa_printf(MSG_DEBUG, "SCARD: USIM is supported"); scard->sim_type = SCARD_USIM; } if (scard->sim_type == SCARD_GSM_SIM) { blen = sizeof(buf); if (scard_select_file(scard, SCARD_FILE_MF, buf, &blen)) { wpa_printf(MSG_DEBUG, "SCARD: Failed to read MF"); goto failed; } blen = sizeof(buf); if (scard_select_file(scard, SCARD_FILE_GSM_DF, buf, &blen)) { wpa_printf(MSG_DEBUG, "SCARD: Failed to read GSM DF"); goto failed; } } else { unsigned char aid[32]; int aid_len; aid_len = scard_get_aid(scard, aid, sizeof(aid)); if (aid_len < 0) { wpa_printf(MSG_DEBUG, "SCARD: Failed to find AID for " "3G USIM app - try to use standard 3G RID"); os_memcpy(aid, "\xa0\x00\x00\x00\x87", 5); aid_len = 5; } wpa_hexdump(MSG_DEBUG, "SCARD: 3G USIM AID", aid, aid_len); /* Select based on AID = 3G RID from EF_DIR. This is usually * starting with A0 00 00 00 87. */ blen = sizeof(buf); if (_scard_select_file(scard, 0, buf, &blen, scard->sim_type, aid, aid_len)) { wpa_printf(MSG_INFO, "SCARD: Failed to read 3G USIM " "app"); wpa_hexdump(MSG_INFO, "SCARD: 3G USIM AID", aid, aid_len); goto failed; } } /* Verify whether CHV1 (PIN1) is needed to access the card. */ pin_needed = scard_pin_needed(scard, buf, blen); if (pin_needed < 0) { wpa_printf(MSG_DEBUG, "SCARD: Failed to determine whether PIN " "is needed"); goto failed; } if (pin_needed) { scard->pin1_required = 1; wpa_printf(MSG_DEBUG, "PIN1 needed for SIM access (retry " "counter=%d)", scard_get_pin_retry_counter(scard)); } ret = SCardEndTransaction(scard->card, SCARD_LEAVE_CARD); if (ret != SCARD_S_SUCCESS) { wpa_printf(MSG_DEBUG, "SCARD: Could not end transaction: " "0x%x", (unsigned int) ret); } return scard; failed: if (transaction) SCardEndTransaction(scard->card, SCARD_LEAVE_CARD); os_free(readers); scard_deinit(scard); return NULL; } /** * scard_set_pin - Set PIN (CHV1/PIN1) code for accessing SIM/USIM commands * @scard: Pointer to private data from scard_init() * @pin: PIN code as an ASCII string (e.g., "1234") * Returns: 0 on success, -1 on failure */ int scard_set_pin(struct scard_data *scard, const char *pin) { if (scard == NULL) return -1; /* Verify whether CHV1 (PIN1) is needed to access the card. */ if (scard->pin1_required) { if (pin == NULL) { wpa_printf(MSG_DEBUG, "No PIN configured for SIM " "access"); return -1; } if (scard_verify_pin(scard, pin)) { wpa_printf(MSG_INFO, "PIN verification failed for " "SIM access"); return -1; } } return 0; } /** * scard_deinit - Deinitialize SIM/USIM connection * @scard: Pointer to private data from scard_init() * * This function closes the SIM/USIM connect opened with scard_init(). */ void scard_deinit(struct scard_data *scard) { long ret; if (scard == NULL) return; wpa_printf(MSG_DEBUG, "SCARD: deinitializing smart card interface"); if (scard->card) { ret = SCardDisconnect(scard->card, SCARD_UNPOWER_CARD); if (ret != SCARD_S_SUCCESS) { wpa_printf(MSG_DEBUG, "SCARD: Failed to disconnect " "smart card (err=%ld)", ret); } } if (scard->ctx) { ret = SCardReleaseContext(scard->ctx); if (ret != SCARD_S_SUCCESS) { wpa_printf(MSG_DEBUG, "Failed to release smart card " "context (err=%ld)", ret); } } os_free(scard); mingw_unload_symbols(); } static long scard_transmit(struct scard_data *scard, unsigned char *_send, size_t send_len, unsigned char *_recv, size_t *recv_len) { long ret; unsigned long rlen; wpa_hexdump_key(MSG_DEBUG, "SCARD: scard_transmit: send", _send, send_len); rlen = *recv_len; ret = SCardTransmit(scard->card, scard->protocol == SCARD_PROTOCOL_T1 ? SCARD_PCI_T1 : SCARD_PCI_T0, _send, (unsigned long) send_len, NULL, _recv, &rlen); *recv_len = rlen; if (ret == SCARD_S_SUCCESS) { wpa_hexdump(MSG_DEBUG, "SCARD: scard_transmit: recv", _recv, rlen); } else { wpa_printf(MSG_WARNING, "SCARD: SCardTransmit failed " "(err=0x%lx)", ret); } return ret; } static int _scard_select_file(struct scard_data *scard, unsigned short file_id, unsigned char *buf, size_t *buf_len, sim_types sim_type, unsigned char *aid, size_t aidlen) { long ret; unsigned char resp[3]; unsigned char cmd[50] = { SIM_CMD_SELECT }; int cmdlen; unsigned char get_resp[5] = { SIM_CMD_GET_RESPONSE }; size_t len, rlen; if (sim_type == SCARD_USIM) { cmd[0] = USIM_CLA; cmd[3] = 0x04; get_resp[0] = USIM_CLA; } wpa_printf(MSG_DEBUG, "SCARD: select file %04x", file_id); if (aid) { wpa_hexdump(MSG_DEBUG, "SCARD: select file by AID", aid, aidlen); if (5 + aidlen > sizeof(cmd)) return -1; cmd[2] = 0x04; /* Select by AID */ cmd[4] = aidlen; /* len */ os_memcpy(cmd + 5, aid, aidlen); cmdlen = 5 + aidlen; } else { cmd[5] = file_id >> 8; cmd[6] = file_id & 0xff; cmdlen = 7; } len = sizeof(resp); ret = scard_transmit(scard, cmd, cmdlen, resp, &len); if (ret != SCARD_S_SUCCESS) { wpa_printf(MSG_WARNING, "SCARD: SCardTransmit failed " "(err=0x%lx)", ret); return -1; } if (len != 2) { wpa_printf(MSG_WARNING, "SCARD: unexpected resp len " "%d (expected 2)", (int) len); return -1; } if (resp[0] == 0x98 && resp[1] == 0x04) { /* Security status not satisfied (PIN_WLAN) */ wpa_printf(MSG_WARNING, "SCARD: Security status not satisfied " "(PIN_WLAN)"); return -1; } if (resp[0] == 0x6e) { wpa_printf(MSG_DEBUG, "SCARD: used CLA not supported"); return -1; } if (resp[0] != 0x6c && resp[0] != 0x9f && resp[0] != 0x61) { wpa_printf(MSG_WARNING, "SCARD: unexpected response 0x%02x " "(expected 0x61, 0x6c, or 0x9f)", resp[0]); return -1; } /* Normal ending of command; resp[1] bytes available */ get_resp[4] = resp[1]; wpa_printf(MSG_DEBUG, "SCARD: trying to get response (%d bytes)", resp[1]); rlen = *buf_len; ret = scard_transmit(scard, get_resp, sizeof(get_resp), buf, &rlen); if (ret == SCARD_S_SUCCESS) { *buf_len = resp[1] < rlen ? resp[1] : rlen; return 0; } wpa_printf(MSG_WARNING, "SCARD: SCardTransmit err=0x%lx\n", ret); return -1; } static int scard_select_file(struct scard_data *scard, unsigned short file_id, unsigned char *buf, size_t *buf_len) { return _scard_select_file(scard, file_id, buf, buf_len, scard->sim_type, NULL, 0); } static int scard_get_record_len(struct scard_data *scard, unsigned char recnum, unsigned char mode) { unsigned char buf[255]; unsigned char cmd[5] = { SIM_CMD_READ_RECORD /* , len */ }; size_t blen; long ret; if (scard->sim_type == SCARD_USIM) cmd[0] = USIM_CLA; cmd[2] = recnum; cmd[3] = mode; cmd[4] = sizeof(buf); blen = sizeof(buf); ret = scard_transmit(scard, cmd, sizeof(cmd), buf, &blen); if (ret != SCARD_S_SUCCESS) { wpa_printf(MSG_DEBUG, "SCARD: failed to determine file " "length for record %d", recnum); return -1; } wpa_hexdump(MSG_DEBUG, "SCARD: file length determination response", buf, blen); if (blen < 2 || (buf[0] != 0x6c && buf[0] != 0x67)) { wpa_printf(MSG_DEBUG, "SCARD: unexpected response to file " "length determination"); return -1; } return buf[1]; } static int scard_read_record(struct scard_data *scard, unsigned char *data, size_t len, unsigned char recnum, unsigned char mode) { unsigned char cmd[5] = { SIM_CMD_READ_RECORD /* , len */ }; size_t blen = len + 3; unsigned char *buf; long ret; if (scard->sim_type == SCARD_USIM) cmd[0] = USIM_CLA; cmd[2] = recnum; cmd[3] = mode; cmd[4] = len; buf = os_malloc(blen); if (buf == NULL) return -1; ret = scard_transmit(scard, cmd, sizeof(cmd), buf, &blen); if (ret != SCARD_S_SUCCESS) { os_free(buf); return -2; } if (blen != len + 2) { wpa_printf(MSG_DEBUG, "SCARD: record read returned unexpected " "length %ld (expected %ld)", (long) blen, (long) len + 2); os_free(buf); return -3; } if (buf[len] != 0x90 || buf[len + 1] != 0x00) { wpa_printf(MSG_DEBUG, "SCARD: record read returned unexpected " "status %02x %02x (expected 90 00)", buf[len], buf[len + 1]); os_free(buf); return -4; } os_memcpy(data, buf, len); os_free(buf); return 0; } static int scard_read_file(struct scard_data *scard, unsigned char *data, size_t len) { unsigned char cmd[5] = { SIM_CMD_READ_BIN /* , len */ }; size_t blen = len + 3; unsigned char *buf; long ret; cmd[4] = len; buf = os_malloc(blen); if (buf == NULL) return -1; if (scard->sim_type == SCARD_USIM) cmd[0] = USIM_CLA; ret = scard_transmit(scard, cmd, sizeof(cmd), buf, &blen); if (ret != SCARD_S_SUCCESS) { os_free(buf); return -2; } if (blen != len + 2) { wpa_printf(MSG_DEBUG, "SCARD: file read returned unexpected " "length %ld (expected %ld)", (long) blen, (long) len + 2); os_free(buf); return -3; } if (buf[len] != 0x90 || buf[len + 1] != 0x00) { wpa_printf(MSG_DEBUG, "SCARD: file read returned unexpected " "status %02x %02x (expected 90 00)", buf[len], buf[len + 1]); os_free(buf); return -4; } os_memcpy(data, buf, len); os_free(buf); return 0; } static int scard_verify_pin(struct scard_data *scard, const char *pin) { long ret; unsigned char resp[3]; unsigned char cmd[5 + 8] = { SIM_CMD_VERIFY_CHV1 }; size_t len; wpa_printf(MSG_DEBUG, "SCARD: verifying PIN"); if (pin == NULL || os_strlen(pin) > 8) return -1; if (scard->sim_type == SCARD_USIM) cmd[0] = USIM_CLA; os_memcpy(cmd + 5, pin, os_strlen(pin)); os_memset(cmd + 5 + os_strlen(pin), 0xff, 8 - os_strlen(pin)); len = sizeof(resp); ret = scard_transmit(scard, cmd, sizeof(cmd), resp, &len); if (ret != SCARD_S_SUCCESS) return -2; if (len != 2 || resp[0] != 0x90 || resp[1] != 0x00) { wpa_printf(MSG_WARNING, "SCARD: PIN verification failed"); return -1; } wpa_printf(MSG_DEBUG, "SCARD: PIN verified successfully"); return 0; } int scard_get_pin_retry_counter(struct scard_data *scard) { long ret; unsigned char resp[3]; unsigned char cmd[5] = { SIM_CMD_VERIFY_CHV1 }; size_t len; u16 val; wpa_printf(MSG_DEBUG, "SCARD: fetching PIN retry counter"); if (scard->sim_type == SCARD_USIM) cmd[0] = USIM_CLA; cmd[4] = 0; /* Empty data */ len = sizeof(resp); ret = scard_transmit(scard, cmd, sizeof(cmd), resp, &len); if (ret != SCARD_S_SUCCESS) return -2; if (len != 2) { wpa_printf(MSG_WARNING, "SCARD: failed to fetch PIN retry " "counter"); return -1; } val = WPA_GET_BE16(resp); if (val == 0x63c0 || val == 0x6983) { wpa_printf(MSG_DEBUG, "SCARD: PIN has been blocked"); return 0; } if (val >= 0x63c0 && val <= 0x63cf) return val & 0x000f; wpa_printf(MSG_DEBUG, "SCARD: Unexpected PIN retry counter response " "value 0x%x", val); return 0; } /** * scard_get_imsi - Read IMSI from SIM/USIM card * @scard: Pointer to private data from scard_init() * @imsi: Buffer for IMSI * @len: Length of imsi buffer; set to IMSI length on success * Returns: 0 on success, -1 if IMSI file cannot be selected, -2 if IMSI file * selection returns invalid result code, -3 if parsing FSP template file fails * (USIM only), -4 if IMSI does not fit in the provided imsi buffer (len is set * to needed length), -5 if reading IMSI file fails. * * This function can be used to read IMSI from the SIM/USIM card. If the IMSI * file is PIN protected, scard_set_pin() must have been used to set the * correct PIN code before calling scard_get_imsi(). */ int scard_get_imsi(struct scard_data *scard, char *imsi, size_t *len) { unsigned char buf[100]; size_t blen, imsilen, i; char *pos; wpa_printf(MSG_DEBUG, "SCARD: reading IMSI from (GSM) EF-IMSI"); blen = sizeof(buf); if (scard_select_file(scard, SCARD_FILE_GSM_EF_IMSI, buf, &blen)) return -1; if (blen < 4) { wpa_printf(MSG_WARNING, "SCARD: too short (GSM) EF-IMSI " "header (len=%ld)", (long) blen); return -2; } if (scard->sim_type == SCARD_GSM_SIM) { blen = (buf[2] << 8) | buf[3]; } else { int file_size; if (scard_parse_fsp_templ(buf, blen, NULL, &file_size)) return -3; blen = file_size; } if (blen < 2 || blen > sizeof(buf)) { wpa_printf(MSG_DEBUG, "SCARD: invalid IMSI file length=%ld", (long) blen); return -3; } imsilen = (blen - 2) * 2 + 1; wpa_printf(MSG_DEBUG, "SCARD: IMSI file length=%ld imsilen=%ld", (long) blen, (long) imsilen); if (blen < 2 || imsilen > *len) { *len = imsilen; return -4; } if (scard_read_file(scard, buf, blen)) return -5; pos = imsi; *pos++ = '0' + (buf[1] >> 4 & 0x0f); for (i = 2; i < blen; i++) { unsigned char digit; digit = buf[i] & 0x0f; if (digit < 10) *pos++ = '0' + digit; else imsilen--; digit = buf[i] >> 4 & 0x0f; if (digit < 10) *pos++ = '0' + digit; else imsilen--; } *len = imsilen; return 0; } /** * scard_get_mnc_len - Read length of MNC in the IMSI from SIM/USIM card * @scard: Pointer to private data from scard_init() * Returns: length (>0) on success, -1 if administrative data file cannot be * selected, -2 if administrative data file selection returns invalid result * code, -3 if parsing FSP template file fails (USIM only), -4 if length of * the file is unexpected, -5 if reading file fails, -6 if MNC length is not * in range (i.e. 2 or 3), -7 if MNC length is not available. * */ int scard_get_mnc_len(struct scard_data *scard) { unsigned char buf[100]; size_t blen; int file_size; wpa_printf(MSG_DEBUG, "SCARD: reading MNC len from (GSM) EF-AD"); blen = sizeof(buf); if (scard_select_file(scard, SCARD_FILE_GSM_EF_AD, buf, &blen)) return -1; if (blen < 4) { wpa_printf(MSG_WARNING, "SCARD: too short (GSM) EF-AD " "header (len=%ld)", (long) blen); return -2; } if (scard->sim_type == SCARD_GSM_SIM) { file_size = (buf[2] << 8) | buf[3]; } else { if (scard_parse_fsp_templ(buf, blen, NULL, &file_size)) return -3; } if (file_size == 3) { wpa_printf(MSG_DEBUG, "SCARD: MNC length not available"); return -7; } if (file_size < 4 || file_size > (int) sizeof(buf)) { wpa_printf(MSG_DEBUG, "SCARD: invalid file length=%ld", (long) file_size); return -4; } if (scard_read_file(scard, buf, file_size)) return -5; buf[3] = buf[3] & 0x0f; /* upper nibble reserved for future use */ if (buf[3] < 2 || buf[3] > 3) { wpa_printf(MSG_DEBUG, "SCARD: invalid MNC length=%ld", (long) buf[3]); return -6; } wpa_printf(MSG_DEBUG, "SCARD: MNC length=%ld", (long) buf[3]); return buf[3]; } /** * scard_gsm_auth - Run GSM authentication command on SIM card * @scard: Pointer to private data from scard_init() * @_rand: 16-byte RAND value from HLR/AuC * @sres: 4-byte buffer for SRES * @kc: 8-byte buffer for Kc * Returns: 0 on success, -1 if SIM/USIM connection has not been initialized, * -2 if authentication command execution fails, -3 if unknown response code * for authentication command is received, -4 if reading of response fails, * -5 if if response data is of unexpected length * * This function performs GSM authentication using SIM/USIM card and the * provided RAND value from HLR/AuC. If authentication command can be completed * successfully, SRES and Kc values will be written into sres and kc buffers. */ int scard_gsm_auth(struct scard_data *scard, const unsigned char *_rand, unsigned char *sres, unsigned char *kc) { unsigned char cmd[5 + 1 + 16] = { SIM_CMD_RUN_GSM_ALG }; int cmdlen; unsigned char get_resp[5] = { SIM_CMD_GET_RESPONSE }; unsigned char resp[3], buf[12 + 3 + 2]; size_t len; long ret; if (scard == NULL) return -1; wpa_hexdump(MSG_DEBUG, "SCARD: GSM auth - RAND", _rand, 16); if (scard->sim_type == SCARD_GSM_SIM) { cmdlen = 5 + 16; os_memcpy(cmd + 5, _rand, 16); } else { cmdlen = 5 + 1 + 16; cmd[0] = USIM_CLA; cmd[3] = 0x80; cmd[4] = 17; cmd[5] = 16; os_memcpy(cmd + 6, _rand, 16); } len = sizeof(resp); ret = scard_transmit(scard, cmd, cmdlen, resp, &len); if (ret != SCARD_S_SUCCESS) return -2; if ((scard->sim_type == SCARD_GSM_SIM && (len != 2 || resp[0] != 0x9f || resp[1] != 0x0c)) || (scard->sim_type == SCARD_USIM && (len != 2 || resp[0] != 0x61 || resp[1] != 0x0e))) { wpa_printf(MSG_WARNING, "SCARD: unexpected response for GSM " "auth request (len=%ld resp=%02x %02x)", (long) len, resp[0], resp[1]); return -3; } get_resp[4] = resp[1]; len = sizeof(buf); ret = scard_transmit(scard, get_resp, sizeof(get_resp), buf, &len); if (ret != SCARD_S_SUCCESS) return -4; if (scard->sim_type == SCARD_GSM_SIM) { if (len != 4 + 8 + 2) { wpa_printf(MSG_WARNING, "SCARD: unexpected data " "length for GSM auth (len=%ld, expected 14)", (long) len); return -5; } os_memcpy(sres, buf, 4); os_memcpy(kc, buf + 4, 8); } else { if (len != 1 + 4 + 1 + 8 + 2) { wpa_printf(MSG_WARNING, "SCARD: unexpected data " "length for USIM auth (len=%ld, " "expected 16)", (long) len); return -5; } if (buf[0] != 4 || buf[5] != 8) { wpa_printf(MSG_WARNING, "SCARD: unexpected SREC/Kc " "length (%d %d, expected 4 8)", buf[0], buf[5]); } os_memcpy(sres, buf + 1, 4); os_memcpy(kc, buf + 6, 8); } wpa_hexdump(MSG_DEBUG, "SCARD: GSM auth - SRES", sres, 4); wpa_hexdump(MSG_DEBUG, "SCARD: GSM auth - Kc", kc, 8); return 0; } /** * scard_umts_auth - Run UMTS authentication command on USIM card * @scard: Pointer to private data from scard_init() * @_rand: 16-byte RAND value from HLR/AuC * @autn: 16-byte AUTN value from HLR/AuC * @res: 16-byte buffer for RES * @res_len: Variable that will be set to RES length * @ik: 16-byte buffer for IK * @ck: 16-byte buffer for CK * @auts: 14-byte buffer for AUTS * Returns: 0 on success, -1 on failure, or -2 if USIM reports synchronization * failure * * This function performs AKA authentication using USIM card and the provided * RAND and AUTN values from HLR/AuC. If authentication command can be * completed successfully, RES, IK, and CK values will be written into provided * buffers and res_len is set to length of received RES value. If USIM reports * synchronization failure, the received AUTS value will be written into auts * buffer. In this case, RES, IK, and CK are not valid. */ int scard_umts_auth(struct scard_data *scard, const unsigned char *_rand, const unsigned char *autn, unsigned char *res, size_t *res_len, unsigned char *ik, unsigned char *ck, unsigned char *auts) { unsigned char cmd[5 + 1 + AKA_RAND_LEN + 1 + AKA_AUTN_LEN] = { USIM_CMD_RUN_UMTS_ALG }; unsigned char get_resp[5] = { USIM_CMD_GET_RESPONSE }; unsigned char resp[3], buf[64], *pos, *end; size_t len; long ret; if (scard == NULL) return -1; if (scard->sim_type == SCARD_GSM_SIM) { wpa_printf(MSG_ERROR, "SCARD: Non-USIM card - cannot do UMTS " "auth"); return -1; } wpa_hexdump(MSG_DEBUG, "SCARD: UMTS auth - RAND", _rand, AKA_RAND_LEN); wpa_hexdump(MSG_DEBUG, "SCARD: UMTS auth - AUTN", autn, AKA_AUTN_LEN); cmd[5] = AKA_RAND_LEN; os_memcpy(cmd + 6, _rand, AKA_RAND_LEN); cmd[6 + AKA_RAND_LEN] = AKA_AUTN_LEN; os_memcpy(cmd + 6 + AKA_RAND_LEN + 1, autn, AKA_AUTN_LEN); len = sizeof(resp); ret = scard_transmit(scard, cmd, sizeof(cmd), resp, &len); if (ret != SCARD_S_SUCCESS) return -1; if (len <= sizeof(resp)) wpa_hexdump(MSG_DEBUG, "SCARD: UMTS alg response", resp, len); if (len == 2 && resp[0] == 0x98 && resp[1] == 0x62) { wpa_printf(MSG_WARNING, "SCARD: UMTS auth failed - " "MAC != XMAC"); return -1; } else if (len != 2 || resp[0] != 0x61) { wpa_printf(MSG_WARNING, "SCARD: unexpected response for UMTS " "auth request (len=%ld resp=%02x %02x)", (long) len, resp[0], resp[1]); return -1; } get_resp[4] = resp[1]; len = sizeof(buf); ret = scard_transmit(scard, get_resp, sizeof(get_resp), buf, &len); if (ret != SCARD_S_SUCCESS || len > sizeof(buf)) return -1; wpa_hexdump(MSG_DEBUG, "SCARD: UMTS get response result", buf, len); if (len >= 2 + AKA_AUTS_LEN && buf[0] == 0xdc && buf[1] == AKA_AUTS_LEN) { wpa_printf(MSG_DEBUG, "SCARD: UMTS Synchronization-Failure"); os_memcpy(auts, buf + 2, AKA_AUTS_LEN); wpa_hexdump(MSG_DEBUG, "SCARD: AUTS", auts, AKA_AUTS_LEN); return -2; } else if (len >= 6 + IK_LEN + CK_LEN && buf[0] == 0xdb) { pos = buf + 1; end = buf + len; /* RES */ if (pos[0] > RES_MAX_LEN || pos + pos[0] > end) { wpa_printf(MSG_DEBUG, "SCARD: Invalid RES"); return -1; } *res_len = *pos++; os_memcpy(res, pos, *res_len); pos += *res_len; wpa_hexdump(MSG_DEBUG, "SCARD: RES", res, *res_len); /* CK */ if (pos[0] != CK_LEN || pos + CK_LEN > end) { wpa_printf(MSG_DEBUG, "SCARD: Invalid CK"); return -1; } pos++; os_memcpy(ck, pos, CK_LEN); pos += CK_LEN; wpa_hexdump(MSG_DEBUG, "SCARD: CK", ck, CK_LEN); /* IK */ if (pos[0] != IK_LEN || pos + IK_LEN > end) { wpa_printf(MSG_DEBUG, "SCARD: Invalid IK"); return -1; } pos++; os_memcpy(ik, pos, IK_LEN); pos += IK_LEN; wpa_hexdump(MSG_DEBUG, "SCARD: IK", ik, IK_LEN); return 0; } wpa_printf(MSG_DEBUG, "SCARD: Unrecognized response"); return -1; } int scard_supports_umts(struct scard_data *scard) { return scard->sim_type == SCARD_USIM; } wpa-2.1/src/utils/pcsc_funcs.h000066400000000000000000000026321227414666700164130ustar00rootroot00000000000000/* * WPA Supplicant / PC/SC smartcard interface for USIM, GSM SIM * Copyright (c) 2004-2006, 2012, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef PCSC_FUNCS_H #define PCSC_FUNCS_H #ifdef PCSC_FUNCS struct scard_data * scard_init(const char *reader); void scard_deinit(struct scard_data *scard); int scard_set_pin(struct scard_data *scard, const char *pin); int scard_get_imsi(struct scard_data *scard, char *imsi, size_t *len); int scard_get_mnc_len(struct scard_data *scard); int scard_gsm_auth(struct scard_data *scard, const unsigned char *_rand, unsigned char *sres, unsigned char *kc); int scard_umts_auth(struct scard_data *scard, const unsigned char *_rand, const unsigned char *autn, unsigned char *res, size_t *res_len, unsigned char *ik, unsigned char *ck, unsigned char *auts); int scard_get_pin_retry_counter(struct scard_data *scard); int scard_supports_umts(struct scard_data *scard); #else /* PCSC_FUNCS */ #define scard_init(r) NULL #define scard_deinit(s) do { } while (0) #define scard_set_pin(s, p) -1 #define scard_get_imsi(s, i, l) -1 #define scard_get_mnc_len(s) -1 #define scard_gsm_auth(s, r, s2, k) -1 #define scard_umts_auth(s, r, a, r2, rl, i, c, a2) -1 #define scard_get_pin_retry_counter(s) -1 #define scard_supports_umts(s) 0 #endif /* PCSC_FUNCS */ #endif /* PCSC_FUNCS_H */ wpa-2.1/src/utils/radiotap.c000066400000000000000000000222721227414666700160650ustar00rootroot00000000000000/* * Radiotap parser * * Copyright 2007 Andy Green * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * Alternatively, this software may be distributed under the terms of BSD * license. * * See README and COPYING for more details. * * * Modified for userspace by Johannes Berg * I only modified some things on top to ease syncing should bugs be found. */ #include "includes.h" #include "common.h" #include "radiotap_iter.h" #define le16_to_cpu le_to_host16 #define le32_to_cpu le_to_host32 #define __le32 uint32_t #define ulong unsigned long #define unlikely(cond) (cond) #define get_unaligned(p) \ ({ \ struct packed_dummy_struct { \ typeof(*(p)) __val; \ } __attribute__((packed)) *__ptr = (void *) (p); \ \ __ptr->__val; \ }) /* function prototypes and related defs are in radiotap_iter.h */ /** * ieee80211_radiotap_iterator_init - radiotap parser iterator initialization * @iterator: radiotap_iterator to initialize * @radiotap_header: radiotap header to parse * @max_length: total length we can parse into (eg, whole packet length) * * Returns: 0 or a negative error code if there is a problem. * * This function initializes an opaque iterator struct which can then * be passed to ieee80211_radiotap_iterator_next() to visit every radiotap * argument which is present in the header. It knows about extended * present headers and handles them. * * How to use: * call __ieee80211_radiotap_iterator_init() to init a semi-opaque iterator * struct ieee80211_radiotap_iterator (no need to init the struct beforehand) * checking for a good 0 return code. Then loop calling * __ieee80211_radiotap_iterator_next()... it returns either 0, * -ENOENT if there are no more args to parse, or -EINVAL if there is a problem. * The iterator's @this_arg member points to the start of the argument * associated with the current argument index that is present, which can be * found in the iterator's @this_arg_index member. This arg index corresponds * to the IEEE80211_RADIOTAP_... defines. * * Radiotap header length: * You can find the CPU-endian total radiotap header length in * iterator->max_length after executing ieee80211_radiotap_iterator_init() * successfully. * * Alignment Gotcha: * You must take care when dereferencing iterator.this_arg * for multibyte types... the pointer is not aligned. Use * get_unaligned((type *)iterator.this_arg) to dereference * iterator.this_arg for type "type" safely on all arches. * * Example code: * See Documentation/networking/radiotap-headers.txt */ int ieee80211_radiotap_iterator_init( struct ieee80211_radiotap_iterator *iterator, struct ieee80211_radiotap_header *radiotap_header, int max_length) { /* Linux only supports version 0 radiotap format */ if (radiotap_header->it_version) return -EINVAL; /* sanity check for allowed length and radiotap length field */ if (max_length < le16_to_cpu(get_unaligned(&radiotap_header->it_len))) return -EINVAL; iterator->rtheader = radiotap_header; iterator->max_length = le16_to_cpu(get_unaligned( &radiotap_header->it_len)); iterator->arg_index = 0; iterator->bitmap_shifter = le32_to_cpu(get_unaligned( &radiotap_header->it_present)); iterator->arg = (u8 *)radiotap_header + sizeof(*radiotap_header); iterator->this_arg = NULL; /* find payload start allowing for extended bitmap(s) */ if (unlikely(iterator->bitmap_shifter & (1<arg)) & (1<arg += sizeof(u32); /* * check for insanity where the present bitmaps * keep claiming to extend up to or even beyond the * stated radiotap header length */ if (((ulong)iterator->arg - (ulong)iterator->rtheader) > (ulong)iterator->max_length) return -EINVAL; } iterator->arg += sizeof(u32); /* * no need to check again for blowing past stated radiotap * header length, because ieee80211_radiotap_iterator_next * checks it before it is dereferenced */ } /* we are all initialized happily */ return 0; } /** * ieee80211_radiotap_iterator_next - return next radiotap parser iterator arg * @iterator: radiotap_iterator to move to next arg (if any) * * Returns: 0 if there is an argument to handle, * -ENOENT if there are no more args or -EINVAL * if there is something else wrong. * * This function provides the next radiotap arg index (IEEE80211_RADIOTAP_*) * in @this_arg_index and sets @this_arg to point to the * payload for the field. It takes care of alignment handling and extended * present fields. @this_arg can be changed by the caller (eg, * incremented to move inside a compound argument like * IEEE80211_RADIOTAP_CHANNEL). The args pointed to are in * little-endian format whatever the endianess of your CPU. * * Alignment Gotcha: * You must take care when dereferencing iterator.this_arg * for multibyte types... the pointer is not aligned. Use * get_unaligned((type *)iterator.this_arg) to dereference * iterator.this_arg for type "type" safely on all arches. */ int ieee80211_radiotap_iterator_next( struct ieee80211_radiotap_iterator *iterator) { /* * small length lookup table for all radiotap types we heard of * starting from b0 in the bitmap, so we can walk the payload * area of the radiotap header * * There is a requirement to pad args, so that args * of a given length must begin at a boundary of that length * -- but note that compound args are allowed (eg, 2 x u16 * for IEEE80211_RADIOTAP_CHANNEL) so total arg length is not * a reliable indicator of alignment requirement. * * upper nybble: content alignment for arg * lower nybble: content length for arg */ static const u8 rt_sizes[] = { [IEEE80211_RADIOTAP_TSFT] = 0x88, [IEEE80211_RADIOTAP_FLAGS] = 0x11, [IEEE80211_RADIOTAP_RATE] = 0x11, [IEEE80211_RADIOTAP_CHANNEL] = 0x24, [IEEE80211_RADIOTAP_FHSS] = 0x22, [IEEE80211_RADIOTAP_DBM_ANTSIGNAL] = 0x11, [IEEE80211_RADIOTAP_DBM_ANTNOISE] = 0x11, [IEEE80211_RADIOTAP_LOCK_QUALITY] = 0x22, [IEEE80211_RADIOTAP_TX_ATTENUATION] = 0x22, [IEEE80211_RADIOTAP_DB_TX_ATTENUATION] = 0x22, [IEEE80211_RADIOTAP_DBM_TX_POWER] = 0x11, [IEEE80211_RADIOTAP_ANTENNA] = 0x11, [IEEE80211_RADIOTAP_DB_ANTSIGNAL] = 0x11, [IEEE80211_RADIOTAP_DB_ANTNOISE] = 0x11, [IEEE80211_RADIOTAP_RX_FLAGS] = 0x22, [IEEE80211_RADIOTAP_TX_FLAGS] = 0x22, [IEEE80211_RADIOTAP_RTS_RETRIES] = 0x11, [IEEE80211_RADIOTAP_DATA_RETRIES] = 0x11, /* * add more here as they are defined in * include/net/ieee80211_radiotap.h */ }; /* * for every radiotap entry we can at * least skip (by knowing the length)... */ while (iterator->arg_index < (int) sizeof(rt_sizes)) { int hit = 0; int pad; if (!(iterator->bitmap_shifter & 1)) goto next_entry; /* arg not present */ /* * arg is present, account for alignment padding * 8-bit args can be at any alignment * 16-bit args must start on 16-bit boundary * 32-bit args must start on 32-bit boundary * 64-bit args must start on 64-bit boundary * * note that total arg size can differ from alignment of * elements inside arg, so we use upper nybble of length * table to base alignment on * * also note: these alignments are ** relative to the * start of the radiotap header **. There is no guarantee * that the radiotap header itself is aligned on any * kind of boundary. * * the above is why get_unaligned() is used to dereference * multibyte elements from the radiotap area */ pad = (((ulong)iterator->arg) - ((ulong)iterator->rtheader)) & ((rt_sizes[iterator->arg_index] >> 4) - 1); if (pad) iterator->arg += (rt_sizes[iterator->arg_index] >> 4) - pad; /* * this is what we will return to user, but we need to * move on first so next call has something fresh to test */ iterator->this_arg_index = iterator->arg_index; iterator->this_arg = iterator->arg; hit = 1; /* internally move on the size of this arg */ iterator->arg += rt_sizes[iterator->arg_index] & 0x0f; /* * check for insanity where we are given a bitmap that * claims to have more arg content than the length of the * radiotap section. We will normally end up equalling this * max_length on the last arg, never exceeding it. */ if (((ulong)iterator->arg - (ulong)iterator->rtheader) > (ulong) iterator->max_length) return -EINVAL; next_entry: iterator->arg_index++; if (unlikely((iterator->arg_index & 31) == 0)) { /* completed current u32 bitmap */ if (iterator->bitmap_shifter & 1) { /* b31 was set, there is more */ /* move to next u32 bitmap */ iterator->bitmap_shifter = le32_to_cpu( get_unaligned(iterator->next_bitmap)); iterator->next_bitmap++; } else /* no more bitmaps: end */ iterator->arg_index = sizeof(rt_sizes); } else /* just try the next bit */ iterator->bitmap_shifter >>= 1; /* if we found a valid arg earlier, return it now */ if (hit) return 0; } /* we don't know how to handle any more args, we're done */ return -ENOENT; } wpa-2.1/src/utils/radiotap.h000066400000000000000000000217571227414666700161010ustar00rootroot00000000000000/* $FreeBSD: src/sys/net80211/ieee80211_radiotap.h,v 1.5 2005/01/22 20:12:05 sam Exp $ */ /* $NetBSD: ieee80211_radiotap.h,v 1.11 2005/06/22 06:16:02 dyoung Exp $ */ /*- * Copyright (c) 2003, 2004 David Young. All rights reserved. * * 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. The name of David Young may not be used to endorse or promote * products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY DAVID YOUNG ``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 DAVID * YOUNG 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. */ /* * Modifications to fit into the linux IEEE 802.11 stack, * Mike Kershaw (dragorn@kismetwireless.net) */ #ifndef IEEE80211RADIOTAP_H #define IEEE80211RADIOTAP_H #include /* Base version of the radiotap packet header data */ #define PKTHDR_RADIOTAP_VERSION 0 /* A generic radio capture format is desirable. There is one for * Linux, but it is neither rigidly defined (there were not even * units given for some fields) nor easily extensible. * * I suggest the following extensible radio capture format. It is * based on a bitmap indicating which fields are present. * * I am trying to describe precisely what the application programmer * should expect in the following, and for that reason I tell the * units and origin of each measurement (where it applies), or else I * use sufficiently weaselly language ("is a monotonically nondecreasing * function of...") that I cannot set false expectations for lawyerly * readers. */ /* The radio capture header precedes the 802.11 header. * All data in the header is little endian on all platforms. */ struct ieee80211_radiotap_header { uint8_t it_version; /* Version 0. Only increases * for drastic changes, * introduction of compatible * new fields does not count. */ uint8_t it_pad; uint16_t it_len; /* length of the whole * header in bytes, including * it_version, it_pad, * it_len, and data fields. */ uint32_t it_present; /* A bitmap telling which * fields are present. Set bit 31 * (0x80000000) to extend the * bitmap by another 32 bits. * Additional extensions are made * by setting bit 31. */ }; /* Name Data type Units * ---- --------- ----- * * IEEE80211_RADIOTAP_TSFT __le64 microseconds * * Value in microseconds of the MAC's 64-bit 802.11 Time * Synchronization Function timer when the first bit of the * MPDU arrived at the MAC. For received frames, only. * * IEEE80211_RADIOTAP_CHANNEL 2 x uint16_t MHz, bitmap * * Tx/Rx frequency in MHz, followed by flags (see below). * * IEEE80211_RADIOTAP_FHSS uint16_t see below * * For frequency-hopping radios, the hop set (first byte) * and pattern (second byte). * * IEEE80211_RADIOTAP_RATE u8 500kb/s * * Tx/Rx data rate * * IEEE80211_RADIOTAP_DBM_ANTSIGNAL s8 decibels from * one milliwatt (dBm) * * RF signal power at the antenna, decibel difference from * one milliwatt. * * IEEE80211_RADIOTAP_DBM_ANTNOISE s8 decibels from * one milliwatt (dBm) * * RF noise power at the antenna, decibel difference from one * milliwatt. * * IEEE80211_RADIOTAP_DB_ANTSIGNAL u8 decibel (dB) * * RF signal power at the antenna, decibel difference from an * arbitrary, fixed reference. * * IEEE80211_RADIOTAP_DB_ANTNOISE u8 decibel (dB) * * RF noise power at the antenna, decibel difference from an * arbitrary, fixed reference point. * * IEEE80211_RADIOTAP_LOCK_QUALITY uint16_t unitless * * Quality of Barker code lock. Unitless. Monotonically * nondecreasing with "better" lock strength. Called "Signal * Quality" in datasheets. (Is there a standard way to measure * this?) * * IEEE80211_RADIOTAP_TX_ATTENUATION uint16_t unitless * * Transmit power expressed as unitless distance from max * power set at factory calibration. 0 is max power. * Monotonically nondecreasing with lower power levels. * * IEEE80211_RADIOTAP_DB_TX_ATTENUATION uint16_t decibels (dB) * * Transmit power expressed as decibel distance from max power * set at factory calibration. 0 is max power. Monotonically * nondecreasing with lower power levels. * * IEEE80211_RADIOTAP_DBM_TX_POWER s8 decibels from * one milliwatt (dBm) * * Transmit power expressed as dBm (decibels from a 1 milliwatt * reference). This is the absolute power level measured at * the antenna port. * * IEEE80211_RADIOTAP_FLAGS u8 bitmap * * Properties of transmitted and received frames. See flags * defined below. * * IEEE80211_RADIOTAP_ANTENNA u8 antenna index * * Unitless indication of the Rx/Tx antenna for this packet. * The first antenna is antenna 0. * * IEEE80211_RADIOTAP_RX_FLAGS uint16_t bitmap * * Properties of received frames. See flags defined below. * * IEEE80211_RADIOTAP_TX_FLAGS uint16_t bitmap * * Properties of transmitted frames. See flags defined below. * * IEEE80211_RADIOTAP_RTS_RETRIES u8 data * * Number of rts retries a transmitted frame used. * * IEEE80211_RADIOTAP_DATA_RETRIES u8 data * * Number of unicast retries a transmitted frame used. * */ enum ieee80211_radiotap_type { IEEE80211_RADIOTAP_TSFT = 0, IEEE80211_RADIOTAP_FLAGS = 1, IEEE80211_RADIOTAP_RATE = 2, IEEE80211_RADIOTAP_CHANNEL = 3, IEEE80211_RADIOTAP_FHSS = 4, IEEE80211_RADIOTAP_DBM_ANTSIGNAL = 5, IEEE80211_RADIOTAP_DBM_ANTNOISE = 6, IEEE80211_RADIOTAP_LOCK_QUALITY = 7, IEEE80211_RADIOTAP_TX_ATTENUATION = 8, IEEE80211_RADIOTAP_DB_TX_ATTENUATION = 9, IEEE80211_RADIOTAP_DBM_TX_POWER = 10, IEEE80211_RADIOTAP_ANTENNA = 11, IEEE80211_RADIOTAP_DB_ANTSIGNAL = 12, IEEE80211_RADIOTAP_DB_ANTNOISE = 13, IEEE80211_RADIOTAP_RX_FLAGS = 14, IEEE80211_RADIOTAP_TX_FLAGS = 15, IEEE80211_RADIOTAP_RTS_RETRIES = 16, IEEE80211_RADIOTAP_DATA_RETRIES = 17, IEEE80211_RADIOTAP_EXT = 31 }; /* Channel flags. */ #define IEEE80211_CHAN_TURBO 0x0010 /* Turbo channel */ #define IEEE80211_CHAN_CCK 0x0020 /* CCK channel */ #define IEEE80211_CHAN_OFDM 0x0040 /* OFDM channel */ #define IEEE80211_CHAN_2GHZ 0x0080 /* 2 GHz spectrum channel. */ #define IEEE80211_CHAN_5GHZ 0x0100 /* 5 GHz spectrum channel */ #define IEEE80211_CHAN_PASSIVE 0x0200 /* Only passive scan allowed */ #define IEEE80211_CHAN_DYN 0x0400 /* Dynamic CCK-OFDM channel */ #define IEEE80211_CHAN_GFSK 0x0800 /* GFSK channel (FHSS PHY) */ /* For IEEE80211_RADIOTAP_FLAGS */ #define IEEE80211_RADIOTAP_F_CFP 0x01 /* sent/received * during CFP */ #define IEEE80211_RADIOTAP_F_SHORTPRE 0x02 /* sent/received * with short * preamble */ #define IEEE80211_RADIOTAP_F_WEP 0x04 /* sent/received * with WEP encryption */ #define IEEE80211_RADIOTAP_F_FRAG 0x08 /* sent/received * with fragmentation */ #define IEEE80211_RADIOTAP_F_FCS 0x10 /* frame includes FCS */ #define IEEE80211_RADIOTAP_F_DATAPAD 0x20 /* frame has padding between * 802.11 header and payload * (to 32-bit boundary) */ /* For IEEE80211_RADIOTAP_RX_FLAGS */ #define IEEE80211_RADIOTAP_F_RX_BADFCS 0x0001 /* frame failed crc check */ /* For IEEE80211_RADIOTAP_TX_FLAGS */ #define IEEE80211_RADIOTAP_F_TX_FAIL 0x0001 /* failed due to excessive * retries */ #define IEEE80211_RADIOTAP_F_TX_CTS 0x0002 /* used cts 'protection' */ #define IEEE80211_RADIOTAP_F_TX_RTS 0x0004 /* used rts/cts handshake */ #define IEEE80211_RADIOTAP_F_TX_NOACK 0x0008 /* don't expect an ACK */ #endif /* IEEE80211_RADIOTAP_H */ wpa-2.1/src/utils/radiotap_iter.h000066400000000000000000000031321227414666700171070ustar00rootroot00000000000000/* * Radiotap parser * * Copyright 2007 Andy Green * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * Alternatively, this software may be distributed under the terms of BSD * license. * * See README and COPYING for more details. */ #ifndef __RADIOTAP_ITER_H #define __RADIOTAP_ITER_H #include "radiotap.h" /* Radiotap header iteration * implemented in radiotap.c */ /** * struct ieee80211_radiotap_iterator - tracks walk thru present radiotap args * @rtheader: pointer to the radiotap header we are walking through * @max_length: length of radiotap header in cpu byte ordering * @this_arg_index: IEEE80211_RADIOTAP_... index of current arg * @this_arg: pointer to current radiotap arg * @arg_index: internal next argument index * @arg: internal next argument pointer * @next_bitmap: internal pointer to next present u32 * @bitmap_shifter: internal shifter for curr u32 bitmap, b0 set == arg present */ struct ieee80211_radiotap_iterator { struct ieee80211_radiotap_header *rtheader; int max_length; int this_arg_index; unsigned char *this_arg; int arg_index; unsigned char *arg; uint32_t *next_bitmap; uint32_t bitmap_shifter; }; extern int ieee80211_radiotap_iterator_init( struct ieee80211_radiotap_iterator *iterator, struct ieee80211_radiotap_header *radiotap_header, int max_length); extern int ieee80211_radiotap_iterator_next( struct ieee80211_radiotap_iterator *iterator); #endif /* __RADIOTAP_ITER_H */ wpa-2.1/src/utils/state_machine.h000066400000000000000000000123751227414666700170760ustar00rootroot00000000000000/* * wpa_supplicant/hostapd - State machine definitions * Copyright (c) 2002-2005, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. * * This file includes a set of pre-processor macros that can be used to * implement a state machine. In addition to including this header file, each * file implementing a state machine must define STATE_MACHINE_DATA to be the * data structure including state variables (enum machine_state, * Boolean changed), and STATE_MACHINE_DEBUG_PREFIX to be a string that is used * as a prefix for all debug messages. If SM_ENTRY_MA macro is used to define * a group of state machines with shared data structure, STATE_MACHINE_ADDR * needs to be defined to point to the MAC address used in debug output. * SM_ENTRY_M macro can be used to define similar group of state machines * without this additional debug info. */ #ifndef STATE_MACHINE_H #define STATE_MACHINE_H /** * SM_STATE - Declaration of a state machine function * @machine: State machine name * @state: State machine state * * This macro is used to declare a state machine function. It is used in place * of a C function definition to declare functions to be run when the state is * entered by calling SM_ENTER or SM_ENTER_GLOBAL. */ #define SM_STATE(machine, state) \ static void sm_ ## machine ## _ ## state ## _Enter(STATE_MACHINE_DATA *sm, \ int global) /** * SM_ENTRY - State machine function entry point * @machine: State machine name * @state: State machine state * * This macro is used inside each state machine function declared with * SM_STATE. SM_ENTRY should be in the beginning of the function body, but * after declaration of possible local variables. This macro prints debug * information about state transition and update the state machine state. */ #define SM_ENTRY(machine, state) \ if (!global || sm->machine ## _state != machine ## _ ## state) { \ sm->changed = TRUE; \ wpa_printf(MSG_DEBUG, STATE_MACHINE_DEBUG_PREFIX ": " #machine \ " entering state " #state); \ } \ sm->machine ## _state = machine ## _ ## state; /** * SM_ENTRY_M - State machine function entry point for state machine group * @machine: State machine name * @_state: State machine state * @data: State variable prefix (full variable: prefix_state) * * This macro is like SM_ENTRY, but for state machine groups that use a shared * data structure for more than one state machine. Both machine and prefix * parameters are set to "sub-state machine" name. prefix is used to allow more * than one state variable to be stored in the same data structure. */ #define SM_ENTRY_M(machine, _state, data) \ if (!global || sm->data ## _ ## state != machine ## _ ## _state) { \ sm->changed = TRUE; \ wpa_printf(MSG_DEBUG, STATE_MACHINE_DEBUG_PREFIX ": " \ #machine " entering state " #_state); \ } \ sm->data ## _ ## state = machine ## _ ## _state; /** * SM_ENTRY_MA - State machine function entry point for state machine group * @machine: State machine name * @_state: State machine state * @data: State variable prefix (full variable: prefix_state) * * This macro is like SM_ENTRY_M, but a MAC address is included in debug * output. STATE_MACHINE_ADDR has to be defined to point to the MAC address to * be included in debug. */ #define SM_ENTRY_MA(machine, _state, data) \ if (!global || sm->data ## _ ## state != machine ## _ ## _state) { \ sm->changed = TRUE; \ wpa_printf(MSG_DEBUG, STATE_MACHINE_DEBUG_PREFIX ": " MACSTR " " \ #machine " entering state " #_state, \ MAC2STR(STATE_MACHINE_ADDR)); \ } \ sm->data ## _ ## state = machine ## _ ## _state; /** * SM_ENTER - Enter a new state machine state * @machine: State machine name * @state: State machine state * * This macro expands to a function call to a state machine function defined * with SM_STATE macro. SM_ENTER is used in a state machine step function to * move the state machine to a new state. */ #define SM_ENTER(machine, state) \ sm_ ## machine ## _ ## state ## _Enter(sm, 0) /** * SM_ENTER_GLOBAL - Enter a new state machine state based on global rule * @machine: State machine name * @state: State machine state * * This macro is like SM_ENTER, but this is used when entering a new state * based on a global (not specific to any particular state) rule. A separate * macro is used to avoid unwanted debug message floods when the same global * rule is forcing a state machine to remain in on state. */ #define SM_ENTER_GLOBAL(machine, state) \ sm_ ## machine ## _ ## state ## _Enter(sm, 1) /** * SM_STEP - Declaration of a state machine step function * @machine: State machine name * * This macro is used to declare a state machine step function. It is used in * place of a C function definition to declare a function that is used to move * state machine to a new state based on state variables. This function uses * SM_ENTER and SM_ENTER_GLOBAL macros to enter new state. */ #define SM_STEP(machine) \ static void sm_ ## machine ## _Step(STATE_MACHINE_DATA *sm) /** * SM_STEP_RUN - Call the state machine step function * @machine: State machine name * * This macro expands to a function call to a state machine step function * defined with SM_STEP macro. */ #define SM_STEP_RUN(machine) sm_ ## machine ## _Step(sm) #endif /* STATE_MACHINE_H */ wpa-2.1/src/utils/trace.c000066400000000000000000000147601227414666700153630ustar00rootroot00000000000000/* * Backtrace debugging * Copyright (c) 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 "trace.h" #ifdef WPA_TRACE static struct dl_list active_references = { &active_references, &active_references }; #ifdef WPA_TRACE_BFD #include #ifdef __linux__ #include #else /* __linux__ */ #include #endif /* __linux__ */ static char *prg_fname = NULL; static bfd *cached_abfd = NULL; static asymbol **syms = NULL; static void get_prg_fname(void) { char exe[50], fname[512]; int len; os_snprintf(exe, sizeof(exe) - 1, "/proc/%u/exe", getpid()); len = readlink(exe, fname, sizeof(fname) - 1); if (len < 0 || len >= (int) sizeof(fname)) { perror("readlink"); return; } fname[len] = '\0'; prg_fname = strdup(fname); } static bfd * open_bfd(const char *fname) { bfd *abfd; char **matching; abfd = bfd_openr(prg_fname, NULL); if (abfd == NULL) { wpa_printf(MSG_INFO, "bfd_openr failed"); return NULL; } if (bfd_check_format(abfd, bfd_archive)) { wpa_printf(MSG_INFO, "bfd_check_format failed"); bfd_close(abfd); return NULL; } if (!bfd_check_format_matches(abfd, bfd_object, &matching)) { wpa_printf(MSG_INFO, "bfd_check_format_matches failed"); free(matching); bfd_close(abfd); return NULL; } return abfd; } static void read_syms(bfd *abfd) { long storage, symcount; bfd_boolean dynamic = FALSE; if (syms) return; if (!(bfd_get_file_flags(abfd) & HAS_SYMS)) { wpa_printf(MSG_INFO, "No symbols"); return; } storage = bfd_get_symtab_upper_bound(abfd); if (storage == 0) { storage = bfd_get_dynamic_symtab_upper_bound(abfd); dynamic = TRUE; } if (storage < 0) { wpa_printf(MSG_INFO, "Unknown symtab upper bound"); return; } syms = malloc(storage); if (syms == NULL) { wpa_printf(MSG_INFO, "Failed to allocate memory for symtab " "(%ld bytes)", storage); return; } if (dynamic) symcount = bfd_canonicalize_dynamic_symtab(abfd, syms); else symcount = bfd_canonicalize_symtab(abfd, syms); if (symcount < 0) { wpa_printf(MSG_INFO, "Failed to canonicalize %ssymtab", dynamic ? "dynamic " : ""); free(syms); syms = NULL; return; } } struct bfd_data { bfd_vma pc; bfd_boolean found; const char *filename; const char *function; unsigned int line; }; static void find_addr_sect(bfd *abfd, asection *section, void *obj) { struct bfd_data *data = obj; bfd_vma vma; bfd_size_type size; if (data->found) return; if (!(bfd_get_section_vma(abfd, section))) return; vma = bfd_get_section_vma(abfd, section); if (data->pc < vma) return; size = bfd_get_section_size(section); if (data->pc >= vma + size) return; data->found = bfd_find_nearest_line(abfd, section, syms, data->pc - vma, &data->filename, &data->function, &data->line); } static void wpa_trace_bfd_addr(void *pc) { bfd *abfd = cached_abfd; struct bfd_data data; const char *name; char *aname = NULL; const char *filename; if (abfd == NULL) return; data.pc = (bfd_vma) pc; data.found = FALSE; bfd_map_over_sections(abfd, find_addr_sect, &data); if (!data.found) return; do { if (data.function) aname = bfd_demangle(abfd, data.function, DMGL_ANSI | DMGL_PARAMS); name = aname ? aname : data.function; filename = data.filename; if (filename) { char *end = os_strrchr(filename, '/'); int i = 0; while (*filename && *filename == prg_fname[i] && filename <= end) { filename++; i++; } } wpa_printf(MSG_INFO, " %s() %s:%u", name, filename, data.line); free(aname); data.found = bfd_find_inliner_info(abfd, &data.filename, &data.function, &data.line); } while (data.found); } static const char * wpa_trace_bfd_addr2func(void *pc) { bfd *abfd = cached_abfd; struct bfd_data data; if (abfd == NULL) return NULL; data.pc = (bfd_vma) pc; data.found = FALSE; bfd_map_over_sections(abfd, find_addr_sect, &data); if (!data.found) return NULL; return data.function; } static void wpa_trace_bfd_init(void) { if (!prg_fname) { get_prg_fname(); if (!prg_fname) return; } if (!cached_abfd) { cached_abfd = open_bfd(prg_fname); if (!cached_abfd) { wpa_printf(MSG_INFO, "Failed to open bfd"); return; } } read_syms(cached_abfd); if (!syms) { wpa_printf(MSG_INFO, "Failed to read symbols"); return; } } void wpa_trace_dump_funcname(const char *title, void *pc) { wpa_printf(MSG_INFO, "WPA_TRACE: %s: %p", title, pc); wpa_trace_bfd_init(); wpa_trace_bfd_addr(pc); } #else /* WPA_TRACE_BFD */ #define wpa_trace_bfd_init() do { } while (0) #define wpa_trace_bfd_addr(pc) do { } while (0) #define wpa_trace_bfd_addr2func(pc) NULL #endif /* WPA_TRACE_BFD */ void wpa_trace_dump_func(const char *title, void **btrace, int btrace_num) { char **sym; int i; enum { TRACE_HEAD, TRACE_RELEVANT, TRACE_TAIL } state; wpa_trace_bfd_init(); wpa_printf(MSG_INFO, "WPA_TRACE: %s - START", title); sym = backtrace_symbols(btrace, btrace_num); state = TRACE_HEAD; for (i = 0; i < btrace_num; i++) { const char *func = wpa_trace_bfd_addr2func(btrace[i]); if (state == TRACE_HEAD && func && (os_strcmp(func, "wpa_trace_add_ref_func") == 0 || os_strcmp(func, "wpa_trace_check_ref") == 0 || os_strcmp(func, "wpa_trace_show") == 0)) continue; if (state == TRACE_TAIL && sym && sym[i] && os_strstr(sym[i], "__libc_start_main")) break; if (state == TRACE_HEAD) state = TRACE_RELEVANT; if (sym) wpa_printf(MSG_INFO, "[%d]: %s", i, sym[i]); else wpa_printf(MSG_INFO, "[%d]: ?? [%p]", i, btrace[i]); wpa_trace_bfd_addr(btrace[i]); if (state == TRACE_RELEVANT && func && os_strcmp(func, "main") == 0) state = TRACE_TAIL; } free(sym); wpa_printf(MSG_INFO, "WPA_TRACE: %s - END", title); } void wpa_trace_show(const char *title) { struct info { WPA_TRACE_INFO } info; wpa_trace_record(&info); wpa_trace_dump(title, &info); } void wpa_trace_add_ref_func(struct wpa_trace_ref *ref, const void *addr) { if (addr == NULL) return; ref->addr = addr; wpa_trace_record(ref); dl_list_add(&active_references, &ref->list); } void wpa_trace_check_ref(const void *addr) { struct wpa_trace_ref *ref; dl_list_for_each(ref, &active_references, struct wpa_trace_ref, list) { if (addr != ref->addr) continue; wpa_trace_show("Freeing referenced memory"); wpa_trace_dump("Reference registration", ref); abort(); } } #endif /* WPA_TRACE */ wpa-2.1/src/utils/trace.h000066400000000000000000000034541227414666700153660ustar00rootroot00000000000000/* * Backtrace debugging * Copyright (c) 2009, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef TRACE_H #define TRACE_H #define WPA_TRACE_LEN 16 #ifdef WPA_TRACE #include #include "list.h" #define WPA_TRACE_INFO void *btrace[WPA_TRACE_LEN]; int btrace_num; struct wpa_trace_ref { struct dl_list list; const void *addr; WPA_TRACE_INFO }; #define WPA_TRACE_REF(name) struct wpa_trace_ref wpa_trace_ref_##name #define wpa_trace_dump(title, ptr) \ wpa_trace_dump_func((title), (ptr)->btrace, (ptr)->btrace_num) void wpa_trace_dump_func(const char *title, void **btrace, int btrace_num); #define wpa_trace_record(ptr) \ (ptr)->btrace_num = backtrace((ptr)->btrace, WPA_TRACE_LEN) void wpa_trace_show(const char *title); #define wpa_trace_add_ref(ptr, name, addr) \ wpa_trace_add_ref_func(&(ptr)->wpa_trace_ref_##name, (addr)) void wpa_trace_add_ref_func(struct wpa_trace_ref *ref, const void *addr); #define wpa_trace_remove_ref(ptr, name, addr) \ do { \ if ((addr)) \ dl_list_del(&(ptr)->wpa_trace_ref_##name.list); \ } while (0) void wpa_trace_check_ref(const void *addr); #else /* WPA_TRACE */ #define WPA_TRACE_INFO #define WPA_TRACE_REF(n) #define wpa_trace_dump(title, ptr) do { } while (0) #define wpa_trace_record(ptr) do { } while (0) #define wpa_trace_show(title) do { } while (0) #define wpa_trace_add_ref(ptr, name, addr) do { } while (0) #define wpa_trace_remove_ref(ptr, name, addr) do { } while (0) #define wpa_trace_check_ref(addr) do { } while (0) #endif /* WPA_TRACE */ #ifdef WPA_TRACE_BFD void wpa_trace_dump_funcname(const char *title, void *pc); #else /* WPA_TRACE_BFD */ #define wpa_trace_dump_funcname(title, pc) do { } while (0) #endif /* WPA_TRACE_BFD */ #endif /* TRACE_H */ wpa-2.1/src/utils/uuid.c000066400000000000000000000024101227414666700152200ustar00rootroot00000000000000/* * Universally Unique IDentifier (UUID) * Copyright (c) 2008, 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 "uuid.h" int uuid_str2bin(const char *str, u8 *bin) { const char *pos; u8 *opos; pos = str; opos = bin; if (hexstr2bin(pos, opos, 4)) return -1; pos += 8; opos += 4; if (*pos++ != '-' || hexstr2bin(pos, opos, 2)) return -1; pos += 4; opos += 2; if (*pos++ != '-' || hexstr2bin(pos, opos, 2)) return -1; pos += 4; opos += 2; if (*pos++ != '-' || hexstr2bin(pos, opos, 2)) return -1; pos += 4; opos += 2; if (*pos++ != '-' || hexstr2bin(pos, opos, 6)) return -1; return 0; } int uuid_bin2str(const u8 *bin, char *str, size_t max_len) { int len; len = os_snprintf(str, max_len, "%02x%02x%02x%02x-%02x%02x-%02x%02x-" "%02x%02x-%02x%02x%02x%02x%02x%02x", bin[0], bin[1], bin[2], bin[3], bin[4], bin[5], bin[6], bin[7], bin[8], bin[9], bin[10], bin[11], bin[12], bin[13], bin[14], bin[15]); if (len < 0 || (size_t) len >= max_len) return -1; return 0; } int is_nil_uuid(const u8 *uuid) { int i; for (i = 0; i < UUID_LEN; i++) if (uuid[i]) return 0; return 1; } wpa-2.1/src/utils/uuid.h000066400000000000000000000006341227414666700152330ustar00rootroot00000000000000/* * Universally Unique IDentifier (UUID) * Copyright (c) 2008, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef UUID_H #define UUID_H #define UUID_LEN 16 int uuid_str2bin(const char *str, u8 *bin); int uuid_bin2str(const u8 *bin, char *str, size_t max_len); int is_nil_uuid(const u8 *uuid); #endif /* UUID_H */ wpa-2.1/src/utils/wpa_debug.c000066400000000000000000000372051227414666700162210ustar00rootroot00000000000000/* * wpa_supplicant/hostapd / Debug prints * Copyright (c) 2002-2013, 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" #ifdef CONFIG_DEBUG_SYSLOG #include static int wpa_debug_syslog = 0; #endif /* CONFIG_DEBUG_SYSLOG */ #ifdef CONFIG_DEBUG_LINUX_TRACING #include #include #include #include #include static FILE *wpa_debug_tracing_file = NULL; #define WPAS_TRACE_PFX "wpas <%d>: " #endif /* CONFIG_DEBUG_LINUX_TRACING */ int wpa_debug_level = MSG_INFO; int wpa_debug_show_keys = 0; int wpa_debug_timestamp = 0; #ifdef CONFIG_ANDROID_LOG #include #ifndef ANDROID_LOG_NAME #define ANDROID_LOG_NAME "wpa_supplicant" #endif /* ANDROID_LOG_NAME */ static int wpa_to_android_level(int level) { if (level == MSG_ERROR) return ANDROID_LOG_ERROR; if (level == MSG_WARNING) return ANDROID_LOG_WARN; if (level == MSG_INFO) return ANDROID_LOG_INFO; return ANDROID_LOG_DEBUG; } #endif /* CONFIG_ANDROID_LOG */ #ifndef CONFIG_NO_STDOUT_DEBUG #ifdef CONFIG_DEBUG_FILE static FILE *out_file = NULL; #endif /* CONFIG_DEBUG_FILE */ void wpa_debug_print_timestamp(void) { #ifndef CONFIG_ANDROID_LOG struct os_time tv; if (!wpa_debug_timestamp) return; os_get_time(&tv); #ifdef CONFIG_DEBUG_FILE if (out_file) { fprintf(out_file, "%ld.%06u: ", (long) tv.sec, (unsigned int) tv.usec); } else #endif /* CONFIG_DEBUG_FILE */ printf("%ld.%06u: ", (long) tv.sec, (unsigned int) tv.usec); #endif /* CONFIG_ANDROID_LOG */ } #ifdef CONFIG_DEBUG_SYSLOG #ifndef LOG_HOSTAPD #define LOG_HOSTAPD LOG_DAEMON #endif /* LOG_HOSTAPD */ void wpa_debug_open_syslog(void) { openlog("wpa_supplicant", LOG_PID | LOG_NDELAY, LOG_HOSTAPD); wpa_debug_syslog++; } void wpa_debug_close_syslog(void) { if (wpa_debug_syslog) closelog(); } static int syslog_priority(int level) { switch (level) { case MSG_MSGDUMP: case MSG_DEBUG: return LOG_DEBUG; case MSG_INFO: return LOG_NOTICE; case MSG_WARNING: return LOG_WARNING; case MSG_ERROR: return LOG_ERR; } return LOG_INFO; } #endif /* CONFIG_DEBUG_SYSLOG */ #ifdef CONFIG_DEBUG_LINUX_TRACING int wpa_debug_open_linux_tracing(void) { int mounts, trace_fd; char buf[4096] = {}; ssize_t buflen; char *line, *tmp1, *path = NULL; mounts = open("/proc/mounts", O_RDONLY); if (mounts < 0) { printf("no /proc/mounts\n"); return -1; } buflen = read(mounts, buf, sizeof(buf) - 1); close(mounts); if (buflen < 0) { printf("failed to read /proc/mounts\n"); return -1; } line = strtok_r(buf, "\n", &tmp1); while (line) { char *tmp2, *tmp_path, *fstype; /* " ..." */ strtok_r(line, " ", &tmp2); tmp_path = strtok_r(NULL, " ", &tmp2); fstype = strtok_r(NULL, " ", &tmp2); if (strcmp(fstype, "debugfs") == 0) { path = tmp_path; break; } line = strtok_r(NULL, "\n", &tmp1); } if (path == NULL) { printf("debugfs mountpoint not found\n"); return -1; } snprintf(buf, sizeof(buf) - 1, "%s/tracing/trace_marker", path); trace_fd = open(buf, O_WRONLY); if (trace_fd < 0) { printf("failed to open trace_marker file\n"); return -1; } wpa_debug_tracing_file = fdopen(trace_fd, "w"); if (wpa_debug_tracing_file == NULL) { close(trace_fd); printf("failed to fdopen()\n"); return -1; } return 0; } void wpa_debug_close_linux_tracing(void) { if (wpa_debug_tracing_file == NULL) return; fclose(wpa_debug_tracing_file); wpa_debug_tracing_file = NULL; } #endif /* CONFIG_DEBUG_LINUX_TRACING */ /** * wpa_printf - conditional printf * @level: priority level (MSG_*) of the message * @fmt: printf format string, followed by optional arguments * * This function is used to print conditional debugging and error messages. The * output may be directed to stdout, stderr, and/or syslog based on * configuration. * * Note: New line '\n' is added to the end of the text when printing to stdout. */ void wpa_printf(int level, const char *fmt, ...) { va_list ap; va_start(ap, fmt); if (level >= wpa_debug_level) { #ifdef CONFIG_ANDROID_LOG __android_log_vprint(wpa_to_android_level(level), ANDROID_LOG_NAME, fmt, ap); #else /* CONFIG_ANDROID_LOG */ #ifdef CONFIG_DEBUG_SYSLOG if (wpa_debug_syslog) { vsyslog(syslog_priority(level), fmt, ap); } else { #endif /* CONFIG_DEBUG_SYSLOG */ wpa_debug_print_timestamp(); #ifdef CONFIG_DEBUG_FILE if (out_file) { vfprintf(out_file, fmt, ap); fprintf(out_file, "\n"); } else { #endif /* CONFIG_DEBUG_FILE */ vprintf(fmt, ap); printf("\n"); #ifdef CONFIG_DEBUG_FILE } #endif /* CONFIG_DEBUG_FILE */ #ifdef CONFIG_DEBUG_SYSLOG } #endif /* CONFIG_DEBUG_SYSLOG */ #endif /* CONFIG_ANDROID_LOG */ } va_end(ap); #ifdef CONFIG_DEBUG_LINUX_TRACING if (wpa_debug_tracing_file != NULL) { va_start(ap, fmt); fprintf(wpa_debug_tracing_file, WPAS_TRACE_PFX, level); vfprintf(wpa_debug_tracing_file, fmt, ap); fprintf(wpa_debug_tracing_file, "\n"); fflush(wpa_debug_tracing_file); va_end(ap); } #endif /* CONFIG_DEBUG_LINUX_TRACING */ } static void _wpa_hexdump(int level, const char *title, const u8 *buf, size_t len, int show) { size_t i; #ifdef CONFIG_DEBUG_LINUX_TRACING if (wpa_debug_tracing_file != NULL) { fprintf(wpa_debug_tracing_file, WPAS_TRACE_PFX "%s - hexdump(len=%lu):", level, title, (unsigned long) len); if (buf == NULL) { fprintf(wpa_debug_tracing_file, " [NULL]\n"); } else if (!show) { fprintf(wpa_debug_tracing_file, " [REMOVED]\n"); } else { for (i = 0; i < len; i++) fprintf(wpa_debug_tracing_file, " %02x", buf[i]); } fflush(wpa_debug_tracing_file); } #endif /* CONFIG_DEBUG_LINUX_TRACING */ if (level < wpa_debug_level) return; #ifdef CONFIG_ANDROID_LOG { const char *display; char *strbuf = NULL; size_t slen = len; if (buf == NULL) { display = " [NULL]"; } else if (len == 0) { display = ""; } else if (show && len) { /* Limit debug message length for Android log */ if (slen > 32) slen = 32; strbuf = os_malloc(1 + 3 * slen); if (strbuf == NULL) { wpa_printf(MSG_ERROR, "wpa_hexdump: Failed to " "allocate message buffer"); return; } for (i = 0; i < slen; i++) os_snprintf(&strbuf[i * 3], 4, " %02x", buf[i]); display = strbuf; } else { display = " [REMOVED]"; } __android_log_print(wpa_to_android_level(level), ANDROID_LOG_NAME, "%s - hexdump(len=%lu):%s%s", title, (long unsigned int) len, display, len > slen ? " ..." : ""); os_free(strbuf); return; } #else /* CONFIG_ANDROID_LOG */ #ifdef CONFIG_DEBUG_SYSLOG if (wpa_debug_syslog) { const char *display; char *strbuf = NULL; if (buf == NULL) { display = " [NULL]"; } else if (len == 0) { display = ""; } else if (show && len) { strbuf = os_malloc(1 + 3 * len); if (strbuf == NULL) { wpa_printf(MSG_ERROR, "wpa_hexdump: Failed to " "allocate message buffer"); return; } for (i = 0; i < len; i++) os_snprintf(&strbuf[i * 3], 4, " %02x", buf[i]); display = strbuf; } else { display = " [REMOVED]"; } syslog(syslog_priority(level), "%s - hexdump(len=%lu):%s", title, (unsigned long) len, display); os_free(strbuf); return; } #endif /* CONFIG_DEBUG_SYSLOG */ wpa_debug_print_timestamp(); #ifdef CONFIG_DEBUG_FILE if (out_file) { fprintf(out_file, "%s - hexdump(len=%lu):", title, (unsigned long) len); if (buf == NULL) { fprintf(out_file, " [NULL]"); } else if (show) { for (i = 0; i < len; i++) fprintf(out_file, " %02x", buf[i]); } else { fprintf(out_file, " [REMOVED]"); } fprintf(out_file, "\n"); } else { #endif /* CONFIG_DEBUG_FILE */ printf("%s - hexdump(len=%lu):", title, (unsigned long) len); if (buf == NULL) { printf(" [NULL]"); } else if (show) { for (i = 0; i < len; i++) printf(" %02x", buf[i]); } else { printf(" [REMOVED]"); } printf("\n"); #ifdef CONFIG_DEBUG_FILE } #endif /* CONFIG_DEBUG_FILE */ #endif /* CONFIG_ANDROID_LOG */ } void wpa_hexdump(int level, const char *title, const void *buf, size_t len) { _wpa_hexdump(level, title, buf, len, 1); } void wpa_hexdump_key(int level, const char *title, const void *buf, size_t len) { _wpa_hexdump(level, title, buf, len, wpa_debug_show_keys); } static void _wpa_hexdump_ascii(int level, const char *title, const void *buf, size_t len, int show) { size_t i, llen; const u8 *pos = buf; const size_t line_len = 16; #ifdef CONFIG_DEBUG_LINUX_TRACING if (wpa_debug_tracing_file != NULL) { fprintf(wpa_debug_tracing_file, WPAS_TRACE_PFX "%s - hexdump_ascii(len=%lu):", level, title, (unsigned long) len); if (buf == NULL) { fprintf(wpa_debug_tracing_file, " [NULL]\n"); } else if (!show) { fprintf(wpa_debug_tracing_file, " [REMOVED]\n"); } else { /* can do ascii processing in userspace */ for (i = 0; i < len; i++) fprintf(wpa_debug_tracing_file, " %02x", pos[i]); } fflush(wpa_debug_tracing_file); } #endif /* CONFIG_DEBUG_LINUX_TRACING */ if (level < wpa_debug_level) return; #ifdef CONFIG_ANDROID_LOG _wpa_hexdump(level, title, buf, len, show); #else /* CONFIG_ANDROID_LOG */ wpa_debug_print_timestamp(); #ifdef CONFIG_DEBUG_FILE if (out_file) { if (!show) { fprintf(out_file, "%s - hexdump_ascii(len=%lu): [REMOVED]\n", title, (unsigned long) len); return; } if (buf == NULL) { fprintf(out_file, "%s - hexdump_ascii(len=%lu): [NULL]\n", title, (unsigned long) len); return; } fprintf(out_file, "%s - hexdump_ascii(len=%lu):\n", title, (unsigned long) len); while (len) { llen = len > line_len ? line_len : len; fprintf(out_file, " "); for (i = 0; i < llen; i++) fprintf(out_file, " %02x", pos[i]); for (i = llen; i < line_len; i++) fprintf(out_file, " "); fprintf(out_file, " "); for (i = 0; i < llen; i++) { if (isprint(pos[i])) fprintf(out_file, "%c", pos[i]); else fprintf(out_file, "_"); } for (i = llen; i < line_len; i++) fprintf(out_file, " "); fprintf(out_file, "\n"); pos += llen; len -= llen; } } else { #endif /* CONFIG_DEBUG_FILE */ if (!show) { printf("%s - hexdump_ascii(len=%lu): [REMOVED]\n", title, (unsigned long) len); return; } if (buf == NULL) { printf("%s - hexdump_ascii(len=%lu): [NULL]\n", title, (unsigned long) len); return; } printf("%s - hexdump_ascii(len=%lu):\n", title, (unsigned long) len); while (len) { llen = len > line_len ? line_len : len; printf(" "); for (i = 0; i < llen; i++) printf(" %02x", pos[i]); for (i = llen; i < line_len; i++) printf(" "); printf(" "); for (i = 0; i < llen; i++) { if (isprint(pos[i])) printf("%c", pos[i]); else printf("_"); } for (i = llen; i < line_len; i++) printf(" "); printf("\n"); pos += llen; len -= llen; } #ifdef CONFIG_DEBUG_FILE } #endif /* CONFIG_DEBUG_FILE */ #endif /* CONFIG_ANDROID_LOG */ } void wpa_hexdump_ascii(int level, const char *title, const void *buf, size_t len) { _wpa_hexdump_ascii(level, title, buf, len, 1); } void wpa_hexdump_ascii_key(int level, const char *title, const void *buf, size_t len) { _wpa_hexdump_ascii(level, title, buf, len, wpa_debug_show_keys); } #ifdef CONFIG_DEBUG_FILE static char *last_path = NULL; #endif /* CONFIG_DEBUG_FILE */ int wpa_debug_reopen_file(void) { #ifdef CONFIG_DEBUG_FILE int rv; if (last_path) { char *tmp = os_strdup(last_path); wpa_debug_close_file(); rv = wpa_debug_open_file(tmp); os_free(tmp); } else { wpa_printf(MSG_ERROR, "Last-path was not set, cannot " "re-open log file."); rv = -1; } return rv; #else /* CONFIG_DEBUG_FILE */ return 0; #endif /* CONFIG_DEBUG_FILE */ } int wpa_debug_open_file(const char *path) { #ifdef CONFIG_DEBUG_FILE if (!path) return 0; if (last_path == NULL || os_strcmp(last_path, path) != 0) { /* Save our path to enable re-open */ os_free(last_path); last_path = os_strdup(path); } out_file = fopen(path, "a"); if (out_file == NULL) { wpa_printf(MSG_ERROR, "wpa_debug_open_file: Failed to open " "output file, using standard output"); return -1; } #ifndef _WIN32 setvbuf(out_file, NULL, _IOLBF, 0); #endif /* _WIN32 */ #endif /* CONFIG_DEBUG_FILE */ return 0; } void wpa_debug_close_file(void) { #ifdef CONFIG_DEBUG_FILE if (!out_file) return; fclose(out_file); out_file = NULL; os_free(last_path); last_path = NULL; #endif /* CONFIG_DEBUG_FILE */ } #endif /* CONFIG_NO_STDOUT_DEBUG */ #ifndef CONFIG_NO_WPA_MSG static wpa_msg_cb_func wpa_msg_cb = NULL; void wpa_msg_register_cb(wpa_msg_cb_func func) { wpa_msg_cb = func; } static wpa_msg_get_ifname_func wpa_msg_ifname_cb = NULL; void wpa_msg_register_ifname_cb(wpa_msg_get_ifname_func func) { wpa_msg_ifname_cb = func; } void wpa_msg(void *ctx, int level, const char *fmt, ...) { va_list ap; char *buf; const int buflen = 2048; int len; char prefix[130]; buf = os_malloc(buflen); if (buf == NULL) { wpa_printf(MSG_ERROR, "wpa_msg: Failed to allocate message " "buffer"); return; } va_start(ap, fmt); prefix[0] = '\0'; if (wpa_msg_ifname_cb) { const char *ifname = wpa_msg_ifname_cb(ctx); if (ifname) { int res = os_snprintf(prefix, sizeof(prefix), "%s: ", ifname); if (res < 0 || res >= (int) sizeof(prefix)) prefix[0] = '\0'; } } len = vsnprintf(buf, buflen, fmt, ap); va_end(ap); wpa_printf(level, "%s%s", prefix, buf); if (wpa_msg_cb) wpa_msg_cb(ctx, level, 0, buf, len); os_free(buf); } void wpa_msg_ctrl(void *ctx, int level, const char *fmt, ...) { va_list ap; char *buf; const int buflen = 2048; int len; if (!wpa_msg_cb) return; buf = os_malloc(buflen); if (buf == NULL) { wpa_printf(MSG_ERROR, "wpa_msg_ctrl: Failed to allocate " "message buffer"); return; } va_start(ap, fmt); len = vsnprintf(buf, buflen, fmt, ap); va_end(ap); wpa_msg_cb(ctx, level, 0, buf, len); os_free(buf); } void wpa_msg_global(void *ctx, int level, const char *fmt, ...) { va_list ap; char *buf; const int buflen = 2048; int len; buf = os_malloc(buflen); if (buf == NULL) { wpa_printf(MSG_ERROR, "wpa_msg_global: Failed to allocate " "message buffer"); return; } va_start(ap, fmt); len = vsnprintf(buf, buflen, fmt, ap); va_end(ap); wpa_printf(level, "%s", buf); if (wpa_msg_cb) wpa_msg_cb(ctx, level, 1, buf, len); os_free(buf); } void wpa_msg_no_global(void *ctx, int level, const char *fmt, ...) { va_list ap; char *buf; const int buflen = 2048; int len; buf = os_malloc(buflen); if (buf == NULL) { wpa_printf(MSG_ERROR, "wpa_msg_no_global: Failed to allocate " "message buffer"); return; } va_start(ap, fmt); len = vsnprintf(buf, buflen, fmt, ap); va_end(ap); wpa_printf(level, "%s", buf); if (wpa_msg_cb) wpa_msg_cb(ctx, level, 2, buf, len); os_free(buf); } #endif /* CONFIG_NO_WPA_MSG */ #ifndef CONFIG_NO_HOSTAPD_LOGGER static hostapd_logger_cb_func hostapd_logger_cb = NULL; void hostapd_logger_register_cb(hostapd_logger_cb_func func) { hostapd_logger_cb = func; } void hostapd_logger(void *ctx, const u8 *addr, unsigned int module, int level, const char *fmt, ...) { va_list ap; char *buf; const int buflen = 2048; int len; buf = os_malloc(buflen); if (buf == NULL) { wpa_printf(MSG_ERROR, "hostapd_logger: Failed to allocate " "message buffer"); return; } va_start(ap, fmt); len = vsnprintf(buf, buflen, fmt, ap); va_end(ap); if (hostapd_logger_cb) hostapd_logger_cb(ctx, addr, module, level, buf, len); else if (addr) wpa_printf(MSG_DEBUG, "hostapd_logger: STA " MACSTR " - %s", MAC2STR(addr), buf); else wpa_printf(MSG_DEBUG, "hostapd_logger: %s", buf); os_free(buf); } #endif /* CONFIG_NO_HOSTAPD_LOGGER */ wpa-2.1/src/utils/wpa_debug.h000066400000000000000000000253111227414666700162210ustar00rootroot00000000000000/* * wpa_supplicant/hostapd / Debug prints * Copyright (c) 2002-2013, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef WPA_DEBUG_H #define WPA_DEBUG_H #include "wpabuf.h" extern int wpa_debug_level; extern int wpa_debug_show_keys; extern int wpa_debug_timestamp; /* Debugging function - conditional printf and hex dump. Driver wrappers can * use these for debugging purposes. */ enum { MSG_EXCESSIVE, MSG_MSGDUMP, MSG_DEBUG, MSG_INFO, MSG_WARNING, MSG_ERROR }; #ifdef CONFIG_NO_STDOUT_DEBUG #define wpa_debug_print_timestamp() do { } while (0) #define wpa_printf(args...) do { } while (0) #define wpa_hexdump(l,t,b,le) do { } while (0) #define wpa_hexdump_buf(l,t,b) do { } while (0) #define wpa_hexdump_key(l,t,b,le) do { } while (0) #define wpa_hexdump_buf_key(l,t,b) do { } while (0) #define wpa_hexdump_ascii(l,t,b,le) do { } while (0) #define wpa_hexdump_ascii_key(l,t,b,le) do { } while (0) #define wpa_debug_open_file(p) do { } while (0) #define wpa_debug_close_file() do { } while (0) #define wpa_dbg(args...) do { } while (0) static inline int wpa_debug_reopen_file(void) { return 0; } #else /* CONFIG_NO_STDOUT_DEBUG */ int wpa_debug_open_file(const char *path); int wpa_debug_reopen_file(void); void wpa_debug_close_file(void); /** * wpa_debug_printf_timestamp - Print timestamp for debug output * * This function prints a timestamp in seconds_from_1970.microsoconds * format if debug output has been configured to include timestamps in debug * messages. */ void wpa_debug_print_timestamp(void); /** * wpa_printf - conditional printf * @level: priority level (MSG_*) of the message * @fmt: printf format string, followed by optional arguments * * This function is used to print conditional debugging and error messages. The * output may be directed to stdout, stderr, and/or syslog based on * configuration. * * Note: New line '\n' is added to the end of the text when printing to stdout. */ void wpa_printf(int level, const char *fmt, ...) PRINTF_FORMAT(2, 3); /** * wpa_hexdump - conditional hex dump * @level: priority level (MSG_*) of the message * @title: title of for the message * @buf: data buffer to be dumped * @len: length of the buf * * This function is used to print conditional debugging and error messages. The * output may be directed to stdout, stderr, and/or syslog based on * configuration. The contents of buf is printed out has hex dump. */ void wpa_hexdump(int level, const char *title, const void *buf, size_t len); static inline void wpa_hexdump_buf(int level, const char *title, const struct wpabuf *buf) { wpa_hexdump(level, title, buf ? wpabuf_head(buf) : NULL, buf ? wpabuf_len(buf) : 0); } /** * wpa_hexdump_key - conditional hex dump, hide keys * @level: priority level (MSG_*) of the message * @title: title of for the message * @buf: data buffer to be dumped * @len: length of the buf * * This function is used to print conditional debugging and error messages. The * output may be directed to stdout, stderr, and/or syslog based on * configuration. The contents of buf is printed out has hex dump. This works * like wpa_hexdump(), but by default, does not include secret keys (passwords, * etc.) in debug output. */ void wpa_hexdump_key(int level, const char *title, const void *buf, size_t len); static inline void wpa_hexdump_buf_key(int level, const char *title, const struct wpabuf *buf) { wpa_hexdump_key(level, title, buf ? wpabuf_head(buf) : NULL, buf ? wpabuf_len(buf) : 0); } /** * wpa_hexdump_ascii - conditional hex dump * @level: priority level (MSG_*) of the message * @title: title of for the message * @buf: data buffer to be dumped * @len: length of the buf * * This function is used to print conditional debugging and error messages. The * output may be directed to stdout, stderr, and/or syslog based on * configuration. The contents of buf is printed out has hex dump with both * the hex numbers and ASCII characters (for printable range) are shown. 16 * bytes per line will be shown. */ void wpa_hexdump_ascii(int level, const char *title, const void *buf, size_t len); /** * wpa_hexdump_ascii_key - conditional hex dump, hide keys * @level: priority level (MSG_*) of the message * @title: title of for the message * @buf: data buffer to be dumped * @len: length of the buf * * This function is used to print conditional debugging and error messages. The * output may be directed to stdout, stderr, and/or syslog based on * configuration. The contents of buf is printed out has hex dump with both * the hex numbers and ASCII characters (for printable range) are shown. 16 * bytes per line will be shown. This works like wpa_hexdump_ascii(), but by * default, does not include secret keys (passwords, etc.) in debug output. */ void wpa_hexdump_ascii_key(int level, const char *title, const void *buf, size_t len); /* * wpa_dbg() behaves like wpa_msg(), but it can be removed from build to reduce * binary size. As such, it should be used with debugging messages that are not * needed in the control interface while wpa_msg() has to be used for anything * that needs to shown to control interface monitors. */ #define wpa_dbg(args...) wpa_msg(args) #endif /* CONFIG_NO_STDOUT_DEBUG */ #ifdef CONFIG_NO_WPA_MSG #define wpa_msg(args...) do { } while (0) #define wpa_msg_ctrl(args...) do { } while (0) #define wpa_msg_global(args...) do { } while (0) #define wpa_msg_no_global(args...) do { } while (0) #define wpa_msg_register_cb(f) do { } while (0) #define wpa_msg_register_ifname_cb(f) do { } while (0) #else /* CONFIG_NO_WPA_MSG */ /** * wpa_msg - Conditional printf for default target and ctrl_iface monitors * @ctx: Pointer to context data; this is the ctx variable registered * with struct wpa_driver_ops::init() * @level: priority level (MSG_*) of the message * @fmt: printf format string, followed by optional arguments * * This function is used to print conditional debugging and error messages. The * output may be directed to stdout, stderr, and/or syslog based on * configuration. This function is like wpa_printf(), but it also sends the * same message to all attached ctrl_iface monitors. * * Note: New line '\n' is added to the end of the text when printing to stdout. */ void wpa_msg(void *ctx, int level, const char *fmt, ...) PRINTF_FORMAT(3, 4); /** * wpa_msg_ctrl - Conditional printf for ctrl_iface monitors * @ctx: Pointer to context data; this is the ctx variable registered * with struct wpa_driver_ops::init() * @level: priority level (MSG_*) of the message * @fmt: printf format string, followed by optional arguments * * This function is used to print conditional debugging and error messages. * This function is like wpa_msg(), but it sends the output only to the * attached ctrl_iface monitors. In other words, it can be used for frequent * events that do not need to be sent to syslog. */ void wpa_msg_ctrl(void *ctx, int level, const char *fmt, ...) PRINTF_FORMAT(3, 4); /** * wpa_msg_global - Global printf for ctrl_iface monitors * @ctx: Pointer to context data; this is the ctx variable registered * with struct wpa_driver_ops::init() * @level: priority level (MSG_*) of the message * @fmt: printf format string, followed by optional arguments * * This function is used to print conditional debugging and error messages. * This function is like wpa_msg(), but it sends the output as a global event, * i.e., without being specific to an interface. For backwards compatibility, * an old style event is also delivered on one of the interfaces (the one * specified by the context data). */ void wpa_msg_global(void *ctx, int level, const char *fmt, ...) PRINTF_FORMAT(3, 4); /** * wpa_msg_no_global - Conditional printf for ctrl_iface monitors * @ctx: Pointer to context data; this is the ctx variable registered * with struct wpa_driver_ops::init() * @level: priority level (MSG_*) of the message * @fmt: printf format string, followed by optional arguments * * This function is used to print conditional debugging and error messages. * This function is like wpa_msg(), but it does not send the output as a global * event. */ void wpa_msg_no_global(void *ctx, int level, const char *fmt, ...) PRINTF_FORMAT(3, 4); typedef void (*wpa_msg_cb_func)(void *ctx, int level, int global, const char *txt, size_t len); /** * wpa_msg_register_cb - Register callback function for wpa_msg() messages * @func: Callback function (%NULL to unregister) */ void wpa_msg_register_cb(wpa_msg_cb_func func); typedef const char * (*wpa_msg_get_ifname_func)(void *ctx); void wpa_msg_register_ifname_cb(wpa_msg_get_ifname_func func); #endif /* CONFIG_NO_WPA_MSG */ #ifdef CONFIG_NO_HOSTAPD_LOGGER #define hostapd_logger(args...) do { } while (0) #define hostapd_logger_register_cb(f) do { } while (0) #else /* CONFIG_NO_HOSTAPD_LOGGER */ void hostapd_logger(void *ctx, const u8 *addr, unsigned int module, int level, const char *fmt, ...) PRINTF_FORMAT(5, 6); typedef void (*hostapd_logger_cb_func)(void *ctx, const u8 *addr, unsigned int module, int level, const char *txt, size_t len); /** * hostapd_logger_register_cb - Register callback function for hostapd_logger() * @func: Callback function (%NULL to unregister) */ void hostapd_logger_register_cb(hostapd_logger_cb_func func); #endif /* CONFIG_NO_HOSTAPD_LOGGER */ #define HOSTAPD_MODULE_IEEE80211 0x00000001 #define HOSTAPD_MODULE_IEEE8021X 0x00000002 #define HOSTAPD_MODULE_RADIUS 0x00000004 #define HOSTAPD_MODULE_WPA 0x00000008 #define HOSTAPD_MODULE_DRIVER 0x00000010 #define HOSTAPD_MODULE_IAPP 0x00000020 #define HOSTAPD_MODULE_MLME 0x00000040 enum hostapd_logger_level { HOSTAPD_LEVEL_DEBUG_VERBOSE = 0, HOSTAPD_LEVEL_DEBUG = 1, HOSTAPD_LEVEL_INFO = 2, HOSTAPD_LEVEL_NOTICE = 3, HOSTAPD_LEVEL_WARNING = 4 }; #ifdef CONFIG_DEBUG_SYSLOG void wpa_debug_open_syslog(void); void wpa_debug_close_syslog(void); #else /* CONFIG_DEBUG_SYSLOG */ static inline void wpa_debug_open_syslog(void) { } static inline void wpa_debug_close_syslog(void) { } #endif /* CONFIG_DEBUG_SYSLOG */ #ifdef CONFIG_DEBUG_LINUX_TRACING int wpa_debug_open_linux_tracing(void); void wpa_debug_close_linux_tracing(void); #else /* CONFIG_DEBUG_LINUX_TRACING */ static inline int wpa_debug_open_linux_tracing(void) { return 0; } static inline void wpa_debug_close_linux_tracing(void) { } #endif /* CONFIG_DEBUG_LINUX_TRACING */ #ifdef EAPOL_TEST #define WPA_ASSERT(a) \ do { \ if (!(a)) { \ printf("WPA_ASSERT FAILED '" #a "' " \ "%s %s:%d\n", \ __FUNCTION__, __FILE__, __LINE__); \ exit(1); \ } \ } while (0) #else #define WPA_ASSERT(a) do { } while (0) #endif #endif /* WPA_DEBUG_H */ wpa-2.1/src/utils/wpabuf.c000066400000000000000000000153321227414666700155450ustar00rootroot00000000000000/* * Dynamic data buffer * Copyright (c) 2007-2012, 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 "trace.h" #include "wpabuf.h" #ifdef WPA_TRACE #define WPABUF_MAGIC 0x51a974e3 struct wpabuf_trace { unsigned int magic; }; static struct wpabuf_trace * wpabuf_get_trace(const struct wpabuf *buf) { return (struct wpabuf_trace *) ((const u8 *) buf - sizeof(struct wpabuf_trace)); } #endif /* WPA_TRACE */ static void wpabuf_overflow(const struct wpabuf *buf, size_t len) { #ifdef WPA_TRACE struct wpabuf_trace *trace = wpabuf_get_trace(buf); if (trace->magic != WPABUF_MAGIC) { wpa_printf(MSG_ERROR, "wpabuf: invalid magic %x", trace->magic); } #endif /* WPA_TRACE */ wpa_printf(MSG_ERROR, "wpabuf %p (size=%lu used=%lu) overflow len=%lu", buf, (unsigned long) buf->size, (unsigned long) buf->used, (unsigned long) len); wpa_trace_show("wpabuf overflow"); abort(); } int wpabuf_resize(struct wpabuf **_buf, size_t add_len) { struct wpabuf *buf = *_buf; #ifdef WPA_TRACE struct wpabuf_trace *trace; #endif /* WPA_TRACE */ if (buf == NULL) { *_buf = wpabuf_alloc(add_len); return *_buf == NULL ? -1 : 0; } #ifdef WPA_TRACE trace = wpabuf_get_trace(buf); if (trace->magic != WPABUF_MAGIC) { wpa_printf(MSG_ERROR, "wpabuf: invalid magic %x", trace->magic); wpa_trace_show("wpabuf_resize invalid magic"); abort(); } #endif /* WPA_TRACE */ if (buf->used + add_len > buf->size) { unsigned char *nbuf; if (buf->flags & WPABUF_FLAG_EXT_DATA) { nbuf = os_realloc(buf->buf, buf->used + add_len); if (nbuf == NULL) return -1; os_memset(nbuf + buf->used, 0, add_len); buf->buf = nbuf; } else { #ifdef WPA_TRACE nbuf = os_realloc(trace, sizeof(struct wpabuf_trace) + sizeof(struct wpabuf) + buf->used + add_len); if (nbuf == NULL) return -1; trace = (struct wpabuf_trace *) nbuf; buf = (struct wpabuf *) (trace + 1); os_memset(nbuf + sizeof(struct wpabuf_trace) + sizeof(struct wpabuf) + buf->used, 0, add_len); #else /* WPA_TRACE */ nbuf = os_realloc(buf, sizeof(struct wpabuf) + buf->used + add_len); if (nbuf == NULL) return -1; buf = (struct wpabuf *) nbuf; os_memset(nbuf + sizeof(struct wpabuf) + buf->used, 0, add_len); #endif /* WPA_TRACE */ buf->buf = (u8 *) (buf + 1); *_buf = buf; } buf->size = buf->used + add_len; } return 0; } /** * wpabuf_alloc - Allocate a wpabuf of the given size * @len: Length for the allocated buffer * Returns: Buffer to the allocated wpabuf or %NULL on failure */ struct wpabuf * wpabuf_alloc(size_t len) { #ifdef WPA_TRACE struct wpabuf_trace *trace = os_zalloc(sizeof(struct wpabuf_trace) + sizeof(struct wpabuf) + len); struct wpabuf *buf; if (trace == NULL) return NULL; trace->magic = WPABUF_MAGIC; buf = (struct wpabuf *) (trace + 1); #else /* WPA_TRACE */ struct wpabuf *buf = os_zalloc(sizeof(struct wpabuf) + len); if (buf == NULL) return NULL; #endif /* WPA_TRACE */ buf->size = len; buf->buf = (u8 *) (buf + 1); return buf; } struct wpabuf * wpabuf_alloc_ext_data(u8 *data, size_t len) { #ifdef WPA_TRACE struct wpabuf_trace *trace = os_zalloc(sizeof(struct wpabuf_trace) + sizeof(struct wpabuf)); struct wpabuf *buf; if (trace == NULL) return NULL; trace->magic = WPABUF_MAGIC; buf = (struct wpabuf *) (trace + 1); #else /* WPA_TRACE */ struct wpabuf *buf = os_zalloc(sizeof(struct wpabuf)); if (buf == NULL) return NULL; #endif /* WPA_TRACE */ buf->size = len; buf->used = len; buf->buf = data; buf->flags |= WPABUF_FLAG_EXT_DATA; return buf; } struct wpabuf * wpabuf_alloc_copy(const void *data, size_t len) { struct wpabuf *buf = wpabuf_alloc(len); if (buf) wpabuf_put_data(buf, data, len); return buf; } struct wpabuf * wpabuf_dup(const struct wpabuf *src) { struct wpabuf *buf = wpabuf_alloc(wpabuf_len(src)); if (buf) wpabuf_put_data(buf, wpabuf_head(src), wpabuf_len(src)); return buf; } /** * wpabuf_free - Free a wpabuf * @buf: wpabuf buffer */ void wpabuf_free(struct wpabuf *buf) { #ifdef WPA_TRACE struct wpabuf_trace *trace; if (buf == NULL) return; trace = wpabuf_get_trace(buf); if (trace->magic != WPABUF_MAGIC) { wpa_printf(MSG_ERROR, "wpabuf_free: invalid magic %x", trace->magic); wpa_trace_show("wpabuf_free magic mismatch"); abort(); } if (buf->flags & WPABUF_FLAG_EXT_DATA) os_free(buf->buf); os_free(trace); #else /* WPA_TRACE */ if (buf == NULL) return; if (buf->flags & WPABUF_FLAG_EXT_DATA) os_free(buf->buf); os_free(buf); #endif /* WPA_TRACE */ } void * wpabuf_put(struct wpabuf *buf, size_t len) { void *tmp = wpabuf_mhead_u8(buf) + wpabuf_len(buf); buf->used += len; if (buf->used > buf->size) { wpabuf_overflow(buf, len); } return tmp; } /** * wpabuf_concat - Concatenate two buffers into a newly allocated one * @a: First buffer * @b: Second buffer * Returns: wpabuf with concatenated a + b data or %NULL on failure * * Both buffers a and b will be freed regardless of the return value. Input * buffers can be %NULL which is interpreted as an empty buffer. */ struct wpabuf * wpabuf_concat(struct wpabuf *a, struct wpabuf *b) { struct wpabuf *n = NULL; size_t len = 0; if (b == NULL) return a; if (a) len += wpabuf_len(a); if (b) len += wpabuf_len(b); n = wpabuf_alloc(len); if (n) { if (a) wpabuf_put_buf(n, a); if (b) wpabuf_put_buf(n, b); } wpabuf_free(a); wpabuf_free(b); return n; } /** * wpabuf_zeropad - Pad buffer with 0x00 octets (prefix) to specified length * @buf: Buffer to be padded * @len: Length for the padded buffer * Returns: wpabuf padded to len octets or %NULL on failure * * If buf is longer than len octets or of same size, it will be returned as-is. * Otherwise a new buffer is allocated and prefixed with 0x00 octets followed * by the source data. The source buffer will be freed on error, i.e., caller * will only be responsible on freeing the returned buffer. If buf is %NULL, * %NULL will be returned. */ struct wpabuf * wpabuf_zeropad(struct wpabuf *buf, size_t len) { struct wpabuf *ret; size_t blen; if (buf == NULL) return NULL; blen = wpabuf_len(buf); if (blen >= len) return buf; ret = wpabuf_alloc(len); if (ret) { os_memset(wpabuf_put(ret, len - blen), 0, len - blen); wpabuf_put_buf(ret, buf); } wpabuf_free(buf); return ret; } void wpabuf_printf(struct wpabuf *buf, char *fmt, ...) { va_list ap; void *tmp = wpabuf_mhead_u8(buf) + wpabuf_len(buf); int res; va_start(ap, fmt); res = vsnprintf(tmp, buf->size - buf->used, fmt, ap); va_end(ap); if (res < 0 || (size_t) res >= buf->size - buf->used) wpabuf_overflow(buf, res); buf->used += res; } wpa-2.1/src/utils/wpabuf.h000066400000000000000000000077641227414666700155640ustar00rootroot00000000000000/* * Dynamic data buffer * Copyright (c) 2007-2012, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef WPABUF_H #define WPABUF_H /* wpabuf::buf is a pointer to external data */ #define WPABUF_FLAG_EXT_DATA BIT(0) /* * Internal data structure for wpabuf. Please do not touch this directly from * elsewhere. This is only defined in header file to allow inline functions * from this file to access data. */ struct wpabuf { size_t size; /* total size of the allocated buffer */ size_t used; /* length of data in the buffer */ u8 *buf; /* pointer to the head of the buffer */ unsigned int flags; /* optionally followed by the allocated buffer */ }; int wpabuf_resize(struct wpabuf **buf, size_t add_len); struct wpabuf * wpabuf_alloc(size_t len); struct wpabuf * wpabuf_alloc_ext_data(u8 *data, size_t len); struct wpabuf * wpabuf_alloc_copy(const void *data, size_t len); struct wpabuf * wpabuf_dup(const struct wpabuf *src); void wpabuf_free(struct wpabuf *buf); void * wpabuf_put(struct wpabuf *buf, size_t len); struct wpabuf * wpabuf_concat(struct wpabuf *a, struct wpabuf *b); struct wpabuf * wpabuf_zeropad(struct wpabuf *buf, size_t len); void wpabuf_printf(struct wpabuf *buf, char *fmt, ...) PRINTF_FORMAT(2, 3); /** * wpabuf_size - Get the currently allocated size of a wpabuf buffer * @buf: wpabuf buffer * Returns: Currently allocated size of the buffer */ static inline size_t wpabuf_size(const struct wpabuf *buf) { return buf->size; } /** * wpabuf_len - Get the current length of a wpabuf buffer data * @buf: wpabuf buffer * Returns: Currently used length of the buffer */ static inline size_t wpabuf_len(const struct wpabuf *buf) { return buf->used; } /** * wpabuf_tailroom - Get size of available tail room in the end of the buffer * @buf: wpabuf buffer * Returns: Tail room (in bytes) of available space in the end of the buffer */ static inline size_t wpabuf_tailroom(const struct wpabuf *buf) { return buf->size - buf->used; } /** * wpabuf_head - Get pointer to the head of the buffer data * @buf: wpabuf buffer * Returns: Pointer to the head of the buffer data */ static inline const void * wpabuf_head(const struct wpabuf *buf) { return buf->buf; } static inline const u8 * wpabuf_head_u8(const struct wpabuf *buf) { return wpabuf_head(buf); } /** * wpabuf_mhead - Get modifiable pointer to the head of the buffer data * @buf: wpabuf buffer * Returns: Pointer to the head of the buffer data */ static inline void * wpabuf_mhead(struct wpabuf *buf) { return buf->buf; } static inline u8 * wpabuf_mhead_u8(struct wpabuf *buf) { return wpabuf_mhead(buf); } static inline void wpabuf_put_u8(struct wpabuf *buf, u8 data) { u8 *pos = wpabuf_put(buf, 1); *pos = data; } static inline void wpabuf_put_le16(struct wpabuf *buf, u16 data) { u8 *pos = wpabuf_put(buf, 2); WPA_PUT_LE16(pos, data); } static inline void wpabuf_put_le32(struct wpabuf *buf, u32 data) { u8 *pos = wpabuf_put(buf, 4); WPA_PUT_LE32(pos, data); } static inline void wpabuf_put_be16(struct wpabuf *buf, u16 data) { u8 *pos = wpabuf_put(buf, 2); WPA_PUT_BE16(pos, data); } static inline void wpabuf_put_be24(struct wpabuf *buf, u32 data) { u8 *pos = wpabuf_put(buf, 3); WPA_PUT_BE24(pos, data); } static inline void wpabuf_put_be32(struct wpabuf *buf, u32 data) { u8 *pos = wpabuf_put(buf, 4); WPA_PUT_BE32(pos, data); } static inline void wpabuf_put_data(struct wpabuf *buf, const void *data, size_t len) { if (data) os_memcpy(wpabuf_put(buf, len), data, len); } static inline void wpabuf_put_buf(struct wpabuf *dst, const struct wpabuf *src) { wpabuf_put_data(dst, wpabuf_head(src), wpabuf_len(src)); } static inline void wpabuf_set(struct wpabuf *buf, const void *data, size_t len) { buf->buf = (u8 *) data; buf->flags = WPABUF_FLAG_EXT_DATA; buf->size = buf->used = len; } static inline void wpabuf_put_str(struct wpabuf *dst, const char *str) { wpabuf_put_data(dst, str, os_strlen(str)); } #endif /* WPABUF_H */ wpa-2.1/src/wps/000077500000000000000000000000001227414666700135625ustar00rootroot00000000000000wpa-2.1/src/wps/Makefile000066400000000000000000000001641227414666700152230ustar00rootroot00000000000000all: @echo Nothing to be made. clean: rm -f *~ *.o *.d *.gcno *.gcda *.gcov install: @echo Nothing to be made. wpa-2.1/src/wps/http.h000066400000000000000000000012621227414666700147130ustar00rootroot00000000000000/* * HTTP for WPS * Copyright (c) 2000-2003 Intel Corporation * Copyright (c) 2006-2007 Sony Corporation * Copyright (c) 2008-2009 Atheros Communications * Copyright (c) 2009, Jouni Malinen * * See wps_upnp.c for more details on licensing and code history. */ #ifndef HTTP_H #define HTTP_H enum http_reply_code { HTTP_OK = 200, HTTP_BAD_REQUEST = 400, UPNP_INVALID_ACTION = 401, UPNP_INVALID_ARGS = 402, HTTP_NOT_FOUND = 404, HTTP_PRECONDITION_FAILED = 412, HTTP_INTERNAL_SERVER_ERROR = 500, HTTP_UNIMPLEMENTED = 501, UPNP_ACTION_FAILED = 501, UPNP_ARG_VALUE_INVALID = 600, UPNP_ARG_VALUE_OUT_OF_RANGE = 601, UPNP_OUT_OF_MEMORY = 603 }; #endif /* HTTP_H */ wpa-2.1/src/wps/http_client.c000066400000000000000000000202421227414666700162430ustar00rootroot00000000000000/* * http_client - HTTP client * Copyright (c) 2009, 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.h" #include "eloop.h" #include "httpread.h" #include "http_client.h" #define HTTP_CLIENT_TIMEOUT_SEC 30 struct http_client { struct sockaddr_in dst; int sd; struct wpabuf *req; size_t req_pos; size_t max_response; void (*cb)(void *ctx, struct http_client *c, enum http_client_event event); void *cb_ctx; struct httpread *hread; struct wpabuf body; }; static void http_client_timeout(void *eloop_data, void *user_ctx) { struct http_client *c = eloop_data; wpa_printf(MSG_DEBUG, "HTTP: Timeout (c=%p)", c); c->cb(c->cb_ctx, c, HTTP_CLIENT_TIMEOUT); } static void http_client_got_response(struct httpread *handle, void *cookie, enum httpread_event e) { struct http_client *c = cookie; wpa_printf(MSG_DEBUG, "HTTP: httpread callback: handle=%p cookie=%p " "e=%d", handle, cookie, e); eloop_cancel_timeout(http_client_timeout, c, NULL); switch (e) { case HTTPREAD_EVENT_FILE_READY: if (httpread_hdr_type_get(c->hread) == HTTPREAD_HDR_TYPE_REPLY) { int reply_code = httpread_reply_code_get(c->hread); if (reply_code == 200 /* OK */) { wpa_printf(MSG_DEBUG, "HTTP: Response OK from " "%s:%d", inet_ntoa(c->dst.sin_addr), ntohs(c->dst.sin_port)); c->cb(c->cb_ctx, c, HTTP_CLIENT_OK); } else { wpa_printf(MSG_DEBUG, "HTTP: Error %d from " "%s:%d", reply_code, inet_ntoa(c->dst.sin_addr), ntohs(c->dst.sin_port)); c->cb(c->cb_ctx, c, HTTP_CLIENT_INVALID_REPLY); } } else c->cb(c->cb_ctx, c, HTTP_CLIENT_INVALID_REPLY); break; case HTTPREAD_EVENT_TIMEOUT: c->cb(c->cb_ctx, c, HTTP_CLIENT_TIMEOUT); break; case HTTPREAD_EVENT_ERROR: c->cb(c->cb_ctx, c, HTTP_CLIENT_FAILED); break; } } static void http_client_tx_ready(int sock, void *eloop_ctx, void *sock_ctx) { struct http_client *c = eloop_ctx; int res; wpa_printf(MSG_DEBUG, "HTTP: Send client request to %s:%d (%lu of %lu " "bytes remaining)", inet_ntoa(c->dst.sin_addr), ntohs(c->dst.sin_port), (unsigned long) wpabuf_len(c->req), (unsigned long) wpabuf_len(c->req) - c->req_pos); res = send(c->sd, wpabuf_head_u8(c->req) + c->req_pos, wpabuf_len(c->req) - c->req_pos, 0); if (res < 0) { wpa_printf(MSG_DEBUG, "HTTP: Failed to send buffer: %s", strerror(errno)); eloop_unregister_sock(c->sd, EVENT_TYPE_WRITE); c->cb(c->cb_ctx, c, HTTP_CLIENT_FAILED); return; } if ((size_t) res < wpabuf_len(c->req) - c->req_pos) { wpa_printf(MSG_DEBUG, "HTTP: Sent %d of %lu bytes; %lu bytes " "remaining", res, (unsigned long) wpabuf_len(c->req), (unsigned long) wpabuf_len(c->req) - c->req_pos - res); c->req_pos += res; return; } wpa_printf(MSG_DEBUG, "HTTP: Full client request sent to %s:%d", inet_ntoa(c->dst.sin_addr), ntohs(c->dst.sin_port)); eloop_unregister_sock(c->sd, EVENT_TYPE_WRITE); wpabuf_free(c->req); c->req = NULL; c->hread = httpread_create(c->sd, http_client_got_response, c, c->max_response, HTTP_CLIENT_TIMEOUT_SEC); if (c->hread == NULL) { c->cb(c->cb_ctx, c, HTTP_CLIENT_FAILED); return; } } struct http_client * http_client_addr(struct sockaddr_in *dst, struct wpabuf *req, size_t max_response, void (*cb)(void *ctx, struct http_client *c, enum http_client_event event), void *cb_ctx) { struct http_client *c; c = os_zalloc(sizeof(*c)); if (c == NULL) return NULL; c->sd = -1; c->dst = *dst; c->max_response = max_response; c->cb = cb; c->cb_ctx = cb_ctx; c->sd = socket(AF_INET, SOCK_STREAM, 0); if (c->sd < 0) { http_client_free(c); return NULL; } if (fcntl(c->sd, F_SETFL, O_NONBLOCK) != 0) { wpa_printf(MSG_DEBUG, "HTTP: fnctl(O_NONBLOCK) failed: %s", strerror(errno)); http_client_free(c); return NULL; } if (connect(c->sd, (struct sockaddr *) dst, sizeof(*dst))) { if (errno != EINPROGRESS) { wpa_printf(MSG_DEBUG, "HTTP: Failed to connect: %s", strerror(errno)); http_client_free(c); return NULL; } /* * Continue connecting in the background; eloop will call us * once the connection is ready (or failed). */ } if (eloop_register_sock(c->sd, EVENT_TYPE_WRITE, http_client_tx_ready, c, NULL)) { http_client_free(c); return NULL; } if (eloop_register_timeout(HTTP_CLIENT_TIMEOUT_SEC, 0, http_client_timeout, c, NULL)) { http_client_free(c); return NULL; } c->req = req; return c; } char * http_client_url_parse(const char *url, struct sockaddr_in *dst, char **ret_path) { char *u, *addr, *port, *path; u = os_strdup(url); if (u == NULL) return NULL; os_memset(dst, 0, sizeof(*dst)); dst->sin_family = AF_INET; addr = u + 7; path = os_strchr(addr, '/'); port = os_strchr(addr, ':'); if (path == NULL) { path = "/"; } else { *path = '\0'; /* temporary nul termination for address */ if (port > path) port = NULL; } if (port) *port++ = '\0'; if (inet_aton(addr, &dst->sin_addr) == 0) { /* TODO: name lookup */ wpa_printf(MSG_DEBUG, "HTTP: Unsupported address in URL '%s' " "(addr='%s' port='%s')", url, addr, port); os_free(u); return NULL; } if (port) dst->sin_port = htons(atoi(port)); else dst->sin_port = htons(80); if (*path == '\0') { /* remove temporary nul termination for address */ *path = '/'; } *ret_path = path; return u; } struct http_client * http_client_url(const char *url, struct wpabuf *req, size_t max_response, void (*cb)(void *ctx, struct http_client *c, enum http_client_event event), void *cb_ctx) { struct sockaddr_in dst; struct http_client *c; char *u, *path; struct wpabuf *req_buf = NULL; if (os_strncmp(url, "http://", 7) != 0) return NULL; u = http_client_url_parse(url, &dst, &path); if (u == NULL) return NULL; if (req == NULL) { req_buf = wpabuf_alloc(os_strlen(url) + 1000); if (req_buf == NULL) { os_free(u); return NULL; } req = req_buf; wpabuf_printf(req, "GET %s HTTP/1.1\r\n" "Cache-Control: no-cache\r\n" "Pragma: no-cache\r\n" "Accept: text/xml, application/xml\r\n" "User-Agent: wpa_supplicant\r\n" "Host: %s:%d\r\n" "\r\n", path, inet_ntoa(dst.sin_addr), ntohs(dst.sin_port)); } os_free(u); c = http_client_addr(&dst, req, max_response, cb, cb_ctx); if (c == NULL) { wpabuf_free(req_buf); return NULL; } return c; } void http_client_free(struct http_client *c) { if (c == NULL) return; httpread_destroy(c->hread); wpabuf_free(c->req); if (c->sd >= 0) { eloop_unregister_sock(c->sd, EVENT_TYPE_WRITE); close(c->sd); } eloop_cancel_timeout(http_client_timeout, c, NULL); os_free(c); } struct wpabuf * http_client_get_body(struct http_client *c) { if (c->hread == NULL) return NULL; wpabuf_set(&c->body, httpread_data_get(c->hread), httpread_length_get(c->hread)); return &c->body; } char * http_client_get_hdr_line(struct http_client *c, const char *tag) { if (c->hread == NULL) return NULL; return httpread_hdr_line_get(c->hread, tag); } char * http_link_update(char *url, const char *base) { char *n; size_t len; const char *pos; /* RFC 2396, Chapter 5.2 */ /* TODO: consider adding all cases described in RFC 2396 */ if (url == NULL) return NULL; if (os_strncmp(url, "http://", 7) == 0) return url; /* absolute link */ if (os_strncmp(base, "http://", 7) != 0) return url; /* unable to handle base URL */ len = os_strlen(url) + 1 + os_strlen(base) + 1; n = os_malloc(len); if (n == NULL) return url; /* failed */ if (url[0] == '/') { pos = os_strchr(base + 7, '/'); if (pos == NULL) { os_snprintf(n, len, "%s%s", base, url); } else { os_memcpy(n, base, pos - base); os_memcpy(n + (pos - base), url, os_strlen(url) + 1); } } else { pos = os_strrchr(base + 7, '/'); if (pos == NULL) { os_snprintf(n, len, "%s/%s", base, url); } else { os_memcpy(n, base, pos - base + 1); os_memcpy(n + (pos - base) + 1, url, os_strlen(url) + 1); } } os_free(url); return n; } wpa-2.1/src/wps/http_client.h000066400000000000000000000022511227414666700162500ustar00rootroot00000000000000/* * http_client - HTTP client * Copyright (c) 2009, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef HTTP_CLIENT_H #define HTTP_CLIENT_H struct http_client; enum http_client_event { HTTP_CLIENT_FAILED, HTTP_CLIENT_TIMEOUT, HTTP_CLIENT_OK, HTTP_CLIENT_INVALID_REPLY, }; char * http_client_url_parse(const char *url, struct sockaddr_in *dst, char **path); struct http_client * http_client_addr(struct sockaddr_in *dst, struct wpabuf *req, size_t max_response, void (*cb)(void *ctx, struct http_client *c, enum http_client_event event), void *cb_ctx); struct http_client * http_client_url(const char *url, struct wpabuf *req, size_t max_response, void (*cb)(void *ctx, struct http_client *c, enum http_client_event event), void *cb_ctx); void http_client_free(struct http_client *c); struct wpabuf * http_client_get_body(struct http_client *c); char * http_client_get_hdr_line(struct http_client *c, const char *tag); char * http_link_update(char *url, const char *base); #endif /* HTTP_CLIENT_H */ wpa-2.1/src/wps/http_server.c000066400000000000000000000143731227414666700163030ustar00rootroot00000000000000/* * http_server - HTTP server * Copyright (c) 2009, 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.h" #include "eloop.h" #include "httpread.h" #include "http_server.h" #define HTTP_SERVER_TIMEOUT 30 #define HTTP_SERVER_MAX_REQ_LEN 8000 #define HTTP_SERVER_MAX_CONNECTIONS 10 struct http_request { struct http_request *next; struct http_server *srv; int fd; struct sockaddr_in cli; struct httpread *hread; }; struct http_server { void (*cb)(void *ctx, struct http_request *req); void *cb_ctx; int fd; int port; struct http_request *requests; unsigned int request_count; }; static void http_request_cb(struct httpread *handle, void *cookie, enum httpread_event en) { struct http_request *req = cookie; struct http_server *srv = req->srv; if (en == HTTPREAD_EVENT_FILE_READY) { wpa_printf(MSG_DEBUG, "HTTP: Request from %s:%d received", inet_ntoa(req->cli.sin_addr), ntohs(req->cli.sin_port)); srv->cb(srv->cb_ctx, req); return; } wpa_printf(MSG_DEBUG, "HTTP: Request from %s:%d could not be received " "completely", inet_ntoa(req->cli.sin_addr), ntohs(req->cli.sin_port)); http_request_deinit(req); } static struct http_request * http_request_init(struct http_server *srv, int fd, struct sockaddr_in *cli) { struct http_request *req; if (srv->request_count >= HTTP_SERVER_MAX_CONNECTIONS) { wpa_printf(MSG_DEBUG, "HTTP: Too many concurrent requests"); return NULL; } req = os_zalloc(sizeof(*req)); if (req == NULL) return NULL; req->srv = srv; req->fd = fd; req->cli = *cli; req->hread = httpread_create(req->fd, http_request_cb, req, HTTP_SERVER_MAX_REQ_LEN, HTTP_SERVER_TIMEOUT); if (req->hread == NULL) { http_request_deinit(req); return NULL; } return req; } void http_request_deinit(struct http_request *req) { struct http_request *r, *p; struct http_server *srv; if (req == NULL) return; srv = req->srv; p = NULL; r = srv->requests; while (r) { if (r == req) { if (p) p->next = r->next; else srv->requests = r->next; srv->request_count--; break; } p = r; r = r->next; } httpread_destroy(req->hread); close(req->fd); os_free(req); } static void http_request_free_all(struct http_request *req) { struct http_request *prev; while (req) { prev = req; req = req->next; http_request_deinit(prev); } } void http_request_send(struct http_request *req, struct wpabuf *resp) { int res; wpa_printf(MSG_DEBUG, "HTTP: Send %lu byte response to %s:%d", (unsigned long) wpabuf_len(resp), inet_ntoa(req->cli.sin_addr), ntohs(req->cli.sin_port)); res = send(req->fd, wpabuf_head(resp), wpabuf_len(resp), 0); if (res < 0) { wpa_printf(MSG_DEBUG, "HTTP: Send failed: %s", strerror(errno)); } else if ((size_t) res < wpabuf_len(resp)) { wpa_printf(MSG_DEBUG, "HTTP: Sent only %d of %lu bytes", res, (unsigned long) wpabuf_len(resp)); /* TODO: add eloop handler for sending rest of the data */ } wpabuf_free(resp); } void http_request_send_and_deinit(struct http_request *req, struct wpabuf *resp) { http_request_send(req, resp); http_request_deinit(req); } enum httpread_hdr_type http_request_get_type(struct http_request *req) { return httpread_hdr_type_get(req->hread); } char * http_request_get_uri(struct http_request *req) { return httpread_uri_get(req->hread); } char * http_request_get_hdr(struct http_request *req) { return httpread_hdr_get(req->hread); } char * http_request_get_data(struct http_request *req) { return httpread_data_get(req->hread); } char * http_request_get_hdr_line(struct http_request *req, const char *tag) { return httpread_hdr_line_get(req->hread, tag); } struct sockaddr_in * http_request_get_cli_addr(struct http_request *req) { return &req->cli; } static void http_server_cb(int sd, void *eloop_ctx, void *sock_ctx) { struct sockaddr_in addr; socklen_t addr_len = sizeof(addr); struct http_server *srv = eloop_ctx; int conn; struct http_request *req; conn = accept(srv->fd, (struct sockaddr *) &addr, &addr_len); if (conn < 0) { wpa_printf(MSG_DEBUG, "HTTP: Failed to accept new connection: " "%s", strerror(errno)); return; } wpa_printf(MSG_DEBUG, "HTTP: Connection from %s:%d", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port)); req = http_request_init(srv, conn, &addr); if (req == NULL) { close(conn); return; } req->next = srv->requests; srv->requests = req; srv->request_count++; } struct http_server * http_server_init(struct in_addr *addr, int port, void (*cb)(void *ctx, struct http_request *req), void *cb_ctx) { struct sockaddr_in sin; struct http_server *srv; int on = 1; srv = os_zalloc(sizeof(*srv)); if (srv == NULL) return NULL; srv->cb = cb; srv->cb_ctx = cb_ctx; srv->fd = socket(AF_INET, SOCK_STREAM, 0); if (srv->fd < 0) goto fail; setsockopt(srv->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); if (fcntl(srv->fd, F_SETFL, O_NONBLOCK) < 0) goto fail; if (port < 0) srv->port = 49152; else srv->port = port; os_memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_addr.s_addr = addr->s_addr; for (;;) { sin.sin_port = htons(srv->port); if (bind(srv->fd, (struct sockaddr *) &sin, sizeof(sin)) == 0) break; if (errno == EADDRINUSE) { /* search for unused port */ if (++srv->port == 65535 || port >= 0) goto fail; continue; } wpa_printf(MSG_DEBUG, "HTTP: Failed to bind server port %d: " "%s", srv->port, strerror(errno)); goto fail; } if (listen(srv->fd, 10 /* max backlog */) < 0) goto fail; if (fcntl(srv->fd, F_SETFL, O_NONBLOCK) < 0) goto fail; if (eloop_register_sock(srv->fd, EVENT_TYPE_READ, http_server_cb, srv, NULL)) goto fail; wpa_printf(MSG_DEBUG, "HTTP: Started server on %s:%d", inet_ntoa(*addr), srv->port); return srv; fail: http_server_deinit(srv); return NULL; } void http_server_deinit(struct http_server *srv) { if (srv == NULL) return; if (srv->fd >= 0) { eloop_unregister_sock(srv->fd, EVENT_TYPE_READ); close(srv->fd); } http_request_free_all(srv->requests); os_free(srv); } int http_server_get_port(struct http_server *srv) { return srv->port; } wpa-2.1/src/wps/http_server.h000066400000000000000000000022211227414666700162750ustar00rootroot00000000000000/* * http_server - HTTP server * Copyright (c) 2009, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef HTTP_SERVER_H #define HTTP_SERVER_H struct http_server; struct http_request; void http_request_deinit(struct http_request *req); void http_request_send(struct http_request *req, struct wpabuf *resp); void http_request_send_and_deinit(struct http_request *req, struct wpabuf *resp); enum httpread_hdr_type http_request_get_type(struct http_request *req); char * http_request_get_uri(struct http_request *req); char * http_request_get_hdr(struct http_request *req); char * http_request_get_data(struct http_request *req); char * http_request_get_hdr_line(struct http_request *req, const char *tag); struct sockaddr_in * http_request_get_cli_addr(struct http_request *req); struct http_server * http_server_init(struct in_addr *addr, int port, void (*cb)(void *ctx, struct http_request *req), void *cb_ctx); void http_server_deinit(struct http_server *srv); int http_server_get_port(struct http_server *srv); #endif /* HTTP_SERVER_H */ wpa-2.1/src/wps/httpread.c000066400000000000000000000535671227414666700155610ustar00rootroot00000000000000/* * httpread - Manage reading file(s) from HTTP/TCP socket * Author: Ted Merrill * Copyright 2008 Atheros Communications * * This software may be distributed under the terms of the BSD license. * See README for more details. * * The files are buffered via internal callbacks from eloop, then presented to * an application callback routine when completely read into memory. May also * be used if no file is expected but just to get the header, including HTTP * replies (e.g. HTTP/1.1 200 OK etc.). * * This does not attempt to be an optimally efficient implementation, but does * attempt to be of reasonably small size and memory consumption; assuming that * only small files are to be read. A maximum file size is provided by * application and enforced. * * It is assumed that the application does not expect any of the following: * -- transfer encoding other than chunked * -- trailer fields * It is assumed that, even if the other side requested that the connection be * kept open, that we will close it (thus HTTP messages sent by application * should have the connection closed field); this is allowed by HTTP/1.1 and * simplifies things for us. * * Other limitations: * -- HTTP header may not exceed a hard-coded size. * * Notes: * This code would be massively simpler without some of the new features of * HTTP/1.1, especially chunked data. */ #include "includes.h" #include "common.h" #include "eloop.h" #include "httpread.h" /* Tunable parameters */ #define HTTPREAD_READBUF_SIZE 1024 /* read in chunks of this size */ #define HTTPREAD_HEADER_MAX_SIZE 4096 /* max allowed for headers */ #define HTTPREAD_BODYBUF_DELTA 4096 /* increase allocation by this */ #if 0 /* httpread_debug -- set this global variable > 0 e.g. from debugger * to enable debugs (larger numbers for more debugs) * Make this a #define of 0 to eliminate the debugging code. */ int httpread_debug = 99; #else #define httpread_debug 0 /* eliminates even the debugging code */ #endif /* control instance -- actual definition (opaque to application) */ struct httpread { /* information from creation */ int sd; /* descriptor of TCP socket to read from */ void (*cb)(struct httpread *handle, void *cookie, enum httpread_event e); /* call on event */ void *cookie; /* pass to callback */ int max_bytes; /* maximum file size else abort it */ int timeout_seconds; /* 0 or total duration timeout period */ /* dynamically used information follows */ int got_hdr; /* nonzero when header is finalized */ char hdr[HTTPREAD_HEADER_MAX_SIZE+1]; /* headers stored here */ int hdr_nbytes; enum httpread_hdr_type hdr_type; int version; /* 1 if we've seen 1.1 */ int reply_code; /* for type REPLY, e.g. 200 for HTTP/1.1 200 OK */ int got_content_length; /* true if we know content length for sure */ int content_length; /* body length, iff got_content_length */ int chunked; /* nonzero for chunked data */ char *uri; int got_body; /* nonzero when body is finalized */ char *body; int body_nbytes; int body_alloc_nbytes; /* amount allocated */ int got_file; /* here when we are done */ /* The following apply if data is chunked: */ int in_chunk_data; /* 0=in/at header, 1=in the data or tail*/ int chunk_start; /* offset in body of chunk hdr or data */ int chunk_size; /* data of chunk (not hdr or ending CRLF)*/ int in_trailer; /* in header fields after data (chunked only)*/ enum trailer_state { trailer_line_begin = 0, trailer_empty_cr, /* empty line + CR */ trailer_nonempty, trailer_nonempty_cr, } trailer_state; }; /* Check words for equality, where words consist of graphical characters * delimited by whitespace * Returns nonzero if "equal" doing case insensitive comparison. */ static int word_eq(char *s1, char *s2) { int c1; int c2; int end1 = 0; int end2 = 0; for (;;) { c1 = *s1++; c2 = *s2++; if (isalpha(c1) && isupper(c1)) c1 = tolower(c1); if (isalpha(c2) && isupper(c2)) c2 = tolower(c2); end1 = !isgraph(c1); end2 = !isgraph(c2); if (end1 || end2 || c1 != c2) break; } return end1 && end2; /* reached end of both words? */ } static void httpread_timeout_handler(void *eloop_data, void *user_ctx); /* httpread_destroy -- if h is non-NULL, clean up * This must eventually be called by the application following * call of the application's callback and may be called * earlier if desired. */ void httpread_destroy(struct httpread *h) { if (httpread_debug >= 10) wpa_printf(MSG_DEBUG, "ENTER httpread_destroy(%p)", h); if (!h) return; eloop_cancel_timeout(httpread_timeout_handler, NULL, h); eloop_unregister_sock(h->sd, EVENT_TYPE_READ); os_free(h->body); os_free(h->uri); os_memset(h, 0, sizeof(*h)); /* aid debugging */ h->sd = -1; /* aid debugging */ os_free(h); } /* httpread_timeout_handler -- called on excessive total duration */ static void httpread_timeout_handler(void *eloop_data, void *user_ctx) { struct httpread *h = user_ctx; wpa_printf(MSG_DEBUG, "httpread timeout (%p)", h); (*h->cb)(h, h->cookie, HTTPREAD_EVENT_TIMEOUT); } /* Analyze options only so far as is needed to correctly obtain the file. * The application can look at the raw header to find other options. */ static int httpread_hdr_option_analyze( struct httpread *h, char *hbp /* pointer to current line in header buffer */ ) { if (word_eq(hbp, "CONTENT-LENGTH:")) { while (isgraph(*hbp)) hbp++; while (*hbp == ' ' || *hbp == '\t') hbp++; if (!isdigit(*hbp)) return -1; h->content_length = atol(hbp); h->got_content_length = 1; return 0; } if (word_eq(hbp, "TRANSFER_ENCODING:") || word_eq(hbp, "TRANSFER-ENCODING:")) { while (isgraph(*hbp)) hbp++; while (*hbp == ' ' || *hbp == '\t') hbp++; /* There should (?) be no encodings of interest * other than chunked... */ if (word_eq(hbp, "CHUNKED")) { h->chunked = 1; h->in_chunk_data = 0; /* ignore possible ; */ } return 0; } /* skip anything we don't know, which is a lot */ return 0; } static int httpread_hdr_analyze(struct httpread *h) { char *hbp = h->hdr; /* pointer into h->hdr */ int standard_first_line = 1; /* First line is special */ h->hdr_type = HTTPREAD_HDR_TYPE_UNKNOWN; if (!isgraph(*hbp)) goto bad; if (os_strncmp(hbp, "HTTP/", 5) == 0) { h->hdr_type = HTTPREAD_HDR_TYPE_REPLY; standard_first_line = 0; hbp += 5; if (hbp[0] == '1' && hbp[1] == '.' && isdigit(hbp[2]) && hbp[2] != '0') h->version = 1; while (isgraph(*hbp)) hbp++; while (*hbp == ' ' || *hbp == '\t') hbp++; if (!isdigit(*hbp)) goto bad; h->reply_code = atol(hbp); } else if (word_eq(hbp, "GET")) h->hdr_type = HTTPREAD_HDR_TYPE_GET; else if (word_eq(hbp, "HEAD")) h->hdr_type = HTTPREAD_HDR_TYPE_HEAD; else if (word_eq(hbp, "POST")) h->hdr_type = HTTPREAD_HDR_TYPE_POST; else if (word_eq(hbp, "PUT")) h->hdr_type = HTTPREAD_HDR_TYPE_PUT; else if (word_eq(hbp, "DELETE")) h->hdr_type = HTTPREAD_HDR_TYPE_DELETE; else if (word_eq(hbp, "TRACE")) h->hdr_type = HTTPREAD_HDR_TYPE_TRACE; else if (word_eq(hbp, "CONNECT")) h->hdr_type = HTTPREAD_HDR_TYPE_CONNECT; else if (word_eq(hbp, "NOTIFY")) h->hdr_type = HTTPREAD_HDR_TYPE_NOTIFY; else if (word_eq(hbp, "M-SEARCH")) h->hdr_type = HTTPREAD_HDR_TYPE_M_SEARCH; else if (word_eq(hbp, "M-POST")) h->hdr_type = HTTPREAD_HDR_TYPE_M_POST; else if (word_eq(hbp, "SUBSCRIBE")) h->hdr_type = HTTPREAD_HDR_TYPE_SUBSCRIBE; else if (word_eq(hbp, "UNSUBSCRIBE")) h->hdr_type = HTTPREAD_HDR_TYPE_UNSUBSCRIBE; else { } if (standard_first_line) { char *rawuri; char *uri; /* skip type */ while (isgraph(*hbp)) hbp++; while (*hbp == ' ' || *hbp == '\t') hbp++; /* parse uri. * Find length, allocate memory for translated * copy, then translate by changing % * into represented value. */ rawuri = hbp; while (isgraph(*hbp)) hbp++; h->uri = os_malloc((hbp - rawuri) + 1); if (h->uri == NULL) goto bad; uri = h->uri; while (rawuri < hbp) { int c = *rawuri; if (c == '%' && isxdigit(rawuri[1]) && isxdigit(rawuri[2])) { *uri++ = hex2byte(rawuri + 1); rawuri += 3; } else { *uri++ = c; rawuri++; } } *uri = 0; /* null terminate */ while (isgraph(*hbp)) hbp++; while (*hbp == ' ' || *hbp == '\t') hbp++; /* get version */ if (0 == strncmp(hbp, "HTTP/", 5)) { hbp += 5; if (hbp[0] == '1' && hbp[1] == '.' && isdigit(hbp[2]) && hbp[2] != '0') h->version = 1; } } /* skip rest of line */ while (*hbp) if (*hbp++ == '\n') break; /* Remainder of lines are options, in any order; * or empty line to terminate */ for (;;) { /* Empty line to terminate */ if (hbp[0] == '\n' || (hbp[0] == '\r' && hbp[1] == '\n')) break; if (!isgraph(*hbp)) goto bad; if (httpread_hdr_option_analyze(h, hbp)) goto bad; /* skip line */ while (*hbp) if (*hbp++ == '\n') break; } /* chunked overrides content-length always */ if (h->chunked) h->got_content_length = 0; /* For some types, we should not try to read a body * This is in addition to the application determining * that we should not read a body. */ switch (h->hdr_type) { case HTTPREAD_HDR_TYPE_REPLY: /* Some codes can have a body and some not. * For now, just assume that any other than 200 * do not... */ if (h->reply_code != 200) h->max_bytes = 0; break; case HTTPREAD_HDR_TYPE_GET: case HTTPREAD_HDR_TYPE_HEAD: /* in practice it appears that it is assumed * that GETs have a body length of 0... ? */ if (h->chunked == 0 && h->got_content_length == 0) h->max_bytes = 0; break; case HTTPREAD_HDR_TYPE_POST: case HTTPREAD_HDR_TYPE_PUT: case HTTPREAD_HDR_TYPE_DELETE: case HTTPREAD_HDR_TYPE_TRACE: case HTTPREAD_HDR_TYPE_CONNECT: case HTTPREAD_HDR_TYPE_NOTIFY: case HTTPREAD_HDR_TYPE_M_SEARCH: case HTTPREAD_HDR_TYPE_M_POST: case HTTPREAD_HDR_TYPE_SUBSCRIBE: case HTTPREAD_HDR_TYPE_UNSUBSCRIBE: default: break; } return 0; bad: /* Error */ return -1; } /* httpread_read_handler -- called when socket ready to read * * Note: any extra data we read past end of transmitted file is ignored; * if we were to support keeping connections open for multiple files then * this would have to be addressed. */ static void httpread_read_handler(int sd, void *eloop_ctx, void *sock_ctx) { struct httpread *h = sock_ctx; int nread; char *rbp; /* pointer into read buffer */ char *hbp; /* pointer into header buffer */ char *bbp; /* pointer into body buffer */ char readbuf[HTTPREAD_READBUF_SIZE]; /* temp use to read into */ if (httpread_debug >= 20) wpa_printf(MSG_DEBUG, "ENTER httpread_read_handler(%p)", h); /* read some at a time, then search for the interal * boundaries between header and data and etc. */ nread = read(h->sd, readbuf, sizeof(readbuf)); if (nread < 0) goto bad; if (nread == 0) { /* end of transmission... this may be normal * or may be an error... in some cases we can't * tell which so we must assume it is normal then. */ if (!h->got_hdr) { /* Must at least have completed header */ wpa_printf(MSG_DEBUG, "httpread premature eof(%p)", h); goto bad; } if (h->chunked || h->got_content_length) { /* Premature EOF; e.g. dropped connection */ wpa_printf(MSG_DEBUG, "httpread premature eof(%p) %d/%d", h, h->body_nbytes, h->content_length); goto bad; } /* No explicit length, hopefully we have all the data * although dropped connections can cause false * end */ if (httpread_debug >= 10) wpa_printf(MSG_DEBUG, "httpread ok eof(%p)", h); h->got_body = 1; goto got_file; } rbp = readbuf; /* Header consists of text lines (terminated by both CR and LF) * and an empty line (CR LF only). */ if (!h->got_hdr) { hbp = h->hdr + h->hdr_nbytes; /* add to headers until: * -- we run out of data in read buffer * -- or, we run out of header buffer room * -- or, we get double CRLF in headers */ for (;;) { if (nread == 0) goto get_more; if (h->hdr_nbytes == HTTPREAD_HEADER_MAX_SIZE) { goto bad; } *hbp++ = *rbp++; nread--; h->hdr_nbytes++; if (h->hdr_nbytes >= 4 && hbp[-1] == '\n' && hbp[-2] == '\r' && hbp[-3] == '\n' && hbp[-4] == '\r' ) { h->got_hdr = 1; *hbp = 0; /* null terminate */ break; } } /* here we've just finished reading the header */ if (httpread_hdr_analyze(h)) { wpa_printf(MSG_DEBUG, "httpread bad hdr(%p)", h); goto bad; } if (h->max_bytes == 0) { if (httpread_debug >= 10) wpa_printf(MSG_DEBUG, "httpread no body hdr end(%p)", h); goto got_file; } if (h->got_content_length && h->content_length == 0) { if (httpread_debug >= 10) wpa_printf(MSG_DEBUG, "httpread zero content length(%p)", h); goto got_file; } } /* Certain types of requests never have data and so * must be specially recognized. */ if (!os_strncasecmp(h->hdr, "SUBSCRIBE", 9) || !os_strncasecmp(h->hdr, "UNSUBSCRIBE", 11) || !os_strncasecmp(h->hdr, "HEAD", 4) || !os_strncasecmp(h->hdr, "GET", 3)) { if (!h->got_body) { if (httpread_debug >= 10) wpa_printf(MSG_DEBUG, "httpread NO BODY for sp. type"); } h->got_body = 1; goto got_file; } /* Data can be just plain binary data, or if "chunked" * consists of chunks each with a header, ending with * an ending header. */ if (nread == 0) goto get_more; if (!h->got_body) { /* Here to get (more of) body */ /* ensure we have enough room for worst case for body * plus a null termination character */ if (h->body_alloc_nbytes < (h->body_nbytes + nread + 1)) { char *new_body; int new_alloc_nbytes; if (h->body_nbytes >= h->max_bytes) goto bad; new_alloc_nbytes = h->body_alloc_nbytes + HTTPREAD_BODYBUF_DELTA; /* For content-length case, the first time * through we allocate the whole amount * we need. */ if (h->got_content_length && new_alloc_nbytes < (h->content_length + 1)) new_alloc_nbytes = h->content_length + 1; if ((new_body = os_realloc(h->body, new_alloc_nbytes)) == NULL) goto bad; h->body = new_body; h->body_alloc_nbytes = new_alloc_nbytes; } /* add bytes */ bbp = h->body + h->body_nbytes; for (;;) { int ncopy; /* See if we need to stop */ if (h->chunked && h->in_chunk_data == 0) { /* in chunk header */ char *cbp = h->body + h->chunk_start; if (bbp-cbp >= 2 && bbp[-2] == '\r' && bbp[-1] == '\n') { /* end of chunk hdr line */ /* hdr line consists solely * of a hex numeral and CFLF */ if (!isxdigit(*cbp)) goto bad; h->chunk_size = strtoul(cbp, NULL, 16); /* throw away chunk header * so we have only real data */ h->body_nbytes = h->chunk_start; bbp = cbp; if (h->chunk_size == 0) { /* end of chunking */ /* trailer follows */ h->in_trailer = 1; if (httpread_debug >= 20) wpa_printf( MSG_DEBUG, "httpread end chunks(%p)", h); break; } h->in_chunk_data = 1; /* leave chunk_start alone */ } } else if (h->chunked) { /* in chunk data */ if ((h->body_nbytes - h->chunk_start) == (h->chunk_size + 2)) { /* end of chunk reached, * new chunk starts */ /* check chunk ended w/ CRLF * which we'll throw away */ if (bbp[-1] == '\n' && bbp[-2] == '\r') { } else goto bad; h->body_nbytes -= 2; bbp -= 2; h->chunk_start = h->body_nbytes; h->in_chunk_data = 0; h->chunk_size = 0; /* just in case */ } } else if (h->got_content_length && h->body_nbytes >= h->content_length) { h->got_body = 1; if (httpread_debug >= 10) wpa_printf( MSG_DEBUG, "httpread got content(%p)", h); goto got_file; } if (nread <= 0) break; /* Now transfer. Optimize using memcpy where we can. */ if (h->chunked && h->in_chunk_data) { /* copy up to remainder of chunk data * plus the required CR+LF at end */ ncopy = (h->chunk_start + h->chunk_size + 2) - h->body_nbytes; } else if (h->chunked) { /*in chunk header -- don't optimize */ *bbp++ = *rbp++; nread--; h->body_nbytes++; continue; } else if (h->got_content_length) { ncopy = h->content_length - h->body_nbytes; } else { ncopy = nread; } /* Note: should never be 0 */ if (ncopy > nread) ncopy = nread; os_memcpy(bbp, rbp, ncopy); bbp += ncopy; h->body_nbytes += ncopy; rbp += ncopy; nread -= ncopy; } /* body copy loop */ } /* !got_body */ if (h->chunked && h->in_trailer) { /* If "chunked" then there is always a trailer, * consisting of zero or more non-empty lines * ending with CR LF and then an empty line w/ CR LF. * We do NOT support trailers except to skip them -- * this is supported (generally) by the http spec. */ bbp = h->body + h->body_nbytes; for (;;) { int c; if (nread <= 0) break; c = *rbp++; nread--; switch (h->trailer_state) { case trailer_line_begin: if (c == '\r') h->trailer_state = trailer_empty_cr; else h->trailer_state = trailer_nonempty; break; case trailer_empty_cr: /* end empty line */ if (c == '\n') { h->trailer_state = trailer_line_begin; h->in_trailer = 0; if (httpread_debug >= 10) wpa_printf( MSG_DEBUG, "httpread got content(%p)", h); h->got_body = 1; goto got_file; } h->trailer_state = trailer_nonempty; break; case trailer_nonempty: if (c == '\r') h->trailer_state = trailer_nonempty_cr; break; case trailer_nonempty_cr: if (c == '\n') h->trailer_state = trailer_line_begin; else h->trailer_state = trailer_nonempty; break; } } } goto get_more; bad: /* Error */ wpa_printf(MSG_DEBUG, "httpread read/parse failure (%p)", h); (*h->cb)(h, h->cookie, HTTPREAD_EVENT_ERROR); return; get_more: return; got_file: if (httpread_debug >= 10) wpa_printf(MSG_DEBUG, "httpread got file %d bytes type %d", h->body_nbytes, h->hdr_type); /* Null terminate for convenience of some applications */ if (h->body) h->body[h->body_nbytes] = 0; /* null terminate */ h->got_file = 1; /* Assume that we do NOT support keeping connection alive, * and just in case somehow we don't get destroyed right away, * unregister now. */ eloop_unregister_sock(h->sd, EVENT_TYPE_READ); /* The application can destroy us whenever they feel like... * cancel timeout. */ eloop_cancel_timeout(httpread_timeout_handler, NULL, h); (*h->cb)(h, h->cookie, HTTPREAD_EVENT_FILE_READY); } /* httpread_create -- start a new reading session making use of eloop. * The new instance will use the socket descriptor for reading (until * it gets a file and not after) but will not close the socket, even * when the instance is destroyed (the application must do that). * Return NULL on error. * * Provided that httpread_create successfully returns a handle, * the callback fnc is called to handle httpread_event events. * The caller should do destroy on any errors or unknown events. * * Pass max_bytes == 0 to not read body at all (required for e.g. * reply to HEAD request). */ struct httpread * httpread_create( int sd, /* descriptor of TCP socket to read from */ void (*cb)(struct httpread *handle, void *cookie, enum httpread_event e), /* call on event */ void *cookie, /* pass to callback */ int max_bytes, /* maximum body size else abort it */ int timeout_seconds /* 0; or total duration timeout period */ ) { struct httpread *h = NULL; h = os_zalloc(sizeof(*h)); if (h == NULL) goto fail; h->sd = sd; h->cb = cb; h->cookie = cookie; h->max_bytes = max_bytes; h->timeout_seconds = timeout_seconds; if (timeout_seconds > 0 && eloop_register_timeout(timeout_seconds, 0, httpread_timeout_handler, NULL, h)) { /* No way to recover (from malloc failure) */ goto fail; } if (eloop_register_sock(sd, EVENT_TYPE_READ, httpread_read_handler, NULL, h)) { /* No way to recover (from malloc failure) */ goto fail; } return h; fail: /* Error */ httpread_destroy(h); return NULL; } /* httpread_hdr_type_get -- When file is ready, returns header type. */ enum httpread_hdr_type httpread_hdr_type_get(struct httpread *h) { return h->hdr_type; } /* httpread_uri_get -- When file is ready, uri_get returns (translated) URI * or possibly NULL (which would be an error). */ char * httpread_uri_get(struct httpread *h) { return h->uri; } /* httpread_reply_code_get -- When reply is ready, returns reply code */ int httpread_reply_code_get(struct httpread *h) { return h->reply_code; } /* httpread_length_get -- When file is ready, returns file length. */ int httpread_length_get(struct httpread *h) { return h->body_nbytes; } /* httpread_data_get -- When file is ready, returns file content * with null byte appened. * Might return NULL in some error condition. */ void * httpread_data_get(struct httpread *h) { return h->body ? h->body : ""; } /* httpread_hdr_get -- When file is ready, returns header content * with null byte appended. * Might return NULL in some error condition. */ char * httpread_hdr_get(struct httpread *h) { return h->hdr; } /* httpread_hdr_line_get -- When file is ready, returns pointer * to line within header content matching the given tag * (after the tag itself and any spaces/tabs). * * The tag should end with a colon for reliable matching. * * If not found, returns NULL; */ char * httpread_hdr_line_get(struct httpread *h, const char *tag) { int tag_len = os_strlen(tag); char *hdr = h->hdr; hdr = os_strchr(hdr, '\n'); if (hdr == NULL) return NULL; hdr++; for (;;) { if (!os_strncasecmp(hdr, tag, tag_len)) { hdr += tag_len; while (*hdr == ' ' || *hdr == '\t') hdr++; return hdr; } hdr = os_strchr(hdr, '\n'); if (hdr == NULL) return NULL; hdr++; } } wpa-2.1/src/wps/httpread.h000066400000000000000000000100521227414666700155440ustar00rootroot00000000000000/* * httpread - Manage reading file(s) from HTTP/TCP socket * Author: Ted Merrill * Copyright 2008 Atheros Communications * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef HTTPREAD_H #define HTTPREAD_H /* event types (passed to callback) */ enum httpread_event { HTTPREAD_EVENT_FILE_READY = 1, /* including reply ready */ HTTPREAD_EVENT_TIMEOUT = 2, HTTPREAD_EVENT_ERROR = 3 /* misc. error, esp malloc error */ }; /* header type detected * available to callback via call to httpread_reply_code_get() */ enum httpread_hdr_type { HTTPREAD_HDR_TYPE_UNKNOWN = 0, /* none of the following */ HTTPREAD_HDR_TYPE_REPLY = 1, /* hdr begins w/ HTTP/ */ HTTPREAD_HDR_TYPE_GET = 2, /* hdr begins with GET */ HTTPREAD_HDR_TYPE_HEAD = 3, /* hdr begins with HEAD */ HTTPREAD_HDR_TYPE_POST = 4, /* hdr begins with POST */ HTTPREAD_HDR_TYPE_PUT = 5, /* hdr begins with ... */ HTTPREAD_HDR_TYPE_DELETE = 6, /* hdr begins with ... */ HTTPREAD_HDR_TYPE_TRACE = 7, /* hdr begins with ... */ HTTPREAD_HDR_TYPE_CONNECT = 8, /* hdr begins with ... */ HTTPREAD_HDR_TYPE_NOTIFY = 9, /* hdr begins with ... */ HTTPREAD_HDR_TYPE_M_SEARCH = 10, /* hdr begins with ... */ HTTPREAD_HDR_TYPE_M_POST = 11, /* hdr begins with ... */ HTTPREAD_HDR_TYPE_SUBSCRIBE = 12, /* hdr begins with ... */ HTTPREAD_HDR_TYPE_UNSUBSCRIBE = 13, /* hdr begins with ... */ HTTPREAD_N_HDR_TYPES /* keep last */ }; /* control instance -- opaque struct declaration */ struct httpread; /* httpread_destroy -- if h is non-NULL, clean up * This must eventually be called by the application following * call of the application's callback and may be called * earlier if desired. */ void httpread_destroy(struct httpread *h); /* httpread_create -- start a new reading session making use of eloop. * The new instance will use the socket descriptor for reading (until * it gets a file and not after) but will not close the socket, even * when the instance is destroyed (the application must do that). * Return NULL on error. * * Provided that httpread_create successfully returns a handle, * the callback fnc is called to handle httpread_event events. * The caller should do destroy on any errors or unknown events. * * Pass max_bytes == 0 to not read body at all (required for e.g. * reply to HEAD request). */ struct httpread * httpread_create( int sd, /* descriptor of TCP socket to read from */ void (*cb)(struct httpread *handle, void *cookie, enum httpread_event e), /* call on event */ void *cookie, /* pass to callback */ int max_bytes, /* maximum file size else abort it */ int timeout_seconds /* 0; or total duration timeout period */ ); /* httpread_hdr_type_get -- When file is ready, returns header type. */ enum httpread_hdr_type httpread_hdr_type_get(struct httpread *h); /* httpread_uri_get -- When file is ready, uri_get returns (translated) URI * or possibly NULL (which would be an error). */ char *httpread_uri_get(struct httpread *h); /* httpread_reply_code_get -- When reply is ready, returns reply code */ int httpread_reply_code_get(struct httpread *h); /* httpread_length_get -- When file is ready, returns file length. */ int httpread_length_get(struct httpread *h); /* httpread_data_get -- When file is ready, returns file content * with null byte appened. * Might return NULL in some error condition. */ void * httpread_data_get(struct httpread *h); /* httpread_hdr_get -- When file is ready, returns header content * with null byte appended. * Might return NULL in some error condition. */ char * httpread_hdr_get(struct httpread *h); /* httpread_hdr_line_get -- When file is ready, returns pointer * to line within header content matching the given tag * (after the tag itself and any spaces/tabs). * * The tag should end with a colon for reliable matching. * * If not found, returns NULL; */ char * httpread_hdr_line_get(struct httpread *h, const char *tag); #endif /* HTTPREAD_H */ wpa-2.1/src/wps/ndef.c000066400000000000000000000112051227414666700146410ustar00rootroot00000000000000/* * NDEF(NFC Data Exchange Format) routines for Wi-Fi Protected Setup * Reference is "NFCForum-TS-NDEF_1.0 2006-07-24". * Copyright (c) 2009-2012, Masashi Honma * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "includes.h" #include "common.h" #include "wps/wps.h" #define FLAG_MESSAGE_BEGIN (1 << 7) #define FLAG_MESSAGE_END (1 << 6) #define FLAG_CHUNK (1 << 5) #define FLAG_SHORT_RECORD (1 << 4) #define FLAG_ID_LENGTH_PRESENT (1 << 3) #define FLAG_TNF_NFC_FORUM (0x01) #define FLAG_TNF_RFC2046 (0x02) struct ndef_record { const u8 *type; const u8 *id; const u8 *payload; u8 type_length; u8 id_length; u32 payload_length; u32 total_length; }; static char wifi_handover_type[] = "application/vnd.wfa.wsc"; static char p2p_handover_type[] = "application/vnd.wfa.p2p"; static int ndef_parse_record(const u8 *data, u32 size, struct ndef_record *record) { const u8 *pos = data + 1; if (size < 2) return -1; record->type_length = *pos++; if (data[0] & FLAG_SHORT_RECORD) { if (size < 3) return -1; record->payload_length = *pos++; } else { if (size < 6) return -1; record->payload_length = ntohl(*(u32 *)pos); pos += sizeof(u32); } if (data[0] & FLAG_ID_LENGTH_PRESENT) { if ((int) size < pos - data + 1) return -1; record->id_length = *pos++; } else record->id_length = 0; record->type = record->type_length == 0 ? NULL : pos; pos += record->type_length; record->id = record->id_length == 0 ? NULL : pos; pos += record->id_length; record->payload = record->payload_length == 0 ? NULL : pos; pos += record->payload_length; record->total_length = pos - data; if (record->total_length > size) return -1; return 0; } static struct wpabuf * ndef_parse_records(const struct wpabuf *buf, int (*filter)(struct ndef_record *)) { struct ndef_record record; int len = wpabuf_len(buf); const u8 *data = wpabuf_head(buf); while (len > 0) { if (ndef_parse_record(data, len, &record) < 0) { wpa_printf(MSG_ERROR, "NDEF : Failed to parse"); return NULL; } if (filter == NULL || filter(&record)) return wpabuf_alloc_copy(record.payload, record.payload_length); data += record.total_length; len -= record.total_length; } wpa_printf(MSG_ERROR, "NDEF : Record not found"); return NULL; } static struct wpabuf * ndef_build_record(u8 flags, void *type, u8 type_length, void *id, u8 id_length, const struct wpabuf *payload) { struct wpabuf *record; size_t total_len; int short_record; u8 local_flag; size_t payload_length = wpabuf_len(payload); short_record = payload_length < 256 ? 1 : 0; total_len = 2; /* flag + type length */ /* payload length */ total_len += short_record ? sizeof(u8) : sizeof(u32); if (id_length > 0) total_len += 1; total_len += type_length + id_length + payload_length; record = wpabuf_alloc(total_len); if (record == NULL) { wpa_printf(MSG_ERROR, "NDEF : Failed to allocate " "record for build"); return NULL; } local_flag = flags; if (id_length > 0) local_flag |= FLAG_ID_LENGTH_PRESENT; if (short_record) local_flag |= FLAG_SHORT_RECORD; wpabuf_put_u8(record, local_flag); wpabuf_put_u8(record, type_length); if (short_record) wpabuf_put_u8(record, payload_length); else wpabuf_put_be32(record, payload_length); if (id_length > 0) wpabuf_put_u8(record, id_length); wpabuf_put_data(record, type, type_length); wpabuf_put_data(record, id, id_length); wpabuf_put_buf(record, payload); return record; } static int wifi_filter(struct ndef_record *record) { if (record->type_length != os_strlen(wifi_handover_type)) return 0; if (os_memcmp(record->type, wifi_handover_type, os_strlen(wifi_handover_type)) != 0) return 0; return 1; } struct wpabuf * ndef_parse_wifi(const struct wpabuf *buf) { return ndef_parse_records(buf, wifi_filter); } struct wpabuf * ndef_build_wifi(const struct wpabuf *buf) { return ndef_build_record(FLAG_MESSAGE_BEGIN | FLAG_MESSAGE_END | FLAG_TNF_RFC2046, wifi_handover_type, os_strlen(wifi_handover_type), NULL, 0, buf); } static int p2p_filter(struct ndef_record *record) { if (record->type_length != os_strlen(p2p_handover_type)) return 0; if (os_memcmp(record->type, p2p_handover_type, os_strlen(p2p_handover_type)) != 0) return 0; return 1; } struct wpabuf * ndef_parse_p2p(const struct wpabuf *buf) { return ndef_parse_records(buf, p2p_filter); } struct wpabuf * ndef_build_p2p(const struct wpabuf *buf) { return ndef_build_record(FLAG_MESSAGE_BEGIN | FLAG_MESSAGE_END | FLAG_TNF_RFC2046, p2p_handover_type, os_strlen(p2p_handover_type), NULL, 0, buf); } wpa-2.1/src/wps/upnp_xml.c000066400000000000000000000175261227414666700156030ustar00rootroot00000000000000/* * UPnP XML helper routines * Copyright (c) 2000-2003 Intel Corporation * Copyright (c) 2006-2007 Sony Corporation * Copyright (c) 2008-2009 Atheros Communications * Copyright (c) 2009, Jouni Malinen * * See wps_upnp.c for more details on licensing and code history. */ #include "includes.h" #include "common.h" #include "base64.h" #include "http.h" #include "upnp_xml.h" /* * XML parsing and formatting * * XML is a markup language based on unicode; usually (and in our case, * always!) based on utf-8. utf-8 uses a variable number of bytes per * character. utf-8 has the advantage that all non-ASCII unicode characters are * represented by sequences of non-ascii (high bit set) bytes, whereas ASCII * characters are single ascii bytes, thus we can use typical text processing. * * (One other interesting thing about utf-8 is that it is possible to look at * any random byte and determine if it is the first byte of a character as * versus a continuation byte). * * The base syntax of XML uses a few ASCII punctionation characters; any * characters that would appear in the payload data are rewritten using * sequences, e.g., & for ampersand(&) and < for left angle bracket (<). * Five such escapes total (more can be defined but that does not apply to our * case). Thus we can safely parse for angle brackets etc. * * XML describes tree structures of tagged data, with each element beginning * with an opening tag with * matching label. (There is also a self-closing tag